Skip to content

root: log host-metadata resolution through the command's logger - #7390

Merged
anton-107 merged 1 commit into
mainfrom
root-host-metadata-logger
Nov 9, 2026
Merged

anton-107 merged 1 commit into
mainfrom
root-host-metadata-logger

Conversation

@anton-107

@anton-107 anton-107 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Host-metadata resolution ignores the logger on the command's context. The SDK's Config.EnsureResolved calls the resolver with context.Background(), so libs/hostmetadata always logs through the process default logger. For most commands you can't tell, because root installs the command's logger as the process default too (cmd/root/logger.go). It does matter when code puts a different logger on the context. For example, work that runs in the background can use a logger that turns its warnings into debug lines. Today, when the host can't be reached, this warning still goes to the user's terminal in the middle of the command's output:

Warn: [hostmetadata] failed to fetch host metadata for <host>, will skip for 1m0s

The fix gives the config a resolver that brings along the command's logger:

 MustWorkspaceClient / MustAccountClient            (cmd/root/auth.go)
   liteswap.Apply(ctx, cfg)
+  bindHostMetadataLogger(ctx, cfg)                 # captures log.GetLogger(cmd ctx)
   ...
   cfg.EnsureResolved()                             # SDK: ctx = context.Background()
     resolveHostMetadata(sdkCtx)
-      DefaultHostMetadataResolverFactory(cfg)(sdkCtx, host)
+      cfg.HostMetadataResolver(sdkCtx, host)
+        resolve := factory(cfg) ?? cfg.DefaultHostMetadataResolver()
+        resolve(log.NewContext(sdkCtx, cmdLogger), host)
           log.Warnf("[hostmetadata] ...")
-            → process default logger
+            → command's logger
  • Only the logger moves into the resolution, not the whole command context. The command context carries user-agent segments and cancellation. Passing it would add those segments to the host-metadata request and would tie the request to the command's cancellation. The request keeps the SDK's context, as before.
  • The resolver is still built only when the SDK asks for it, through the registered factory. So the libs/hostmetadata disk cache and test overrides still apply, and configs with no host build no resolver. When no package registers a factory, it falls back to the SDK's own fetch, as the SDK does.
  • Existing commands print the same output, so there is no changelog entry.

Evidence

New test TestMustClientResolvesHostMetadataWithCommandLogger (workspace and account clients):

ctx = command ctx with logger L and an unrelated value K
MustWorkspaceClient(cmd) / MustAccountClient(cmd)
assert resolution saw logger L
assert resolution did not see K
  • Before (main's auth.go, run through go test -overlay):
    --- FAIL: TestMustClientResolvesHostMetadataWithCommandLogger/workspace
    --- FAIL: TestMustClientResolvesHostMetadataWithCommandLogger/account
        Not same: expected: &slog.Logger{handler:slog.discardHandler{}}
                  actual  : &slog.Logger{handler:(*slog.defaultHandler)(…)}
    
    After (this PR):
    --- PASS: TestMustClientResolvesHostMetadataWithCommandLogger/workspace
    --- PASS: TestMustClientResolvesHostMetadataWithCommandLogger/account
    --- PASS: TestMustWorkspaceClientWithoutHostBuildsNoHostMetadataResolver
    
  • TestMustWorkspaceClientWithoutHostBuildsNoHostMetadataResolver passes on main too. It is a guard: it fails if the CLI builds the resolver for every client attempt instead of only when the SDK asks for it.
  • go test ./acceptance -run 'TestAccept/(auth|cmd)/' passes with no changes to the expected-output files. That confirms the host-metadata request and the command output are unchanged.

Merge Danger

Door: two-way

One commit, with no state, config, or telemetry change. Revert to undo.

Blast Radius: wide

MustWorkspaceClient and MustAccountClient run for every workspace and account command, so every host-metadata resolution on these configs now goes through the new resolver. We expect no output change, because the command's logger is the process default logger. Commands that load a bundle build their client with b.WorkspaceClientE, which replaces cfg, so those commands keep the old behavior. Tested on Linux and macOS. The change has no platform-specific code.

This pull request and its description were written by Isaac.

The SDK's Config.EnsureResolved hands the host-metadata resolver
context.Background(), so libs/hostmetadata logs through the process
default logger and ignores any logger the command set on its context.

MustWorkspaceClient and MustAccountClient now wrap the per-config
resolver: it gets the SDK's context plus the command's logger and
nothing else, so the request keeps the SDK's user agent and is not tied
to the command's cancellation. The resolver is still built only when
the SDK asks for it, through the registered factory, so the disk cache
and test overrides still apply. When the factory returns nil, the
wrapper falls back to the SDK's own fetch, as the SDK does.

No output change for existing commands: their context logger is the
one root installs as the process default (cmd/root/logger.go). A
command that sets its own logger, such as a background auth resolution
that should log only at debug level, now keeps host-metadata warnings
out of its output.

Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

✅ Integration test report

Commit: 83016f4

Run: 42230533573

Env 🔄​flaky ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 271 15 3:53
✅​ aws-windows-latest-4core-16gb 273 13 2:49
✅​ azure linux-2core-8gb 270 15 3:50
🔄​ azure-windows-latest-4core-16gb 1 271 13 6:10
✅​ gcp linux-2core-8gb 271 15 3:55
✅​ gcp-windows-latest-4core-16gb 273 13 2:53
Test Name azure-windows-latest-4core-16gb
🔄​ TestSyncNestedSpacePlusAndHashAreEscapedSync 🔄​f
Top 3 slowest tests (at least 2 minutes):
duration env testname
2:48 gcp-windows-latest-4core-16gb TestAccept
2:44 aws-windows-latest-4core-16gb TestAccept
2:44 azure-windows-latest-4core-16gb TestAccept

@mihaimitrea-db
mihaimitrea-db requested review from mihaimitrea-db and removed request for tejaskochar-db November 6, 2026 14:35

@mihaimitrea-db mihaimitrea-db left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks good to me.

@anton-107
anton-107 added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit fbfa9cb Nov 9, 2026
28 checks passed
@anton-107
anton-107 deleted the root-host-metadata-logger branch November 9, 2026 12:17
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

❌ Integration test report

Commit: fbfa9cb

Run: 42615843881

Env ❌​FAIL ✅​pass 🙈​skip Time
❌​ aws linux-2core-8gb 37 1493 1263 180:07
❌​ aws-windows-latest-4core-16gb 35 1414 1290 188:59
❌​ azure linux-2core-8gb 37 1473 1271 170:15
❌​ azure-windows-latest-4core-16gb 35 1394 1298 187:56
❌​ gcp linux-2core-8gb 37 1480 1269 208:26
❌​ gcp-windows-latest-4core-16gb 38 1398 1296 218:25
40 interesting tests: 40 FAIL
Test Name aws linux-2core-8gb aws-windows-latest-4core-16gb azure linux-2core-8gb azure-windows-latest-4core-16gb gcp linux-2core-8gb gcp-windows-latest-4core-16gb
❌​ TestAccept ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/invariant/no_drift ✅​p ✅​p ❌​F ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/invariant/no_drift/DMS=true/INPUT_CONFIG=app.yml.tmpl/READPLAN= ✅​p ✅​p ❌​F ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/postgres_synced_tables/recreate ❌​F ✅​p ✅​p ✅​p ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS= ✅​p ✅​p ✅​p ✅​p ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS=true ❌​F ✅​p ✅​p ✅​p ✅​p ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=no/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=no/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=no/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=no/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=yes/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=yes/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=yes/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=no/DMS=/NBOOK=yes/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=no/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=no/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=no/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=no/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=yes/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=yes/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=yes/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/classic/DLT=yes/DMS=/NBOOK=yes/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=no/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=no/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=no/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=no/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=yes/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=yes/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=yes/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=yes/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=no/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=no/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=no/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=no/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=yes/PY=no/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=yes/PY=no/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=yes/PY=yes/READPLAN= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=yes/DMS=/NBOOK=yes/PY=yes/READPLAN=1 ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
Top 50 slowest tests (at least 2 minutes):
duration env testname
18:42 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
14:18 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
14:17 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
13:56 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
13:22 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
11:23 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
10:20 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
9:44 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:43 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
9:08 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:00 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
8:58 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:52 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
8:50 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
8:49 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:42 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:07 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:46 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
7:22 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:13 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
7:08 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS=true
7:04 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
6:54 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
6:50 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
6:36 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:33 gcp-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
6:30 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:29 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
6:28 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/inline_config/DMS=
6:20 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:16 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:16 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:15 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:14 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:01 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:43 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:43 gcp-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=
5:35 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
5:30 aws-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:23 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started-omitted/DMS=true
5:22 gcp linux-2core-8gb TestAccept/bundle/resources/apps/inline_config/DMS=
5:18 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
5:16 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
5:11 gcp linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:09 gcp linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=
4:59 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS=true
4:58 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started-omitted/DMS=
4:56 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS=
4:54 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
4:41 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started-omitted/DMS=true

anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`).

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)              # waits until start + 3 s, at most
+      finished → SetConfigUsed(cmd ctx)
+      not finished → no config, no upload
 root: telemetry.Upload
```

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`).

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)              # waits until start + 3 s, at most
+      finished → SetConfigUsed(cmd ctx)
+      not finished → no config, no upload
 root: telemetry.Upload
```

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`).

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)              # waits until start + 3 s, at most
+      finished → SetConfigUsed(cmd ctx)
+      not finished → no config, no upload
 root: telemetry.Upload
```

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`).

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)              # waits until start + 3 s, at most
+      finished → SetConfigUsed(cmd ctx)
+      not finished → no config, no upload
 root: telemetry.Upload
```

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`).

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)              # waits until start + 3 s, at most
+      finished → SetConfigUsed(cmd ctx)
+      not finished → no config, no upload
 root: telemetry.Upload
```

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 11, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107 added a commit that referenced this pull request Nov 12, 2026
`aitools install` resolved auth for telemetry in PreRunE, before doing any work, so an unreachable workspace blocked the install for as long as the SDK retried. It now resolves auth in the background while the install runs and waits for it at most 3 s after the command started. Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new `telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process may exit before it finishes, and the server rotates the single-use refresh token, so an abandoned refresh could lose the new one and log the user out. The new `root.SkipTokenRefresh` makes `MustWorkspaceClient` and `MustAccountClient` use `auth.CredentialsWithoutRefresh`, whose databricks-cli strategy passes the new `u2m.WithoutRefresh`: the cached token is used only while it is valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background warning such as `[hostmetadata] failed to fetch host metadata ...` shows only with `--debug` and does not interrupt the install's output or prompts. This relies on root's host-metadata resolver logging through the command's logger (#7390). A panic in the background resolution is recovered and logged at debug level; the install does not fail.

Changelog fragment: `.nextchanges/cli/aitools-install-telemetry-auth.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
tedjt pushed a commit to tedjt/cli that referenced this pull request Nov 12, 2026
Stacked on databricks#7467.

Stacked on: aitools: log install-hint telemetry and bound telemetry
uploads

## Summary

`aitools install` resolved auth for telemetry in PreRunE, before doing
any work, so an unreachable workspace blocked the install for as long as
the SDK retried. It now resolves auth in the background while the
install runs and waits for it at most 3 s after the command started.
Nothing is resolved when `DATABRICKS_CLI_DISABLE_TELEMETRY` is set (new
`telemetry.Disabled`), and a run that logged no event does not wait.

```diff
 aitools install
-  PreRunE: tryConfigureAuth
-    MustAnyClient(cmd)              # blocks while the SDK retries host metadata
-  RunE: install skills
+  PreRunE: startTelemetryAuth       # returns at once
+    if telemetry.Disabled → skip
+    go MustAnyClient(shadow cmd)    # shares cmd's flags (--profile), not its context
+                                    # SkipTokenRefresh: never refreshes an OAuth token
+  RunE wrapper
+    runE: install skills            # runs while auth resolves
+    pending.apply(cmd)
+      no event logged → return
+      wait until start + 3 s, at most
+      succeeded → SetConfigUsed(cmd ctx)
+      failed or not finished → no config, no upload
 root: telemetry.Upload
```

The background resolution never refreshes an OAuth token. The process
may exit before it finishes, and the server rotates the single-use
refresh token, so an abandoned refresh could lose the new one and log
the user out. The new `root.SkipTokenRefresh` makes
`MustWorkspaceClient` and `MustAccountClient` use
`auth.CredentialsWithoutRefresh`, whose `databricks-cli` strategy passes
the new `u2m.WithoutRefresh`: the cached token is used only while it is
valid, and an expired one fails auth, so nothing is uploaded.

The shadow command logs through `debugOnlyHandler`, so a background
warning such as `[hostmetadata] failed to fetch host metadata ...` shows
only with `--debug` and does not interrupt the install's output or
prompts. This relies on root's host-metadata resolver logging through
the command's logger (databricks#7390). A panic in the background resolution is
recovered and logged at debug level; the install does not fail.

Alternatives considered: resolving auth inside `telemetry.Upload` would
serialize it after the install at exit and drop `MustAnyClient`'s
account-client fallback and `--profile` handling. Bounding a synchronous
PreRunE with the SDK's `HTTPTimeoutSeconds`/`RetryTimeoutSeconds` would
still add that wait to every install before any work starts and keep the
hostmetadata warning in its output; resolving in the background overlaps
auth with the install instead.

The deprecated `experimental aitools skills install` alias runs its own
install command, so it now copies that command's context back, and the
config resolved for telemetry reaches root's upload (before, the alias
never uploaded).

Changelog fragment:
`.nextchanges/cli/aitools-install-telemetry-auth.md`.

## Evidence

`aitools install --skills-only --scope=global --agents=claude-code`,
skills served locally, fresh HOME and cache per run; before = parent
branch binary, after = this branch's head:

```text
DATABRICKS_HOST=https://localhost:1   (connection refused; the SDK retries)
  before: no output, killed by `timeout 120` after 120.0 s
  after:  "Installed 1 skill.", exit 0 in 3.0 s (two runs)
          with DATABRICKS_CLI_DISABLE_TELEMETRY=1: 0.03 s

DATABRICKS_HOST=https://unreachable.test   (fails fast)
  before: "Warn: [hostmetadata] failed to fetch host metadata for https://unreachable.test, will skip for 1m0s"
          printed above the install output
  after:  no hostmetadata line; exit 0 in 0.1 s
```

Tests at head, all pass locally on Linux and in CI on Linux, macOS and
Windows:

```text
cmd/aitools TestTelemetryAuth*:
  ResolvedConfigReachesTheUpload            config reaches the upload, --profile seen
  FailedResolutionUploadsNothing            a failed auth leaves no config
  NoEventDoesNotWait                        no event logged: no wait, no config
  SlowResolutionIsBounded                   command finishes < 2 s, no config
  FinishedResolutionSurvivesAnExpiredBudget budget 0, ×50
  PanicDoesNotFailTheCommand
  SkippedWhenTelemetryDisabled              resolver never called
  LogsStayOutOfNormalOutput                 background Warn/Error hidden; own Warn still prints
  LogsShowAtDebug                           background lines shown as Debug
  RunEWithoutPreRun
acceptance/cmd/aitools/install-host-metadata
  no hostmetadata line normally; "Debug: [hostmetadata] ..." with --debug
acceptance/cmd/aitools/telemetry/install
  PAT and databricks-cli with a valid cached token: the install event is uploaded
  databricks-cli with an expired cached token: no /oidc/v1/token request, no upload, cache unchanged
libs/auth/u2m TestToken_WithoutRefresh       valid and expiring-soon tokens returned, expired fails; no request
libs/auth TestCredentialChainWithoutRefresh, TestCLICredentialsConfigure_NoRefresh
cmd/root TestSkipTokenRefresh
cmd/aitools TestLegacySkillsInstallKeepsTelemetryAuthConfig   the alias's context gets the resolved config
```

## Merge Danger

**Door:** two-way

No telemetry schema change, persisted state or config. A revert restores
the blocking PreRunE.

**Blast Radius:** `aitools install`. `root.SkipTokenRefresh` is new and
only `aitools install` sets it; without it `MustWorkspaceClient`,
`MustAccountClient` and `PersistentAuth.Token` behave as before.

- **Telemetry loss:** an install uploads no event when its auth has not
resolved by the later of the end of the install and 3 s after the
command started.
- **Slow but reachable workspace:** the upload still follows a finished
resolution with its own timeout (`DATABRICKS_CLI_TELEMETRY_TIMEOUT`,
default 3 s), so after a short install the end can take up to about 6 s.
No run waits longer for auth than it did before.
- **Unreachable workspace:** every `aitools install` run that logged an
event lasts at least 3 s. Runs with nothing to upload (e.g. a
flag-validation error) do not wait. The process exits while the SDK is
still retrying, before libs/hostmetadata writes its negative-cache
entry, so the next run probes the host again.
- **Expired U2M token, no event:** an install whose cached
`databricks-cli` access token has expired uploads no install event,
because the background resolution does not refresh it. This is on
purpose: a refresh that the exit can cut short is what can strand a
rotated refresh token. The next command that authenticates refreshes the
token as usual. Before, the install refreshed it in PreRunE and uploaded
the event.
- **Expired U2M token without an `auth_type`:** with no `auth_type`
(e.g. `DATABRICKS_HOST` from the environment, or a hand-written
profile), the SDK chain skips a strategy whose dry run fails, so an
expired token moves on to the strategies after `databricks-cli`
(metadata service, OIDC, Azure, Google). If one succeeds, e.g. `az`
logged in on an Azure host, the event is uploaded with that identity.
The default chain would have refreshed and stopped at `databricks-cli`.
- **Warnings that can still print:** with the cache off or unusable and
the `databricks-cli` strategy, `libs/auth` `ToOAuthArgument` looks up
host metadata outside the command's logger; SDK warnings logged on its
own `context.Background()` (e.g. missing `oidc_endpoint`) are not
filtered either.
- **Platforms:** CI runs the tests above on Linux, macOS and Windows;
the timings under Evidence were measured on Linux only.

This pull request and its description were written by Isaac.

Co-authored-by: Isaac <no-reply@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants