Skip to content

Commit 956a9e6

Browse files
Automatically read auth info from override, bundle and .databrickscfg, in order (#995)
## Changes * Read auth profile from overrides. * If not found, read the auth information hard coded in bundle (the actual reading is NOT implemented here) * If not found, find a unique profile matching the host ## Tests <!-- How is this tested? -->
1 parent 263f731 commit 956a9e6

File tree

10 files changed

+526
-120
lines changed

10 files changed

+526
-120
lines changed

packages/databricks-vscode/src/configuration/ConnectionCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export class ConnectionCommands implements Disposable {
6767
}
6868

6969
async configureWorkspaceCommand() {
70-
await this.connectionManager.configureWorkspace();
70+
await this.connectionManager.configureLogin();
7171
}
7272

7373
openDatabricksConfigFileCommand() {

packages/databricks-vscode/src/configuration/ConnectionManager.ts

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import {WorkspaceClient, ApiClient, logging} from "@databricks/databricks-sdk";
2-
import {Cluster, WorkspaceFsEntity, WorkspaceFsUtils} from "../sdk-extensions";
2+
import {Cluster} from "../sdk-extensions";
33
import {EventEmitter, Uri, window, Disposable} from "vscode";
44
import {CliWrapper} from "../cli/CliWrapper";
55
import {
66
SyncDestinationMapper,
77
RemoteUri,
88
LocalUri,
99
} from "../sync/SyncDestination";
10-
import {ConfigureWorkspaceWizard} from "./ConfigureWorkspaceWizard";
10+
import {LoginWizard, listProfiles} from "./LoginWizard";
1111
import {ClusterManager} from "../cluster/ClusterManager";
1212
import {DatabricksWorkspace} from "./DatabricksWorkspace";
1313
import {CustomWhenContext} from "../vscode-objs/CustomWhenContext";
14-
import {workspaceConfigs} from "../vscode-objs/WorkspaceConfigs";
1514
import {ConfigModel} from "./models/ConfigModel";
1615
import {onError} from "../utils/onErrorDecorator";
17-
import {AuthProvider} from "./auth/AuthProvider";
16+
import {AuthProvider, ProfileAuthProvider} from "./auth/AuthProvider";
1817
import {Mutex} from "../locking";
1918

2019
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -124,21 +123,9 @@ export class ConnectionManager implements Disposable {
124123
this.updateClusterManager,
125124
this
126125
),
127-
this.configModel.onDidChangeTarget(this.loginWithSavedAuth, this),
128-
this.configModel.onDidChangeKey("authParams")(async () => {
129-
const config = await this.configModel.getS("authParams");
130-
if (config === undefined) {
131-
return;
132-
}
133-
134-
// We only react to auth changes coming from the bundle.
135-
// If an override is set, then all settings must have gone
136-
// through this class, which means that we have already logged in
137-
// using those settings, so we don't double login.
138-
if (config.source === "bundle") {
139-
await this.loginWithSavedAuth();
140-
}
141-
})
126+
this.configModel.onDidChangeTarget(this.loginWithSavedAuth, this)
127+
// TODO: We don't react to changes in authProfile from the config model. We instead listen to changes
128+
// in individual models to react to (Overrides and BundlePreValidate)
142129
);
143130
}
144131

@@ -175,16 +162,54 @@ export class ConnectionManager implements Disposable {
175162
return this._workspaceClient?.apiClient;
176163
}
177164

165+
async resolveAuth() {
166+
const host = await this.configModel.get("host");
167+
const target = this.configModel.target;
168+
if (host === undefined || target === undefined) {
169+
return;
170+
}
171+
172+
// Try to load a profile user had previously selected for this target
173+
const savedProfile = (
174+
await this.configModel.overrideableConfigModel.load()
175+
).authProfile;
176+
if (savedProfile !== undefined) {
177+
const authProvider = new ProfileAuthProvider(host, savedProfile);
178+
if (await authProvider.check()) {
179+
return authProvider;
180+
}
181+
}
182+
183+
// Try to load any parameters that are hard coded in the bundle
184+
const bundleAuthParams =
185+
await this.configModel.bundlePreValidateModel.load();
186+
if (bundleAuthParams.authParams !== undefined) {
187+
throw new Error("Bundle auth params not implemented");
188+
}
189+
190+
// Try to load a unique profile that matches the host
191+
const profiles = (await listProfiles(this.cli)).filter(
192+
(p) => p.host?.toString() === host.toString()
193+
);
194+
if (profiles.length !== 1) {
195+
return;
196+
}
197+
const authProvider = new ProfileAuthProvider(host, profiles[0].name);
198+
if (await authProvider.check()) {
199+
return authProvider;
200+
}
201+
}
202+
178203
@onError({
179-
popup: {prefix: "Can't login with saved auth. "},
204+
popup: {prefix: "Can't login with saved auth."},
180205
})
181206
private async loginWithSavedAuth() {
182-
const authParams = await this.configModel.get("authParams");
183-
if (authParams === undefined) {
207+
const authProvider = await this.resolveAuth();
208+
if (authProvider === undefined) {
184209
await this.logout();
185210
return;
186211
}
187-
await this.login(AuthProvider.fromJSON(authParams, this.cli.cliPath));
212+
await this.login(authProvider);
188213
}
189214

190215
private async login(authProvider: AuthProvider): Promise<void> {
@@ -211,10 +236,12 @@ export class ConnectionManager implements Disposable {
211236
this._workspaceClient
212237
);
213238

214-
await this.createWsFsRootDirectory(this._workspaceClient);
215239
await this.updateSyncDestinationMapper();
216240
await this.updateClusterManager();
217-
await this.configModel.set("authParams", authProvider.toJSON());
241+
await this.configModel.set(
242+
"authProfile",
243+
authProvider.toJSON().profile as string | undefined
244+
);
218245
await this.configModel.setAuthProvider(authProvider);
219246
this.updateState("CONNECTED");
220247
});
@@ -229,35 +256,6 @@ export class ConnectionManager implements Disposable {
229256
}
230257
}
231258

232-
async createWsFsRootDirectory(wsClient: WorkspaceClient) {
233-
if (
234-
!this.databricksWorkspace ||
235-
!workspaceConfigs.enableFilesInWorkspace
236-
) {
237-
return;
238-
}
239-
const rootDirPath = this.databricksWorkspace.workspaceFsRoot;
240-
const me = this.databricksWorkspace.userName;
241-
let rootDir = await WorkspaceFsEntity.fromPath(
242-
wsClient,
243-
rootDirPath.path
244-
);
245-
if (rootDir) {
246-
return;
247-
}
248-
const meDir = await WorkspaceFsEntity.fromPath(
249-
wsClient,
250-
`/Users/${me}`
251-
);
252-
if (WorkspaceFsUtils.isDirectory(meDir)) {
253-
rootDir = await meDir.mkdir(rootDirPath.path);
254-
}
255-
if (!rootDir) {
256-
window.showErrorMessage(`Can't find or create ${rootDirPath.path}`);
257-
return;
258-
}
259-
}
260-
261259
@onError({popup: {prefix: "Can't logout"}})
262260
@Mutex.synchronise("loginLogoutMutex")
263261
async logout() {
@@ -266,19 +264,16 @@ export class ConnectionManager implements Disposable {
266264
await this.updateClusterManager();
267265
await this.updateSyncDestinationMapper();
268266
if (this.configModel.target !== undefined) {
269-
await this.configModel.set("authParams", undefined);
267+
await this.configModel.set("authProfile", undefined);
270268
}
271269
this.updateState("DISCONNECTED");
272270
}
273271

274272
@onError({
275273
popup: {prefix: "Can't configure workspace. "},
276274
})
277-
async configureWorkspace() {
278-
const authProvider = await ConfigureWorkspaceWizard.run(
279-
this.cli,
280-
this.configModel
281-
);
275+
async configureLogin() {
276+
const authProvider = await LoginWizard.run(this.cli, this.configModel);
282277
if (!authProvider) {
283278
return;
284279
}
@@ -315,6 +310,7 @@ export class ConnectionManager implements Disposable {
315310
return;
316311
}
317312
await this.configModel.set("remoteRootPath", remoteWorkspace.path);
313+
await this.configModel.set("remoteRootPath", remoteWorkspace.path);
318314
}
319315

320316
@onError({

0 commit comments

Comments
 (0)