diff --git a/bundle/config/mutator/initialize_urls.go b/bundle/config/mutator/initialize_urls.go index 3247a70f71d..6e32ee24a28 100644 --- a/bundle/config/mutator/initialize_urls.go +++ b/bundle/config/mutator/initialize_urls.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/diag" ) @@ -34,7 +34,7 @@ func (m *initializeURLs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn // strictly parses the org ID header as an integer and would error on a // UUID. Skip the API call entirely and pass it through unchanged. cfgID := client.Config.WorkspaceID - if cfgID != "" && cfgID != auth.WorkspaceIDNone { + if cfgID != "" && cfgID != profile.WorkspaceIDNone { if _, err := strconv.ParseInt(cfgID, 10, 64); err != nil { if err := initializeForWorkspace(b, cfgID, host); err != nil { return diag.FromErr(err) @@ -55,7 +55,7 @@ func (m *initializeURLs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn } orgID := strconv.FormatInt(apiID, 10) - if cfgID != "" && cfgID != auth.WorkspaceIDNone && cfgID != orgID { + if cfgID != "" && cfgID != profile.WorkspaceIDNone && cfgID != orgID { return diag.Errorf( "workspace_id %s in your configuration does not match the connected workspace (ID: %s); "+ "remove or correct workspace_id in your profile or bundle config to disambiguate", diff --git a/cmd/api/api.go b/cmd/api/api.go index 1a4eac11ec1..e6bd47b8ec2 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -15,6 +15,7 @@ import ( "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/databrickscfg" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/env" "github.com/databricks/cli/libs/flags" "github.com/databricks/databricks-sdk-go/client" @@ -205,7 +206,7 @@ func renderResponse(ctx context.Context, raw json.RawMessage) error { // this sentinel and would otherwise send the literal "none" as a routing // identifier. func normalizeWorkspaceID(workspaceID string) string { - if workspaceID == auth.WorkspaceIDNone { + if workspaceID == profile.WorkspaceIDNone { return "" } return workspaceID diff --git a/cmd/api/api_test.go b/cmd/api/api_test.go index fd665b8e59c..3fe132b8e23 100644 --- a/cmd/api/api_test.go +++ b/cmd/api/api_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/flags" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -321,7 +321,7 @@ func TestNormalizeWorkspaceID(t *testing.T) { in string want string }{ - {"sentinel stripped to empty", auth.WorkspaceIDNone, ""}, + {"sentinel stripped to empty", profile.WorkspaceIDNone, ""}, {"empty passes through", "", ""}, {"normal value passes through", "900800700600", "900800700600"}, } diff --git a/cmd/auth/docker/docker_configure.go b/cmd/auth/docker/docker_configure.go index d1d2bd79821..22e6a4dce2a 100644 --- a/cmd/auth/docker/docker_configure.go +++ b/cmd/auth/docker/docker_configure.go @@ -6,7 +6,6 @@ import ( "fmt" "path/filepath" - authlib "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/databrickscfg" "github.com/databricks/cli/libs/databrickscfg/profile" @@ -74,7 +73,7 @@ func configureDockerTarget(cmd *cobra.Command, args []string, region string) (*d } func (t *dockerTarget) configure(ctx context.Context) (dockercredentials.Configuration, error) { - if t.profile.WorkspaceID == "" || t.profile.WorkspaceID == authlib.WorkspaceIDNone { + if t.profile.WorkspaceID == "" || t.profile.WorkspaceID == profile.WorkspaceIDNone { err := databrickscfg.SaveToProfile(ctx, &config.Config{ ConfigFile: env.Get(ctx, "DATABRICKS_CONFIG_FILE"), Profile: t.profile.Name, diff --git a/cmd/auth/docker/docker_profile.go b/cmd/auth/docker/docker_profile.go index c6e9a82fe98..c616157cf33 100644 --- a/cmd/auth/docker/docker_profile.go +++ b/cmd/auth/docker/docker_profile.go @@ -6,7 +6,6 @@ import ( "os" "strings" - authlib "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/databrickscfg" "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/dockercredentials" @@ -62,7 +61,7 @@ func loadDockerWorkspace(ctx context.Context, name string, region *string) (*doc return nil, fmt.Errorf("locate databricks executable: %w", err) } var client *databricks.WorkspaceClient - if region == nil || p.WorkspaceID == "" || p.WorkspaceID == authlib.WorkspaceIDNone { + if region == nil || p.WorkspaceID == "" || p.WorkspaceID == profile.WorkspaceIDNone { client, err = databricks.NewWorkspaceClient(&databricks.Config{ Profile: p.Name, Host: p.Host, diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 2c60557a809..1f0ddb9b707 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -473,7 +473,7 @@ func shouldPromptWorkspace(authArguments *auth.AuthArguments, existingProfile *p } if existingProfile != nil && existingProfile.AccountID == authArguments.AccountID && - (existingProfile.WorkspaceID == "" || existingProfile.WorkspaceID == auth.WorkspaceIDNone) { + (existingProfile.WorkspaceID == "" || existingProfile.WorkspaceID == profile.WorkspaceIDNone) { return false } return true diff --git a/cmd/auth/login_test.go b/cmd/auth/login_test.go index 1c4428596dd..026a002a158 100644 --- a/cmd/auth/login_test.go +++ b/cmd/auth/login_test.go @@ -635,7 +635,7 @@ func TestSetHostAndAccountId_WorkspaceIDNoneSentinelInherited(t *testing.T) { } err := setHostAndAccountId(ctx, skipProfile, &args, []string{}) assert.NoError(t, err) - assert.Equal(t, auth.WorkspaceIDNone, args.WorkspaceID) + assert.Equal(t, profile.WorkspaceIDNone, args.WorkspaceID) } func TestShouldPromptWorkspace(t *testing.T) { diff --git a/cmd/root/auth.go b/cmd/root/auth.go index 5879bf00c81..5c03c8cfa8c 100644 --- a/cmd/root/auth.go +++ b/cmd/root/auth.go @@ -56,12 +56,12 @@ func initProfileFlag(cmd *cobra.Command) { // isPATOnSPOGWithoutWorkspaceID reports whether the resolved config is a PAT // profile pointing at a SPOG host with no workspace_id set. The SDK strips the // routing identifier from the request, which lands on the account-plane where -// PATs aren't accepted. The legacy "none" sentinel (auth.WorkspaceIDNone) is +// PATs aren't accepted. The legacy "none" sentinel (profile.WorkspaceIDNone) is // treated as empty here, matching the convention used elsewhere in the repo // (e.g. libs/databrickscfg/profile/profiler.go). func isPATOnSPOGWithoutWorkspaceID(cfg *config.Config) bool { return cfg.AuthType == auth.AuthTypePat && - (cfg.WorkspaceID == "" || cfg.WorkspaceID == auth.WorkspaceIDNone) && + (cfg.WorkspaceID == "" || cfg.WorkspaceID == profile.WorkspaceIDNone) && auth.HasUnifiedHostSignal(cfg.DiscoveryURL) } @@ -260,7 +260,7 @@ func MustAccountClient(cmd *cobra.Command, args []string) error { func workspaceClientOrPrompt(ctx context.Context, cfg *config.Config, allowPrompt bool) (*databricks.WorkspaceClient, error) { w, err := databricks.NewWorkspaceClient((*databricks.Config)(cfg)) if err == nil && cfg.Profile != "" && cfg.AccountID != "" && - (cfg.WorkspaceID == "" || cfg.WorkspaceID == auth.WorkspaceIDNone) { + (cfg.WorkspaceID == "" || cfg.WorkspaceID == profile.WorkspaceIDNone) { // Account-only profile (created with --skip-workspace): account_id is // set but workspace_id is absent (new shape) or the legacy "none" // sentinel. Without a workspace_id the SDK would either send "none" as diff --git a/cmd/root/auth_test.go b/cmd/root/auth_test.go index 5e5173ea151..b455dd0e382 100644 --- a/cmd/root/auth_test.go +++ b/cmd/root/auth_test.go @@ -11,9 +11,9 @@ import ( "time" "github.com/databricks/cli/internal/testutil" - "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/config" "github.com/stretchr/testify/assert" @@ -675,7 +675,7 @@ func TestIsPATOnSPOGWithoutWorkspaceID(t *testing.T) { name: "pat on spog with legacy 'none' sentinel is treated as missing", cfg: &config.Config{ AuthType: "pat", - WorkspaceID: auth.WorkspaceIDNone, + WorkspaceID: profile.WorkspaceIDNone, DiscoveryURL: "https://spog.example.test/oidc/accounts/abc/.well-known/oauth-authorization-server", }, want: true, diff --git a/cmd/sandbox/api.go b/cmd/sandbox/api.go index e2816ee26c0..61fe94d9de5 100644 --- a/cmd/sandbox/api.go +++ b/cmd/sandbox/api.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/client" @@ -237,11 +237,11 @@ func newSandboxAPI(w *databricks.WorkspaceClient) (*sandboxAPI, error) { // headers attaches the workspace routing identifier so multi-workspace // gateways (e.g. SPOG hosts) can scope the credential. The -// auth.WorkspaceIDNone sentinel ("none") is treated as unset so the +// profile.WorkspaceIDNone sentinel ("none") is treated as unset so the // literal string never goes on the wire. func (a *sandboxAPI) headers() map[string]string { wsID := a.c.Config.WorkspaceID - if wsID == "" || wsID == auth.WorkspaceIDNone { + if wsID == "" || wsID == profile.WorkspaceIDNone { return nil } return map[string]string{orgIDHeader: wsID} diff --git a/libs/auth/arguments.go b/libs/auth/arguments.go index deac0b5b1cc..cfd1ec1927d 100644 --- a/libs/auth/arguments.go +++ b/libs/auth/arguments.go @@ -2,13 +2,10 @@ package auth import ( "github.com/databricks/cli/libs/auth/u2m" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go/config" ) -// WorkspaceIDNone is a sentinel value persisted to .databrickscfg when the -// user explicitly skips workspace selection for SPOG account-level access. -const WorkspaceIDNone = "none" - // AuthArguments is a struct that contains the common arguments passed to // `databricks auth` commands. type AuthArguments struct { @@ -31,7 +28,7 @@ type AuthArguments struct { func (a AuthArguments) ToOAuthArgument() (u2m.OAuthArgument, error) { // Strip the "none" sentinel so it is never passed to the SDK. workspaceID := a.WorkspaceID - if workspaceID == WorkspaceIDNone { + if workspaceID == profile.WorkspaceIDNone { workspaceID = "" } diff --git a/libs/auth/config_type.go b/libs/auth/config_type.go index 0d93b1bf075..d9417971feb 100644 --- a/libs/auth/config_type.go +++ b/libs/auth/config_type.go @@ -3,6 +3,7 @@ package auth import ( "strings" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go/config" ) @@ -54,7 +55,7 @@ func ResolveConfigType(cfg *config.Config) config.ConfigType { return configType } - if cfg.WorkspaceID != "" && cfg.WorkspaceID != WorkspaceIDNone { + if cfg.WorkspaceID != "" && cfg.WorkspaceID != profile.WorkspaceIDNone { return config.WorkspaceConfig } return config.AccountConfig diff --git a/libs/auth/headers.go b/libs/auth/headers.go index 86ac006f4fe..a712f2bf537 100644 --- a/libs/auth/headers.go +++ b/libs/auth/headers.go @@ -1,6 +1,7 @@ package auth import ( + "github.com/databricks/cli/libs/databrickscfg/profile" sdkconfig "github.com/databricks/databricks-sdk-go/config" ) @@ -22,7 +23,7 @@ func WorkspaceIDHeaders(cfg *sdkconfig.Config) map[string]string { return nil } wsID := cfg.WorkspaceID - if wsID == "" || wsID == WorkspaceIDNone { + if wsID == "" || wsID == profile.WorkspaceIDNone { return nil } return map[string]string{ diff --git a/libs/auth/headers_test.go b/libs/auth/headers_test.go index fb3ab0ea3fe..5ca31dc7d30 100644 --- a/libs/auth/headers_test.go +++ b/libs/auth/headers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" sdkconfig "github.com/databricks/databricks-sdk-go/config" "github.com/stretchr/testify/assert" ) @@ -31,7 +32,7 @@ func TestWorkspaceIDHeaders(t *testing.T) { }, { name: `"none" sentinel returns nil`, - cfg: &sdkconfig.Config{WorkspaceID: auth.WorkspaceIDNone}, + cfg: &sdkconfig.Config{WorkspaceID: profile.WorkspaceIDNone}, want: nil, }, { diff --git a/libs/auth/workspace_id.go b/libs/auth/workspace_id.go index be48a6578b4..84313e3f3aa 100644 --- a/libs/auth/workspace_id.go +++ b/libs/auth/workspace_id.go @@ -4,6 +4,7 @@ import ( "context" "strconv" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go" ) @@ -25,7 +26,7 @@ import ( // type lets callers pass the value to URL builders, env vars, and other // string-typed sinks without a manual strconv.FormatInt step. func ResolveWorkspaceID(ctx context.Context, w *databricks.WorkspaceClient) (string, error) { - if id := w.Config.WorkspaceID; id != "" && id != WorkspaceIDNone { + if id := w.Config.WorkspaceID; id != "" && id != profile.WorkspaceIDNone { return id, nil } id, err := w.CurrentWorkspaceID(ctx) diff --git a/libs/auth/workspace_id_test.go b/libs/auth/workspace_id_test.go index 9b8f0cb214b..fd0a32aa194 100644 --- a/libs/auth/workspace_id_test.go +++ b/libs/auth/workspace_id_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/testserver" "github.com/databricks/databricks-sdk-go" "github.com/stretchr/testify/assert" @@ -65,7 +66,7 @@ func TestResolveWorkspaceID_NoneSentinelFallsThroughToAPI(t *testing.T) { w, err := databricks.NewWorkspaceClient(&databricks.Config{ Host: server.URL, Token: "testtoken", - WorkspaceID: auth.WorkspaceIDNone, + WorkspaceID: profile.WorkspaceIDNone, }) require.NoError(t, err) diff --git a/libs/databrickscfg/profile/profile.go b/libs/databrickscfg/profile/profile.go index 3d32989e13d..d3627946ee5 100644 --- a/libs/databrickscfg/profile/profile.go +++ b/libs/databrickscfg/profile/profile.go @@ -6,6 +6,10 @@ import ( "github.com/databricks/databricks-sdk-go/config" ) +// WorkspaceIDNone is a sentinel value persisted to .databrickscfg when the +// user explicitly skips workspace selection for SPOG account-level access. +const WorkspaceIDNone = "none" + // Profile holds a subset of the keys in a databrickscfg profile. // It should only be used for prompting and filtering. // Use its name to construct a config.Config. diff --git a/libs/databrickscfg/profile/profiler.go b/libs/databrickscfg/profile/profiler.go index 56cfdb5f522..d7fb38f255a 100644 --- a/libs/databrickscfg/profile/profiler.go +++ b/libs/databrickscfg/profile/profiler.go @@ -3,7 +3,6 @@ package profile import ( "context" - "github.com/databricks/cli/libs/auth" "github.com/databricks/databricks-sdk-go/config" ) @@ -13,7 +12,7 @@ func MatchWorkspaceProfiles(p Profile) bool { // Workspace profile: has workspace_id (covers both classic and SPOG profiles), // or is a regular workspace host (no account_id). // workspace_id = "none" is a sentinel for "skip workspace", so it does NOT count. - return (p.WorkspaceID != "" && p.WorkspaceID != auth.WorkspaceIDNone) || p.AccountID == "" + return (p.WorkspaceID != "" && p.WorkspaceID != WorkspaceIDNone) || p.AccountID == "" } func MatchAccountProfiles(p Profile) bool { @@ -21,7 +20,7 @@ func MatchAccountProfiles(p Profile) bool { // workspace_id = "none" is a sentinel for account-level access, treated as empty. // This covers classic accounts.* profiles, legacy unified-host account profiles, // and new SPOG account profiles. - return p.Host != "" && p.AccountID != "" && (p.WorkspaceID == "" || p.WorkspaceID == auth.WorkspaceIDNone) + return p.Host != "" && p.AccountID != "" && (p.WorkspaceID == "" || p.WorkspaceID == WorkspaceIDNone) } func MatchAllProfiles(p Profile) bool { diff --git a/libs/dockercredentials/profile.go b/libs/dockercredentials/profile.go index a2d3231132c..e2a1ca0f092 100644 --- a/libs/dockercredentials/profile.go +++ b/libs/dockercredentials/profile.go @@ -32,7 +32,7 @@ func isAccountOnlyProfile(p profile.Profile) bool { if auth.IsClassicAccountHost(cfg.CanonicalHostName()) { return true } - return p.AccountID != "" && (p.WorkspaceID == "" || p.WorkspaceID == auth.WorkspaceIDNone) + return p.AccountID != "" && (p.WorkspaceID == "" || p.WorkspaceID == profile.WorkspaceIDNone) } // LoadProfile loads and validates the named Docker credential helper profile. @@ -59,7 +59,7 @@ func EnsureUniqueProfile(ctx context.Context, profiler profile.Profiler, p profi return err } - if p.WorkspaceID == "" || p.WorkspaceID == auth.WorkspaceIDNone { + if p.WorkspaceID == "" || p.WorkspaceID == profile.WorkspaceIDNone { matches = append(matches, p) } var names []string diff --git a/libs/dockercredentials/workspace.go b/libs/dockercredentials/workspace.go index 5c1fb6daa98..3c1ed0f810e 100644 --- a/libs/dockercredentials/workspace.go +++ b/libs/dockercredentials/workspace.go @@ -14,7 +14,7 @@ import ( // WorkspaceID uses the selected profile's ID, resolving it only when absent. // The client is only needed for profiles without an ID. func WorkspaceID(ctx context.Context, p profile.Profile, w *databricks.WorkspaceClient) (string, error) { - if p.WorkspaceID != "" && p.WorkspaceID != auth.WorkspaceIDNone { + if p.WorkspaceID != "" && p.WorkspaceID != profile.WorkspaceIDNone { return p.WorkspaceID, nil } // Neither an ambient ID nor the CLI-only "none" sentinel may become a routing header. diff --git a/libs/dockercredentials/workspace_test.go b/libs/dockercredentials/workspace_test.go index 6ef5b8b5333..3375ff08902 100644 --- a/libs/dockercredentials/workspace_test.go +++ b/libs/dockercredentials/workspace_test.go @@ -24,7 +24,7 @@ func TestWorkspaceID(t *testing.T) { assert.Equal(t, "123", got) }) - for _, id := range []string{"", auth.WorkspaceIDNone} { + for _, id := range []string{"", profile.WorkspaceIDNone} { t.Run(id, func(t *testing.T) { server := testserver.New(t) server.Handle("GET", "/api/2.0/preview/scim/v2/Me", func(req testserver.Request) any { diff --git a/libs/filer/files_client.go b/libs/filer/files_client.go index e079d975d93..1638acb73bd 100644 --- a/libs/filer/files_client.go +++ b/libs/filer/files_client.go @@ -11,7 +11,7 @@ import ( "slices" "time" - "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/env" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/config" @@ -198,7 +198,7 @@ func newFilesAPIClient(ctx context.Context, cfg *config.Config) (*files.Client, } // The workspace routing header is needed on unified ("SPOG") hosts; the CLI's // "none" sentinel means "no workspace ID", so it is not forwarded. - if id := cfg.WorkspaceID; id != "" && id != auth.WorkspaceIDNone { + if id := cfg.WorkspaceID; id != "" && id != profile.WorkspaceIDNone { copts = append(copts, client.WithWorkspaceID(id)) } return files.NewClient(ctx, copts...) diff --git a/libs/filer/files_client_test.go b/libs/filer/files_client_test.go index 18a68a4d896..c259d4a9f14 100644 --- a/libs/filer/files_client_test.go +++ b/libs/filer/files_client_test.go @@ -11,6 +11,7 @@ import ( "testing" cliauth "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/cli/libs/env" "github.com/databricks/cli/libs/testserver" "github.com/databricks/databricks-sdk-go" @@ -30,7 +31,7 @@ func TestNewFilesAPIClientDoesNotResolveAmbientConfig(t *testing.T) { workspaceClient, err := databricks.NewWorkspaceClient(&databricks.Config{ Host: server.URL, Token: "resolved-token", - WorkspaceID: cliauth.WorkspaceIDNone, + WorkspaceID: profile.WorkspaceIDNone, }) require.NoError(t, err)