Skip to content

Reject existing profile names after "Create a new profile" - #7196

Merged
maksim-zuev-db merged 18 commits into
mainfrom
login-new-profile-existing-name
Oct 30, 2026
Merged

maksim-zuev-db merged 18 commits into
mainfrom
login-new-profile-existing-name

Conversation

@maksim-zuev-db

@maksim-zuev-db maksim-zuev-db commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Changes

After "Create a new profile" in the databricks auth login and databricks auth token pickers, the profile name prompt rejects the name of any existing config section with profile X already exists. Choose a different name. This includes the profiles the device code picker hides and sections without a host. A taken DEFAULT is no longer suggested: the prompt then has no default and requires a name.

All profile name prompts now also trim spaces and pasted newlines, and reject the reserved __settings__ name before the OAuth flow rather than after it.

Saving a profile now rejects names that contain line breaks, for example a pasted foo\nbar. go-ini writes section headers unescaped, so such a name produced a config file that no longer parses. This applies to every command that saves a profile or sets the default profile, such as auth login, auth token, auth switch and configure.

The auth token inline login can no longer target an existing profile, so its code for keeping that profile's scopes, client ID and resources is removed.

Why

Follow-up to a review comment on #7179. With no host known yet, the prompt suggested DEFAULT, and pressing Enter logged in to the existing DEFAULT profile and reused its host. With --use-device-code, if that was an account profile the picker hid, login failed with --use-device-code is only supported for workspace hosts.

A generated name like DEFAULT-1 is not offered instead: the host is not known yet, so the name would not say which workspace the profile is for.

Tests

  • TestPromptForProfile* cover the prompt: free and taken defaults, and empty, taken, reserved and pasted names.
  • TestLoginCreateNewProfileRejectsTakenName, TestLoginDeviceCodeCreateNewProfileRejectsHiddenProfileName and TestResolveNoArgsTokenCreateNewProfileRejectsTakenName cover the browser login, device code login and auth token flows. Like the other picker tests, they are skipped on Windows.
  • TestGetSectionNames covers sections without a host and empty sections.
  • TestValidateProfileName and TestSaveToProfile_SquareBracketsRoundTrip cover line breaks, and check that names with square brackets are still accepted and parse back.
  • cmd/auth unit tests and TestAccept/cmd/auth pass.

This pull request and its description were written by Isaac.

maksim-zuev-db and others added 5 commits October 27, 2026 14:01
After "Create a new profile", the profile name prompt suggests DEFAULT,
and pressing Enter logged in to an existing DEFAULT profile and reused
its host. For device code login that profile may be an account profile
the picker hid, which then failed with an error about a profile the user
did not pick. The name prompt now rejects the names of all existing
profiles in this case.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
The name prompt suggested DEFAULT even when a DEFAULT profile already
existed, and then rejected it on Enter. A taken suggestion is now left
out, so the prompt asks for a name without a default and rejects an
empty answer.

Co-authored-by: Isaac <no-reply@databricks.com>
auth token offers the same "Create a new profile" picker entry, and its
name prompt could still log in to an existing profile. It now rejects
existing profile names too.

The error no longer tells the user to select the profile from the list,
because device code login hides some profiles from the picker. auth login
now loads the profiles once and filters the picker list from them.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@maksim-zuev-db
maksim-zuev-db marked this pull request as ready for review October 27, 2026 14:56
@maksim-zuev-db
maksim-zuev-db requested review from a team as code owners October 27, 2026 14:56
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 73e6af8

Run: 41086466059

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 281 15 4:46
✅​ aws-windows-latest-4core-16gb 283 13 3:33
✅​ azure linux-2core-8gb 280 15 4:53
✅​ azure-windows-latest-4core-16gb 282 13 3:39
✅​ gcp linux-2core-8gb 281 15 4:51
✅​ gcp-windows-latest-4core-16gb 283 13 3:35
Top 6 slowest tests (at least 2 minutes):
duration env testname
3:33 azure-windows-latest-4core-16gb TestAccept
3:28 gcp-windows-latest-4core-16gb TestAccept
3:26 aws-windows-latest-4core-16gb TestAccept
2:56 azure linux-2core-8gb TestAccept
2:52 gcp linux-2core-8gb TestAccept
2:44 aws linux-2core-8gb TestAccept

@simonfaltum simonfaltum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice fix. A few things below.

The main one is that Validate checks the raw input while RunPrompt returns it with trailing \r\n stripped. So a pasted name with a trailing newline gets past the taken-name check, and a pasted blank line gets past the "required" check. I think trimming at the top of Validate fixes both (details inline).

One coverage gap that doesn't fit on a single line: there's no test for "Create a new profile" in the regular browser auth login flow, which is where the original bug was. The promptForProfile unit tests and the device-code/auth token termtests wouldn't catch it if the takenProfileNames = allProfiles.Names() wiring broke for that path. A termtest similar to TestResolveNoArgsTokenCreateNewProfileRejectsTakenName would cover it.

Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login_device_code_test.go Resolved
Comment thread cmd/auth/token_test.go Resolved
Comment thread cmd/auth/token.go Resolved
maksim-zuev-db and others added 9 commits October 28, 2026 09:19
The prompt validated the raw buffer, but RunPrompt trims the trailing
newline only after validation. A pasted "taken\n" passed the taken-name
check, and a pasted blank line or spaces passed the required check.

Co-authored-by: Isaac <no-reply@databricks.com>
SaveToProfile rejects it, but only after the user has gone through the
OAuth flow.

Co-authored-by: Isaac <no-reply@databricks.com>
LoadProfiles skips sections without a host, so a name like [foo] with
only cluster_id was accepted as a new profile, and SaveToProfile merged
the login into that section, keeping its old keys. Read the taken names
from the config file sections instead.

Co-authored-by: Isaac <no-reply@databricks.com>
The taken profile names now come from the config file sections, so
login no longer needs all profiles to filter the picker list from.

Co-authored-by: Isaac <no-reply@databricks.com>
The test only checked that an empty answer is rejected, so it would
still pass if the hidden DEFAULT profile name were only dropped from
the suggestion.

Co-authored-by: Isaac <no-reply@databricks.com>
… picker test

env.Get falls back to the OS environment, so with either one exported
resolveNoArgsToken returned before the picker and the test timed out.

Co-authored-by: Isaac <no-reply@databricks.com>
The profile name prompt rejects the names of all existing sections, so
runInlineLogin always creates a new profile. Looking up the existing
profile, preserving its scopes, resources and client ID, and clearing
its keys could no longer do anything.

Co-authored-by: Isaac <no-reply@databricks.com>
This is the flow of the original bug. The other tests cover the prompt
itself, device code login and auth token, but not this wiring.

Co-authored-by: Isaac <no-reply@databricks.com>
TestLoginCreateNewProfileRejectsTakenName copied the setup of
startDeviceCodeLogin. Move that setup into startLogin, which
startDeviceCodeLogin now calls.

Co-authored-by: Isaac <no-reply@databricks.com>

@simonfaltum simonfaltum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me, and all my earlier comments are addressed. A few non-blocking things inline. Most are bugs that already exist on main but get past the new check, so follow-ups are fine.

One more that has no changed line to anchor on: in runInlineLogin (token.go L461) the timeout ctx is created after NewPersistentAuth, and Challenge uses the ctx stored at construction, so defaultTimeout never applies. Closing the browser tab leaves auth token waiting until Ctrl+C. Moving those two lines above NewPersistentAuth, as login.go does, fixes it. Unrelated to this change, so a follow-up is fine.

Comment thread libs/databrickscfg/ops.go
func SaveToProfile(ctx context.Context, cfg *config.Config, clearKeys ...string) error {
if cfg.Profile == databricksSettingsSection {
return fmt.Errorf("profile name %q is reserved for internal use", databricksSettingsSection)
if err := ValidateProfileName(cfg.Profile); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[nice to have] Further down (L494), section.Key(attr.Name) falls back to the parent section's key when the section doesn't have it, because go-ini treats team.prod as a child of [team]. So with [team] holding host and token, typing team.prod after "Create a new profile" passes the taken-name check. After OAuth, the save rewrites [team]'s host and auth_type, keeps its token, and leaves an empty [team.prod].

Using section.NewKey(attr.Name, attr.GetString(cfg)) only looks at the section's own keys, which fixes it (L312 in SaveResourcesToProfile has the same pattern). Fine as a follow-up since it predates this PR.

Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/login.go Resolved
Comment thread cmd/auth/token.go Resolved
maksim-zuev-db and others added 3 commits October 30, 2026 10:41
The taken profile names are read before the OAuth wait. If a section with
the chosen name appears in the meantime, the save merges into it, so clear
the same keys as the auth login saves do.

Co-authored-by: Isaac <no-reply@databricks.com>
A section without a host is hidden from the picker but rejected at the
name prompt, so the error now says to rerun the command with --profile.
The prompt is shared by auth login and auth token, so the hint does not
name a specific command.

Co-authored-by: Isaac <no-reply@databricks.com>
go-ini writes section headers unescaped, so a pasted "foo\nbar" or a
name like "[foo]" produced a config file that no longer parses.

Co-authored-by: Isaac <no-reply@databricks.com>
go-ini reads the section name up to the last ']' on the line, so names
like "[foo]" or "a[b" round-trip. Rejecting them broke existing profiles
with such names in auth switch, configure and auth login --profile.

Co-authored-by: Isaac <no-reply@databricks.com>
@maksim-zuev-db
maksim-zuev-db added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit a7e6114 Oct 30, 2026
28 checks passed
@maksim-zuev-db
maksim-zuev-db deleted the login-new-profile-existing-name branch October 30, 2026 16:36
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: a7e6114

Run: 41091489337

Env 🔄​flaky ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 1504 1226 136:37
🔄​ aws-windows-latest-4core-16gb 3 1426 1251 160:01
✅​ azure linux-2core-8gb 1351 1278 109:42
🔄​ azure-windows-latest-4core-16gb 3 1273 1303 126:56
✅​ gcp linux-2core-8gb 1338 1283 115:45
✅​ gcp-windows-latest-4core-16gb 1263 1308 131:18
Test Name aws-windows-latest-4core-16gb azure-windows-latest-4core-16gb
🔄​ TestAccept 🔄​f 🔄​f
🔄​ TestAccept/bundle/invariant/destroy_idempotent ✅​p 🔄​f
🔄​ TestAccept/bundle/invariant/destroy_idempotent/DMS=/INPUT_CONFIG=feature.yml.tmpl/READPLAN=1 ✅​p 🔄​f
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate 🔄​f 🙈​s
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS= 🔄​f
Top 50 slowest tests (at least 2 minutes):
duration env testname
13:21 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
12:11 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
11:07 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
10:45 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
10:29 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
10:15 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
9:35 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:27 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:19 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:17 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:51 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:38 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:28 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
8:14 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
8:14 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
8:03 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:59 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
7:54 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:49 azure linux-2core-8gb TestAccept/bundle/invariant/no_drift/DMS=/INPUT_CONFIG=cluster_libraries.yml.tmpl/READPLAN=
7:34 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:31 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:26 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
7:18 aws linux-2core-8gb TestAccept/bundle/resources/clusters/resize-terminated-fallback/DMS=
7:12 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:12 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/resize-terminated-fallback/DMS=true
7:12 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
7:01 aws linux-2core-8gb TestAccept/bundle/resources/clusters/resize-terminated-fallback/DMS=true
6:55 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:53 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/update-after-create/DMS=true
6:51 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/resize-terminated-fallback/DMS=
6:46 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
6:43 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
6:41 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:30 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:29 aws linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:29 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:24 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:22 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
6:22 aws linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
6:19 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:16 azure-windows-latest-4core-16gb TestAccept/bundle/invariant/no_drift/DMS=/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
6:15 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:06 azure linux-2core-8gb TestAccept/bundle/invariant/no_drift/DMS=/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
6:04 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
5:43 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:30 aws linux-2core-8gb TestAccept/bundle/invariant/no_drift/DMS=true/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
5:24 azure linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
5:19 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS=true
5:14 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
5:13 aws linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=

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