Commit d00476d
authored
Preserve profile fields on re-login instead of wiping all keys (#4597)
## Why
`SaveToProfile` deleted **every key** in a `.databrickscfg` profile
section before writing, then only wrote back the small subset of fields
the caller explicitly set. This meant every `databricks auth login` or
`databricks configure` silently destroyed fields like `cluster_id`,
`warehouse_id`, `scopes`, `azure_environment`, and any custom keys the
user had added to their profile.
This is especially problematic as profiles carry more explicit fields in
the host-agnostic auth work (`workspace_id`, `account_id`,
`azure_environment`). Users shouldn't have to re-specify everything
every time they re-login.
## Changes
### Core: `SaveToProfile` merge semantics (`libs/databrickscfg/ops.go`)
**Before:** Delete all keys in the section, then write only non-zero
fields from the new config.
**After:** Existing keys not mentioned in the new config are preserved.
Non-zero fields from the new config overwrite existing values. A new
`clearKeys ...string` variadic parameter lets callers explicitly remove
specific keys.
### `auth login` (`cmd/auth/login.go`)
**Before:** Re-login wiped everything. `cluster_id` and
`serverless_compute_id` were manually read back from the existing
profile in the default case (no
`--configure-cluster`/`--configure-serverless`), but `warehouse_id`,
`azure_environment`, custom keys, etc. were always lost.
**After:**
- All non-auth fields are preserved automatically via merge semantics
(no manual read-back needed).
- Incompatible auth credentials (PAT token, basic auth, M2M client
secrets, Azure/GCP credentials, metadata service URL, OIDC tokens) are
explicitly cleared when switching to OAuth.
- `--configure-cluster` explicitly clears `serverless_compute_id` (and
vice versa) for mutual exclusion.
- `experimental_is_unified_host` is explicitly cleared when `false`
(since `false` is a zero value and would otherwise be skipped by the
merge, leaving a stale `true` from a previous login).
- **Scopes are preserved:** when `--scopes` is not passed, existing
scopes from the profile are read back and used for the OAuth challenge.
This means the minted token matches the profile's scope configuration.
Previously, scopes were always wiped and the default `all-apis` was
used.
### Inline login in `auth token` (`cmd/auth/token.go`)
**Before:** `runInlineLogin` (the "create new profile" path in the
interactive `auth token` flow) saved a minimal set of fields and wiped
everything else. Did not handle scopes or `experimental_is_unified_host`
clearing.
**After:**
- Same auth credential clearing as `auth login`.
- `experimental_is_unified_host` explicitly cleared when `false`.
- Existing profile scopes are read back and used for the OAuth challenge
(same as `auth login`).
### `databricks configure` (`cmd/configure/configure.go`)
**Before:** PAT configure wiped all keys including `auth_type`,
`scopes`, `azure_environment`, etc. — which was correct for
`auth_type`/`scopes` but destroyed useful non-auth fields.
**After:**
- Non-auth fields (`cluster_id`, `warehouse_id`, `azure_environment`,
`account_id`, `workspace_id`, custom keys) are preserved.
- OAuth metadata (`auth_type`, `scopes`, `databricks_cli_path`) is
explicitly cleared — a PAT profile shouldn't keep OAuth artifacts.
- All non-PAT auth credentials (basic auth, M2M, Azure, GCP, OIDC,
metadata service) are explicitly cleared to prevent multi-auth
conflicts.
- `experimental_is_unified_host` is always cleared (PAT profiles don't
use unified hosts).
- `serverless_compute_id` is cleared when `cluster_id` is set — whether
via `--configure-cluster` flag **or** via `DATABRICKS_CLUSTER_ID` env
var (previously only the flag was checked).
### Profile struct (`libs/databrickscfg/profile/`)
- Added `Scopes` field to `profile.Profile` struct and read it from the
INI file in `LoadProfiles`. This allows `auth login` and `auth token` to
read existing scopes back from the profile.
## Test plan
**Unit tests (`libs/databrickscfg/ops_test.go`):**
- [x] `TestSaveToProfile_MergePreservesExistingKeys` — token survives
when only auth_type is written
- [x] `TestSaveToProfile_ClearKeysRemovesSpecifiedKeys` — token and
cluster_id cleared, serverless_compute_id added
- [x] `TestSaveToProfile_OverwritesExistingValues` — host updated from
old to new
- [x] `TestSaveToProfile_ClearKeysOnNonExistentKeyIsNoop` — clearing
nonexistent keys doesn't error
**Unit tests (`cmd/configure/configure_test.go`):**
- [x] `TestConfigureClearsOAuthAuthType` — PAT configure clears
`auth_type` and `scopes` from a previously OAuth-configured profile
- [x] `TestConfigureClearsUnifiedHostMetadata` — PAT configure clears
`experimental_is_unified_host` while preserving
`account_id`/`workspace_id`
- [x] `TestConfigureClearsServerlessWhenClusterFromEnv` —
`serverless_compute_id` cleared when `DATABRICKS_CLUSTER_ID` env var
provides cluster_id
**Acceptance test (`acceptance/cmd/auth/login/preserve-fields/`):**
- [x] Profile with `cluster_id`, `warehouse_id`, `azure_environment`,
and `custom_key` → all four survive `auth login` re-login without
`--configure-cluster`/`--configure-serverless`
**Existing tests:**
- [x] All 7 `auth login` acceptance tests pass (including
`configure-serverless` which verifies `cluster_id` is still cleared)
- [x] All `cmd/auth/` unit tests pass
- [x] All `cmd/configure/` unit tests pass
- [x] `make checks` clean
- [x] `make lintfull` clean (0 issues)
🤖 Generated with [Claude Code](https://claude.com/claude-code)1 parent db6b7af commit d00476d
19 files changed
Lines changed: 349 additions & 90 deletions
File tree
- acceptance/cmd
- auth/login/preserve-fields
- configure
- clears-oauth-on-pat
- clears-serverless-when-cluster-from-env
- cmd
- auth
- configure
- libs/databrickscfg
- profile
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
0 commit comments