mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 18:48:33 -05:00
new postgres model (#12305)
Co-authored-by: Brian Bergeron <brberger@microsoft.com>
This commit is contained in:
@@ -194,23 +194,6 @@ export function getErrorMessage(error: any): string {
|
||||
return error.message ?? error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an instance name from the controller. An instance name will either be just its name
|
||||
* e.g. myinstance or namespace_name e.g. mynamespace_my-instance.
|
||||
* @param instanceName The instance name in one of the formats described
|
||||
*/
|
||||
export function parseInstanceName(instanceName: string | undefined): string {
|
||||
instanceName = instanceName ?? '';
|
||||
const parts: string[] = instanceName.split('_');
|
||||
if (parts.length === 2) {
|
||||
instanceName = parts[1];
|
||||
}
|
||||
else if (parts.length > 2) {
|
||||
throw new Error(`Cannot parse resource '${instanceName}'. Acceptable formats are 'namespace_name' or 'name'.`);
|
||||
}
|
||||
return instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an address into its separate ip and port values. Address must be in the form <ip>:<port>
|
||||
* @param address The address to parse
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ControllerInfo, ResourceType } from 'arc';
|
||||
import * as azdataExt from 'azdata-ext';
|
||||
import * as vscode from 'vscode';
|
||||
import { parseInstanceName, UserCancelledError } from '../common/utils';
|
||||
import { UserCancelledError } from '../common/utils';
|
||||
import * as loc from '../localizedConstants';
|
||||
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||
@@ -171,7 +171,7 @@ export class ControllerModel {
|
||||
|
||||
public getRegistration(type: ResourceType, name: string): Registration | undefined {
|
||||
return this._registrations.find(r => {
|
||||
return r.instanceType === type && parseInstanceName(r.instanceName) === name;
|
||||
return r.instanceType === type && r.instanceName === name;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,278 +4,46 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResourceInfo } from 'arc';
|
||||
import * as azdataExt from 'azdata-ext';
|
||||
import * as vscode from 'vscode';
|
||||
import * as loc from '../localizedConstants';
|
||||
import { Registration } from './controllerModel';
|
||||
import { ControllerModel, Registration } from './controllerModel';
|
||||
import { ResourceModel } from './resourceModel';
|
||||
|
||||
export enum PodRole {
|
||||
Monitor,
|
||||
Router,
|
||||
Shard
|
||||
}
|
||||
|
||||
export interface V1Pod {
|
||||
'apiVersion'?: string;
|
||||
'kind'?: string;
|
||||
'metadata'?: any; // V1ObjectMeta;
|
||||
'spec'?: any; // V1PodSpec;
|
||||
'status'?: V1PodStatus;
|
||||
}
|
||||
|
||||
export interface V1PodStatus {
|
||||
'conditions'?: any[]; // Array<V1PodCondition>;
|
||||
'containerStatuses'?: Array<V1ContainerStatus>;
|
||||
'ephemeralContainerStatuses'?: any[]; // Array<V1ContainerStatus>;
|
||||
'hostIP'?: string;
|
||||
'initContainerStatuses'?: any[]; // Array<V1ContainerStatus>;
|
||||
'message'?: string;
|
||||
'nominatedNodeName'?: string;
|
||||
'phase'?: string;
|
||||
'podIP'?: string;
|
||||
'podIPs'?: any[]; // Array<V1PodIP>;
|
||||
'qosClass'?: string;
|
||||
'reason'?: string;
|
||||
'startTime'?: Date | null;
|
||||
}
|
||||
|
||||
export interface V1ContainerStatus {
|
||||
'containerID'?: string;
|
||||
'image'?: string;
|
||||
'imageID'?: string;
|
||||
'lastState'?: any; // V1ContainerState;
|
||||
'name'?: string;
|
||||
'ready'?: boolean;
|
||||
'restartCount'?: number;
|
||||
'started'?: boolean | null;
|
||||
'state'?: any; // V1ContainerState;
|
||||
}
|
||||
|
||||
export interface DuskyObjectModelsDatabaseService {
|
||||
'apiVersion'?: string;
|
||||
'kind'?: string;
|
||||
'metadata'?: any; // V1ObjectMeta;
|
||||
'spec'?: any; // DuskyObjectModelsDatabaseServiceSpec;
|
||||
'status'?: any; // DuskyObjectModelsDatabaseServiceStatus;
|
||||
'arc'?: any; // DuskyObjectModelsDatabaseServiceArcPayload;
|
||||
}
|
||||
|
||||
export interface V1Status {
|
||||
'apiVersion'?: string;
|
||||
'code'?: number | null;
|
||||
'details'?: any; // V1StatusDetails;
|
||||
'kind'?: string;
|
||||
'message'?: string;
|
||||
'metadata'?: any; // V1ListMeta;
|
||||
'reason'?: string;
|
||||
'status'?: string;
|
||||
'hasObject'?: boolean;
|
||||
}
|
||||
|
||||
export interface DuskyObjectModelsDatabase {
|
||||
'name'?: string;
|
||||
'owner'?: string;
|
||||
'sharded'?: boolean | null;
|
||||
}
|
||||
import { parseIpAndPort } from '../common/utils';
|
||||
|
||||
export class PostgresModel extends ResourceModel {
|
||||
private _service?: DuskyObjectModelsDatabaseService;
|
||||
private _pods?: V1Pod[];
|
||||
private readonly _onServiceUpdated = new vscode.EventEmitter<DuskyObjectModelsDatabaseService>();
|
||||
private readonly _onPodsUpdated = new vscode.EventEmitter<V1Pod[]>();
|
||||
public onServiceUpdated = this._onServiceUpdated.event;
|
||||
public onPodsUpdated = this._onPodsUpdated.event;
|
||||
public serviceLastUpdated?: Date;
|
||||
public podsLastUpdated?: Date;
|
||||
private _config?: azdataExt.PostgresServerShowResult;
|
||||
private readonly _azdataApi: azdataExt.IExtension;
|
||||
|
||||
constructor(info: ResourceInfo, registration: Registration) {
|
||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.PostgresServerShowResult>();
|
||||
public onConfigUpdated = this._onConfigUpdated.event;
|
||||
public configLastUpdated?: Date;
|
||||
|
||||
constructor(private _controllerModel: ControllerModel, info: ResourceInfo, registration: Registration) {
|
||||
super(info, registration);
|
||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
||||
}
|
||||
|
||||
/** Returns the service's Kubernetes namespace */
|
||||
public get namespace(): string | undefined {
|
||||
return ''; // TODO chgagnon return this.info.namespace;
|
||||
public get config(): azdataExt.PostgresServerShowResult | undefined {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
/** Returns the service's name */
|
||||
public get name(): string {
|
||||
return this.info.name;
|
||||
/** Returns the IP address and port of the server */
|
||||
public get endpoint(): { ip: string, port: string } {
|
||||
return this._config
|
||||
? parseIpAndPort(this._config.status.externalEndpoint)
|
||||
: { ip: '', port: '' };
|
||||
}
|
||||
|
||||
/** Returns the service's fully qualified name in the format namespace.name */
|
||||
public get fullName(): string {
|
||||
return `${this.namespace}.${this.name}`;
|
||||
}
|
||||
|
||||
/** Returns the service's spec */
|
||||
public get service(): DuskyObjectModelsDatabaseService | undefined {
|
||||
return this._service;
|
||||
}
|
||||
|
||||
/** Returns the service's pods */
|
||||
public get pods(): V1Pod[] | undefined {
|
||||
return this._pods;
|
||||
/** Returns the server's configuration e.g. '3 nodes, 1.5 vCores, 1GiB RAM, 2GiB storage per node' */
|
||||
public get configuration(): string {
|
||||
return ''; // TODO
|
||||
}
|
||||
|
||||
/** Refreshes the model */
|
||||
public async refresh() {
|
||||
await Promise.all([
|
||||
/* TODO enable
|
||||
this._databaseRouter.getDuskyDatabaseService(this.info.namespace || 'test', this.info.name).then(response => {
|
||||
this._service = response.body;
|
||||
this.serviceLastUpdated = new Date();
|
||||
this._onServiceUpdated.fire(this._service);
|
||||
}),
|
||||
this._databaseRouter.getDuskyPods(this.info.namespace || 'test', this.info.name).then(response => {
|
||||
this._pods = response.body;
|
||||
this.podsLastUpdated = new Date();
|
||||
this._onPodsUpdated.fire(this._pods!);
|
||||
})
|
||||
*/
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the service
|
||||
* @param func A function of modifications to apply to the service
|
||||
*/
|
||||
public async update(_func: (service: DuskyObjectModelsDatabaseService) => void): Promise<DuskyObjectModelsDatabaseService> {
|
||||
return <any>undefined;
|
||||
/*
|
||||
// Get the latest spec of the service in case it has changed
|
||||
const service = (await this._databaseRouter.getDuskyDatabaseService(this.info.namespace || 'test', this.info.name)).body;
|
||||
service.status = undefined; // can't update the status
|
||||
func(service);
|
||||
|
||||
return await this._databaseRouter.updateDuskyDatabaseService(this.namespace || 'test', this.name, service).then(r => {
|
||||
this._service = r.body;
|
||||
return this._service;
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
/** Deletes the service */
|
||||
public async delete(): Promise<V1Status> {
|
||||
return <any>undefined;
|
||||
// return (await this._databaseRouter.deleteDuskyDatabaseService(this.info.namespace || 'test', this.info.name)).body;
|
||||
}
|
||||
|
||||
/** Creates a SQL database in the service */
|
||||
public async createDatabase(_db: DuskyObjectModelsDatabase): Promise<DuskyObjectModelsDatabase> {
|
||||
return <any>undefined;
|
||||
// return (await this._databaseRouter.createDuskyDatabase(this.namespace || 'test', 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 get endpoint(): { ip?: string, port?: number } {
|
||||
const externalIp = this._service?.status?.externalIP;
|
||||
const internalIp = this._service?.status?.internalIP;
|
||||
const externalPort = this._service?.status?.externalPort;
|
||||
const internalPort = this._service?.status?.internalPort;
|
||||
|
||||
return externalIp ? { ip: externalIp, port: externalPort ?? undefined }
|
||||
: internalIp ? { ip: internalIp, port: internalPort ?? undefined }
|
||||
: { ip: undefined, port: undefined };
|
||||
}
|
||||
|
||||
/** Returns the service's configuration e.g. '3 nodes, 1.5 vCores, 1GiB RAM, 2GiB storage per node' */
|
||||
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.
|
||||
const cpuLimit = this._service?.spec?.scheduling?._default?.resources?.limits?.['cpu'];
|
||||
const ramLimit = this._service?.spec?.scheduling?._default?.resources?.limits?.['memory'];
|
||||
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;
|
||||
|
||||
let configuration: string[] = [];
|
||||
|
||||
if (nodes) {
|
||||
configuration.push(`${nodes} ${nodes > 1 ? loc.nodes : loc.node}`);
|
||||
}
|
||||
|
||||
// Prefer limits if they're provided, otherwise use requests if they're provided
|
||||
if (cpuLimit || cpuRequest) {
|
||||
configuration.push(`${this.formatCores(cpuLimit ?? cpuRequest!)} ${loc.vCores}`);
|
||||
}
|
||||
|
||||
if (ramLimit || ramRequest) {
|
||||
configuration.push(`${this.formatMemory(ramLimit ?? ramRequest!)} ${loc.ram}`);
|
||||
}
|
||||
|
||||
if (storage) {
|
||||
configuration.push(`${this.formatMemory(storage)} ${loc.storagePerNode}`);
|
||||
}
|
||||
|
||||
return configuration.join(', ');
|
||||
}
|
||||
|
||||
/** Given a V1Pod, returns its PodRole or undefined if the role isn't known */
|
||||
public static getPodRole(pod: V1Pod): PodRole | undefined {
|
||||
const name = pod.metadata?.name;
|
||||
const role = name?.substring(name.lastIndexOf('-'))[1];
|
||||
switch (role) {
|
||||
case 'm': return PodRole.Monitor;
|
||||
case 'r': return PodRole.Router;
|
||||
case 's': return PodRole.Shard;
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a PodRole, returns its localized name */
|
||||
public static getPodRoleName(role?: PodRole): string {
|
||||
switch (role) {
|
||||
case PodRole.Monitor: return loc.monitor;
|
||||
case PodRole.Router: return loc.coordinator;
|
||||
case PodRole.Shard: return loc.worker;
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a V1Pod returns its status */
|
||||
public static getPodStatus(pod: V1Pod): string {
|
||||
const phase = pod.status?.phase;
|
||||
if (phase !== 'Running') {
|
||||
return phase ?? '';
|
||||
}
|
||||
|
||||
// Pods can be in the running phase while some
|
||||
// containers are crashing, so check those too.
|
||||
for (let c of pod.status?.containerStatuses?.filter(c => !c.ready) ?? []) {
|
||||
const wReason = c.state?.waiting?.reason;
|
||||
const tReason = c.state?.terminated?.reason;
|
||||
if (wReason) { return wReason; }
|
||||
if (tReason) { return tReason; }
|
||||
}
|
||||
|
||||
return loc.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts millicores to cores (600m -> 0.6 cores)
|
||||
* https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
|
||||
* @param cores The millicores to format e.g. 600m
|
||||
*/
|
||||
private formatCores(cores: string): number {
|
||||
return cores?.endsWith('m') ? +cores.slice(0, -1) / 1000 : +cores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the memory to end with 'B' e.g:
|
||||
* 1 -> 1B
|
||||
* 1K -> 1KB, 1Ki -> 1KiB
|
||||
* 1M -> 1MB, 1Mi -> 1MiB
|
||||
* 1G -> 1GB, 1Gi -> 1GiB
|
||||
* 1T -> 1TB, 1Ti -> 1TiB
|
||||
* https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
|
||||
* @param memory The amount + unit of memory to format e.g. 1K
|
||||
*/
|
||||
private formatMemory(memory: string): string {
|
||||
return memory && !memory.endsWith('B') ? `${memory}B` : memory;
|
||||
await this._controllerModel.azdataLogin();
|
||||
this._config = (await this._azdataApi.azdata.arc.postgres.server.show(this.info.name)).result;
|
||||
this.configLastUpdated = new Date();
|
||||
this._onConfigUpdated.fire(this._config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ResourceType } from 'arc';
|
||||
import 'mocha';
|
||||
import * as should from 'should';
|
||||
import * as vscode from 'vscode';
|
||||
import { getAzurecoreApi, getConnectionModeDisplayText, getDatabaseStateDisplayText, getErrorMessage, getResourceTypeIcon, parseEndpoint, parseInstanceName, parseIpAndPort, promptAndConfirmPassword, promptForResourceDeletion, resourceTypeToDisplayName } from '../../common/utils';
|
||||
import { getAzurecoreApi, getConnectionModeDisplayText, getDatabaseStateDisplayText, getErrorMessage, getResourceTypeIcon, parseEndpoint, parseIpAndPort, promptAndConfirmPassword, promptForResourceDeletion, resourceTypeToDisplayName } from '../../common/utils';
|
||||
import { ConnectionMode as ConnectionMode, IconPathHelper } from '../../constants';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { MockInputBox } from '../stubs';
|
||||
@@ -47,24 +47,6 @@ describe('parseEndpoint Method Tests', function (): void {
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseInstanceName Method Tests', () => {
|
||||
it('Should parse valid instanceName with namespace correctly', function (): void {
|
||||
should(parseInstanceName('mynamespace_myinstance')).equal('myinstance');
|
||||
});
|
||||
|
||||
it('Should parse valid instanceName without namespace correctly', function (): void {
|
||||
should(parseInstanceName('myinstance')).equal('myinstance');
|
||||
});
|
||||
|
||||
it('Should return empty string when undefined value passed in', function (): void {
|
||||
should(parseInstanceName(undefined)).equal('');
|
||||
});
|
||||
|
||||
it('Should return empty string when empty string value passed in', function (): void {
|
||||
should(parseInstanceName('')).equal('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAzurecoreApi Method Tests', function () {
|
||||
it('Should get azurecore API correctly', function (): void {
|
||||
should(getAzurecoreApi()).not.be.undefined();
|
||||
@@ -260,22 +242,6 @@ describe('getErrorMessage Method Tests', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseInstanceName Method Tests', function () {
|
||||
it('2 part name', function (): void {
|
||||
const name = 'MyName';
|
||||
should(parseInstanceName(`MyNamespace_${name}`)).equal(name);
|
||||
});
|
||||
|
||||
it('1 part name', function (): void {
|
||||
const name = 'MyName';
|
||||
should(parseInstanceName(name)).equal(name);
|
||||
});
|
||||
|
||||
it('Invalid name', function (): void {
|
||||
should(() => parseInstanceName('Some_Invalid_Name')).throwError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseIpAndPort', function (): void {
|
||||
it('Valid address', function (): void {
|
||||
const ip = '127.0.0.1';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ResourceType } from 'arc';
|
||||
import * as azdata from 'azdata';
|
||||
import * as azurecore from 'azurecore';
|
||||
import * as vscode from 'vscode';
|
||||
import { getConnectionModeDisplayText, getResourceTypeIcon, parseInstanceName, resourceTypeToDisplayName } from '../../../common/utils';
|
||||
import { getConnectionModeDisplayText, getResourceTypeIcon, resourceTypeToDisplayName } from '../../../common/utils';
|
||||
import { cssStyles, Endpoints, IconPathHelper, iconSize } from '../../../constants';
|
||||
import * as loc from '../../../localizedConstants';
|
||||
import { ControllerModel } from '../../../models/controllerModel';
|
||||
@@ -233,7 +233,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
||||
url: ''
|
||||
}).component();
|
||||
(<azdata.HyperlinkComponent>nameComponent).onDidClick(async () => {
|
||||
await this._controllerModel.treeDataProvider.openResourceDashboard(this._controllerModel, r.instanceType || '', parseInstanceName(r.instanceName));
|
||||
await this._controllerModel.treeDataProvider.openResourceDashboard(this._controllerModel, r.instanceType || '', r.instanceName);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
||||
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
||||
super(modelView);
|
||||
|
||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||
this.disposables.push(this._postgresModel.onConfigUpdated(
|
||||
() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())));
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
||||
this.loading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.keyValueContainer.container)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._postgresModel.serviceLastUpdated
|
||||
loading: !this._postgresModel.configLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.loading);
|
||||
@@ -98,7 +98,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private getConnectionStrings(): KeyValue[] {
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
const endpoint: { ip: string, port: string } = this._postgresModel.endpoint;
|
||||
|
||||
return [
|
||||
new InputKeyValue(this.modelView.modelBuilder, 'ADO.NET', `Server=${endpoint.ip};Database=postgres;Port=${endpoint.port};User Id=postgres;Password={your_password_here};Ssl Mode=Require;`),
|
||||
|
||||
@@ -14,11 +14,10 @@ import { PostgresPropertiesPage } from './postgresPropertiesPage';
|
||||
import { Dashboard } from '../../components/dashboard';
|
||||
import { PostgresDiagnoseAndSolveProblemsPage } from './postgresDiagnoseAndSolveProblemsPage';
|
||||
import { PostgresSupportRequestPage } from './postgresSupportRequestPage';
|
||||
import { PostgresResourceHealthPage } from './postgresResourceHealthPage';
|
||||
|
||||
export class PostgresDashboard extends Dashboard {
|
||||
constructor(private _context: vscode.ExtensionContext, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||
super(loc.postgresDashboard(_postgresModel.name));
|
||||
super(loc.postgresDashboard(_postgresModel.info.name));
|
||||
}
|
||||
|
||||
public async showDashboard(): Promise<void> {
|
||||
@@ -33,7 +32,6 @@ export class PostgresDashboard extends Dashboard {
|
||||
const overviewPage = new PostgresOverviewPage(modelView, this._controllerModel, this._postgresModel);
|
||||
const connectionStringsPage = new PostgresConnectionStringsPage(modelView, this._postgresModel);
|
||||
const propertiesPage = new PostgresPropertiesPage(modelView, this._controllerModel, this._postgresModel);
|
||||
const resourceHealthPage = new PostgresResourceHealthPage(modelView, this._postgresModel);
|
||||
const diagnoseAndSolveProblemsPage = new PostgresDiagnoseAndSolveProblemsPage(modelView, this._context, this._postgresModel);
|
||||
const supportRequestPage = new PostgresSupportRequestPage(modelView);
|
||||
|
||||
@@ -49,7 +47,6 @@ export class PostgresDashboard extends Dashboard {
|
||||
{
|
||||
title: loc.supportAndTroubleshooting,
|
||||
tabs: [
|
||||
resourceHealthPage.tab,
|
||||
diagnoseAndSolveProblemsPage.tab,
|
||||
supportRequestPage.tab
|
||||
]
|
||||
|
||||
@@ -50,8 +50,9 @@ export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
|
||||
|
||||
this.disposables.push(
|
||||
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.config?.metadata.namespace;
|
||||
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.info.name;
|
||||
// TODO set env POSTGRES_SERVER_VERSION
|
||||
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arcDataServices'), true, 'postgres/tsg100-troubleshoot-postgres');
|
||||
}));
|
||||
|
||||
|
||||
@@ -17,12 +17,10 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
private propertiesLoading?: azdata.LoadingComponent;
|
||||
private kibanaLoading?: azdata.LoadingComponent;
|
||||
private grafanaLoading?: azdata.LoadingComponent;
|
||||
private nodesTableLoading?: azdata.LoadingComponent;
|
||||
|
||||
private properties?: azdata.PropertiesContainerComponent;
|
||||
private kibanaLink?: azdata.HyperlinkComponent;
|
||||
private grafanaLink?: azdata.HyperlinkComponent;
|
||||
private nodesTable?: azdata.DeclarativeTableComponent;
|
||||
|
||||
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||
super(modelView);
|
||||
@@ -30,8 +28,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
this.disposables.push(
|
||||
this._controllerModel.onEndpointsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated())),
|
||||
this._controllerModel.onRegistrationsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())),
|
||||
this._postgresModel.onServiceUpdated(() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())),
|
||||
this._postgresModel.onPodsUpdated(() => this.eventuallyRunOnInitialized(() => this.handlePodsUpdated())));
|
||||
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated())));
|
||||
}
|
||||
|
||||
protected get title(): string {
|
||||
@@ -60,7 +57,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
this.propertiesLoading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.properties)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._controllerModel.registrationsLastUpdated && !this._postgresModel.serviceLastUpdated && !this._postgresModel.podsLastUpdated
|
||||
loading: !this._controllerModel.registrationsLastUpdated && !this._postgresModel.configLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.propertiesLoading, { CSSStyles: cssStyles.text });
|
||||
@@ -134,60 +131,8 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
[loc.kibanaDashboard, this.kibanaLoading, loc.kibanaDashboardDescription],
|
||||
[loc.grafanaDashboard, this.grafanaLoading, loc.grafanaDashboardDescription]]
|
||||
}).component();
|
||||
|
||||
content.addItem(endpointsTable);
|
||||
|
||||
// Server group nodes
|
||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: loc.serverGroupNodes,
|
||||
CSSStyles: titleCSS
|
||||
}).component());
|
||||
|
||||
this.nodesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
width: '100%',
|
||||
columns: [
|
||||
{
|
||||
displayName: loc.name,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '30%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.type,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '15%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.status,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '20%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.fullyQualifiedDomain,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '35%',
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
}
|
||||
],
|
||||
data: this.getNodes()
|
||||
}).component();
|
||||
|
||||
this.nodesTableLoading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.nodesTable)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._postgresModel.serviceLastUpdated && !this._postgresModel.podsLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.nodesTableLoading, { CSSStyles: { 'margin-bottom': '20px' } });
|
||||
this.initialized = true;
|
||||
return root;
|
||||
}
|
||||
@@ -205,11 +150,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
try {
|
||||
const password = await promptAndConfirmPassword(input => !input ? loc.enterANonEmptyPassword : '');
|
||||
if (password) {
|
||||
await this._postgresModel.update(s => {
|
||||
// TODO chgagnon
|
||||
// s.arc = s.arc ?? new DuskyObjectModelsDatabaseServiceArcPayload();
|
||||
s.arc.servicePassword = password;
|
||||
});
|
||||
// TODO: azdata arc postgres server edit --admin-password
|
||||
vscode.window.showInformationMessage(loc.passwordReset);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -237,7 +178,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
}
|
||||
*/
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._postgresModel.fullName, error));
|
||||
vscode.window.showErrorMessage(loc.resourceDeletionFailed(this._postgresModel.info.name, error));
|
||||
} finally {
|
||||
deleteButton.enabled = true;
|
||||
}
|
||||
@@ -256,7 +197,6 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
this.propertiesLoading!.loading = true;
|
||||
this.kibanaLoading!.loading = true;
|
||||
this.grafanaLoading!.loading = true;
|
||||
this.nodesTableLoading!.loading = true;
|
||||
|
||||
await Promise.all([
|
||||
this._postgresModel.refresh(),
|
||||
@@ -317,36 +257,18 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
}
|
||||
|
||||
private getKibanaLink(): string {
|
||||
const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace}" and custom_resource_name:"${this._postgresModel.name}"`;
|
||||
const namespace = this._postgresModel.config?.metadata.namespace;
|
||||
const kibanaQuery = `kubernetes_namespace:"${namespace}" and custom_resource_name:"${this._postgresModel.info.name}"`;
|
||||
return `${this._controllerModel.getEndpoint(Endpoints.logsui)?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
|
||||
|
||||
}
|
||||
|
||||
private getGrafanaLink(): string {
|
||||
const grafanaQuery = `var-Namespace=${this._postgresModel.namespace}&var-Name=${this._postgresModel.name}`;
|
||||
const namespace = this._postgresModel.config?.metadata.namespace;
|
||||
const grafanaQuery = `var-Namespace=${namespace}&var-Name=${this._postgresModel.info.name}`;
|
||||
return `${this._controllerModel.getEndpoint(Endpoints.metricsui)?.endpoint}/d/postgres-metrics?${grafanaQuery}`;
|
||||
}
|
||||
|
||||
private getNodes(): string[][] {
|
||||
/* TODO chgagnon
|
||||
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
|
||||
return 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'];
|
||||
const internalDns = service ? `${name}.${service}` : '';
|
||||
|
||||
return [
|
||||
name,
|
||||
PostgresModel.getPodRoleName(role),
|
||||
PostgresModel.getPodStatus(pod),
|
||||
role === PodRole.Router ? `${endpoint.ip}:${endpoint.port}` : internalDns
|
||||
];
|
||||
}) ?? [];
|
||||
*/
|
||||
return [];
|
||||
}
|
||||
|
||||
private handleEndpointsUpdated() {
|
||||
this.kibanaLink!.label = this.getKibanaLink();
|
||||
this.kibanaLink!.url = this.getKibanaLink();
|
||||
@@ -362,19 +284,8 @@ export class PostgresOverviewPage extends DashboardPage {
|
||||
this.propertiesLoading!.loading = false;
|
||||
}
|
||||
|
||||
private handleServiceUpdated() {
|
||||
private handleConfigUpdated() {
|
||||
this.properties!.propertyItems = this.getProperties();
|
||||
this.propertiesLoading!.loading = false;
|
||||
|
||||
this.nodesTable!.data = this.getNodes();
|
||||
this.nodesTableLoading!.loading = false;
|
||||
}
|
||||
|
||||
private handlePodsUpdated() {
|
||||
this.properties!.propertyItems = this.getProperties();
|
||||
this.propertiesLoading!.loading = false;
|
||||
|
||||
this.nodesTable!.data = this.getNodes();
|
||||
this.nodesTableLoading!.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
||||
constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||
super(modelView);
|
||||
|
||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||
this.disposables.push(this._postgresModel.onConfigUpdated(
|
||||
() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())));
|
||||
|
||||
this.disposables.push(this._controllerModel.onRegistrationsUpdated(
|
||||
@@ -54,7 +54,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
||||
this.loading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.keyValueContainer.container)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._postgresModel.serviceLastUpdated && !this._controllerModel.registrationsLastUpdated
|
||||
loading: !this._postgresModel.configLastUpdated && !this._controllerModel.registrationsLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.loading);
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as loc from '../../../localizedConstants';
|
||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||
import { DashboardPage } from '../../components/dashboardPage';
|
||||
import { PostgresModel } from '../../../models/postgresModel';
|
||||
import { fromNow } from '../../../common/date';
|
||||
|
||||
export class PostgresResourceHealthPage extends DashboardPage {
|
||||
private interval: NodeJS.Timeout;
|
||||
private podsUpdated?: azdata.TextComponent;
|
||||
private podsLoading?: azdata.LoadingComponent;
|
||||
private conditionsLoading?: azdata.LoadingComponent;
|
||||
private podsTable?: azdata.DeclarativeTableComponent;
|
||||
private conditionsTable?: azdata.DeclarativeTableComponent;
|
||||
|
||||
constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) {
|
||||
super(modelView);
|
||||
|
||||
this.disposables.push(
|
||||
modelView.onClosed(() => {
|
||||
try { clearInterval(this.interval); }
|
||||
catch { }
|
||||
}));
|
||||
|
||||
this.disposables.push(this._postgresModel.onServiceUpdated(
|
||||
() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())));
|
||||
|
||||
// Keep the last updated timestamps up to date with the current time
|
||||
this.interval = setInterval(() => this.handleServiceUpdated(), 60 * 1000);
|
||||
}
|
||||
|
||||
protected get title(): string {
|
||||
return loc.resourceHealth;
|
||||
}
|
||||
|
||||
protected get id(): string {
|
||||
return 'postgres-resource-health';
|
||||
}
|
||||
|
||||
protected get icon(): { dark: string; light: string; } {
|
||||
return IconPathHelper.health;
|
||||
}
|
||||
|
||||
protected get container(): azdata.Component {
|
||||
const root = this.modelView.modelBuilder.divContainer().component();
|
||||
const content = this.modelView.modelBuilder.divContainer().component();
|
||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||
|
||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: loc.resourceHealth,
|
||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '30px' }
|
||||
}).component());
|
||||
|
||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: loc.podOverview,
|
||||
CSSStyles: { ...cssStyles.title, 'margin-block-end': '0' }
|
||||
}).component());
|
||||
|
||||
this.podsUpdated = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: this.getPodsLastUpdated(),
|
||||
CSSStyles: { ...cssStyles.text, 'font-size': '12px', 'margin-block-start': '0' }
|
||||
}).component();
|
||||
|
||||
content.addItem(this.podsUpdated);
|
||||
|
||||
// Pod overview
|
||||
this.podsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
columns: [
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '35%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: { ...cssStyles.tableRow, 'font-size': '20px', 'font-weight': 'bold', 'padding': '7px' }
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '65%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: { ...cssStyles.tableRow, 'padding': '7px' }
|
||||
}
|
||||
],
|
||||
data: this.getPodsTable()
|
||||
}).component();
|
||||
|
||||
this.podsLoading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.podsTable)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._postgresModel.serviceLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.podsLoading, { CSSStyles: { 'margin-bottom': '30px' } });
|
||||
|
||||
// Conditions table
|
||||
this.conditionsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
width: '100%',
|
||||
columns: [
|
||||
{
|
||||
displayName: loc.condition,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '15%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '1%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.details,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '64%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
},
|
||||
{
|
||||
displayName: loc.lastUpdated,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '20%',
|
||||
isReadOnly: true,
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: { ...cssStyles.tableRow, 'white-space': 'nowrap' }
|
||||
}
|
||||
],
|
||||
data: this.getConditionsTable()
|
||||
}).component();
|
||||
|
||||
this.conditionsLoading = this.modelView.modelBuilder.loadingComponent()
|
||||
.withItem(this.conditionsTable)
|
||||
.withProperties<azdata.LoadingComponentProperties>({
|
||||
loading: !this._postgresModel.serviceLastUpdated
|
||||
}).component();
|
||||
|
||||
content.addItem(this.conditionsLoading);
|
||||
this.initialized = true;
|
||||
return root;
|
||||
}
|
||||
|
||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
||||
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.podsLoading!.loading = true;
|
||||
this.conditionsLoading!.loading = true;
|
||||
await this._postgresModel.refresh();
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||
} finally {
|
||||
refreshButton.enabled = true;
|
||||
}
|
||||
}));
|
||||
|
||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||
{ component: refreshButton }
|
||||
]).component();
|
||||
}
|
||||
|
||||
private getPodsLastUpdated(): string {
|
||||
return this._postgresModel.serviceLastUpdated
|
||||
? loc.updated(fromNow(this._postgresModel.serviceLastUpdated!, true)) : '';
|
||||
}
|
||||
|
||||
private getPodsTable(): (string | number)[][] {
|
||||
return [
|
||||
[this._postgresModel.service?.status?.podsRunning ?? 0, loc.running],
|
||||
[this._postgresModel.service?.status?.podsPending ?? 0, loc.pending],
|
||||
[this._postgresModel.service?.status?.podsFailed ?? 0, loc.failed],
|
||||
[this._postgresModel.service?.status?.podsUnknown ?? 0, loc.unknown]
|
||||
];
|
||||
}
|
||||
|
||||
private getConditionsTable(): (string | azdata.ImageComponent)[][] {
|
||||
/* TODO chgagnon
|
||||
return this._postgresModel.service?.status?.conditions?.map(c => {
|
||||
const healthy = c.type === 'Ready' ? c.status === 'True' : c.status === 'False';
|
||||
|
||||
const image = this.modelView.modelBuilder.image().withProperties<azdata.ImageComponentProperties>({
|
||||
iconPath: healthy ? IconPathHelper.success : IconPathHelper.fail,
|
||||
iconHeight: '20px',
|
||||
iconWidth: '20px',
|
||||
width: '20px',
|
||||
height: '20px'
|
||||
}).component();
|
||||
|
||||
return [
|
||||
c.type ?? '',
|
||||
image,
|
||||
c.message ?? '',
|
||||
c.lastTransitionTime ? fromNow(c.lastTransitionTime!, true) : ''
|
||||
];
|
||||
}) ?? [];
|
||||
*/
|
||||
return [];
|
||||
}
|
||||
|
||||
private handleServiceUpdated() {
|
||||
this.podsUpdated!.value = this.getPodsLastUpdated();
|
||||
this.podsTable!.data = this.getPodsTable();
|
||||
this.podsLoading!.loading = false;
|
||||
|
||||
this.conditionsTable!.data = this.getConditionsTable();
|
||||
this.conditionsLoading!.loading = false;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ResourceInfo, ResourceType } from 'arc';
|
||||
import * as vscode from 'vscode';
|
||||
import { parseInstanceName, UserCancelledError } from '../../common/utils';
|
||||
import { UserCancelledError } from '../../common/utils';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { ControllerModel, Registration } from '../../models/controllerModel';
|
||||
import { MiaaModel } from '../../models/miaaModel';
|
||||
@@ -83,7 +83,7 @@ export class ControllerTreeNode extends TreeNode {
|
||||
}
|
||||
|
||||
const resourceInfo: ResourceInfo = {
|
||||
name: parseInstanceName(registration.instanceName),
|
||||
name: registration.instanceName,
|
||||
resourceType: registration.instanceType ?? ''
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ export class ControllerTreeNode extends TreeNode {
|
||||
|
||||
switch (registration.instanceType) {
|
||||
case ResourceType.postgresInstances:
|
||||
const postgresModel = new PostgresModel(resourceInfo, registration);
|
||||
const postgresModel = new PostgresModel(this.model, resourceInfo, registration);
|
||||
node = new PostgresTreeNode(postgresModel, this.model, this._context);
|
||||
break;
|
||||
case ResourceType.sqlManagedInstances:
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ResourceTreeNode } from './resourceTreeNode';
|
||||
export class PostgresTreeNode extends ResourceTreeNode {
|
||||
|
||||
constructor(private _model: PostgresModel, private _controllerModel: ControllerModel, private _context: vscode.ExtensionContext) {
|
||||
super(_model.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, _model);
|
||||
super(_model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, _model);
|
||||
}
|
||||
|
||||
public async openDashboard(): Promise<void> {
|
||||
|
||||
@@ -83,6 +83,34 @@ export class AzdataTool implements IAzdataTool {
|
||||
},
|
||||
show: async (name: string) => {
|
||||
return this.executeCommand<azdataExt.PostgresServerShowResult>(['arc', 'postgres', 'server', 'show', '-n', name]);
|
||||
},
|
||||
edit: async (args: {
|
||||
name: string,
|
||||
adminPassword?: boolean,
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
engineSettings?: string,
|
||||
extensions?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean,
|
||||
port?: number,
|
||||
replaceEngineSettings?: boolean,
|
||||
workers?: number
|
||||
}) => {
|
||||
const argsArray = ['arc', 'postgres', 'server', 'edit', '-n', args.name];
|
||||
if (args.adminPassword) { argsArray.push('--admin-password'); }
|
||||
if (args.coresLimit !== undefined) { argsArray.push('--cores-limit', args.coresLimit); }
|
||||
if (args.coresRequest !== undefined) { argsArray.push('--cores-request', args.coresRequest); }
|
||||
if (args.engineSettings !== undefined) { argsArray.push('--engine-settings', args.engineSettings); }
|
||||
if (args.extensions !== undefined) { argsArray.push('--extensions', args.extensions); }
|
||||
if (args.memoryLimit !== undefined) { argsArray.push('--memory-limit', args.memoryLimit); }
|
||||
if (args.memoryRequest !== undefined) { argsArray.push('--memory-request', args.memoryRequest); }
|
||||
if (args.noWait) { argsArray.push('--no-wait'); }
|
||||
if (args.port !== undefined) { argsArray.push('--port', args.port.toString()); }
|
||||
if (args.replaceEngineSettings) { argsArray.push('--replace-engine-settings'); }
|
||||
if (args.workers !== undefined) { argsArray.push('--workers', args.workers.toString()); }
|
||||
return this.executeCommand<void>(argsArray);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,6 +92,23 @@ export async function activate(context: vscode.ExtensionContext): Promise<azdata
|
||||
show: async (name: string) => {
|
||||
await throwIfNoAzdataOrEulaNotAccepted();
|
||||
return localAzdata!.arc.postgres.server.show(name);
|
||||
},
|
||||
edit: async (args: {
|
||||
name: string,
|
||||
adminPassword?: boolean,
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
engineSettings?: string,
|
||||
extensions?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean,
|
||||
port?: number,
|
||||
replaceEngineSettings?: boolean,
|
||||
workers?: number
|
||||
}) => {
|
||||
await throwIfNoAzdataOrEulaNotAccepted();
|
||||
return localAzdata!.arc.postgres.server.edit(args);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
87
extensions/azdata/src/typings/azdata-ext.d.ts
vendored
87
extensions/azdata/src/typings/azdata-ext.d.ts
vendored
@@ -142,20 +142,6 @@ declare module 'azdata-ext' {
|
||||
}
|
||||
}
|
||||
|
||||
export interface PostgresServerShowResult {
|
||||
apiVersion: string, // "arcdata.microsoft.com/v1alpha1"
|
||||
kind: string, // "postgresql-12"
|
||||
metadata: {
|
||||
creationTimestamp: string, // "2020-08-19T20:25:11Z"
|
||||
generation: number, // 1
|
||||
name: string, // "chgagnon-pg"
|
||||
namespace: string, // "arc",
|
||||
resourceVersion: string, // "214944",
|
||||
selfLink: string, // "/apis/arcdata.microsoft.com/v1alpha1/namespaces/arc/postgresql-12s/chgagnon-pg",
|
||||
uid: string, // "26d0f5bb-0c0b-4225-a6b5-5be2bf6feac0"
|
||||
}
|
||||
}
|
||||
|
||||
export interface PostgresServerShowResult {
|
||||
apiVersion: string, // "arcdata.microsoft.com/v1alpha1"
|
||||
kind: string, // "postgresql-12"
|
||||
@@ -169,25 +155,54 @@ declare module 'azdata-ext' {
|
||||
uid: string, // "26d0f5bb-0c0b-4225-a6b5-5be2bf6feac0"
|
||||
},
|
||||
spec: {
|
||||
backups: {
|
||||
deltaMinutes: number, // 3,
|
||||
fullMinutes: number, // 10,
|
||||
tiers: [
|
||||
{
|
||||
retention: {
|
||||
maximums: string[], // [ "6", "512MB" ],
|
||||
minimums: string[], // [ "3" ]
|
||||
engine: {
|
||||
extensions: {
|
||||
name: string // "citus"
|
||||
}[],
|
||||
settings: {
|
||||
default: { } // { "max_connections": "101", "work_mem": "4MB" }
|
||||
}
|
||||
},
|
||||
scale: {
|
||||
shards: number // 1
|
||||
},
|
||||
scheduling: {
|
||||
default: {
|
||||
resources: {
|
||||
requests: {
|
||||
cpu: string, // "1.5"
|
||||
memory: string // "256Mi"
|
||||
},
|
||||
storage: {
|
||||
volumeSize: string, // "1Gi"
|
||||
limits: {
|
||||
cpu: string, // "1.5"
|
||||
memory: string // "256Mi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
status: {
|
||||
readyPods: string, // "1/1",
|
||||
state: string // "Ready"
|
||||
service: {
|
||||
type: string, // "NodePort"
|
||||
port: number // 5432
|
||||
},
|
||||
storage: {
|
||||
data: {
|
||||
className: string, // "local-storage"
|
||||
size: string // "5Gi"
|
||||
},
|
||||
logs: {
|
||||
className: string, // "local-storage"
|
||||
size: string // "5Gi"
|
||||
},
|
||||
backups: {
|
||||
className: string, // "local-storage"
|
||||
size: string // "5Gi"
|
||||
}
|
||||
}
|
||||
},
|
||||
status: {
|
||||
externalEndpoint: string, // "10.130.12.136:26630"
|
||||
readyPods: string, // "1/1",
|
||||
state: string // "Ready"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +229,21 @@ declare module 'azdata-ext' {
|
||||
postgres: {
|
||||
server: {
|
||||
list(): Promise<AzdataOutput<PostgresServerListResult[]>>,
|
||||
show(name: string): Promise<AzdataOutput<PostgresServerShowResult>>
|
||||
show(name: string): Promise<AzdataOutput<PostgresServerShowResult>>,
|
||||
edit(args: {
|
||||
name: string,
|
||||
adminPassword?: boolean,
|
||||
coresLimit?: string,
|
||||
coresRequest?: string,
|
||||
engineSettings?: string,
|
||||
extensions?: string,
|
||||
memoryLimit?: string,
|
||||
memoryRequest?: string,
|
||||
noWait?: boolean,
|
||||
port?: number,
|
||||
replaceEngineSettings?: boolean,
|
||||
workers?: number
|
||||
}): Promise<AzdataOutput<void>>
|
||||
}
|
||||
},
|
||||
sql: {
|
||||
|
||||
Reference in New Issue
Block a user