mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
Hook up MIAA dashboard overview (#10890)
* Hook up MIAA dashboard overview * Fix merge conflicts * Fix links * Remove extra &
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as azurecore from '../../../azurecore/src/azurecore';
|
||||
import * as loc from '../localizedConstants';
|
||||
|
||||
export enum ResourceType {
|
||||
@@ -35,3 +37,15 @@ export function parseEndpoint(endpoint?: string): { ip: string, port: string } {
|
||||
port: endpoint.substr(separatorIndex + 1)
|
||||
};
|
||||
}
|
||||
|
||||
let azurecoreApi: azurecore.IExtension;
|
||||
|
||||
export async function getAzurecoreApi(): Promise<azurecore.IExtension> {
|
||||
if (!azurecoreApi) {
|
||||
azurecoreApi = await vscode.extensions.getExtension(azurecore.extension.name)?.activate();
|
||||
if (!azurecoreApi) {
|
||||
throw new Error('Unable to retrieve azurecore API');
|
||||
}
|
||||
}
|
||||
return azurecoreApi;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import http = require('http');
|
||||
import { CreateSqlInstanceMessage } from '../model/createSqlInstanceMessage';
|
||||
import { Authentication, HttpBasicAuth, HttpBearerAuth, Interceptor, ObjectSerializer, VoidAuth } from '../model/models';
|
||||
import { HttpError } from './apis';
|
||||
import { HybridSqlNsNameGetResponse } from '../model/hybridSqlNsNameGetResponse';
|
||||
|
||||
|
||||
|
||||
@@ -307,7 +308,7 @@ export class SqlInstanceRouterApi {
|
||||
* @param ns
|
||||
* @param name
|
||||
*/
|
||||
public async apiV1HybridSqlNsNameGet (ns: string, name: string, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<{ response: http.IncomingMessage; body?: any; }> {
|
||||
public async apiV1HybridSqlNsNameGet (ns: string, name: string, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<{ response: http.IncomingMessage; body: HybridSqlNsNameGetResponse; }> {
|
||||
const localVarPath = this.basePath + '/api/v1/hybrid/sql/{ns}/{name}'
|
||||
.replace('{' + 'ns' + '}', encodeURIComponent(String(ns)))
|
||||
.replace('{' + 'name' + '}', encodeURIComponent(String(name)));
|
||||
@@ -360,11 +361,12 @@ export class SqlInstanceRouterApi {
|
||||
localVarRequestOptions.form = localVarFormParams;
|
||||
}
|
||||
}
|
||||
return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => {
|
||||
return new Promise<{ response: http.IncomingMessage; body: HybridSqlNsNameGetResponse; }>((resolve, reject) => {
|
||||
localVarRequest(localVarRequestOptions, (error, response, body) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
body = ObjectSerializer.deserialize(body, "HybridSqlNsNameGetResponse");
|
||||
if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
|
||||
resolve({ response: response, body: body });
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* SQL Server Big Data Cluster API
|
||||
* OpenAPI specification for **SQL Server Big Data Cluster**.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export class HybridSqlNsNameGetResponse {
|
||||
'name'?: string;
|
||||
'status'?: string;
|
||||
'cluster_endpoint'?: string;
|
||||
'external_endpoint'?: string;
|
||||
'service_name'?: string;
|
||||
'vcores'?: string;
|
||||
}
|
||||
|
||||
@@ -56,11 +56,12 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
||||
groupId: undefined,
|
||||
options: connection.options
|
||||
};
|
||||
const instanceNamespace = '';
|
||||
const instanceName = '';
|
||||
|
||||
try {
|
||||
const controllerModel = new ControllerModel(controllerUrl, auth);
|
||||
const miaaModel = new MiaaModel(connectionProfile, instanceName);
|
||||
const miaaModel = new MiaaModel(connectionProfile, controllerUrl, auth, instanceNamespace, instanceName);
|
||||
const miaaDashboard = new MiaaDashboard(controllerModel, miaaModel);
|
||||
|
||||
await Promise.all([
|
||||
|
||||
@@ -32,7 +32,6 @@ export const resetPassword = localize('arc.resetPassword', "Reset Password");
|
||||
export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal");
|
||||
export const resourceGroup = localize('arc.resourceGroup', "Resource Group");
|
||||
export const region = localize('arc.region', "Region");
|
||||
export const subscription = localize('arc.subscription', "Subscription");
|
||||
export const subscriptionId = localize('arc.subscriptionId', "Subscription ID");
|
||||
export const state = localize('arc.state', "State");
|
||||
export const connectionMode = localize('arc.connectionMode', "Connection Mode");
|
||||
@@ -41,12 +40,14 @@ export const host = localize('arc.host', "Host");
|
||||
export const name = localize('arc.name', "Name");
|
||||
export const type = localize('arc.type', "Type");
|
||||
export const status = localize('arc.status', "Status");
|
||||
export const miaaAdmin = localize('arc.miaaAdmin', "Managed instance admin");
|
||||
export const dataController = localize('arc.dataController', "Data controller");
|
||||
export const kibanaDashboard = localize('arc.kibanaDashboard', "Kibana Dashboard");
|
||||
export const grafanaDashboard = localize('arc.grafanaDashboard', "Grafana Dashboard");
|
||||
export const kibanaDashboardDescription = localize('arc.kibanaDashboardDescription', "Dashboard for viewing logs");
|
||||
export const grafanaDashboardDescription = localize('arc.grafanaDashboardDescription', "Dashboard for viewing metrics");
|
||||
export const serviceEndpoints = localize('arc.serviceEndpoints', "Service endpoints");
|
||||
export const databases = localize('arc.databases', "Databases");
|
||||
export const endpoint = localize('arc.endpoint', "Endpoint");
|
||||
export const description = localize('arc.description', "Description");
|
||||
export const yes = localize('arc.yes', "Yes");
|
||||
|
||||
@@ -61,19 +61,19 @@ export class ControllerModel {
|
||||
]);
|
||||
}
|
||||
|
||||
public endpoints(): EndpointModel[] {
|
||||
public get endpoints(): EndpointModel[] {
|
||||
return this._endpoints;
|
||||
}
|
||||
|
||||
public endpoint(name: string): EndpointModel | undefined {
|
||||
public getEndpoint(name: string): EndpointModel | undefined {
|
||||
return this._endpoints.find(e => e.name === name);
|
||||
}
|
||||
|
||||
public namespace(): string {
|
||||
public get namespace(): string {
|
||||
return this._namespace;
|
||||
}
|
||||
|
||||
public registrations(): Registration[] {
|
||||
public get registrations(): Registration[] {
|
||||
return this._registrations;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,24 +5,72 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { SqlInstanceRouterApi } from '../controller/generated/v1/api/sqlInstanceRouterApi';
|
||||
import { HybridSqlNsNameGetResponse } from '../controller/generated/v1/model/hybridSqlNsNameGetResponse';
|
||||
import { Authentication } from '../controller/generated/v1/api';
|
||||
|
||||
export type DatabaseModel = { name: string, status: string };
|
||||
|
||||
export class MiaaModel {
|
||||
|
||||
private _sqlInstanceRouter: SqlInstanceRouterApi;
|
||||
private _status: HybridSqlNsNameGetResponse | undefined;
|
||||
|
||||
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
|
||||
private readonly _onStatusUpdated = new vscode.EventEmitter<HybridSqlNsNameGetResponse>();
|
||||
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
||||
public onPasswordUpdated = this._onPasswordUpdated.event;
|
||||
public onStatusUpdated = this._onStatusUpdated.event;
|
||||
public onDatabasesUpdated = this._onDatabasesUpdated.event;
|
||||
public passwordLastUpdated?: Date;
|
||||
|
||||
constructor(public connectionProfile: azdata.IConnectionProfile, private _name: string) {
|
||||
constructor(public connectionProfile: azdata.IConnectionProfile, controllerUrl: string, auth: Authentication, private _namespace: string, private _name: string) {
|
||||
this._sqlInstanceRouter = new SqlInstanceRouterApi(controllerUrl);
|
||||
this._sqlInstanceRouter.setDefaultAuthentication(auth);
|
||||
}
|
||||
|
||||
/** Returns the service's name */
|
||||
/**
|
||||
* The name of this instance
|
||||
*/
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The namespace of this instance
|
||||
*/
|
||||
public get namespace(): string {
|
||||
return this._namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of this instance
|
||||
*/
|
||||
public get status(): string {
|
||||
return this._status?.status || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* The cluster endpoint of this instance
|
||||
*/
|
||||
public get clusterEndpoint(): string {
|
||||
return this._status?.cluster_endpoint || '';
|
||||
}
|
||||
|
||||
public get databases(): DatabaseModel[] {
|
||||
return [
|
||||
{ name: 'contosoMI54', status: 'online' },
|
||||
{ name: 'contosoMI56', status: 'online' },
|
||||
{ name: 'contosoMI58', status: 'online' },
|
||||
];
|
||||
}
|
||||
|
||||
/** Refreshes the model */
|
||||
public async refresh() {
|
||||
await Promise.all([
|
||||
]);
|
||||
public async refresh(): Promise<void> {
|
||||
this._sqlInstanceRouter.apiV1HybridSqlNsNameGet(this._namespace, this._name).then(response => {
|
||||
this._status = response.body;
|
||||
this._onStatusUpdated.fire(this._status);
|
||||
this._onDatabasesUpdated.fire(this.databases);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,32 +35,32 @@ export class PostgresModel {
|
||||
}
|
||||
|
||||
/** Returns the service's Kubernetes namespace */
|
||||
public namespace(): string {
|
||||
public get namespace(): string {
|
||||
return this._namespace;
|
||||
}
|
||||
|
||||
/** Returns the service's name */
|
||||
public name(): string {
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/** Returns the service's fully qualified name in the format namespace.name */
|
||||
public fullName(): string {
|
||||
public get fullName(): string {
|
||||
return `${this._namespace}.${this._name}`;
|
||||
}
|
||||
|
||||
/** Returns the service's spec */
|
||||
public service(): DuskyObjectModelsDatabaseService | undefined {
|
||||
public get service(): DuskyObjectModelsDatabaseService | undefined {
|
||||
return this._service;
|
||||
}
|
||||
|
||||
/** Returns the service's password */
|
||||
public password(): string | undefined {
|
||||
public get password(): string | undefined {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
/** Returns the service's pods */
|
||||
public pods(): V1Pod[] | undefined {
|
||||
public get pods(): V1Pod[] | undefined {
|
||||
return this._pods;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export class PostgresModel {
|
||||
service.status = undefined; // can't update the status
|
||||
func(service);
|
||||
|
||||
return await this._databaseRouter.updateDuskyDatabaseService(this.namespace(), this.name(), service).then(r => {
|
||||
return await this._databaseRouter.updateDuskyDatabaseService(this.namespace, this.name, service).then(r => {
|
||||
this._service = r.body;
|
||||
return this._service;
|
||||
});
|
||||
@@ -108,14 +108,14 @@ export class PostgresModel {
|
||||
|
||||
/** Creates a SQL database in the service */
|
||||
public async createDatabase(db: DuskyObjectModelsDatabase): Promise<DuskyObjectModelsDatabase> {
|
||||
return await (await this._databaseRouter.createDuskyDatabase(this.namespace(), this.name(), db)).body;
|
||||
return await (await this._databaseRouter.createDuskyDatabase(this.namespace, this.name, db)).body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP address and port of the service, preferring external IP over
|
||||
* internal IP. If either field is not available it will be set to undefined.
|
||||
*/
|
||||
public endpoint(): { ip?: string, port?: number } {
|
||||
public get endpoint(): { ip?: string, port?: number } {
|
||||
const externalIp = this._service?.status?.externalIP;
|
||||
const internalIp = this._service?.status?.internalIP;
|
||||
const externalPort = this._service?.status?.externalPort;
|
||||
@@ -127,7 +127,7 @@ export class PostgresModel {
|
||||
}
|
||||
|
||||
/** Returns the service's configuration e.g. '3 nodes, 1.5 vCores, 1GiB RAM, 2GiB storage per node' */
|
||||
public configuration(): string {
|
||||
public get configuration(): string {
|
||||
|
||||
// TODO: Resource requests and limits can be configured per role. Figure out how
|
||||
// to display that in the UI. For now, only show the default configuration.
|
||||
@@ -136,7 +136,7 @@ export class PostgresModel {
|
||||
const cpuRequest = this._service?.spec?.scheduling?._default?.resources?.requests?.['cpu'];
|
||||
const ramRequest = this._service?.spec?.scheduling?._default?.resources?.requests?.['memory'];
|
||||
const storage = this._service?.spec?.storage?.volumeSize;
|
||||
const nodes = this.pods()?.length;
|
||||
const nodes = this.pods?.length;
|
||||
|
||||
let configuration: string[] = [];
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as loc from '../../../localizedConstants';
|
||||
import { DashboardPage } from '../../components/dashboardPage';
|
||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||
import { ControllerModel } from '../../../models/controllerModel';
|
||||
import { resourceTypeToDisplayName, ResourceType } from '../../../common/utils';
|
||||
import { resourceTypeToDisplayName, ResourceType, getAzurecoreApi } from '../../../common/utils';
|
||||
import { RegistrationResponse } from '../../../controller/generated/v1/model/registrationResponse';
|
||||
|
||||
export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
@@ -21,7 +21,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
super(modelView);
|
||||
this._controllerModel.onRegistrationsUpdated((_: RegistrationResponse[]) => {
|
||||
this.eventuallyRunOnInitialized(() => {
|
||||
this.handleRegistrationsUpdated();
|
||||
this.handleRegistrationsUpdated().catch(e => console.log(e));
|
||||
});
|
||||
});
|
||||
this.refresh().catch(e => {
|
||||
@@ -141,7 +141,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
).component();
|
||||
}
|
||||
|
||||
private handleRegistrationsUpdated(): void {
|
||||
private async handleRegistrationsUpdated(): Promise<void> {
|
||||
const reg = this._controllerModel.controllerRegistration;
|
||||
if (reg) {
|
||||
this._propertiesContainer.propertyItems = [
|
||||
@@ -155,7 +155,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
},
|
||||
{
|
||||
displayName: loc.region,
|
||||
value: reg.location || '-'
|
||||
value: (await getAzurecoreApi()).getRegionDisplayName(reg.location) || '-'
|
||||
},
|
||||
{
|
||||
displayName: loc.subscriptionId,
|
||||
@@ -180,7 +180,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
];
|
||||
}
|
||||
|
||||
this._arcResourcesTable.data = this._controllerModel.registrations()
|
||||
this._arcResourcesTable.data = this._controllerModel.registrations
|
||||
.filter(r => r.instanceType !== ResourceType.dataControllers)
|
||||
.map(r => [r.instanceName, resourceTypeToDisplayName(r.instanceType), r.vCores]);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export class MiaaDashboard extends Dashboard {
|
||||
}
|
||||
|
||||
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
||||
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel);
|
||||
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel, this._miaaModel);
|
||||
const connectionStringsPage = new MiaaConnectionStringsPage(modelView, this._controllerModel, this._miaaModel);
|
||||
return [
|
||||
overviewPage.tab,
|
||||
|
||||
@@ -6,16 +6,48 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as loc from '../../../localizedConstants';
|
||||
import { DashboardPage } from '../../components/dashboardPage';
|
||||
import { IconPathHelper } from '../../../constants';
|
||||
import { ControllerModel } from '../../../models/controllerModel';
|
||||
import { resourceTypeToDisplayName } from '../../../common/utils';
|
||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||
import { ControllerModel, Registration } from '../../../models/controllerModel';
|
||||
import { ResourceType, getAzurecoreApi } from '../../../common/utils';
|
||||
import { MiaaModel, DatabaseModel } from '../../../models/miaaModel';
|
||||
import { HybridSqlNsNameGetResponse } from '../../../controller/generated/v1/model/hybridSqlNsNameGetResponse';
|
||||
import { EndpointModel } from '../../../controller/generated/v1/api';
|
||||
|
||||
export class MiaaDashboardOverviewPage extends DashboardPage {
|
||||
|
||||
private _arcResourcesTable!: azdata.DeclarativeTableComponent;
|
||||
private _propertiesLoading!: azdata.LoadingComponent;
|
||||
private _kibanaLoading!: azdata.LoadingComponent;
|
||||
private _grafanaLoading!: azdata.LoadingComponent;
|
||||
private _databasesTableLoading!: azdata.LoadingComponent;
|
||||
|
||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel) {
|
||||
private _propertiesContainer!: azdata.PropertiesContainerComponent;
|
||||
private _kibanaLink!: azdata.HyperlinkComponent;
|
||||
private _grafanaLink!: azdata.HyperlinkComponent;
|
||||
private _databasesTable!: azdata.DeclarativeTableComponent;
|
||||
|
||||
private _instanceProperties = {
|
||||
resourceGroup: '-',
|
||||
status: '-',
|
||||
dataController: '-',
|
||||
region: '-',
|
||||
subscriptionId: '-',
|
||||
miaaAdmin: '-',
|
||||
host: '-',
|
||||
computeAndStorage: '-'
|
||||
};
|
||||
|
||||
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||
super(modelView);
|
||||
this._instanceProperties.miaaAdmin = this._miaaModel.connectionProfile.userName;
|
||||
this._controllerModel.onRegistrationsUpdated((_: Registration[]) => {
|
||||
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._miaaModel.onDatabasesUpdated(databases => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated(databases)));
|
||||
|
||||
this.refresh().catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
@@ -34,8 +66,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
||||
}
|
||||
|
||||
protected async refresh(): Promise<void> {
|
||||
await this._controllerModel.refresh();
|
||||
this.eventuallyRunOnInitialized(() => this._arcResourcesTable.data = this._controllerModel.registrations().map(r => [r.instanceName, resourceTypeToDisplayName(r.instanceType), r.vCores]));
|
||||
await Promise.all([this._controllerModel.refresh(), this._miaaModel.refresh()]);
|
||||
}
|
||||
|
||||
public get container(): azdata.Component {
|
||||
@@ -45,76 +76,90 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
||||
.withProperties({ CSSStyles: { 'margin': '18px' } })
|
||||
.component();
|
||||
|
||||
const propertiesContainer = this.modelView.modelBuilder.propertiesContainer().withProperties<azdata.PropertiesContainerComponentProperties>({
|
||||
propertyItems: [
|
||||
{
|
||||
displayName: loc.resourceGroup,
|
||||
value: 'contosoRG123'
|
||||
},
|
||||
{
|
||||
displayName: loc.region,
|
||||
value: 'West US'
|
||||
},
|
||||
{
|
||||
displayName: loc.subscription,
|
||||
value: 'contososub5678'
|
||||
},
|
||||
{
|
||||
displayName: loc.subscriptionId,
|
||||
value: '88abe223-c630-4f2c-8782-00bb5be874f6'
|
||||
},
|
||||
{
|
||||
displayName: loc.state,
|
||||
value: 'Connected'
|
||||
},
|
||||
{
|
||||
displayName: loc.host,
|
||||
value: 'plainscluster.sqlarcdm.database.windows.net'
|
||||
}
|
||||
]
|
||||
}).component();
|
||||
// Properties
|
||||
this._propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component();
|
||||
this._propertiesLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._propertiesContainer).component();
|
||||
rootContainer.addItem(this._propertiesLoading, { CSSStyles: cssStyles.text });
|
||||
|
||||
rootContainer.addItem(propertiesContainer);
|
||||
// Service endpoints
|
||||
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
|
||||
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.serviceEndpoints, CSSStyles: titleCSS }).component());
|
||||
|
||||
const arcResourcesTitle = this.modelView.modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({ value: loc.arcResources })
|
||||
.component();
|
||||
this._kibanaLink = this.modelView.modelBuilder.hyperlink().component();
|
||||
this._grafanaLink = this.modelView.modelBuilder.hyperlink().component();
|
||||
this._kibanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._kibanaLink).component();
|
||||
this._grafanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._grafanaLink).component();
|
||||
|
||||
rootContainer.addItem(arcResourcesTitle, {
|
||||
CSSStyles: {
|
||||
'font-size': '14px'
|
||||
}
|
||||
});
|
||||
|
||||
this._arcResourcesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
data: [],
|
||||
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
width: '100%',
|
||||
columns: [
|
||||
{
|
||||
displayName: loc.name,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '33%',
|
||||
isReadOnly: true
|
||||
}, {
|
||||
displayName: loc.type,
|
||||
isReadOnly: true,
|
||||
width: '20%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.endpoint,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: '50%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: {
|
||||
...cssStyles.tableRow,
|
||||
'overflow': 'hidden',
|
||||
'text-overflow': 'ellipsis',
|
||||
'white-space': 'nowrap',
|
||||
'max-width': '0'
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: loc.description,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '33%',
|
||||
isReadOnly: true
|
||||
}, {
|
||||
displayName: loc.computeAndStorage,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '34%',
|
||||
isReadOnly: true
|
||||
isReadOnly: true,
|
||||
width: '30%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
}
|
||||
],
|
||||
width: '100%',
|
||||
ariaLabel: loc.arcResources
|
||||
data: [
|
||||
[loc.kibanaDashboard, this._kibanaLoading, loc.kibanaDashboardDescription],
|
||||
[loc.grafanaDashboard, this._grafanaLoading, loc.grafanaDashboardDescription]]
|
||||
}).component();
|
||||
|
||||
const arcResourcesTableContainer = this.modelView.modelBuilder.divContainer()
|
||||
.withItems([this._arcResourcesTable])
|
||||
.component();
|
||||
rootContainer.addItem(endpointsTable);
|
||||
|
||||
// Databases
|
||||
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.databases, CSSStyles: titleCSS }).component());
|
||||
this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
width: '100%',
|
||||
columns: [
|
||||
{
|
||||
displayName: loc.name,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '80%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.status,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '20%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
}
|
||||
],
|
||||
data: []
|
||||
}).component();
|
||||
|
||||
this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._databasesTable).component();
|
||||
this._databasesTableLoading.loading = false;
|
||||
rootContainer.addItem(this._databasesTableLoading, { CSSStyles: { 'margin-bottom': '20px' } });
|
||||
|
||||
rootContainer.addItem(arcResourcesTableContainer);
|
||||
this.initialized = true;
|
||||
return rootContainer;
|
||||
}
|
||||
@@ -151,4 +196,80 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
||||
).component();
|
||||
}
|
||||
|
||||
private async handleRegistrationsUpdated(): Promise<void> {
|
||||
const reg = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.namespace, this._miaaModel.name);
|
||||
if (reg) {
|
||||
this._instanceProperties.resourceGroup = reg.resourceGroupName || '-';
|
||||
this._instanceProperties.dataController = this._controllerModel.controllerRegistration?.instanceName || '-';
|
||||
this._instanceProperties.region = (await getAzurecoreApi()).getRegionDisplayName(reg.location);
|
||||
this._instanceProperties.subscriptionId = reg.subscriptionId || '-';
|
||||
this._instanceProperties.computeAndStorage = reg.vCores || '-';
|
||||
this._instanceProperties.host = reg.externalEndpoint || '-';
|
||||
this.refreshDisplayedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private async handleMiaaStatusUpdated(status: HybridSqlNsNameGetResponse): Promise<void> {
|
||||
this._instanceProperties.status = status.status || '-';
|
||||
this.refreshDisplayedProperties();
|
||||
}
|
||||
|
||||
private handleEndpointsUpdated(endpoints: EndpointModel[]): void {
|
||||
const kibanaQuery = `kubernetes_namespace:"${this._miaaModel.namespace}" and instance_name :"${this._miaaModel.name}"`;
|
||||
const kibanaUrl = `${endpoints.find(e => e.name === 'logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
||||
this._kibanaLink.label = kibanaUrl;
|
||||
this._kibanaLink.url = kibanaUrl;
|
||||
|
||||
const grafanaUrl = `${endpoints.find(e => e.name === 'metricsui')?.endpoint}/d/wZx3OUdmz/azure-sql-db-managed-instance-metrics?var-hostname=${this._miaaModel.name}-0`;
|
||||
this._grafanaLink.label = grafanaUrl;
|
||||
this._grafanaLink.url = grafanaUrl;
|
||||
|
||||
this._kibanaLoading!.loading = false;
|
||||
this._grafanaLoading!.loading = false;
|
||||
}
|
||||
|
||||
private handleDatabasesUpdated(databases: DatabaseModel[]): void {
|
||||
this._databasesTable.data = databases.map(d => [d.name, d.status]);
|
||||
this._databasesTableLoading.loading = false;
|
||||
}
|
||||
|
||||
private refreshDisplayedProperties(): void {
|
||||
this._propertiesContainer.propertyItems = [
|
||||
{
|
||||
displayName: loc.resourceGroup,
|
||||
value: this._instanceProperties.resourceGroup
|
||||
},
|
||||
{
|
||||
displayName: loc.status,
|
||||
value: this._instanceProperties.status
|
||||
},
|
||||
{
|
||||
displayName: loc.dataController,
|
||||
value: this._instanceProperties.dataController
|
||||
},
|
||||
{
|
||||
displayName: loc.region,
|
||||
value: this._instanceProperties.region
|
||||
},
|
||||
{
|
||||
displayName: loc.subscriptionId,
|
||||
value: this._instanceProperties.subscriptionId
|
||||
},
|
||||
{
|
||||
displayName: loc.miaaAdmin,
|
||||
value: this._instanceProperties.miaaAdmin
|
||||
},
|
||||
{
|
||||
displayName: loc.host,
|
||||
value: this._instanceProperties.host
|
||||
},
|
||||
{
|
||||
displayName: loc.computeAndStorage,
|
||||
value: this._instanceProperties.computeAndStorage
|
||||
}
|
||||
];
|
||||
|
||||
this._propertiesLoading.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private refresh() {
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
|
||||
this.keyValueContainer?.refresh([
|
||||
new InputKeyValue('ADO.NET', `Server=${endpoint.ip};Database=postgres;Port=${endpoint.port};User Id=postgres;Password={your_password_here};Ssl Mode=Require;`),
|
||||
|
||||
@@ -49,8 +49,8 @@ export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
|
||||
}).component();
|
||||
|
||||
troubleshootButton.onDidClick(() => {
|
||||
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace();
|
||||
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name();
|
||||
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace;
|
||||
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name;
|
||||
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arc'), true, 'postgres/tsg100-troubleshoot-postgres');
|
||||
});
|
||||
|
||||
|
||||
@@ -204,9 +204,9 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
s.arc = s.arc ?? new DuskyObjectModelsDatabaseServiceArcPayload();
|
||||
s.arc.servicePassword = password;
|
||||
});
|
||||
vscode.window.showInformationMessage(loc.passwordReset(this._postgresModel.fullName()));
|
||||
vscode.window.showInformationMessage(loc.passwordReset(this._postgresModel.fullName));
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(loc.passwordResetFailed(this._postgresModel.fullName(), error));
|
||||
vscode.window.showErrorMessage(loc.passwordResetFailed(this._postgresModel.fullName, error));
|
||||
} finally {
|
||||
resetPasswordButton.enabled = true;
|
||||
}
|
||||
@@ -222,13 +222,13 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
deleteButton.enabled = false;
|
||||
try {
|
||||
const response = await vscode.window.showQuickPick([loc.yes, loc.no], {
|
||||
placeHolder: loc.deleteServicePrompt(this._postgresModel.fullName())
|
||||
placeHolder: loc.deleteServicePrompt(this._postgresModel.fullName)
|
||||
});
|
||||
if (response !== loc.yes) { return; }
|
||||
await this._postgresModel.delete();
|
||||
vscode.window.showInformationMessage(loc.serviceDeleted(this._postgresModel.fullName()));
|
||||
vscode.window.showInformationMessage(loc.serviceDeleted(this._postgresModel.fullName));
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(loc.serviceDeletionFailed(this._postgresModel.fullName(), error));
|
||||
vscode.window.showErrorMessage(loc.serviceDeletionFailed(this._postgresModel.fullName, error));
|
||||
} finally {
|
||||
deleteButton.enabled = true;
|
||||
}
|
||||
@@ -267,9 +267,9 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
}).component();
|
||||
|
||||
openInAzurePortalButton.onDidClick(async () => {
|
||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
|
||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||
if (!r) {
|
||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName()));
|
||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
||||
} else {
|
||||
vscode.env.openExternal(vscode.Uri.parse(
|
||||
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}`));
|
||||
@@ -286,30 +286,30 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private refreshProperties() {
|
||||
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
|
||||
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
|
||||
this.properties!.propertyItems = [
|
||||
{ displayName: loc.name, value: this._postgresModel.name() },
|
||||
{ displayName: loc.coordinatorEndpoint, value: `postgresql://postgres:${this._postgresModel.password()}@${endpoint.ip}:${endpoint.port}` },
|
||||
{ displayName: loc.status, value: this._postgresModel.service()?.status?.state ?? '' },
|
||||
{ displayName: loc.name, value: this._postgresModel.name },
|
||||
{ displayName: loc.coordinatorEndpoint, value: `postgresql://postgres:${this._postgresModel.password}@${endpoint.ip}:${endpoint.port}` },
|
||||
{ displayName: loc.status, value: this._postgresModel.service?.status?.state ?? '' },
|
||||
{ displayName: loc.postgresAdminUsername, value: 'postgres' },
|
||||
{ displayName: loc.dataController, value: this._controllerModel?.namespace() ?? '' },
|
||||
{ displayName: loc.nodeConfiguration, value: this._postgresModel.configuration() },
|
||||
{ displayName: loc.dataController, value: this._controllerModel?.namespace ?? '' },
|
||||
{ displayName: loc.nodeConfiguration, value: this._postgresModel.configuration },
|
||||
{ displayName: loc.subscriptionId, value: registration?.subscriptionId ?? '' },
|
||||
{ displayName: loc.postgresVersion, value: this._postgresModel.service()?.spec?.engine?.version?.toString() ?? '' }
|
||||
{ displayName: loc.postgresVersion, value: this._postgresModel.service?.spec?.engine?.version?.toString() ?? '' }
|
||||
];
|
||||
|
||||
this.propertiesLoading!.loading = false;
|
||||
}
|
||||
|
||||
private refreshEndpoints() {
|
||||
const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace()}" and cluster_name:"${this._postgresModel.name()}"`;
|
||||
const kibanaUrl = `${this._controllerModel.endpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
||||
const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace}" and cluster_name:"${this._postgresModel.name}"`;
|
||||
const kibanaUrl = `${this._controllerModel.getEndpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
||||
this.kibanaLink!.label = kibanaUrl;
|
||||
this.kibanaLink!.url = kibanaUrl;
|
||||
|
||||
const grafanaUrl = `${this._controllerModel.endpoint('metricsui')?.endpoint}/d/postgres-metrics?var-Namespace=${this._postgresModel.namespace()}&var-Name=${this._postgresModel.name()}`;
|
||||
const grafanaUrl = `${this._controllerModel.getEndpoint('metricsui')?.endpoint}/d/postgres-metrics?var-Namespace=${this._postgresModel.namespace}&var-Name=${this._postgresModel.name}`;
|
||||
this.grafanaLink!.label = grafanaUrl;
|
||||
this.grafanaLink!.url = grafanaUrl;
|
||||
|
||||
@@ -318,9 +318,9 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private refreshNodes() {
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
|
||||
this.nodesTable!.data = this._postgresModel.pods()?.map((pod: V1Pod) => {
|
||||
this.nodesTable!.data = this._postgresModel.pods?.map((pod: V1Pod) => {
|
||||
const name = pod.metadata?.name;
|
||||
const role: PodRole | undefined = PostgresModel.getPodRole(pod);
|
||||
const service = pod.metadata?.annotations?.['arcdata.microsoft.com/serviceHost'];
|
||||
|
||||
@@ -78,17 +78,17 @@ export class PostgresPropertiesPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private refresh() {
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
|
||||
const connectionString = `postgresql://postgres:${this._postgresModel.password()}@${endpoint.ip}:${endpoint.port}`;
|
||||
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
const connectionString = `postgresql://postgres:${this._postgresModel.password}@${endpoint.ip}:${endpoint.port}`;
|
||||
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||
|
||||
this.keyValueContainer?.refresh([
|
||||
new InputKeyValue(loc.coordinatorEndpoint, connectionString),
|
||||
new InputKeyValue(loc.postgresAdminUsername, 'postgres'),
|
||||
new TextKeyValue(loc.status, this._postgresModel.service()?.status?.state ?? 'Unknown'),
|
||||
new LinkKeyValue(loc.dataController, this._controllerModel.namespace() ?? '', _ => vscode.window.showInformationMessage('TODO: Go to data controller')),
|
||||
new LinkKeyValue(loc.nodeConfiguration, this._postgresModel.configuration(), _ => vscode.window.showInformationMessage('TODO: Go to configuration')),
|
||||
new TextKeyValue(loc.postgresVersion, this._postgresModel.service()?.spec?.engine?.version?.toString() ?? ''),
|
||||
new TextKeyValue(loc.status, this._postgresModel.service?.status?.state ?? 'Unknown'),
|
||||
new LinkKeyValue(loc.dataController, this._controllerModel.namespace ?? '', _ => vscode.window.showInformationMessage('TODO: Go to data controller')),
|
||||
new LinkKeyValue(loc.nodeConfiguration, this._postgresModel.configuration, _ => vscode.window.showInformationMessage('TODO: Go to configuration')),
|
||||
new TextKeyValue(loc.postgresVersion, this._postgresModel.service?.spec?.engine?.version?.toString() ?? ''),
|
||||
new TextKeyValue(loc.resourceGroup, registration?.resourceGroupName ?? ''),
|
||||
new TextKeyValue(loc.subscriptionId, registration?.subscriptionId ?? '')
|
||||
]);
|
||||
|
||||
@@ -51,9 +51,9 @@ export class PostgresSupportRequestPage extends DashboardPage {
|
||||
}).component();
|
||||
|
||||
supportRequestButton.onDidClick(() => {
|
||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
|
||||
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
|
||||
if (!r) {
|
||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName()));
|
||||
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
|
||||
} else {
|
||||
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`));
|
||||
|
||||
Reference in New Issue
Block a user