mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 11:01:37 -05:00
Improve handling of connection errors for arc controllers (#11139)
* some fixes for failed connections * cleanup * add back in prompt
This commit is contained in:
@@ -146,3 +146,4 @@ export function resourceDeletionWarning(namespace: string, name: string): string
|
|||||||
export function invalidResourceDeletionName(name: string): string { return localize('arc.invalidResourceDeletionName', "The value '{0}' does not match the instance name. Try again or press escape to exit", name); }
|
export function invalidResourceDeletionName(name: string): string { return localize('arc.invalidResourceDeletionName', "The value '{0}' does not match the instance name. Try again or press escape to exit", name); }
|
||||||
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
||||||
export function passwordResetFailed(error: any): string { return localize('arc.passwordResetFailed', "Failed to reset password. {0}", getErrorMessage(error)); }
|
export function passwordResetFailed(error: any): string { return localize('arc.passwordResetFailed', "Failed to reset password. {0}", getErrorMessage(error)); }
|
||||||
|
export function errorConnectingToController(error: any): string { return localize('arc.errorConnectingToController', "Error connecting to controller. {0}", getErrorMessage(error)); }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Authentication, BasicAuth } from '../controller/auth';
|
import { Authentication, BasicAuth } from '../controller/auth';
|
||||||
import { EndpointsRouterApi, EndpointModel, RegistrationRouterApi, RegistrationResponse, TokenRouterApi, SqlInstanceRouterApi } from '../controller/generated/v1/api';
|
import { EndpointsRouterApi, EndpointModel, RegistrationRouterApi, RegistrationResponse, TokenRouterApi, SqlInstanceRouterApi } from '../controller/generated/v1/api';
|
||||||
import { getAzurecoreApi, parseEndpoint, parseInstanceName } from '../common/utils';
|
import { getAzurecoreApi, parseEndpoint, parseInstanceName, UserCancelledError } from '../common/utils';
|
||||||
import { ResourceType } from '../constants';
|
import { ResourceType } from '../constants';
|
||||||
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
@@ -63,24 +63,26 @@ export class ControllerModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refresh(showErrors: boolean = true): Promise<void> {
|
public async refresh(showErrors: boolean = true, promptReconnect: boolean = false): Promise<void> {
|
||||||
// We haven't gotten our password yet, fetch it now
|
// We haven't gotten our password yet or we want to prompt for a reconnect
|
||||||
if (!this._auth) {
|
if (!this._auth || promptReconnect) {
|
||||||
let password = '';
|
let password = '';
|
||||||
if (this.info.rememberPassword) {
|
if (this.info.rememberPassword) {
|
||||||
// It should be in the credentials store, get it from there
|
// It should be in the credentials store, get it from there
|
||||||
password = await this.treeDataProvider.getPassword(this.info);
|
password = await this.treeDataProvider.getPassword(this.info);
|
||||||
}
|
}
|
||||||
if (password) {
|
if (password && !promptReconnect) {
|
||||||
this.setAuthentication(new BasicAuth(this.info.username, password));
|
this.setAuthentication(new BasicAuth(this.info.username, password));
|
||||||
} else {
|
} else {
|
||||||
// No password yet so prompt for it from the user
|
// No password yet or we want to reprompt for credentials so prompt for it from the user
|
||||||
const dialog = new ConnectToControllerDialog(this.treeDataProvider);
|
const dialog = new ConnectToControllerDialog(this.treeDataProvider);
|
||||||
dialog.showDialog(this.info);
|
dialog.showDialog(this.info, password);
|
||||||
const model = await dialog.waitForClose();
|
const model = await dialog.waitForClose();
|
||||||
if (model) {
|
if (model) {
|
||||||
this.treeDataProvider.addOrUpdateController(model.controllerModel, model.password, false);
|
this.treeDataProvider.addOrUpdateController(model.controllerModel, model.password, false);
|
||||||
this.setAuthentication(new BasicAuth(this.info.username, model.password));
|
this.setAuthentication(new BasicAuth(this.info.username, model.password));
|
||||||
|
} else {
|
||||||
|
throw new UserCancelledError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
// loading icon forever)
|
// loading icon forever)
|
||||||
if (err instanceof UserCancelledError) {
|
if (err instanceof UserCancelledError) {
|
||||||
vscode.window.showErrorMessage(loc.connectionRequired);
|
vscode.window.showWarningMessage(loc.connectionRequired);
|
||||||
} else {
|
} else {
|
||||||
vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err));
|
vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class ConnectToControllerDialog {
|
|||||||
|
|
||||||
constructor(private _treeDataProvider: AzureArcTreeDataProvider) { }
|
constructor(private _treeDataProvider: AzureArcTreeDataProvider) { }
|
||||||
|
|
||||||
public showDialog(controllerInfo?: ControllerInfo): void {
|
public showDialog(controllerInfo?: ControllerInfo, password?: string): void {
|
||||||
const dialog = azdata.window.createModelViewDialog(loc.connectToController);
|
const dialog = azdata.window.createModelViewDialog(loc.connectToController);
|
||||||
dialog.cancelButton.onClick(() => this.handleCancel());
|
dialog.cancelButton.onClick(() => this.handleCancel());
|
||||||
dialog.registerContent(async view => {
|
dialog.registerContent(async view => {
|
||||||
@@ -43,6 +43,7 @@ export class ConnectToControllerDialog {
|
|||||||
this.passwordInputBox = this.modelBuilder.inputBox()
|
this.passwordInputBox = this.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
inputType: 'password',
|
inputType: 'password',
|
||||||
|
value: password
|
||||||
})
|
})
|
||||||
.component();
|
.component();
|
||||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import { PostgresTreeNode } from './postgresTreeNode';
|
|||||||
import { ControllerModel, Registration, ResourceInfo } from '../../models/controllerModel';
|
import { ControllerModel, Registration, ResourceInfo } from '../../models/controllerModel';
|
||||||
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
import { ControllerDashboard } from '../dashboards/controller/controllerDashboard';
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
import { PostgresModel } from '../../models/postgresModel';
|
||||||
import { parseInstanceName } from '../../common/utils';
|
import { parseInstanceName, UserCancelledError } from '../../common/utils';
|
||||||
import { MiaaModel } from '../../models/miaaModel';
|
import { MiaaModel } from '../../models/miaaModel';
|
||||||
import { Deferred } from '../../common/promise';
|
import { Deferred } from '../../common/promise';
|
||||||
import { RefreshTreeNode } from './refreshTreeNode';
|
import { RefreshTreeNode } from './refreshTreeNode';
|
||||||
import { ResourceTreeNode } from './resourceTreeNode';
|
import { ResourceTreeNode } from './resourceTreeNode';
|
||||||
import { AzureArcTreeDataProvider } from './azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from './azureArcTreeDataProvider';
|
||||||
|
import * as loc from '../../localizedConstants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TreeNode for displaying an Azure Arc Controller
|
* The TreeNode for displaying an Azure Arc Controller
|
||||||
@@ -38,11 +39,20 @@ export class ControllerTreeNode extends TreeNode {
|
|||||||
await this.model.refresh(false);
|
await this.model.refresh(false);
|
||||||
await this._childrenRefreshPromise.promise;
|
await this._childrenRefreshPromise.promise;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
vscode.window.showErrorMessage(loc.errorConnectingToController(err));
|
||||||
|
try {
|
||||||
|
await this.model.refresh(false, true);
|
||||||
|
await this._childrenRefreshPromise.promise;
|
||||||
|
} catch (err) {
|
||||||
|
if (!(err instanceof UserCancelledError)) {
|
||||||
|
vscode.window.showErrorMessage(loc.errorConnectingToController(err));
|
||||||
|
}
|
||||||
// Couldn't get the children and TreeView doesn't have a way to collapse a node
|
// Couldn't get the children and TreeView doesn't have a way to collapse a node
|
||||||
// in a way that will refetch its children when expanded again so instead we
|
// in a way that will refetch its children when expanded again so instead we
|
||||||
// display a tempory node that will prompt the user to re-enter credentials
|
// display a tempory node that will prompt the user to re-enter credentials
|
||||||
return [new RefreshTreeNode(this)];
|
return [new RefreshTreeNode(this)];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this._children;
|
return this._children;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user