Skip to content

Commit 10fb7e8

Browse files
Add a flag to configure DB Connect Serverless profiles to databricks auth login. (#3728)
## Changes This PR extends `databricks auth login` with a new `--configure-serverless` flag that triggers the flow to configure the profile for DB Connect Serverless. The serverless configuration flow simply takes care of configuring the profile by setting `serverless_compute_id = auto` and deleting the value of `cluster_id` if any. Though, it is meant to be extended to prompt users to provide additional properties. Side note: this PR illustrates the opportunity to consolidate the profile code management. ## Tests New acceptance test. --------- Co-authored-by: Pieter Noordhuis <[email protected]>
1 parent 4aceb7d commit 10fb7e8

File tree

9 files changed

+96
-24
lines changed

9 files changed

+96
-24
lines changed

NEXT_CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
### CLI
88

9+
* Add the `--configure-serverless` flag to `databricks auth login` to configure Databricks Connect to use serverless.
10+
911
### Dependency updates
1012

1113
### Bundles
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[DEFAULT]
2+
host = [DATABRICKS_URL]
3+
serverless_compute_id = auto
4+
auth_type = databricks-cli

acceptance/cmd/auth/login-configure-serverless/out.test.toml

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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
=== Initial profile with cluster_id
3+
[DEFAULT]
4+
host = [DATABRICKS_URL]
5+
cluster_id = existing-cluster-123
6+
7+
=== Run auth login with --configure-serverless
8+
>>> [CLI] auth login --host [DATABRICKS_URL] --profile DEFAULT --configure-serverless
9+
Profile DEFAULT was successfully saved
10+
11+
=== Profile after auth login with --configure-serverless
12+
[DEFAULT]
13+
host = [DATABRICKS_URL]
14+
serverless_compute_id = auto
15+
auth_type = databricks-cli
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
sethome "./home"
2+
3+
# Create an initial profile with cluster_id to be cleared out.
4+
cat > "./home/.databrickscfg" <<EOF
5+
[DEFAULT]
6+
host = $DATABRICKS_HOST
7+
cluster_id = existing-cluster-123
8+
EOF
9+
10+
title "Initial profile with cluster_id\n"
11+
cat "./home/.databrickscfg"
12+
13+
# Use a fake browser that performs a GET on the authorization URL
14+
# and follows the redirect back to localhost.
15+
export BROWSER="browser.py"
16+
17+
title "Run auth login with --configure-serverless"
18+
trace $CLI auth login --host $DATABRICKS_HOST --profile DEFAULT --configure-serverless
19+
20+
title "Profile after auth login with --configure-serverless\n"
21+
cat "./home/.databrickscfg"
22+
23+
# Track the .databrickscfg file that was created to surface changes.
24+
mv "./home/.databrickscfg" "./out.databrickscfg"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Ignore = [
2+
"home"
3+
]

cmd/auth/login.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,23 @@ depends on the existing profiles you have set in your configuration file
9797

9898
var loginTimeout time.Duration
9999
var configureCluster bool
100+
var configureServerless bool
100101
cmd.Flags().DurationVar(&loginTimeout, "timeout", defaultTimeout,
101102
"Timeout for completing login challenge in the browser")
102103
cmd.Flags().BoolVar(&configureCluster, "configure-cluster", false,
103104
"Prompts to configure cluster")
105+
cmd.Flags().BoolVar(&configureServerless, "configure-serverless", false,
106+
"Prompts to configure serverless")
104107

105108
cmd.RunE = func(cmd *cobra.Command, args []string) error {
106109
ctx := cmd.Context()
107110
profileName := cmd.Flag("profile").Value.String()
108111

112+
// Cluster and Serverless are mutually exclusive.
113+
if configureCluster && configureServerless {
114+
return errors.New("please either configure serverless or cluster, not both")
115+
}
116+
109117
// If the user has not specified a profile name, prompt for one.
110118
if profileName == "" {
111119
var err error
@@ -119,22 +127,16 @@ depends on the existing profiles you have set in your configuration file
119127
}
120128
}
121129

130+
// Load parameters from the existing profile if any.
122131
existingProfile, err := loadProfileByName(ctx, profileName, profile.DefaultProfiler)
123132
if err != nil {
124133
return err
125134
}
126-
127-
// Set the host and account-id based on the provided arguments and flags.
128135
err = setHostAndAccountId(ctx, existingProfile, authArguments, args)
129136
if err != nil {
130137
return err
131138
}
132139

133-
clusterID := ""
134-
if existingProfile != nil {
135-
clusterID = existingProfile.ClusterID
136-
}
137-
138140
oauthArgument, err := authArguments.ToOAuthArgument()
139141
if err != nil {
140142
return err
@@ -155,7 +157,6 @@ depends on the existing profiles you have set in your configuration file
155157
Host: authArguments.Host,
156158
AccountID: authArguments.AccountID,
157159
AuthType: "databricks-cli",
158-
ClusterID: clusterID,
159160
}
160161
databricksCfgFile := os.Getenv("DATABRICKS_CONFIG_FILE")
161162
if databricksCfgFile != "" {
@@ -169,28 +170,44 @@ depends on the existing profiles you have set in your configuration file
169170
return err
170171
}
171172

172-
if configureCluster {
173+
switch {
174+
case configureCluster:
173175
w, err := databricks.NewWorkspaceClient((*databricks.Config)(&cfg))
174176
if err != nil {
175177
return err
176178
}
177-
ctx := cmd.Context()
178179
clusterID, err := cfgpickers.AskForCluster(ctx, w,
179180
cfgpickers.WithDatabricksConnect(minimalDbConnectVersion))
180181
if err != nil {
181182
return err
182183
}
183184
cfg.ClusterID = clusterID
185+
case configureServerless:
186+
cfg.ClusterID = ""
187+
cfg.ServerlessComputeID = "auto"
188+
default:
189+
// Respect the existing profile if it exists, even if it has
190+
// both cluster and serverless configured. Tools relying on
191+
// these fields from the profile will need to handle this case.
192+
//
193+
// TODO: consider whether we should use this an an opportunity
194+
// to clean up the profile under the assumption that serverless
195+
// is the preferred option.
196+
if existingProfile != nil {
197+
cfg.ClusterID = existingProfile.ClusterID
198+
cfg.ServerlessComputeID = existingProfile.ServerlessComputeID
199+
}
184200
}
185201

186202
if profileName != "" {
187203
err = databrickscfg.SaveToProfile(ctx, &config.Config{
188-
Profile: profileName,
189-
Host: cfg.Host,
190-
AuthType: cfg.AuthType,
191-
AccountID: cfg.AccountID,
192-
ClusterID: cfg.ClusterID,
193-
ConfigFile: cfg.ConfigFile,
204+
Profile: profileName,
205+
Host: cfg.Host,
206+
AuthType: cfg.AuthType,
207+
AccountID: cfg.AccountID,
208+
ClusterID: cfg.ClusterID,
209+
ConfigFile: cfg.ConfigFile,
210+
ServerlessComputeID: cfg.ServerlessComputeID,
194211
})
195212
if err != nil {
196213
return err

libs/databrickscfg/profile/file.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ func (f FileProfilerImpl) LoadProfiles(ctx context.Context, fn ProfileMatchFunct
7979
continue
8080
}
8181
profile := Profile{
82-
Name: v.Name(),
83-
Host: host,
84-
AccountID: all["account_id"],
85-
ClusterID: all["cluster_id"],
82+
Name: v.Name(),
83+
Host: host,
84+
AccountID: all["account_id"],
85+
ClusterID: all["cluster_id"],
86+
ServerlessComputeID: all["serverless_compute_id"],
8687
}
8788
if fn(profile) {
8889
profiles = append(profiles, profile)

libs/databrickscfg/profile/profile.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import (
1010
// It should only be used for prompting and filtering.
1111
// Use its name to construct a config.Config.
1212
type Profile struct {
13-
Name string
14-
Host string
15-
AccountID string
16-
ClusterID string
13+
Name string
14+
Host string
15+
AccountID string
16+
ClusterID string
17+
ServerlessComputeID string
1718
}
1819

1920
func (p Profile) Cloud() string {

0 commit comments

Comments
 (0)