Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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 .nextchanges/cli/auth-docker-host.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Deprecate `--region` in `databricks auth docker configure` ahead of its removal in the next release, infer the Artifact Registry region when it is omitted, and add `databricks auth docker host --profile <name>` to show the profile's registry host and credential-helper status. ([#6781](https://github.com/databricks/cli/pull/6781))
52 changes: 52 additions & 0 deletions acceptance/cmd/auth/docker/help/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Usage:

Available Commands:
configure (Experimental) Configure Docker authentication for Databricks Artifact Registry
host (Experimental) Show the Databricks Artifact Registry host for a profile
token (Experimental) Generate a Docker credential

Flags:
Expand All @@ -57,3 +58,54 @@ Global Flags:
--workspace-id string Databricks Workspace ID

Use "databricks auth docker [command] --help" for more information about a command.

>>> [CLI] auth docker configure --help
(Experimental) Configure Docker authentication for Databricks Artifact Registry.

This command installs docker-credential-databricks and configures Docker to use
it for the selected workspace's Artifact Registry host. If the selected profile
does not already include a workspace_id, the command resolves and saves it so
the Docker helper can map the registry host back to the profile. The registry
region is inferred from the workspace's metastore. Select the workspace with
[PROFILE] or --profile; --host, --account-id, and --workspace-id are not
supported. The deprecated --region flag is retained for compatibility; omit it
because it will be fully removed in the next release.

Usage:
databricks auth docker configure [PROFILE] [flags]

Flags:
-h, --help help for configure
--region string Artifact Registry region; we recommend omitting this flag because the region is inferred automatically (DEPRECATED: --region will be fully removed in the next release)

Global Flags:
--account-id string Databricks Account ID
--debug enable debug logging
--host string Databricks Host
-o, --output type output type: text or json (default text)
-p, --profile string ~/.databrickscfg profile
-t, --target string bundle target to use (if applicable)
--workspace-id string Databricks Workspace ID

>>> [CLI] auth docker host --help
(Experimental) Show the Databricks Artifact Registry host for a profile.

The --profile flag is required.

Usage:
databricks auth docker host [flags]

Examples:
databricks auth docker host --profile DEFAULT

Flags:
-h, --help help for host

Global Flags:
--account-id string Databricks Account ID
--debug enable debug logging
--host string Databricks Host
-o, --output type output type: text or json (default text)
-p, --profile string ~/.databrickscfg profile
-t, --target string bundle target to use (if applicable)
--workspace-id string Databricks Workspace ID
2 changes: 2 additions & 0 deletions acceptance/cmd/auth/docker/help/script
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
trace "$CLI" auth --help
trace "$CLI" auth docker --help
trace "$CLI" auth docker configure --help
trace "$CLI" auth docker host --help
1 change: 1 addition & 0 deletions cmd/auth/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func New(load TokenLoader) *cobra.Command {
}
cmd.AddCommand(newDockerTokenCommand(load))
cmd.AddCommand(newDockerConfigureCommand())
cmd.AddCommand(newDockerHostCommand())
return cmd
}

Expand Down
95 changes: 58 additions & 37 deletions cmd/auth/docker/docker_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

Expand All @@ -20,24 +19,14 @@ import (
)

type configureDockerDeps struct {
profiler profile.Profiler
newWorkspaceClient func(*databricks.Config) (*databricks.WorkspaceClient, error)
resolveWorkspaceID func(context.Context, *databricks.WorkspaceClient) (string, error)
executable func() (string, error)
registryHost func(string, string, string) (string, error)
dockerProfileDeps
installShim func(string) (dockercredentials.ShimInstallResult, error)
setCredentialHelper func(string, string) error
}

func defaultConfigureDockerDeps() configureDockerDeps {
return configureDockerDeps{
profiler: profile.DefaultProfiler,
newWorkspaceClient: func(cfg *databricks.Config) (*databricks.WorkspaceClient, error) {
return databricks.NewWorkspaceClient(cfg)
},
resolveWorkspaceID: authlib.ResolveWorkspaceID,
executable: os.Executable,
registryHost: dockercredentials.RegistryHost,
dockerProfileDeps: defaultDockerProfileDeps(),
installShim: dockercredentials.InstallShim,
setCredentialHelper: dockercredentials.SetCredentialHelper,
}
Expand All @@ -49,55 +38,83 @@ func newDockerConfigureCommand() *cobra.Command {

func newDockerConfigureCommandWithDeps(deps configureDockerDeps) *cobra.Command {
cmd := &cobra.Command{
Use: "configure [PROFILE] --region REGION",
Use: "configure [PROFILE]",
Short: "(Experimental) Configure Docker authentication for Databricks Artifact Registry",
Long: `(Experimental) Configure Docker authentication for Databricks Artifact Registry.

This command installs docker-credential-databricks and configures Docker to use
it for the selected workspace's Artifact Registry host. If the selected profile
does not already include a workspace_id, the command resolves and saves it so
the Docker helper can map the registry host back to the profile. The required
region must match the workspace home region because it cannot be inferred from
the profile. Select the workspace with [PROFILE] or --profile; --host,
--account-id, and --workspace-id are not supported.`,
the Docker helper can map the registry host back to the profile. The registry
region is inferred from the workspace's metastore. Select the workspace with
[PROFILE] or --profile; --host, --account-id, and --workspace-id are not
supported. The deprecated --region flag is retained for compatibility; omit it
because it will be fully removed in the next release.`,
Args: cobra.MaximumNArgs(1),
}
var region string
cmd.Flags().StringVar(&region, "region", "", "Cloud region for the Databricks Artifact Registry host; must match the workspace home region")
var regionFlag string
cmd.Flags().StringVar(&regionFlag, "region", "", "Artifact Registry region; we recommend omitting this flag because the region is inferred automatically")
cmd.Flags().Lookup("region").Deprecated = "--region will be fully removed in the next release"
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if err := errorOnUnsupportedConfigureDockerFlags(cmd); err != nil {
return err
}
if region == "" {
return errors.New("--region is required because workspace region cannot be inferred from this profile; it must match the workspace home region")
}

profileName, err := configureDockerProfileName(ctx, cmd, args, deps.profiler)
if err != nil {
return err
}

p, err := loadAndValidateConfigureDockerProfile(ctx, profileName, deps.profiler)
p, err := loadAndValidateDockerProfile(ctx, profileName, deps.profiler)
if err != nil {
return err
}
if err := deps.validateWorkspaceHost(p.Host); err != nil {
return err
}
regionProvided := cmd.Flags().Changed("region")
region := strings.TrimSpace(regionFlag)
if regionProvided {
if err := dockercredentials.ValidateRegion(region); err != nil {
return err
}
}

executable, err := deps.executable()
if err != nil {
return fmt.Errorf("locate databricks executable: %w", err)
}
workspaceID, err := resolveConfigureDockerWorkspaceID(ctx, p, executable, deps)
if err != nil {
return err
needsWorkspaceClient := !regionProvided || p.WorkspaceID == "" || p.WorkspaceID == authlib.WorkspaceIDNone
var w *databricks.WorkspaceClient
if needsWorkspaceClient {
w, err = newDockerWorkspaceClient(ctx, p, executable, deps.dockerProfileDeps)
if err != nil {
return err
}
}
registryHost, err := deps.registryHost(workspaceID, region, p.Host)
workspaceID, err := resolveDockerWorkspaceID(ctx, p, w, deps.dockerProfileDeps)
if err != nil {
return err
}
if err := ensureConfigureDockerUniqueProfile(ctx, deps.profiler, p, workspaceID); err != nil {
return err
}
if !regionProvided {
w.Config.WorkspaceID = workspaceID
region, err = deps.resolveWorkspaceRegion(ctx, w)
if err != nil {
return fmt.Errorf("resolve workspace region for profile %q: %w", p.Name, err)
}
region = strings.TrimSpace(region)
if region == "" {
return fmt.Errorf("resolve workspace region for profile %q: metastore summary did not include a region", p.Name)
}
}
registryHost, err := deps.registryHost(workspaceID, region, p.Host)
if err != nil {
return err
}
if p.WorkspaceID == "" || p.WorkspaceID == authlib.WorkspaceIDNone {
if err := persistConfigureDockerWorkspaceID(ctx, p, workspaceID); err != nil {
return fmt.Errorf("save workspace ID to profile %q: %w", p.Name, err)
Expand All @@ -108,7 +125,7 @@ the profile. Select the workspace with [PROFILE] or --profile; --host,
if err != nil {
return fmt.Errorf("install Docker credential helper: %w", err)
}
dockerConfigPath, err := configureDockerConfigPath(ctx)
dockerConfigPath, err := dockerConfigPath(ctx)
if err != nil {
return err
}
Expand Down Expand Up @@ -180,7 +197,7 @@ func configureDockerProfileName(ctx context.Context, cmd *cobra.Command, args []
})
}

func loadAndValidateConfigureDockerProfile(ctx context.Context, profileName string, profiler profile.Profiler) (profile.Profile, error) {
func loadAndValidateDockerProfile(ctx context.Context, profileName string, profiler profile.Profiler) (profile.Profile, error) {
profiles, err := profiler.LoadProfiles(ctx, profile.WithName(profileName))
if err != nil {
return profile.Profile{}, err
Expand All @@ -194,11 +211,7 @@ func loadAndValidateConfigureDockerProfile(ctx context.Context, profileName stri
return profiles[0], nil
}

func resolveConfigureDockerWorkspaceID(ctx context.Context, p profile.Profile, executable string, deps configureDockerDeps) (string, error) {
if p.WorkspaceID != "" && p.WorkspaceID != authlib.WorkspaceIDNone {
return p.WorkspaceID, nil
}

func newDockerWorkspaceClient(ctx context.Context, p profile.Profile, executable string, deps dockerProfileDeps) (*databricks.WorkspaceClient, error) {
cfg := &databricks.Config{
Profile: p.Name,
Host: p.Host,
Expand All @@ -210,8 +223,16 @@ func resolveConfigureDockerWorkspaceID(ctx context.Context, p profile.Profile, e
}
w, err := deps.newWorkspaceClient(cfg)
if err != nil {
return "", fmt.Errorf("load workspace profile %q: %w. Run databricks auth login --host <workspace-url> and retry with that profile", p.Name, err)
return nil, fmt.Errorf("load workspace profile %q: %w. Run databricks auth login --host <workspace-url> and retry with that profile", p.Name, err)
}
return w, nil
}

func resolveDockerWorkspaceID(ctx context.Context, p profile.Profile, w *databricks.WorkspaceClient, deps dockerProfileDeps) (string, error) {
if p.WorkspaceID != "" && p.WorkspaceID != authlib.WorkspaceIDNone {
return p.WorkspaceID, nil
}

// The selected profile may contain the CLI-only "none" sentinel, which the SDK would send as a routing header.
w.Config.WorkspaceID = ""
workspaceID, err := deps.resolveWorkspaceID(ctx, w)
Expand Down Expand Up @@ -255,7 +276,7 @@ func persistConfigureDockerWorkspaceID(ctx context.Context, p profile.Profile, w
})
}

func configureDockerConfigPath(ctx context.Context) (string, error) {
func dockerConfigPath(ctx context.Context) (string, error) {
if dockerConfig := env.Get(ctx, "DOCKER_CONFIG"); dockerConfig != "" {
return filepath.Join(dockerConfig, "config.json"), nil
}
Expand Down
Loading
Loading