Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

* `databricks auth describe` now reports where U2M (`databricks-cli`) tokens are stored: `plaintext` (`~/.databricks/token-cache.json`) or `secure` (OS keyring), and the source of the choice (env var, config setting, or default).
* Marked the default profile in the interactive pickers shown by `databricks auth switch`, `databricks auth logout`, `databricks auth token`, and `databricks auth login`, and moved it to the top of the list. `databricks auth login` and `databricks auth logout` now offer the same selectors as `databricks auth token` and `databricks auth switch` respectively.
* `[__settings__].default_profile` is now honored by `databricks api`, `databricks auth token`, and bundle commands when no `--profile` flag and no `DATABRICKS_CONFIG_PROFILE` env var is set. For bundle commands, `default_profile` only applies when the bundle does not pin its own `workspace.host`.

### Bundles

Expand Down
5 changes: 5 additions & 0 deletions acceptance/auth/bundle_default_profile/databricks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bundle:
name: test-default-profile

# No workspace.host on purpose: this is the surface where
# [__settings__].default_profile should be applied.
3 changes: 3 additions & 0 deletions acceptance/auth/bundle_default_profile/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions acceptance/auth/bundle_default_profile/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

=== Bundle without workspace.host: default_profile is honored

>>> [CLI] bundle validate -o json
{
"host": null,
"profile": "default-target"
}

=== --profile overrides default_profile (negative case)

>>> errcode [CLI] bundle validate -p other -o json
Warn: [hostmetadata] failed to fetch host metadata for https://other.test, will skip for 1m0s
Error: Get "https://other.test/api/2.0/preview/scim/v2/Me": (redacted)


Exit code: 1
{
"host": null,
"profile": "other"
}

=== Bundle with workspace.host: default_profile is NOT applied

>>> errcode [CLI] bundle validate -o json
Error: failed during request visitor: default auth: cannot configure default credentials, please check https://docs.databricks.com/en/dev-tools/auth.html#databricks-client-unified-authentication to configure credentials for your preferred authentication method. Config: host=[DATABRICKS_URL], workspace_id=[NUMID], databricks_cli_path=[CLI]. Env: DATABRICKS_CLI_PATH


Exit code: 1
{
"host": "[DATABRICKS_URL]",
"profile": null
}
43 changes: 43 additions & 0 deletions acceptance/auth/bundle_default_profile/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
sethome "./home"

# Save the test server host so we can pin a bundle-with-host variant below.
host_value="$DATABRICKS_HOST"

cat > "./home/.databrickscfg" <<EOF
[default-target]
host = $DATABRICKS_HOST
token = $DATABRICKS_TOKEN

[other]
host = https://other.test
token = other-token

[__settings__]
default_profile = default-target
EOF

unset DATABRICKS_HOST
unset DATABRICKS_TOKEN
unset DATABRICKS_CONFIG_PROFILE

title "Bundle without workspace.host: default_profile is honored\n"
trace $CLI bundle validate -o json | jq '{host: .workspace.host, profile: .workspace.profile}'

title "--profile overrides default_profile (negative case)\n"
trace errcode $CLI bundle validate -p other -o json | jq '{host: .workspace.host, profile: .workspace.profile}'

# Switch to a bundle that pins workspace.host. The default_profile guard in
# configureProfile must NOT apply default_profile here, because that would
# silently route the user to a profile pointing at a different host than the
# bundle declares.
mkdir -p ./bundle-with-host
cat > ./bundle-with-host/databricks.yml <<EOF
bundle:
name: bundle-with-host

workspace:
host: $host_value
EOF

title "Bundle with workspace.host: default_profile is NOT applied\n"
(cd ./bundle-with-host && trace errcode $CLI bundle validate -o json | jq '{host: .workspace.host, profile: .workspace.profile}')
11 changes: 11 additions & 0 deletions acceptance/auth/bundle_default_profile/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Ignore = [
"home",
".databricks",
"bundle-with-host",
]

# Negative case: -p other tries to reach a non-existing host. Redact the
# OS-/network-dependent suffix so the test is stable across runners.
[[Repls]]
Old = 'Get "https://other.test/api/2.0/preview/scim/v2/Me": .*'
New = 'Get "https://other.test/api/2.0/preview/scim/v2/Me": (redacted)'
3 changes: 3 additions & 0 deletions acceptance/cmd/api/default-profile/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions acceptance/cmd/api/default-profile/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

=== default_profile is used when no --profile flag and no DATABRICKS_CONFIG_PROFILE

>>> [CLI] api get /api/2.0/clusters/list
{}

>>> print_requests.py --get //api/2.0/clusters/list
{
"headers": {
"Authorization": [
"Bearer [DATABRICKS_TOKEN]"
],
"User-Agent": [
"cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/api_get cmd-exec-id/[UUID] interactive/none auth/pat"
],
"X-Databricks-Org-Id": [
"[NUMID]"
]
},
"method": "GET",
"path": "/api/2.0/clusters/list"
}

=== --profile overrides default_profile (negative case)
Warn: [hostmetadata] failed to fetch host metadata for https://other.test, will skip for 1m0s
Error: Get "https://other.test/api/2.0/clusters/list": (redacted)

Exit code: 1
28 changes: 28 additions & 0 deletions acceptance/cmd/api/default-profile/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
sethome "./home"

# Two profiles plus an explicit default_profile pointing at the test server.
# The 'other' profile points at an RFC 2606 reserved host so we can assert
# that --profile overrides default_profile without making a real request.
cat > "./home/.databrickscfg" <<EOF
[default-target]
host = $DATABRICKS_HOST
token = $DATABRICKS_TOKEN

[other]
host = https://other.test
token = other-token

[__settings__]
default_profile = default-target
EOF

unset DATABRICKS_HOST
unset DATABRICKS_TOKEN
unset DATABRICKS_CONFIG_PROFILE

title "default_profile is used when no --profile flag and no DATABRICKS_CONFIG_PROFILE\n"
MSYS_NO_PATHCONV=1 trace $CLI api get /api/2.0/clusters/list
trace print_requests.py --get //api/2.0/clusters/list | contains.py "X-Databricks-Org-Id"

title "--profile overrides default_profile (negative case)\n"
MSYS_NO_PATHCONV=1 errcode $CLI api get /api/2.0/clusters/list -p other 2>&1 | contains.py "other.test"
10 changes: 10 additions & 0 deletions acceptance/cmd/api/default-profile/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Ignore = [
"home",
]

# Redact the OS- and network-dependent suffix on the failed lookup so the
# negative case (--profile overrides default_profile) is stable across
# runners. We still assert the requested host appears in output.
[[Repls]]
Old = 'Get "https://other.test/api/2.0/clusters/list": .*'
New = 'Get "https://other.test/api/2.0/clusters/list": (redacted)'
3 changes: 3 additions & 0 deletions acceptance/cmd/auth/token/default-profile/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions acceptance/cmd/auth/token/default-profile/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

=== default_profile is honored when no args, --profile, or env var

>>> errcode [CLI] auth token
Warn: [hostmetadata] failed to fetch host metadata for https://myworkspace.cloud.databricks.com, will skip for 1m0s
Error: cache: databricks OAuth is not configured for this host. Try logging in again with `databricks auth login --profile myprofile` before retrying. If this fails, please report this issue to the Databricks CLI maintainers at https://github.com/databricks/cli/issues/new

Exit code: 1

=== default_profile pointing at a missing profile falls through to picker

>>> errcode [CLI] auth token
Error: no profile specified. Use --profile <name> to specify which profile to use

Exit code: 1
34 changes: 34 additions & 0 deletions acceptance/cmd/auth/token/default-profile/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
sethome "./home"

unset DATABRICKS_HOST
unset DATABRICKS_TOKEN
unset DATABRICKS_CONFIG_PROFILE

# default_profile points at "myprofile". Without it, `auth token` would fall
# through to the non-interactive error "no profile specified".
cat > "./home/.databrickscfg" <<'ENDCFG'
[myprofile]
host = https://myworkspace.cloud.databricks.com
auth_type = databricks-cli

[other]
host = https://other.example
auth_type = databricks-cli

[__settings__]
default_profile = myprofile
ENDCFG

title "default_profile is honored when no args, --profile, or env var\n"
trace errcode $CLI auth token

title "default_profile pointing at a missing profile falls through to picker\n"
cat > "./home/.databrickscfg" <<'ENDCFG'
[myprofile]
host = https://myworkspace.cloud.databricks.com
auth_type = databricks-cli

[__settings__]
default_profile = does-not-exist
ENDCFG
trace errcode $CLI auth token
17 changes: 14 additions & 3 deletions cmd/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/auth"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/flags"
"github.com/databricks/databricks-sdk-go/client"
"github.com/databricks/databricks-sdk-go/config"
Expand Down Expand Up @@ -77,11 +79,20 @@ func makeCommand(method string) *cobra.Command {

cfg := &config.Config{}

// command-line flag can specify the profile in use
profileFlag := cmd.Flag("profile")
if profileFlag != nil {
// Resolve the profile mirroring MustWorkspaceClient precedence:
// 1. --profile flag, 2. DATABRICKS_CONFIG_PROFILE env var (the SDK
// also reads it, but setting cfg.Profile here keeps any error
// messages we render referring to the same name), 3.
// [__settings__].default_profile in the config file.
if profileFlag := cmd.Flag("profile"); profileFlag != nil {
cfg.Profile = profileFlag.Value.String()
}
if cfg.Profile == "" {
cfg.Profile = env.Get(cmd.Context(), "DATABRICKS_CONFIG_PROFILE")
}
if cfg.Profile == "" {
cfg.Profile = databrickscfg.ResolveDefaultProfile(cmd.Context())
}

api, err := client.New(cfg)
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions cmd/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/databricks/cli/libs/databrickscfg/profile"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/flags"
"github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go/config"
"github.com/databricks/databricks-sdk-go/credentials/u2m"
"github.com/databricks/databricks-sdk-go/credentials/u2m/cache"
Expand Down Expand Up @@ -327,6 +328,18 @@ func resolveNoArgsToken(ctx context.Context, profiler profile.Profiler, authArgs
return envProfile, p, nil
}

// Step 2.5: Try [__settings__].default_profile from the config file.
// default_profile is advisory: if it points at a profile that no longer
// exists, fall through to the interactive picker rather than erroring.
if defaultProfile := databrickscfg.ResolveDefaultProfile(ctx); defaultProfile != "" {
p, err := loadProfileByName(ctx, defaultProfile, profiler)
if err != nil {
log.Warnf(ctx, "default_profile %q not loadable: %v", defaultProfile, err)
} else if p != nil {
return defaultProfile, p, nil
}
}

// Step 3: No env vars resolved. Load all profiles for interactive selection
// or non-interactive error.
allProfiles, err := profiler.LoadProfiles(ctx, profile.MatchAllProfiles)
Expand Down
11 changes: 2 additions & 9 deletions cmd/root/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/databrickscfg/profile"
envlib "github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/log"
"github.com/databricks/cli/libs/logdiag"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/config"
Expand Down Expand Up @@ -299,14 +298,8 @@ func resolveDefaultProfile(ctx context.Context, cfg *config.Config) {
if cfg.Profile != "" || envlib.Get(ctx, "DATABRICKS_CONFIG_PROFILE") != "" {
return
}
configFilePath := envlib.Get(ctx, "DATABRICKS_CONFIG_FILE")
resolvedProfile, err := databrickscfg.GetConfiguredDefaultProfile(ctx, configFilePath)
if err != nil {
log.Warnf(ctx, "Failed to load default profile: %v", err)
return
}
if resolvedProfile != "" {
cfg.Profile = resolvedProfile
if resolved := databrickscfg.ResolveDefaultProfile(ctx); resolved != "" {
cfg.Profile = resolved
}
}

Expand Down
10 changes: 10 additions & 0 deletions cmd/root/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ func getProfile(cmd *cobra.Command) (value string) {
// configureProfile applies the profile flag to the bundle.
func configureProfile(cmd *cobra.Command, b *bundle.Bundle) {
profile := getProfile(cmd)

// Fall back to [__settings__].default_profile only when the bundle does
// not pin its own host. If the bundle declares workspace.host, applying
// default_profile here could route the user to a profile that points at
// a different host than the bundle expects — let the SDK resolve auth
// from the host instead.
if profile == "" && b.Config.Workspace.Host == "" && b.Config.Workspace.Profile == "" {
profile = databrickscfg.ResolveDefaultProfile(cmd.Context())
}

if profile == "" {
return
}
Expand Down
Loading
Loading