mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Some general cleanup/fixes for Arc dashboards (#11066)
* Some general cleanup/fixes for Arc dashboards * more disposables * refresh controller model for miaa dashboard too * fixes
This commit is contained in:
@@ -16,19 +16,28 @@ A gui-based experience to deploy an Azure Arc data controller as well as resourc
|
|||||||
|
|
||||||
### Management Dashboards
|
### Management Dashboards
|
||||||
|
|
||||||
After connecting to an existing Azure Arc data controller in the *Azure Arc Controllers* view of the *Connections* viewlet a list of the active resources registered to the controller is shown, which allow launching a dashboard for further management capabilities.
|
After connecting to an existing Azure Arc data controller in the **Azure Arc Controllers** view of the **Connections** viewlet a list of the active resources registered to the controller is shown, which allow launching a dashboard for further management capabilities.
|
||||||
|
|
||||||
## Usage Guide
|
## Usage Guide
|
||||||
|
|
||||||
### Deployment Wizards
|
### Deployment Wizards
|
||||||
|
|
||||||
* After installing this extension click on '...' to the right of the Connections section in the left Panel and click on 'New Deployment...'.
|
* After installing this extension open the **Connections** viewlet
|
||||||
|
* Click on '...' in the top right corner of the viewlet panel and click on 'New Deployment...'.
|
||||||
* This opens a dialog box that shows several deployment tiles. This extension adds tiles for the supported resources listed above.
|
* This opens a dialog box that shows several deployment tiles. This extension adds tiles for the supported resources listed above.
|
||||||
* Click on that tile, accept any license agreements, and choose the appropriate 'Resource Type' to install
|
* Click on that tile, accept any license agreements, and choose the appropriate 'Resource Type' to install
|
||||||
* A required tools check will run, if any required tools are missing then instructions will be given for installing those tools
|
* A required tools check will run, if any required tools are missing then instructions will be given for installing those tools
|
||||||
* Once the check has passed successfully then click the *Select* button
|
* Once the check has passed successfully then click the **Select** button
|
||||||
* This opens up a new dialog where you can enter input parameters specific to the deployment selected and then open a notebook that does the actual deployment.
|
* This opens up a new dialog where you can enter input parameters specific to the deployment selected and then open a notebook that does the actual deployment.
|
||||||
*
|
|
||||||
|
### Controller View/Management Dashboards
|
||||||
|
|
||||||
|
* The **Azure Arc Controllers** view can be found in the **Connections** viewlet
|
||||||
|
* You can create a new controller by clicking the + button in the view title bar. This will launch the deployment wizard detailed above
|
||||||
|
* You can connect to an existing controller by clicking the plug button in the view title bar or the **Connect Controller** button in the view area when no controllers are registered
|
||||||
|
* You will then be prompted for the connection information to the controller, if it's successful then a node will be added to the tree for that controller
|
||||||
|
* Right click on one of the nodes to launch a dashboard for that resource - either the Data Controller or an instance deployed on it
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import * as azurecore from '../../../azurecore/src/azurecore';
|
|||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { IconPathHelper, IconPath, ResourceType, Connectionmode } from '../constants';
|
import { IconPathHelper, IconPath, ResourceType, Connectionmode } from '../constants';
|
||||||
|
|
||||||
|
export class UserCancelledError extends Error { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the resource type name into the localized Display Name for that type.
|
* Converts the resource type name into the localized Display Name for that type.
|
||||||
* @param resourceType The resource type name to convert
|
* @param resourceType The resource type name to convert
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ export const lastUpdated = localize('arc.lastUpdated', "Last updated");
|
|||||||
|
|
||||||
export function databaseCreated(name: string): string { return localize('arc.databaseCreated', "Database {0} created", name); }
|
export function databaseCreated(name: string): string { return localize('arc.databaseCreated', "Database {0} created", name); }
|
||||||
export function resourceDeleted(name: string): string { return localize('arc.resourceDeleted', "Resource '{0}' deleted", name); }
|
export function resourceDeleted(name: string): string { return localize('arc.resourceDeleted', "Resource '{0}' deleted", name); }
|
||||||
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
|
||||||
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
||||||
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
||||||
export function numVCores(vCores: string | undefined): string {
|
export function numVCores(vCores: string | undefined): string {
|
||||||
@@ -130,15 +129,20 @@ export function numVCores(vCores: string | undefined): string {
|
|||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function couldNotFindRegistration(namespace: string, name: string) { return localize('arc.couldNotFindRegistration', "Could not find controller registration for {0} ({1})", name, namespace); }
|
|
||||||
export function resourceDeletionWarning(namespace: string, name: string): string { return localize('arc.resourceDeletionWarning', "Warning! Deleting a resource is permanent and cannot be undone. To delete the resource '{0}.{1}' type the name '{1}' below to proceed.", namespace, 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 updated(when: string): string { return localize('arc.updated', "Updated {0}", when); }
|
export function updated(when: string): string { return localize('arc.updated', "Updated {0}", when); }
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
|
export const connectionRequired = localize('arc.connectionRequired', "A connection is required to show all properties. Click refresh to re-enter connection information");
|
||||||
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
||||||
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
||||||
export function resourceDeletionFailed(name: string, error: any): string { return localize('arc.resourceDeletionFailed', "Failed to delete resource {0}. {1}", name, getErrorMessage(error)); }
|
export function resourceDeletionFailed(name: string, error: any): string { return localize('arc.resourceDeletionFailed', "Failed to delete resource {0}. {1}", name, getErrorMessage(error)); }
|
||||||
export function databaseCreationFailed(name: string, error: any): string { return localize('arc.databaseCreationFailed', "Failed to create database {0}. {1}", name, getErrorMessage(error)); }
|
export function databaseCreationFailed(name: string, error: any): string { return localize('arc.databaseCreationFailed', "Failed to create database {0}. {1}", name, getErrorMessage(error)); }
|
||||||
export function connectToControllerFailed(url: string, error: any): string { return localize('arc.connectToControllerFailed', "Could not connect to controller {0}. {1}", url, getErrorMessage(error)); }
|
export function connectToControllerFailed(url: string, error: any): string { return localize('arc.connectToControllerFailed', "Could not connect to controller {0}. {1}", url, getErrorMessage(error)); }
|
||||||
|
export function fetchStatusFailed(name: string, error: any): string { return localize('arc.fetchStatusFailed', "An unexpected error occured retrieving the status for resource '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
|
export function fetchEndpointsFailed(name: string, error: any): string { return localize('arc.fetchEndpointsFailed', "An unexpected error occured retrieving the endpoints for '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
|
export function fetchRegistrationsFailed(name: string, error: any): string { return localize('arc.fetchRegistrationsFailed', "An unexpected error occured retrieving the registrations for '{0}'. {1}", name, getErrorMessage(error)); }
|
||||||
|
export function couldNotFindRegistration(namespace: string, name: string) { return localize('arc.couldNotFindRegistration', "Could not find controller registration for {0} ({1})", name, namespace); }
|
||||||
|
export function resourceDeletionWarning(namespace: string, name: string): string { return localize('arc.resourceDeletionWarning', "Warning! Deleting a resource is permanent and cannot be undone. To delete the resource '{0}.{1}' type the name '{1}' below to proceed.", namespace, 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 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)); }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { parseEndpoint, parseInstanceName } 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';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
|
||||||
export type ControllerInfo = {
|
export type ControllerInfo = {
|
||||||
url: string,
|
url: string,
|
||||||
@@ -88,6 +89,13 @@ export class ControllerModel {
|
|||||||
this._endpoints = response.body;
|
this._endpoints = response.body;
|
||||||
this.endpointsLastUpdated = new Date();
|
this.endpointsLastUpdated = new Date();
|
||||||
this._onEndpointsUpdated.fire(this._endpoints);
|
this._onEndpointsUpdated.fire(this._endpoints);
|
||||||
|
}).catch(err => {
|
||||||
|
// If an error occurs show a message so the user knows something failed but still
|
||||||
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
|
// loading icon forever)
|
||||||
|
vscode.window.showErrorMessage(loc.fetchEndpointsFailed(this.info.url, err));
|
||||||
|
this._onEndpointsUpdated.fire(this._endpoints);
|
||||||
|
throw err;
|
||||||
}),
|
}),
|
||||||
this._tokenRouter.apiV1TokenPost().then(async response => {
|
this._tokenRouter.apiV1TokenPost().then(async response => {
|
||||||
this._namespace = response.body.namespace!;
|
this._namespace = response.body.namespace!;
|
||||||
@@ -95,7 +103,14 @@ export class ControllerModel {
|
|||||||
this._controllerRegistration = this._registrations.find(r => r.instanceType === ResourceType.dataControllers);
|
this._controllerRegistration = this._registrations.find(r => r.instanceType === ResourceType.dataControllers);
|
||||||
this.registrationsLastUpdated = new Date();
|
this.registrationsLastUpdated = new Date();
|
||||||
this._onRegistrationsUpdated.fire(this._registrations);
|
this._onRegistrationsUpdated.fire(this._registrations);
|
||||||
})
|
}).catch(err => {
|
||||||
|
// If an error occurs show a message so the user knows something failed but still
|
||||||
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
|
// loading icon forever)
|
||||||
|
vscode.window.showErrorMessage(loc.fetchRegistrationsFailed(this.info.url, err));
|
||||||
|
this._onRegistrationsUpdated.fire(this._registrations);
|
||||||
|
throw err;
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { ResourceModel } from './resourceModel';
|
|||||||
import { ResourceInfo, Registration } from './controllerModel';
|
import { ResourceInfo, Registration } from './controllerModel';
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
import { UserCancelledError } from '../common/utils';
|
||||||
|
|
||||||
export type DatabaseModel = { name: string, status: string };
|
export type DatabaseModel = { name: string, status: string };
|
||||||
|
|
||||||
@@ -26,7 +28,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
private _activeConnectionId: string | undefined = undefined;
|
private _activeConnectionId: string | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
|
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
|
||||||
private readonly _onStatusUpdated = new vscode.EventEmitter<HybridSqlNsNameGetResponse>();
|
private readonly _onStatusUpdated = new vscode.EventEmitter<HybridSqlNsNameGetResponse | undefined>();
|
||||||
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
||||||
public onPasswordUpdated = this._onPasswordUpdated.event;
|
public onPasswordUpdated = this._onPasswordUpdated.event;
|
||||||
public onStatusUpdated = this._onStatusUpdated.event;
|
public onStatusUpdated = this._onStatusUpdated.event;
|
||||||
@@ -77,36 +79,61 @@ export class MiaaModel extends ResourceModel {
|
|||||||
const instanceRefresh = this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this.info.namespace, this.info.name).then(response => {
|
const instanceRefresh = this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this.info.namespace, this.info.name).then(response => {
|
||||||
this._status = response.body;
|
this._status = response.body;
|
||||||
this._onStatusUpdated.fire(this._status);
|
this._onStatusUpdated.fire(this._status);
|
||||||
|
}).catch(err => {
|
||||||
|
// If an error occurs show a message so the user knows something failed but still
|
||||||
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
|
// loading icon forever)
|
||||||
|
vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err));
|
||||||
|
this._onStatusUpdated.fire(undefined);
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
const promises: Thenable<any>[] = [instanceRefresh];
|
const promises: Thenable<any>[] = [instanceRefresh];
|
||||||
await this.getConnectionProfile();
|
try {
|
||||||
if (this._connectionProfile) {
|
await this.getConnectionProfile();
|
||||||
// We haven't connected yet so do so now and then store the ID for the active connection
|
if (this._connectionProfile) {
|
||||||
if (!this._activeConnectionId) {
|
// We haven't connected yet so do so now and then store the ID for the active connection
|
||||||
const result = await azdata.connection.connect(this._connectionProfile, false, false);
|
if (!this._activeConnectionId) {
|
||||||
if (!result.connected) {
|
const result = await azdata.connection.connect(this._connectionProfile, false, false);
|
||||||
throw new Error(result.errorMessage);
|
if (!result.connected) {
|
||||||
|
throw new Error(result.errorMessage);
|
||||||
|
}
|
||||||
|
this._activeConnectionId = result.connectionId;
|
||||||
}
|
}
|
||||||
this._activeConnectionId = result.connectionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile.providerName, azdata.DataProviderType.MetadataProvider);
|
const provider = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(this._connectionProfile.providerName, azdata.DataProviderType.MetadataProvider);
|
||||||
const databasesRefresh = azdata.connection.getUriForConnection(this._activeConnectionId).then(ownerUri => {
|
const databasesRefresh = azdata.connection.getUriForConnection(this._activeConnectionId).then(ownerUri => {
|
||||||
provider.getDatabases(ownerUri).then(databases => {
|
provider.getDatabases(ownerUri).then(databases => {
|
||||||
if (!databases) {
|
if (!databases) {
|
||||||
throw new Error('Could not fetch databases');
|
throw new Error('Could not fetch databases');
|
||||||
}
|
}
|
||||||
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
if (databases.length > 0 && typeof (databases[0]) === 'object') {
|
||||||
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
this._databases = (<azdata.DatabaseInfo[]>databases).map(db => { return { name: db.options['name'], status: db.options['state'] }; });
|
||||||
} else {
|
} else {
|
||||||
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
this._databases = (<string[]>databases).map(db => { return { name: db, status: '-' }; });
|
||||||
}
|
}
|
||||||
this._onDatabasesUpdated.fire(this._databases);
|
this._onDatabasesUpdated.fire(this._databases);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
promises.push(databasesRefresh);
|
||||||
promises.push(databasesRefresh);
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// If an error occurs show a message so the user knows something failed but still
|
||||||
|
// fire the event so callers can know to update (e.g. so dashboards don't show the
|
||||||
|
// loading icon forever)
|
||||||
|
if (err instanceof UserCancelledError) {
|
||||||
|
vscode.window.showErrorMessage(loc.connectionRequired);
|
||||||
|
} else {
|
||||||
|
vscode.window.showErrorMessage(loc.fetchStatusFailed(this.info.name, err));
|
||||||
|
}
|
||||||
|
this._onDatabasesUpdated.fire(this._databases);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
this._refreshPromise.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
this._refreshPromise.reject(err);
|
||||||
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
this._refreshPromise = undefined;
|
this._refreshPromise = undefined;
|
||||||
}
|
}
|
||||||
@@ -133,7 +160,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
connection = existingConnection;
|
connection = existingConnection;
|
||||||
} else {
|
} else {
|
||||||
// We need the password so prompt the user for it
|
// We need the password so prompt the user for it
|
||||||
const connectionProfile = {
|
const connectionProfile: azdata.IConnectionProfile = {
|
||||||
serverName: existingConnection.options['serverName'],
|
serverName: existingConnection.options['serverName'],
|
||||||
databaseName: existingConnection.options['databaseName'],
|
databaseName: existingConnection.options['databaseName'],
|
||||||
authenticationType: existingConnection.options['authenticationType'],
|
authenticationType: existingConnection.options['authenticationType'],
|
||||||
@@ -162,7 +189,8 @@ export class MiaaModel extends ResourceModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (connection) {
|
if (connection) {
|
||||||
this._connectionProfile = {
|
const profile = {
|
||||||
|
// The option name might be different here based on where it came from
|
||||||
serverName: connection.options['serverName'] || connection.options['server'],
|
serverName: connection.options['serverName'] || connection.options['server'],
|
||||||
databaseName: connection.options['databaseName'] || connection.options['database'],
|
databaseName: connection.options['databaseName'] || connection.options['database'],
|
||||||
authenticationType: connection.options['authenticationType'],
|
authenticationType: connection.options['authenticationType'],
|
||||||
@@ -177,9 +205,15 @@ export class MiaaModel extends ResourceModel {
|
|||||||
groupId: undefined,
|
groupId: undefined,
|
||||||
options: connection.options
|
options: connection.options
|
||||||
};
|
};
|
||||||
this.info.connectionId = connection.connectionId;
|
this.updateConnectionProfile(profile);
|
||||||
await this._treeDataProvider.saveControllers();
|
} else {
|
||||||
|
throw new UserCancelledError();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateConnectionProfile(connectionProfile: azdata.IConnectionProfile): Promise<void> {
|
||||||
|
this._connectionProfile = connectionProfile;
|
||||||
|
this.info.connectionId = connectionProfile.id;
|
||||||
|
await this._treeDataProvider.saveControllers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,21 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { InitializingComponent } from './initializingComponent';
|
import { InitializingComponent } from './initializingComponent';
|
||||||
|
|
||||||
export abstract class DashboardPage extends InitializingComponent {
|
export abstract class DashboardPage extends InitializingComponent {
|
||||||
|
|
||||||
|
protected disposables: vscode.Disposable[] = [];
|
||||||
|
|
||||||
constructor(protected modelView: azdata.ModelView) {
|
constructor(protected modelView: azdata.ModelView) {
|
||||||
super();
|
super();
|
||||||
|
this.disposables.push(modelView.onClosed(() => {
|
||||||
|
// Clean up best we can
|
||||||
|
this.disposables.forEach(d => {
|
||||||
|
try { d.dispose(); } catch { }
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get tab(): azdata.DashboardTab {
|
public get tab(): azdata.DashboardTab {
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ export class ControllerDashboard extends Dashboard {
|
|||||||
super(loc.arcControllerDashboard);
|
super(loc.arcControllerDashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showDashboard(): Promise<void> {
|
||||||
|
await super.showDashboard();
|
||||||
|
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
||||||
|
this._controllerModel.refresh().catch(err => console.log(`Error refreshing Controller dashboard ${err}`));
|
||||||
|
}
|
||||||
|
|
||||||
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
||||||
const overviewPage = new ControllerDashboardOverviewPage(modelView, this._controllerModel);
|
const overviewPage = new ControllerDashboardOverviewPage(modelView, this._controllerModel);
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -145,28 +145,49 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.add
|
iconPath: IconPathHelper.add
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
newInstance.onDidClick(async () => {
|
this.disposables.push(
|
||||||
await vscode.commands.executeCommand('azdata.resource.deploy');
|
newInstance.onDidClick(async () => {
|
||||||
});
|
await vscode.commands.executeCommand('azdata.resource.deploy');
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Refresh
|
||||||
|
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
|
label: loc.refresh,
|
||||||
|
iconPath: IconPathHelper.refresh
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
refreshButton.onDidClick(async () => {
|
||||||
|
refreshButton.enabled = false;
|
||||||
|
try {
|
||||||
|
this._propertiesLoadingComponent!.loading = true;
|
||||||
|
this._arcResourcesLoadingComponent!.loading = true;
|
||||||
|
await this._controllerModel.refresh();
|
||||||
|
} finally {
|
||||||
|
refreshButton.enabled = true;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab
|
iconPath: IconPathHelper.openInTab
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
openInAzurePortalButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
const r = this._controllerModel.controllerRegistration;
|
openInAzurePortalButton.onDidClick(async () => {
|
||||||
if (r) {
|
const r = this._controllerModel.controllerRegistration;
|
||||||
vscode.env.openExternal(vscode.Uri.parse(
|
if (r) {
|
||||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.dataControllers}/${r.instanceName}`));
|
vscode.env.openExternal(vscode.Uri.parse(
|
||||||
} else {
|
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.dataControllers}/${r.instanceName}`));
|
||||||
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._controllerModel.namespace, 'controller'));
|
} else {
|
||||||
}
|
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._controllerModel.namespace, 'controller'));
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
||||||
[
|
[
|
||||||
{ component: newInstance, toolbarSeparatorAfter: true },
|
{ component: newInstance },
|
||||||
|
{ component: refreshButton, toolbarSeparatorAfter: true },
|
||||||
{ component: openInAzurePortalButton }
|
{ component: openInAzurePortalButton }
|
||||||
]
|
]
|
||||||
).component();
|
).component();
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ export class MiaaConnectionStringsPage extends DashboardPage {
|
|||||||
|
|
||||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
this._controllerModel.onRegistrationsUpdated(_ => {
|
this.disposables.push(this._controllerModel.onRegistrationsUpdated(_ => {
|
||||||
this._instanceRegistration = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
this._instanceRegistration = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
this.eventuallyRunOnInitialized(() => this.updateConnectionStrings());
|
this.eventuallyRunOnInitialized(() => this.updateConnectionStrings());
|
||||||
});
|
}));
|
||||||
this.refresh().catch(err => console.error(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async refresh(): Promise<void> {
|
protected async refresh(): Promise<void> {
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ export class MiaaDashboard extends Dashboard {
|
|||||||
super(loc.miaaDashboard);
|
super(loc.miaaDashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showDashboard(): Promise<void> {
|
||||||
|
await super.showDashboard();
|
||||||
|
// Kick off the model refreshes but don't wait on it since that's all handled with callbacks anyways
|
||||||
|
this._controllerModel.refresh().catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`));
|
||||||
|
this._miaaModel.refresh().catch(err => console.log(`Error refreshing MIAA model for MIAA dashboard ${err}`));
|
||||||
|
}
|
||||||
|
|
||||||
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
||||||
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel, this._miaaModel);
|
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel, this._miaaModel);
|
||||||
const connectionStringsPage = new MiaaConnectionStringsPage(modelView, this._controllerModel, this._miaaModel);
|
const connectionStringsPage = new MiaaConnectionStringsPage(modelView, this._controllerModel, this._miaaModel);
|
||||||
|
|||||||
@@ -40,18 +40,16 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
||||||
this._controllerModel.onRegistrationsUpdated((_: Registration[]) => {
|
this.disposables.push(
|
||||||
this.eventuallyRunOnInitialized(() => {
|
this._controllerModel.onRegistrationsUpdated((_: Registration[]) => {
|
||||||
this.handleRegistrationsUpdated().catch(e => console.log(e));
|
this.eventuallyRunOnInitialized(() => {
|
||||||
});
|
this.handleRegistrationsUpdated().catch(e => console.log(e));
|
||||||
});
|
});
|
||||||
this._controllerModel.onEndpointsUpdated(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated(endpoints)));
|
}),
|
||||||
this._miaaModel.onStatusUpdated(status => this.eventuallyRunOnInitialized(() => this.handleMiaaStatusUpdated(status)));
|
this._controllerModel.onEndpointsUpdated(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated(endpoints))),
|
||||||
this._miaaModel.onDatabasesUpdated(databases => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated(databases)));
|
this._miaaModel.onStatusUpdated(status => this.eventuallyRunOnInitialized(() => this.handleMiaaStatusUpdated(status))),
|
||||||
|
this._miaaModel.onDatabasesUpdated(databases => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated(databases)))
|
||||||
this.refresh().catch(e => {
|
);
|
||||||
console.log(e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get title(): string {
|
public get title(): string {
|
||||||
@@ -171,38 +169,65 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.delete
|
iconPath: IconPathHelper.delete
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
deleteButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
deleteButton.enabled = false;
|
deleteButton.onDidClick(async () => {
|
||||||
try {
|
deleteButton.enabled = false;
|
||||||
if (await promptForResourceDeletion(this._miaaModel.info.namespace, this._miaaModel.info.name)) {
|
try {
|
||||||
await this._controllerModel.miaaDelete(this._miaaModel.info.namespace, this._miaaModel.info.name);
|
if (await promptForResourceDeletion(this._miaaModel.info.namespace, this._miaaModel.info.name)) {
|
||||||
vscode.window.showInformationMessage(loc.resourceDeleted(this._miaaModel.info.name));
|
await this._controllerModel.miaaDelete(this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
|
vscode.window.showInformationMessage(loc.resourceDeleted(this._miaaModel.info.name));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._miaaModel.info.name, error));
|
||||||
|
} finally {
|
||||||
|
deleteButton.enabled = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}));
|
||||||
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._miaaModel.info.name, error));
|
|
||||||
} finally {
|
// Refresh
|
||||||
deleteButton.enabled = true;
|
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
}
|
label: loc.refresh,
|
||||||
});
|
iconPath: IconPathHelper.refresh
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
refreshButton.onDidClick(async () => {
|
||||||
|
refreshButton.enabled = false;
|
||||||
|
try {
|
||||||
|
this._propertiesLoading!.loading = true;
|
||||||
|
this._kibanaLoading!.loading = true;
|
||||||
|
this._grafanaLoading!.loading = true;
|
||||||
|
this._databasesTableLoading!.loading = true;
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
this._miaaModel.refresh(),
|
||||||
|
this._controllerModel.refresh()
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
refreshButton.enabled = true;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab
|
iconPath: IconPathHelper.openInTab
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
openInAzurePortalButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
const r = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
openInAzurePortalButton.onDidClick(async () => {
|
||||||
if (r) {
|
const r = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.info.namespace, this._miaaModel.info.name);
|
||||||
vscode.env.openExternal(vscode.Uri.parse(
|
if (r) {
|
||||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.sqlManagedInstances}/${r.instanceName}`));
|
vscode.env.openExternal(vscode.Uri.parse(
|
||||||
} else {
|
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.sqlManagedInstances}/${r.instanceName}`));
|
||||||
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._miaaModel.info.namespace, this._miaaModel.info.name));
|
} else {
|
||||||
}
|
vscode.window.showErrorMessage(loc.couldNotFindRegistration(this._miaaModel.info.namespace, this._miaaModel.info.name));
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems(
|
||||||
[
|
[
|
||||||
{ component: deleteButton, toolbarSeparatorAfter: true },
|
{ component: deleteButton },
|
||||||
|
{ component: refreshButton, toolbarSeparatorAfter: true },
|
||||||
{ component: openInAzurePortalButton }
|
{ component: openInAzurePortalButton }
|
||||||
]
|
]
|
||||||
).component();
|
).component();
|
||||||
@@ -221,8 +246,8 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleMiaaStatusUpdated(status: HybridSqlNsNameGetResponse): Promise<void> {
|
private async handleMiaaStatusUpdated(status: HybridSqlNsNameGetResponse | undefined): Promise<void> {
|
||||||
this._instanceProperties.status = status.status || '-';
|
this._instanceProperties.status = status?.status || '-';
|
||||||
this.refreshDisplayedProperties();
|
this.refreshDisplayedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,18 +12,11 @@ import { DashboardPage } from '../../components/dashboardPage';
|
|||||||
import { PostgresModel } from '../../../models/postgresModel';
|
import { PostgresModel } from '../../../models/postgresModel';
|
||||||
|
|
||||||
export class PostgresConnectionStringsPage extends DashboardPage {
|
export class PostgresConnectionStringsPage extends DashboardPage {
|
||||||
private disposables: vscode.Disposable[] = [];
|
|
||||||
private keyValueContainer?: KeyValueContainer;
|
private keyValueContainer?: KeyValueContainer;
|
||||||
|
|
||||||
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
|
|
||||||
modelView.onClosed(() =>
|
|
||||||
this.disposables.forEach(d => {
|
|
||||||
try { d.dispose(); }
|
|
||||||
catch { }
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||||
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
||||||
}
|
}
|
||||||
@@ -77,16 +70,17 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
refreshButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
refreshButton.enabled = false;
|
refreshButton.onDidClick(async () => {
|
||||||
try {
|
refreshButton.enabled = false;
|
||||||
await this._postgresModel.refresh();
|
try {
|
||||||
} catch (error) {
|
await this._postgresModel.refresh();
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
} catch (error) {
|
||||||
} finally {
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
refreshButton.enabled = true;
|
} finally {
|
||||||
}
|
refreshButton.enabled = true;
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
{ component: refreshButton }
|
{ component: refreshButton }
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ export class PostgresDashboard extends Dashboard {
|
|||||||
super(loc.postgresDashboard);
|
super(loc.postgresDashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showDashboard(): Promise<void> {
|
||||||
|
await super.showDashboard();
|
||||||
|
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
||||||
|
this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres dashboard ${err}`));
|
||||||
|
}
|
||||||
|
|
||||||
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
||||||
const overviewPage = new PostgresOverviewPage(modelView, this._controllerModel, this._postgresModel);
|
const overviewPage = new PostgresOverviewPage(modelView, this._controllerModel, this._postgresModel);
|
||||||
const connectionStringsPage = new PostgresConnectionStringsPage(modelView, this._postgresModel);
|
const connectionStringsPage = new PostgresConnectionStringsPage(modelView, this._postgresModel);
|
||||||
|
|||||||
@@ -48,11 +48,12 @@ export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
|
|||||||
width: '160px'
|
width: '160px'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
troubleshootButton.onDidClick(() => {
|
this.disposables.push(
|
||||||
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace;
|
troubleshootButton.onDidClick(() => {
|
||||||
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name;
|
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace;
|
||||||
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arcDataServices'), true, 'postgres/tsg100-troubleshoot-postgres');
|
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name;
|
||||||
});
|
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arcDataServices'), true, 'postgres/tsg100-troubleshoot-postgres');
|
||||||
|
}));
|
||||||
|
|
||||||
content.addItem(troubleshootButton);
|
content.addItem(troubleshootButton);
|
||||||
return root;
|
return root;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { PostgresModel, PodRole } from '../../../models/postgresModel';
|
|||||||
import { promptForResourceDeletion, promptAndConfirmPassword } from '../../../common/utils';
|
import { promptForResourceDeletion, promptAndConfirmPassword } from '../../../common/utils';
|
||||||
|
|
||||||
export class PostgresOverviewPage extends DashboardPage {
|
export class PostgresOverviewPage extends DashboardPage {
|
||||||
private disposables: vscode.Disposable[] = [];
|
|
||||||
|
|
||||||
private propertiesLoading?: azdata.LoadingComponent;
|
private propertiesLoading?: azdata.LoadingComponent;
|
||||||
private kibanaLoading?: azdata.LoadingComponent;
|
private kibanaLoading?: azdata.LoadingComponent;
|
||||||
@@ -29,29 +28,27 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
|
|
||||||
modelView.onClosed(() =>
|
this.disposables.push(
|
||||||
this.disposables.forEach(d => {
|
this._controllerModel.onEndpointsUpdated(
|
||||||
try { d.dispose(); }
|
() => this.eventuallyRunOnInitialized(() => this.refreshEndpoints())));
|
||||||
catch { }
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.disposables.push(this._controllerModel.onEndpointsUpdated(
|
this.disposables.push(
|
||||||
() => this.eventuallyRunOnInitialized(() => this.refreshEndpoints())));
|
this._controllerModel.onRegistrationsUpdated(
|
||||||
|
() => this.eventuallyRunOnInitialized(() => this.refreshProperties())));
|
||||||
|
|
||||||
this.disposables.push(this._controllerModel.onRegistrationsUpdated(
|
this.disposables.push(
|
||||||
() => this.eventuallyRunOnInitialized(() => this.refreshProperties())));
|
this._postgresModel.onServiceUpdated(
|
||||||
|
() => this.eventuallyRunOnInitialized(() => {
|
||||||
|
this.refreshProperties();
|
||||||
|
this.refreshNodes();
|
||||||
|
})));
|
||||||
|
|
||||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
this.disposables.push(
|
||||||
() => this.eventuallyRunOnInitialized(() => {
|
this._postgresModel.onPodsUpdated(
|
||||||
this.refreshProperties();
|
() => this.eventuallyRunOnInitialized(() => {
|
||||||
this.refreshNodes();
|
this.refreshProperties();
|
||||||
})));
|
this.refreshNodes();
|
||||||
|
})));
|
||||||
this.disposables.push(this._postgresModel.onPodsUpdated(
|
|
||||||
() => this.eventuallyRunOnInitialized(() => {
|
|
||||||
this.refreshProperties();
|
|
||||||
this.refreshNodes();
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get title(): string {
|
protected get title(): string {
|
||||||
@@ -186,22 +183,23 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.add
|
iconPath: IconPathHelper.add
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
newDatabaseButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
newDatabaseButton.enabled = false;
|
newDatabaseButton.onDidClick(async () => {
|
||||||
let name;
|
newDatabaseButton.enabled = false;
|
||||||
try {
|
let name;
|
||||||
name = await vscode.window.showInputBox({ prompt: loc.databaseName });
|
try {
|
||||||
if (name) {
|
name = await vscode.window.showInputBox({ prompt: loc.databaseName });
|
||||||
const db: DuskyObjectModelsDatabase = { name: name }; // TODO support other options (sharded, owner)
|
if (name) {
|
||||||
await this._postgresModel.createDatabase(db);
|
const db: DuskyObjectModelsDatabase = { name: name }; // TODO support other options (sharded, owner)
|
||||||
vscode.window.showInformationMessage(loc.databaseCreated(db.name ?? ''));
|
await this._postgresModel.createDatabase(db);
|
||||||
|
vscode.window.showInformationMessage(loc.databaseCreated(db.name ?? ''));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window.showErrorMessage(loc.databaseCreationFailed(name ?? '', error));
|
||||||
|
} finally {
|
||||||
|
newDatabaseButton.enabled = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}));
|
||||||
vscode.window.showErrorMessage(loc.databaseCreationFailed(name ?? '', error));
|
|
||||||
} finally {
|
|
||||||
newDatabaseButton.enabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset password
|
// Reset password
|
||||||
const resetPasswordButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const resetPasswordButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
@@ -209,23 +207,24 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.edit
|
iconPath: IconPathHelper.edit
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
resetPasswordButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
resetPasswordButton.enabled = false;
|
resetPasswordButton.onDidClick(async () => {
|
||||||
try {
|
resetPasswordButton.enabled = false;
|
||||||
const password = await promptAndConfirmPassword(input => !input ? loc.enterANonEmptyPassword : '');
|
try {
|
||||||
if (password) {
|
const password = await promptAndConfirmPassword(input => !input ? loc.enterANonEmptyPassword : '');
|
||||||
await this._postgresModel.update(s => {
|
if (password) {
|
||||||
s.arc = s.arc ?? new DuskyObjectModelsDatabaseServiceArcPayload();
|
await this._postgresModel.update(s => {
|
||||||
s.arc.servicePassword = password;
|
s.arc = s.arc ?? new DuskyObjectModelsDatabaseServiceArcPayload();
|
||||||
});
|
s.arc.servicePassword = password;
|
||||||
vscode.window.showInformationMessage(loc.passwordReset);
|
});
|
||||||
|
vscode.window.showInformationMessage(loc.passwordReset);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window.showErrorMessage(loc.passwordResetFailed(error));
|
||||||
|
} finally {
|
||||||
|
resetPasswordButton.enabled = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}));
|
||||||
vscode.window.showErrorMessage(loc.passwordResetFailed(error));
|
|
||||||
} finally {
|
|
||||||
resetPasswordButton.enabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete service
|
// Delete service
|
||||||
const deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
@@ -233,19 +232,20 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.delete
|
iconPath: IconPathHelper.delete
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
deleteButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
deleteButton.enabled = false;
|
deleteButton.onDidClick(async () => {
|
||||||
try {
|
deleteButton.enabled = false;
|
||||||
if (await promptForResourceDeletion(this._postgresModel.namespace, this._postgresModel.name)) {
|
try {
|
||||||
await this._postgresModel.delete();
|
if (await promptForResourceDeletion(this._postgresModel.namespace, this._postgresModel.name)) {
|
||||||
vscode.window.showInformationMessage(loc.resourceDeleted(this._postgresModel.fullName));
|
await this._postgresModel.delete();
|
||||||
|
vscode.window.showInformationMessage(loc.resourceDeleted(this._postgresModel.fullName));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._postgresModel.fullName, error));
|
||||||
|
} finally {
|
||||||
|
deleteButton.enabled = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}));
|
||||||
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._postgresModel.fullName, error));
|
|
||||||
} finally {
|
|
||||||
deleteButton.enabled = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
@@ -253,25 +253,26 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
refreshButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
refreshButton.enabled = false;
|
refreshButton.onDidClick(async () => {
|
||||||
try {
|
refreshButton.enabled = false;
|
||||||
this.propertiesLoading!.loading = true;
|
try {
|
||||||
this.kibanaLoading!.loading = true;
|
this.propertiesLoading!.loading = true;
|
||||||
this.grafanaLoading!.loading = true;
|
this.kibanaLoading!.loading = true;
|
||||||
this.nodesTableLoading!.loading = true;
|
this.grafanaLoading!.loading = true;
|
||||||
|
this.nodesTableLoading!.loading = true;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._postgresModel.refresh(),
|
this._postgresModel.refresh(),
|
||||||
this._controllerModel.refresh()
|
this._controllerModel.refresh()
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
refreshButton.enabled = true;
|
refreshButton.enabled = true;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Open in Azure portal
|
// Open in Azure portal
|
||||||
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
@@ -279,15 +280,16 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.openInTab
|
iconPath: IconPathHelper.openInTab
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
openInAzurePortalButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
openInAzurePortalButton.onDidClick(async () => {
|
||||||
if (!r) {
|
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
if (!r) {
|
||||||
} else {
|
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
||||||
vscode.env.openExternal(vscode.Uri.parse(
|
} else {
|
||||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}`));
|
vscode.env.openExternal(vscode.Uri.parse(
|
||||||
}
|
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}`));
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
{ component: newDatabaseButton },
|
{ component: newDatabaseButton },
|
||||||
|
|||||||
@@ -13,18 +13,11 @@ import { ControllerModel } from '../../../models/controllerModel';
|
|||||||
import { PostgresModel } from '../../../models/postgresModel';
|
import { PostgresModel } from '../../../models/postgresModel';
|
||||||
|
|
||||||
export class PostgresPropertiesPage extends DashboardPage {
|
export class PostgresPropertiesPage extends DashboardPage {
|
||||||
private disposables: vscode.Disposable[] = [];
|
|
||||||
private keyValueContainer?: KeyValueContainer;
|
private keyValueContainer?: KeyValueContainer;
|
||||||
|
|
||||||
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
|
|
||||||
modelView.onClosed(() =>
|
|
||||||
this.disposables.forEach(d => {
|
|
||||||
try { d.dispose(); }
|
|
||||||
catch { }
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||||
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
||||||
|
|
||||||
@@ -66,20 +59,21 @@ export class PostgresPropertiesPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
refreshButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
refreshButton.enabled = false;
|
refreshButton.onDidClick(async () => {
|
||||||
try {
|
refreshButton.enabled = false;
|
||||||
await Promise.all([
|
try {
|
||||||
this._postgresModel.refresh(),
|
await Promise.all([
|
||||||
this._controllerModel.refresh()
|
this._postgresModel.refresh(),
|
||||||
]);
|
this._controllerModel.refresh()
|
||||||
} catch (error) {
|
]);
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
} catch (error) {
|
||||||
}
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
finally {
|
}
|
||||||
refreshButton.enabled = true;
|
finally {
|
||||||
}
|
refreshButton.enabled = true;
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
{ component: refreshButton }
|
{ component: refreshButton }
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { PostgresModel } from '../../../models/postgresModel';
|
|||||||
import { fromNow } from '../../../common/date';
|
import { fromNow } from '../../../common/date';
|
||||||
|
|
||||||
export class PostgresResourceHealthPage extends DashboardPage {
|
export class PostgresResourceHealthPage extends DashboardPage {
|
||||||
private disposables: vscode.Disposable[] = [];
|
|
||||||
private interval: NodeJS.Timeout;
|
private interval: NodeJS.Timeout;
|
||||||
private podsUpdated?: azdata.TextComponent;
|
private podsUpdated?: azdata.TextComponent;
|
||||||
private podsTable?: azdata.DeclarativeTableComponent;
|
private podsTable?: azdata.DeclarativeTableComponent;
|
||||||
@@ -21,15 +20,11 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
||||||
super(modelView);
|
super(modelView);
|
||||||
|
|
||||||
modelView.onClosed(() => {
|
this.disposables.push(
|
||||||
try { clearInterval(this.interval); }
|
modelView.onClosed(() => {
|
||||||
catch { }
|
try { clearInterval(this.interval); }
|
||||||
|
|
||||||
this.disposables.forEach(d => {
|
|
||||||
try { d.dispose(); }
|
|
||||||
catch { }
|
catch { }
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||||
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
() => this.eventuallyRunOnInitialized(() => this.refresh())));
|
||||||
@@ -144,16 +139,17 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
refreshButton.onDidClick(async () => {
|
this.disposables.push(
|
||||||
refreshButton.enabled = false;
|
refreshButton.onDidClick(async () => {
|
||||||
try {
|
refreshButton.enabled = false;
|
||||||
await this._postgresModel.refresh();
|
try {
|
||||||
} catch (error) {
|
await this._postgresModel.refresh();
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
} catch (error) {
|
||||||
} finally {
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
refreshButton.enabled = true;
|
} finally {
|
||||||
}
|
refreshButton.enabled = true;
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
{ component: refreshButton }
|
{ component: refreshButton }
|
||||||
|
|||||||
@@ -49,15 +49,16 @@ export class PostgresSupportRequestPage extends DashboardPage {
|
|||||||
width: '205px'
|
width: '205px'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
supportRequestButton.onDidClick(() => {
|
this.disposables.push(
|
||||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
supportRequestButton.onDidClick(() => {
|
||||||
if (!r) {
|
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
if (!r) {
|
||||||
} else {
|
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
||||||
vscode.env.openExternal(vscode.Uri.parse(
|
} else {
|
||||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}/supportrequest`));
|
vscode.env.openExternal(vscode.Uri.parse(
|
||||||
}
|
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}/supportrequest`));
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
content.addItem(supportRequestButton);
|
content.addItem(supportRequestButton);
|
||||||
return root;
|
return root;
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ export class MiaaTreeNode extends TreeNode {
|
|||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
const miaaDashboard = new MiaaDashboard(this._controllerModel, this.model);
|
const miaaDashboard = new MiaaDashboard(this._controllerModel, this.model);
|
||||||
await Promise.all([
|
await miaaDashboard.showDashboard();
|
||||||
miaaDashboard.showDashboard(),
|
|
||||||
this.model.refresh()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ export class PostgresTreeNode extends ResourceTreeNode {
|
|||||||
|
|
||||||
public async openDashboard(): Promise<void> {
|
public async openDashboard(): Promise<void> {
|
||||||
const postgresDashboard = new PostgresDashboard(this._context, this._controllerModel, this._model);
|
const postgresDashboard = new PostgresDashboard(this._context, this._controllerModel, this._model);
|
||||||
await Promise.all([
|
await postgresDashboard.showDashboard();
|
||||||
postgresDashboard.showDashboard(),
|
|
||||||
this._model.refresh()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user