From 96b7335cddf9114826da433d4fa399da3b4d29ed Mon Sep 17 00:00:00 2001 From: Candice Ye Date: Sun, 28 Aug 2022 17:00:32 -0700 Subject: [PATCH] Fixed PG update (#20484) Co-authored-by: Candice Ye --- extensions/arc/src/models/postgresModel.ts | 49 -- .../postgres/postgresComputeAndStoragePage.ts | 247 +++---- .../postgresCoordinatorNodeParametersPage.ts | 38 - .../postgres/postgresExtensionsPage.ts | 291 -------- .../dashboards/postgres/postgresParameters.ts | 649 ------------------ .../postgresWorkerNodeParametersPage.ts | 39 -- extensions/azcli/src/typings/az-ext.d.ts | 31 - 7 files changed, 97 insertions(+), 1247 deletions(-) delete mode 100644 extensions/arc/src/ui/dashboards/postgres/postgresCoordinatorNodeParametersPage.ts delete mode 100644 extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts delete mode 100644 extensions/arc/src/ui/dashboards/postgres/postgresParameters.ts delete mode 100644 extensions/arc/src/ui/dashboards/postgres/postgresWorkerNodeParametersPage.ts diff --git a/extensions/arc/src/models/postgresModel.ts b/extensions/arc/src/models/postgresModel.ts index 16d7791de6..0f02cd94e6 100644 --- a/extensions/arc/src/models/postgresModel.ts +++ b/extensions/arc/src/models/postgresModel.ts @@ -50,11 +50,6 @@ export class PostgresModel extends ResourceModel { return this._config; } - /** Returns the major version of Postgres */ - public get engineVersion(): string | undefined { - return this._config?.spec.engine.version; - } - /** Returns the IP address and port of Postgres */ public get endpoint(): { ip: string, port: string } | undefined { return this._config?.status.primaryEndpoint @@ -76,12 +71,7 @@ export class PostgresModel extends ResourceModel { const logStorage = this._config.spec.storage?.logs?.volumes?.[0]?.size; const backupsStorage = this._config.spec.storage?.backups?.volumes?.[0]?.size; - // scale.shards was renamed to scale.workers. Check both for backwards compatibility. - const scale = this._config.spec.scale; - const nodes = (scale?.workers ?? scale?.shards ?? 0) + 1; // An extra node for the coordinator - let configuration: string[] = []; - 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) { @@ -162,12 +152,6 @@ export class PostgresModel extends ResourceModel { await this.createCoordinatorEngineSettings(provider, ownerUri, skippedEngineSettings); - const scale = this._config?.spec.scale; - const nodes = (scale?.workers ?? scale?.shards ?? 0); - if (nodes !== 0) { - await this.createWorkerEngineSettings(provider, ownerUri, skippedEngineSettings); - } - this.engineSettingsLastUpdated = new Date(); this._engineSettingsPromise.resolve(); } catch (err) { @@ -202,39 +186,6 @@ export class PostgresModel extends ResourceModel { } - private async createWorkerEngineSettings(provider: azdata.QueryProvider, ownerUri: string, skip: String[]): Promise { - - const engineSettingsWorker = await provider.runQueryAndReturn(ownerUri, - `with settings as (select nodename, success, result from run_command_on_workers('select json_agg(pg_settings) from pg_settings') order by success desc, nodename asc) - select * from settings limit case when exists(select 1 from settings where success) then 1 end`); - - if (engineSettingsWorker.rows[0][1].displayValue === 'False') { - let errorString = engineSettingsWorker.rows.map(row => row[2].displayValue); - throw new Error(errorString.join('\n')); - } - - let engineSettingsWorkerJSON = JSON.parse(engineSettingsWorker.rows[0][2].displayValue); - this.workerNodesEngineSettings = []; - - for (let i = 0; i < engineSettingsWorkerJSON.length; i++) { - let rowValues = engineSettingsWorkerJSON[i]; - let name = rowValues.name; - if (!skip.includes(name!)) { - let result: EngineSettingsModel = { - parameterName: name, - value: rowValues.setting, - description: rowValues.short_desc, - min: rowValues.min_val, - max: rowValues.max_val, - options: rowValues.enumvals, - type: rowValues.vartype - }; - - this.workerNodesEngineSettings.push(result); - } - } - - } protected createConnectionProfile(): azdata.IConnectionProfile { const ipAndPort = parseIpAndPort(this.config?.status.primaryEndpoint || ''); diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts index 2b0cf3d738..2aeee355cc 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts @@ -18,19 +18,19 @@ export type RoleSpecifier = { export type ConfigurationSpecModel = { workers?: number, - coresRequest?: RoleSpecifier, - coresLimit?: RoleSpecifier, - memoryRequest?: RoleSpecifier, - memoryLimit?: RoleSpecifier + coresRequest?: string, + coresLimit?: string, + memoryRequest?: string, + memoryLimit?: string }; export class PostgresComputeAndStoragePage extends DashboardPage { private userInputContainer!: azdata.DivContainer; - private coordinatorCoresLimitBox!: azdata.InputBoxComponent; - private coordinatorCoresRequestBox!: azdata.InputBoxComponent; - private coordinatorMemoryLimitBox!: azdata.InputBoxComponent; - private coordinatorMemoryRequestBox!: azdata.InputBoxComponent; + private coresLimitBox!: azdata.InputBoxComponent; + private coresRequestBox!: azdata.InputBoxComponent; + private memoryLimitBox!: azdata.InputBoxComponent; + private memoryRequestBox!: azdata.InputBoxComponent; private currentConfiguration: ConfigurationSpecModel = {}; private saveArgs: ConfigurationSpecModel = {}; @@ -154,10 +154,10 @@ export class PostgresComputeAndStoragePage extends DashboardPage { await this._azApi.az.postgres.serverarc.update( this._postgresModel.info.name, { - coresRequest: this.schedulingParamsToEdit(this.saveArgs.coresRequest!), - coresLimit: this.schedulingParamsToEdit(this.saveArgs.coresLimit!), - memoryRequest: this.schedulingParamsToEdit(this.saveArgs.memoryRequest!), - memoryLimit: this.schedulingParamsToEdit(this.saveArgs.memoryLimit!) + coresRequest: this.saveArgs.coresRequest!, + coresLimit: this.saveArgs.coresLimit!, + memoryRequest: this.saveArgs.memoryRequest!, + memoryLimit: this.saveArgs.memoryLimit! }, this._postgresModel.controllerModel.info.namespace, this._postgresModel.controllerModel.azAdditionalEnvVars); @@ -195,10 +195,10 @@ export class PostgresComputeAndStoragePage extends DashboardPage { this.discardButton.onDidClick(async () => { this.discardButton.enabled = false; try { - this.coordinatorCoresRequestBox.value = this.currentConfiguration.coresRequest!.coordinator; - this.coordinatorCoresLimitBox.value = this.currentConfiguration.coresLimit!.coordinator; - this.coordinatorMemoryRequestBox.value = this.currentConfiguration.memoryRequest!.coordinator; - this.coordinatorMemoryLimitBox.value = this.currentConfiguration.memoryLimit!.coordinator; + this.coresRequestBox.value = this.currentConfiguration.coresRequest!; + this.coresLimitBox.value = this.currentConfiguration.coresLimit!; + this.memoryRequestBox.value = this.currentConfiguration.memoryRequest!; + this.memoryLimitBox.value = this.currentConfiguration.memoryLimit!; } catch (error) { vscode.window.showErrorMessage(loc.pageDiscardFailed(error)); } finally { @@ -212,18 +212,9 @@ export class PostgresComputeAndStoragePage extends DashboardPage { ]).component(); } - private schedulingParamsToEdit(arg: RoleSpecifier): string | undefined { - // A comma-separated list of roles with values can be specified in format =. - if (arg.coordinator) { - return `"${arg.coordinator}"`; - } else { - return arg.coordinator ?? undefined; - } - } - private initializeConfigurationBoxes(): void { - // Coordinator node cores request - this.coordinatorCoresRequestBox = this.modelView.modelBuilder.inputBox().withProps({ + // Cores request + this.coresRequestBox = this.modelView.modelBuilder.inputBox().withProps({ readOnly: false, min: 1, inputType: 'number', @@ -232,40 +223,34 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.coordinatorCoresRequestBox.onTextChanged(() => { - if (!(this.saveValueToEdit(this.coordinatorCoresRequestBox, this.currentConfiguration.coresRequest!.coordinator!))) { - this.saveArgs.coresRequest!.coordinator = undefined; - } else if (this.coordinatorCoresRequestBox.value === '') { - this.saveArgs.coresRequest!.coordinator = 'c='; + this.coresRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coresRequestBox!))) { + this.saveArgs.coresRequest = undefined; } else { - this.saveArgs.coresRequest!.coordinator = `c=${this.coordinatorCoresRequestBox.value}`; + this.saveArgs.coresRequest = this.coresRequestBox!.value; } }) ); - - // Coordinator node cores limit - this.coordinatorCoresLimitBox = this.modelView.modelBuilder.inputBox().withProps({ + // Cores limit + this.coresLimitBox = this.modelView.modelBuilder.inputBox().withProps({ readOnly: false, min: 1, inputType: 'number', placeHolder: loc.loading, ariaLabel: loc.coresLimit }).component(); - this.disposables.push( - this.coordinatorCoresLimitBox.onTextChanged(() => { - if (!(this.saveValueToEdit(this.coordinatorCoresLimitBox, this.currentConfiguration.coresLimit!.coordinator!))) { - this.saveArgs.coresLimit!.coordinator = undefined; - } else if (this.coordinatorCoresLimitBox.value === '') { - this.saveArgs.coresLimit!.coordinator = 'c='; + this.coresLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coresLimitBox!))) { + this.saveArgs.coresLimit = undefined; } else { - this.saveArgs.coresLimit!.coordinator = `c=${this.coordinatorCoresLimitBox.value}`; + this.saveArgs.coresLimit = this.coresLimitBox!.value; } }) ); - // Coordinator node memory request - this.coordinatorMemoryRequestBox = this.modelView.modelBuilder.inputBox().withProps({ + // Memory request + this.memoryRequestBox = this.modelView.modelBuilder.inputBox().withProps({ readOnly: false, min: 0.25, inputType: 'number', @@ -274,19 +259,17 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.coordinatorMemoryRequestBox.onTextChanged(() => { - if (!(this.saveValueToEdit(this.coordinatorMemoryRequestBox, this.currentConfiguration.memoryRequest!.coordinator!))) { - this.saveArgs.memoryRequest!.coordinator = undefined; - } else if (this.coordinatorMemoryRequestBox.value === '') { - this.saveArgs.memoryRequest!.coordinator = 'c='; + this.memoryRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.memoryRequestBox!))) { + this.saveArgs.memoryRequest = undefined; } else { - this.saveArgs.memoryRequest!.coordinator = `c=${this.coordinatorMemoryRequestBox.value}Gi`; + this.saveArgs.memoryRequest = this.memoryRequestBox!.value + 'Gi'; } }) ); - // Coordinator node memory limit - this.coordinatorMemoryLimitBox = this.modelView.modelBuilder.inputBox().withProps({ + // Memory limit + this.memoryLimitBox = this.modelView.modelBuilder.inputBox().withProps({ readOnly: false, min: 0.25, inputType: 'number', @@ -295,13 +278,11 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.coordinatorMemoryLimitBox.onTextChanged(() => { - if (!(this.saveValueToEdit(this.coordinatorMemoryLimitBox, this.currentConfiguration.memoryLimit!.coordinator!))) { - this.saveArgs.memoryLimit!.coordinator = undefined; - } else if (this.coordinatorMemoryLimitBox.value === '') { - this.saveArgs.memoryLimit!.coordinator = 'c='; + this.memoryLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.memoryLimitBox!))) { + this.saveArgs.memoryLimit = undefined; } else { - this.saveArgs.memoryLimit!.coordinator = `c=${this.coordinatorMemoryLimitBox.value}Gi`; + this.saveArgs.memoryLimit = this.memoryLimitBox!.value + 'Gi'; } }) ); @@ -309,18 +290,16 @@ export class PostgresComputeAndStoragePage extends DashboardPage { private createUserInputWorkerSection(): azdata.Component[] { if (this._postgresModel.configLastUpdated) { - this.refreshCoresRequest(); - this.refreshCoresLimit(); - this.refreshMemoryRequest(); - this.refreshMemoryLimit(); + this.editCores(); + this.editMemory(); } return [ this.createCoresMemorySection(loc.configuration), - this.createConfigurationSectionContainer(loc.coresRequest, this.coordinatorCoresRequestBox), - this.createConfigurationSectionContainer(loc.coresLimit, this.coordinatorCoresLimitBox), - this.createConfigurationSectionContainer(loc.memoryRequest, this.coordinatorMemoryRequestBox), - this.createConfigurationSectionContainer(loc.memoryLimit, this.coordinatorMemoryLimitBox) + this.createConfigurationSectionContainer(loc.coresRequest, this.coresRequestBox), + this.createConfigurationSectionContainer(loc.coresLimit, this.coresLimitBox), + this.createConfigurationSectionContainer(loc.memoryRequest, this.memoryRequestBox), + this.createConfigurationSectionContainer(loc.memoryLimit, this.memoryLimitBox) ]; } @@ -351,30 +330,24 @@ export class PostgresComputeAndStoragePage extends DashboardPage { return flexContainer; } - /** - * A function that determines if an input box's value should be considered or not. - * Triggers the save and discard buttons to become enabled depending on the value change. - * - * If new value is the same as value found in config, do not consider this new value for editing. - * If new value is invalid, do not consider this new value for editing and enable discard button. - * If value is valid and not equal to original value found in config, add this new value to be considered - * for editing and enable save/discard buttons. - * - * @param component The input box that had an onTextChanged event triggered. - * @param originalValue The value that was contained in the input box before user interaction. - * @return A boolean that reads true if the new value should be taken in for editing or not. - */ - private saveValueToEdit(component: azdata.InputBoxComponent, originalValue: string): boolean { - if (component.value === originalValue) { + private handleOnTextChanged(component: azdata.InputBoxComponent): boolean { + if ((!component.value)) { + // if there is no text found in the inputbox component return false return false; } else if ((!component.valid)) { - this.discardButton.enabled = true; + // if value given by user is not valid enable discard button for user + // to clear all inputs and return false + this.discardButton!.enabled = true; return false; } else { - this.saveButton.enabled = true; - this.discardButton.enabled = true; + // if a valid value has been entered into the input box, enable save and discard buttons + // so that user could choose to either edit instance or clear all inputs + // return true + this.saveButton!.enabled = true; + this.discardButton!.enabled = true; return true; } + } private createCoresMemorySection(title: string): azdata.DivContainer { @@ -401,88 +374,62 @@ export class PostgresComputeAndStoragePage extends DashboardPage { return configurationSection; } - private refreshCoresRequest(): void { - // Coordinator - let coordinatorCR = this._postgresModel.config?.spec.scheduling?.roles?.coordinator?.resources?.requests?.cpu ?? this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.cpu; - if (!coordinatorCR) { - coordinatorCR = ''; + private editCores(): void { + let currentCPUSize = this._postgresModel.config?.spec?.scheduling?.default?.resources?.requests?.cpu; + + if (!currentCPUSize) { + currentCPUSize = ''; } - this.coordinatorCoresRequestBox.placeHolder = ''; - this.coordinatorCoresRequestBox.value = coordinatorCR; + this.coresRequestBox!.value = currentCPUSize; + this.coresRequestBox!.placeHolder = ''; - // Update saved current configuration - this.currentConfiguration.coresRequest = { - coordinator: coordinatorCR - }; + this.saveArgs.coresRequest = undefined; - // Discard argument changes - this.saveArgs.coresRequest = {}; + currentCPUSize = this._postgresModel.config?.spec?.scheduling?.default?.resources?.limits?.cpu; + + if (!currentCPUSize) { + currentCPUSize = ''; + } + + this.coresLimitBox!.placeHolder = currentCPUSize; + this.coresLimitBox!.value = ''; + + this.saveArgs.coresLimit = undefined; } - private refreshCoresLimit(): void { - // Coordinator - let coordinatorCL = this._postgresModel.config?.spec.scheduling?.roles?.coordinator?.resources?.limits?.cpu ?? this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.cpu; - if (!coordinatorCL) { - coordinatorCL = ''; + private editMemory(): void { + let currentMemSizeConversion: string; + let currentMemorySize = this._postgresModel.config?.spec?.scheduling?.default?.resources?.requests?.memory; + + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); } - this.coordinatorCoresLimitBox.placeHolder = ''; - this.coordinatorCoresLimitBox.value = coordinatorCL; + this.memoryRequestBox!.placeHolder = currentMemSizeConversion!; + this.memoryRequestBox!.value = ''; - // Update saved current configuration - this.currentConfiguration.coresLimit = { - coordinator: coordinatorCL - }; + this.saveArgs.memoryRequest = undefined; - // Discard argument changes - this.saveArgs.coresLimit = {}; - } + currentMemorySize = this._postgresModel.config?.spec?.scheduling?.default?.resources?.limits?.memory; - private refreshMemoryRequest(): void { - // Coordinator - let currentCoordinatorMemoryRequest = this._postgresModel.config?.spec.scheduling?.roles?.coordinator?.resources?.requests?.memory ?? this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.memory; - let coordinatorMR = ''; - if (currentCoordinatorMemoryRequest) { - coordinatorMR = convertToGibibyteString(currentCoordinatorMemoryRequest); + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); } - this.coordinatorMemoryRequestBox.placeHolder = ''; - this.coordinatorMemoryRequestBox.value = coordinatorMR; + this.memoryLimitBox!.placeHolder = currentMemSizeConversion!; + this.memoryLimitBox!.value = ''; - // Update saved current configuration - this.currentConfiguration.memoryRequest = { - coordinator: coordinatorMR - }; - // Discard argument changes - this.saveArgs.memoryRequest = {}; - } - - private refreshMemoryLimit(): void { - // Coordinator - let currentCoordinatorMemoryLimit = this._postgresModel.config?.spec.scheduling?.roles?.coordinator?.resources?.limits?.memory ?? this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.memory; - let coordinatorML = ''; - if (currentCoordinatorMemoryLimit) { - coordinatorML = convertToGibibyteString(currentCoordinatorMemoryLimit); - } - - this.coordinatorMemoryLimitBox.placeHolder = ''; - this.coordinatorMemoryLimitBox.value = coordinatorML; - - // Update saved current configuration - this.currentConfiguration.memoryLimit = { - coordinator: coordinatorML - }; - - // Discard argument changes - this.saveArgs.memoryLimit = {}; + this.saveArgs.memoryLimit = undefined; } private handleServiceUpdated(): void { - this.refreshCoresRequest(); - this.refreshCoresLimit(); - this.refreshMemoryRequest(); - this.refreshMemoryLimit(); + this.editCores(); + this.editMemory(); } } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresCoordinatorNodeParametersPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresCoordinatorNodeParametersPage.ts deleted file mode 100644 index 1f984f2bc5..0000000000 --- a/extensions/arc/src/ui/dashboards/postgres/postgresCoordinatorNodeParametersPage.ts +++ /dev/null @@ -1,38 +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 azdata from 'azdata'; -import * as loc from '../../../localizedConstants'; -import { IconPathHelper } from '../../../constants'; -import { PostgresParametersPage } from './postgresParameters'; -import { PostgresModel, EngineSettingsModel } from '../../../models/postgresModel'; - -export class PostgresCoordinatorNodeParametersPage extends PostgresParametersPage { - - constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, postgresModel: PostgresModel) { - super(modelView, dashboard, postgresModel); - } - - protected get title(): string { - return loc.coordinatorNodeParameters; - } - - protected get id(): string { - return 'postgres-coordinator-node-parameters'; - } - - protected get icon(): { dark: string; light: string; } { - return IconPathHelper.gearGray; - } - - protected get description(): string { - return loc.coordinatorNodeParametersDescription; - } - - protected get engineSettings(): EngineSettingsModel[] { - return this._postgresModel.coordinatorNodeEngineSettings; - } - -} diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts deleted file mode 100644 index cb9d9ba86d..0000000000 --- a/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts +++ /dev/null @@ -1,291 +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 azExt from 'az-ext'; -import * as loc from '../../../localizedConstants'; -import { IconPathHelper, cssStyles } from '../../../constants'; -import { DashboardPage } from '../../components/dashboardPage'; -import { PostgresModel } from '../../../models/postgresModel'; -import { AddPGExtensionsDialog } from '../../dialogs/addPGExtensionsDialog'; - -export class PostgresExtensionsPage extends DashboardPage { - - private extensionNames: string[] = []; - private droppedExtensions: string[] = []; - private extensionsTable!: azdata.DeclarativeTableComponent; - private extensionsLoading!: azdata.LoadingComponent; - private addExtensionsButton!: azdata.ButtonComponent; - private dropExtensionsButton!: azdata.ButtonComponent; - private extensionsLink!: azdata.HyperlinkComponent; - - private readonly _azApi: azExt.IExtension; - - constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) { - super(modelView, dashboard); - this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports; - - this.disposables.push( - this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated()))); - } - - protected get title(): string { - return loc.preLoadedExtensions; - } - - protected get id(): string { - return 'postgres-extensions'; - } - - protected get icon(): { dark: string; light: string; } { - return IconPathHelper.extensions; - } - - protected get container(): azdata.Component { - const root = this.modelView.modelBuilder.divContainer().component(); - const content = this.modelView.modelBuilder.divContainer().component(); - root.addItem(content, { CSSStyles: { 'margin': '10px 20px 0px 20px' } }); - - content.addItem(this.modelView.modelBuilder.text().withProps({ - value: this.title, - CSSStyles: { ...cssStyles.title } - }).component()); - - content.addItem(this.modelView.modelBuilder.text().withProps({ - value: loc.extensionsDescription, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component()); - - const info = this.modelView.modelBuilder.text().withProps({ - value: loc.extensionsFunction, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component(); - - this.extensionsLink = this.modelView.modelBuilder.hyperlink().withProps({ - label: loc.learnMore, - url: 'https://www.postgresql.org/docs/12/external-extensions.html', - }).component(); - - const infoAndLink = this.modelView.modelBuilder.flexContainer().withLayout({ flexWrap: 'wrap' }).component(); - infoAndLink.addItem(info, { CSSStyles: { 'margin-right': '5px' } }); - infoAndLink.addItem(this.extensionsLink); - content.addItem(infoAndLink, { CSSStyles: { 'margin-bottom': '15px', 'margin-top': '25px' } }); - - this.extensionsTable = this.modelView.modelBuilder.declarativeTable().withProps({ - ariaLabel: loc.extensionsTableLabel, - width: '100%', - columns: [ - { - displayName: '', - valueType: azdata.DeclarativeDataType.component, - width: '20px', - isReadOnly: true, - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - }, - { - displayName: loc.extensionName, - valueType: azdata.DeclarativeDataType.string, - isReadOnly: true, - width: '100%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - } - ], - dataValues: [] - }).component(); - - this.extensionsLoading = this.modelView.modelBuilder.loadingComponent() - .withItem(this.extensionsTable).withProps({ - loading: !this._postgresModel.configLastUpdated, - loadingText: loc.extensionsTableLoading, - loadingCompletedText: loc.extensionsTableLoadingComplete - }).component(); - - content.addItem(this.extensionsLoading, { CSSStyles: cssStyles.text }); - - this.initialized = true; - return root; - } - - protected get toolbarContainer(): azdata.ToolbarContainer { - // Add extensions - this.addExtensionsButton = this.modelView.modelBuilder.button().withProps({ - label: loc.loadExtensions, - ariaLabel: loc.loadExtensions, - iconPath: IconPathHelper.add - }).component(); - - this.disposables.push( - this.addExtensionsButton.onDidClick(async () => { - const addExtDialog = new AddPGExtensionsDialog(this._postgresModel); - addExtDialog.showDialog(loc.loadExtensions); - - let extArg = await addExtDialog.waitForClose(); - if (extArg) { - try { - this.addExtensionsButton.enabled = false; - this.dropExtensionsButton.enabled = false; - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - - await this._azApi.az.postgres.serverarc.update( - this._postgresModel.info.name, - {}, - this._postgresModel.controllerModel.info.namespace, - this._postgresModel.controllerModel.azAdditionalEnvVars); - - try { - await this._postgresModel.refresh(); - } catch (error) { - vscode.window.showErrorMessage(loc.refreshFailed(error)); - } - } - ); - - vscode.window.showInformationMessage(loc.extensionsAdded(extArg)); - - } catch (error) { - vscode.window.showErrorMessage(loc.updateExtensionsFailed(error)); - } finally { - this.addExtensionsButton.enabled = true; - } - } - })); - - // Drop extensions - this.dropExtensionsButton = this.modelView.modelBuilder.button().withProps({ - label: loc.unloadExtensions, - ariaLabel: loc.unloadExtensions, - iconPath: IconPathHelper.delete, - enabled: false - }).component(); - - this.disposables.push( - this.dropExtensionsButton.onDidClick(async () => { - try { - this.addExtensionsButton.enabled = false; - this.dropExtensionsButton.enabled = false; - await this.dropExtension(); - - try { - await this._postgresModel.refresh(); - } catch (error) { - vscode.window.showErrorMessage(loc.refreshFailed(error)); - } - - vscode.window.showInformationMessage(loc.extensionsDropped(this.droppedExtensions.join())); - this.droppedExtensions = []; - - } catch (error) { - vscode.window.showErrorMessage(loc.updateExtensionsFailed(error)); - } finally { - this.addExtensionsButton.enabled = true; - } - })); - - return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([ - { component: this.addExtensionsButton }, - { component: this.dropExtensionsButton } - ]).component(); - } - - private refreshExtensionsTable(): void { - let extensions = this._postgresModel.config!.spec.engine.extensions; - let extenesionFinalData: azdata.DeclarativeTableCellValue[][] = []; - let extensionBasicData: (string | azdata.CheckBoxComponent | azdata.ImageComponent)[][] = []; - - if (extensions) { - extensionBasicData = extensions.map(e => { - this.extensionNames.push(e.name); - return [this.createDropCheckBox(e.name), e.name]; - }); - } else { - extensionBasicData = [[this.modelView.modelBuilder.image().component(), loc.noExtensions]]; - } - - extenesionFinalData = extensionBasicData.map(e => { - return e.map((value): azdata.DeclarativeTableCellValue => { - return { value: value }; - }); - }); - - this.extensionsTable.setDataValues(extenesionFinalData); - } - - /** - * Creates checkboxes to select which extensions to drop. - * Allows user to drop multiple extension. - * @param name name of postgres extension the checkbox will be tied to. - */ - public createDropCheckBox(name: string): azdata.CheckBoxComponent { - // Can select extensions to drop - let checkBox = this.modelView.modelBuilder.checkBox().withProps({ - ariaLabel: loc.unloadExtensions, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component(); - - if (name === 'citus') { - checkBox.enabled = false; - } - - this.disposables.push( - checkBox.onChanged(() => { - if (checkBox.checked) { - this.droppedExtensions.push(name); - this.dropExtensionsButton.focus(); - } else { - let index = this.droppedExtensions.indexOf(name, 0); - this.droppedExtensions.splice(index, 1); - } - this.dropExtensionsButton.enabled = this.droppedExtensions.length ? true : false; - }) - ); - - return checkBox; - } - - /** - * Calls edit on postgres extensions with an updated extensions list. - */ - public async dropExtension(): Promise { - this.droppedExtensions.forEach(d => { - let index = this.extensionNames.indexOf(d, 0); - this.extensionNames.splice(index, 1); - }); - - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - await this._azApi.az.postgres.serverarc.update( - this._postgresModel.info.name, - {}, - this._postgresModel.controllerModel.info.namespace, - this._postgresModel.controllerModel.azAdditionalEnvVars - ); - } - ); - } - - private handleConfigUpdated(): void { - if (this._postgresModel.config) { - this.extensionsLoading.loading = false; - this.extensionsLink.url = `https://www.postgresql.org/docs/${this._postgresModel.engineVersion}/external-extensions.html`; - this.extensionNames = []; - this.refreshExtensionsTable(); - this.addExtensionsButton.focus(); - } - } -} diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresParameters.ts b/extensions/arc/src/ui/dashboards/postgres/postgresParameters.ts deleted file mode 100644 index 2048c37b29..0000000000 --- a/extensions/arc/src/ui/dashboards/postgres/postgresParameters.ts +++ /dev/null @@ -1,649 +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 azExt from 'az-ext'; -import * as loc from '../../../localizedConstants'; -import { UserCancelledError } from '../../../common/api'; -import { IconPathHelper, cssStyles } from '../../../constants'; -import { DashboardPage } from '../../components/dashboardPage'; -import { EngineSettingsModel, PostgresModel } from '../../../models/postgresModel'; -import { debounce, instanceOfCheckBox } from '../../../common/utils'; - -export type ParametersModel = { - parameterName: string, - originalValue: string, - valueComponent: azdata.TextComponent | azdata.DropDownComponent | azdata.CheckBoxComponent, - information?: azdata.ButtonComponent, - description: string, - resetButton: azdata.ButtonComponent -}; - -export abstract class PostgresParametersPage extends DashboardPage { - private searchBox!: azdata.InputBoxComponent; - protected _parametersTable!: azdata.DeclarativeTableComponent; - private parameterContainer!: azdata.DivContainer; - private parametersTableLoading?: azdata.LoadingComponent; - - private discardButton!: azdata.ButtonComponent; - private saveButton!: azdata.ButtonComponent; - private resetAllButton!: azdata.ButtonComponent; - private connectToServerButton?: azdata.ButtonComponent; - - protected _parameters: ParametersModel[] = []; - private changedComponentValues: Set = new Set(); - private parameterUpdates: Map = new Map(); - - protected readonly _azApi: azExt.IExtension; - - constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, protected _postgresModel: PostgresModel) { - super(modelView, dashboard); - - this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports; - - this.initializeSearchBox(); - - this.disposables.push( - this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())) - ); - } - - protected abstract get description(): string; - - protected abstract get engineSettings(): EngineSettingsModel[]; - - 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().withProps({ - value: this.title, - CSSStyles: { ...cssStyles.title } - }).component()); - - content.addItem(this.modelView.modelBuilder.text().withProps({ - value: this.description, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component()); - - content.addItem(this.modelView.modelBuilder.hyperlink().withProps({ - label: loc.learnAboutNodeParameters, - url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-server-parameters-postgresql-hyperscale' - }).component(), { CSSStyles: { 'margin-bottom': '20px' } }); - - content.addItem(this.searchBox!, { CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'margin-bottom': '20px' } }); - - this._parametersTable = this.modelView.modelBuilder.declarativeTable().withProps({ - width: '100%', - columns: [ - { - displayName: loc.parameterName, - valueType: azdata.DeclarativeDataType.string, - isReadOnly: true, - width: '20%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - }, - { - displayName: loc.value, - valueType: azdata.DeclarativeDataType.component, - isReadOnly: false, - width: '20%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - }, - { - displayName: loc.description, - valueType: azdata.DeclarativeDataType.string, - isReadOnly: true, - width: '50%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: { - ...cssStyles.tableRow - } - }, - { - displayName: loc.resetToDefault, - valueType: azdata.DeclarativeDataType.component, - isReadOnly: false, - width: '10%', - headerCssStyles: cssStyles.tableHeader, - rowCssStyles: cssStyles.tableRow - } - ], - data: [] - }).component(); - - this.parameterContainer = this.modelView.modelBuilder.divContainer().component(); - this.selectComponent(); - content.addItem(this.parameterContainer); - - this.initialized = true; - - return root; - } - - protected get toolbarContainer(): azdata.ToolbarContainer { - // Save Edits - this.saveButton = this.modelView.modelBuilder.button().withProps({ - label: loc.saveText, - iconPath: IconPathHelper.save, - enabled: false - }).component(); - - let engineSettings: string[] = []; - this.disposables.push( - this.saveButton.onDidClick(async () => { - this.saveButton.enabled = false; - try { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - try { - this.parameterUpdates.forEach((value, key) => { - engineSettings.push(`${key}="${value}"`); - }); - } catch (err) { - // If an error occurs while editing the instance then re-enable the save button since - // the edit wasn't successfully applied - this.saveButton.enabled = true; - throw err; - } - try { - await this.callGetEngineSettings(); - } catch (error) { - vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, error)); - } - } - ); - - vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name)); - - engineSettings = []; - this.changedComponentValues.clear(); - this.parameterUpdates.clear(); - this.discardButton.enabled = false; - this.resetAllButton.enabled = true; - - } catch (error) { - vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error)); - } - }) - ); - - // Discard - this.discardButton = this.modelView.modelBuilder.button().withProps({ - label: loc.discardText, - iconPath: IconPathHelper.discard, - enabled: false - }).component(); - - this.disposables.push( - this.discardButton.onDidClick(async () => { - this.discardButton.enabled = false; - try { - this.discardParametersTableChanges(); - } catch (error) { - this.discardButton!.enabled = true; - vscode.window.showErrorMessage(loc.pageDiscardFailed(error)); - } finally { - this.changedComponentValues.clear(); - this.saveButton.enabled = false; - this.parameterUpdates.clear(); - } - }) - ); - - // Reset all - this.resetAllButton = this.modelView.modelBuilder.button().withProps({ - label: loc.resetAllToDefault, - iconPath: IconPathHelper.reset, - enabled: false - }).component(); - - this.disposables.push( - this.resetAllButton.onDidClick(async () => { - this.resetAllButton.enabled = false; - this.discardButton.enabled = false; - this.saveButton.enabled = false; - try { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - try { - } catch (err) { - // If an error occurs while resetting the instance then re-enable the reset button since - // the edit wasn't successfully applied - if (this.parameterUpdates.size > 0) { - this.discardButton.enabled = true; - this.saveButton.enabled = true; - } - this.resetAllButton.enabled = true; - throw err; - } - this.changedComponentValues.clear(); - try { - await this.callGetEngineSettings(); - } catch (error) { - vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, error)); - } - } - ); - - vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name)); - this.parameterUpdates.clear(); - - } catch (error) { - vscode.window.showErrorMessage(loc.resetFailed(error)); - } - }) - ); - - return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([ - { component: this.saveButton }, - { component: this.discardButton }, - { component: this.resetAllButton } - ]).component(); - } - - protected initializeConnectButton(): void { - this.connectToServerButton = this.modelView.modelBuilder.button().withProps({ - label: loc.connectToServer, - enabled: false, - CSSStyles: { 'max-width': '125px' } - }).component(); - - this.disposables.push( - this.connectToServerButton.onDidClick(async () => { - let scale = this._postgresModel.config?.spec.scale; - let nodes = (scale?.workers ?? scale?.shards ?? 0); - if (this.title === loc.workerNodeParameters && nodes === 0) { - vscode.window.showInformationMessage(loc.noWorkerPods); - return; - } - - this.connectToServerButton!.enabled = false; - if (!vscode.extensions.getExtension(loc.postgresExtension)) { - const response = await vscode.window.showErrorMessage(loc.missingExtension('PostgreSQL'), loc.yes, loc.no); - if (response !== loc.yes) { - this.connectToServerButton!.enabled = true; - return; - } - - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.installingExtension(loc.postgresExtension), - cancellable: false - }, - async (_progress, _token): Promise => { - try { - await vscode.commands.executeCommand('workbench.extensions.installExtension', loc.postgresExtension); - } catch (err) { - vscode.window.showErrorMessage(loc.extensionInstallationFailed(loc.postgresExtension)); - this.connectToServerButton!.enabled = true; - throw err; - } - } - ); - vscode.window.showInformationMessage(loc.extensionInstalled(loc.postgresExtension)); - } - - this.parametersTableLoading!.loading = true; - await this.callGetEngineSettings().finally(() => this.parametersTableLoading!.loading = false); - this.searchBox.enabled = true; - this.resetAllButton.enabled = true; - this.parameterContainer.clearItems(); - this.parameterContainer.addItem(this._parametersTable); - }) - ); - } - - private selectComponent(): void { - if (!this._postgresModel.engineSettingsLastUpdated) { - this.parameterContainer.addItem(this.modelView.modelBuilder.text().withProps({ - value: loc.connectToPostgresDescription, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component()); - this.initializeConnectButton(); - this.parameterContainer.addItem(this.connectToServerButton!, { CSSStyles: { 'max-width': '125px' } }); - this.parametersTableLoading = this.modelView.modelBuilder.loadingComponent().component(); - this.parameterContainer.addItem(this.parametersTableLoading); - } else { - this.searchBox.enabled = true; - this.resetAllButton.enabled = true; - this.parameterContainer.addItem(this._parametersTable!); - this.refreshParametersTableValues(); - } - } - - private async callGetEngineSettings(): Promise { - try { - await this._postgresModel.getEngineSettings().then(() => { - if (this._parametersTable.data?.length !== 0) { - this.refreshParametersTableValues(); - } else { - this.populateParametersTable(); - } - }); - } catch (error) { - if (error instanceof UserCancelledError) { - vscode.window.showWarningMessage(loc.pgConnectionRequired); - } else { - vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, error)); - } - this.connectToServerButton!.enabled = true; - throw error; - } - } - - protected initializeSearchBox(): void { - this.searchBox = this.modelView.modelBuilder.inputBox().withProps({ - readOnly: false, - enabled: false, - placeHolder: loc.searchToFilter - }).component(); - - this.disposables.push( - this.searchBox.onTextChanged(() => { - this.onSearchFilter(); - }) - ); - } - - @debounce(500) - private onSearchFilter(): void { - if (!this.searchBox.value) { - this._parametersTable.setFilter(undefined); - } else { - this.filterParameters(this.searchBox.value); - } - } - - private filterParameters(search: string): void { - const filteredRowIndexes: number[] = []; - this._parametersTable.data?.forEach((row, index) => { - if (row[0].toUpperCase()?.search(search.toUpperCase()) !== -1 || row[2].toUpperCase()?.search(search.toUpperCase()) !== -1) { - filteredRowIndexes.push(index); - } - }); - this._parametersTable.setFilter(filteredRowIndexes); - } - - private handleOnTextChanged(component: azdata.InputBoxComponent, name: string, currentValue: string | undefined): boolean { - if (!component.valid) { - // If invalid value return false and enable discard button - this.discardButton.enabled = true; - this.collectChangedComponents(name); - return false; - } else if (component.value === currentValue) { - this.removeFromChangedComponents(name); - return false; - } else { - /* If a valid value has been entered into the input box, enable save and discard buttons - so that user could choose to either edit instance or clear all inputs - return true */ - this.saveButton.enabled = true; - this.discardButton.enabled = true; - this.collectChangedComponents(name); - return true; - } - } - - protected createParameterComponents(engineSetting: EngineSettingsModel): ParametersModel { - - // Can reset individual parameter - const resetParameterButton = this.modelView.modelBuilder.button().withProps({ - iconPath: IconPathHelper.reset, - title: loc.resetToDefault, - width: '20px', - height: '20px', - enabled: true - }).component(); - - this.disposables.push( - resetParameterButton.onDidClick(async () => { - try { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - try { - await this.callGetEngineSettings(); - } catch (error) { - vscode.window.showErrorMessage(loc.fetchEngineSettingsFailed(this._postgresModel.info.name, error)); - } - } - ); - this.removeFromChangedComponents(engineSetting.parameterName!); - vscode.window.showInformationMessage(loc.instanceUpdated(this._postgresModel.info.name)); - } catch (error) { - vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._postgresModel.info.name, error)); - } - }) - ); - - let valueComponent: azdata.Component; - if (engineSetting.type === 'enum') { - // If type is enum, component should be drop down menu - let values: string[] = []; - if (typeof engineSetting.options === 'string') { - let options = engineSetting.options?.slice(1, -1).split(','); - values = options.map(option => option.slice(option.indexOf('"') + 1, -1)); - } else if (engineSetting.options) { - values = engineSetting.options; - } - - let valueBox = this.modelView.modelBuilder.dropDown().withProps({ - values: values, - value: engineSetting.value, - width: '150px' - }).component(); - valueComponent = valueBox; - - this.disposables.push( - valueBox.onValueChanged(() => { - if (engineSetting.value !== String(valueBox.value)) { - this.parameterUpdates.set(engineSetting.parameterName!, String(valueBox.value)); - this.collectChangedComponents(engineSetting.parameterName!); - this.saveButton.enabled = true; - this.discardButton.enabled = true; - } else if (this.parameterUpdates.has(engineSetting.parameterName!)) { - this.parameterUpdates.delete(engineSetting.parameterName!); - this.removeFromChangedComponents(engineSetting.parameterName!); - } - }) - ); - } else if (engineSetting.type === 'bool') { - // If type is bool, component should be checkbox to turn on or off - let valueBox = this.modelView.modelBuilder.checkBox().withProps({ - label: loc.on, - CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } - }).component(); - valueComponent = valueBox; - - if (engineSetting.value === 'on') { - valueBox.checked = true; - } else { - valueBox.checked = false; - } - - this.disposables.push( - valueBox.onChanged(() => { - if (valueBox.checked && engineSetting.value === 'off') { - this.parameterUpdates.set(engineSetting.parameterName!, loc.on); - this.collectChangedComponents(engineSetting.parameterName!); - this.saveButton.enabled = true; - this.discardButton.enabled = true; - } else if (!valueBox.checked && engineSetting.value === 'on') { - this.parameterUpdates.set(engineSetting.parameterName!, loc.off); - this.collectChangedComponents(engineSetting.parameterName!); - this.saveButton.enabled = true; - this.discardButton.enabled = true; - } else if (this.parameterUpdates.has(engineSetting.parameterName!)) { - this.parameterUpdates.delete(engineSetting.parameterName!); - this.removeFromChangedComponents(engineSetting.parameterName!); - } - }) - ); - } else if (engineSetting.type === 'string') { - // If type is string, component should be text inputbox - let valueBox = this.modelView.modelBuilder.inputBox().withProps({ - required: true, - readOnly: false, - value: engineSetting.value, - width: '150px' - }).component(); - valueComponent = valueBox; - - this.disposables.push( - valueBox.onTextChanged(() => { - if ((this.handleOnTextChanged(valueBox, engineSetting.parameterName!, engineSetting.value))) { - this.parameterUpdates.set(engineSetting.parameterName!, `"${valueBox.value!}"`); - } else if (this.parameterUpdates.has(engineSetting.parameterName!)) { - this.parameterUpdates.delete(engineSetting.parameterName!); - } - }) - ); - } else { - // If type is real or interger, component should be inputbox set to inputType of number. Max and min values also set. - let valueBox = this.modelView.modelBuilder.inputBox().withProps({ - required: true, - readOnly: false, - min: parseInt(engineSetting.min!), - max: parseInt(engineSetting.max!), - inputType: 'number', - value: engineSetting.value, - width: '150px' - }).component(); - valueComponent = valueBox; - - this.disposables.push( - valueBox.onTextChanged(() => { - if ((this.handleOnTextChanged(valueBox, engineSetting.parameterName!, engineSetting.value))) { - this.parameterUpdates.set(engineSetting.parameterName!, valueBox.value!); - } else if (this.parameterUpdates.has(engineSetting.parameterName!)) { - this.parameterUpdates.delete(engineSetting.parameterName!); - } - }) - ); - - // Information bubble title to show allowed values - let information = this.modelView.modelBuilder.button().withProps({ - iconPath: IconPathHelper.information, - width: '15px', - height: '15px', - description: loc.rangeSetting(engineSetting.min!, engineSetting.max!) - }).component(); - - return { - parameterName: engineSetting.parameterName!, - originalValue: engineSetting.value!, - valueComponent: valueComponent, - information: information, - description: engineSetting.description!, - resetButton: resetParameterButton - }; - } - - return { - parameterName: engineSetting.parameterName!, - originalValue: engineSetting.value!, - valueComponent: valueComponent, - description: engineSetting.description!, - resetButton: resetParameterButton - }; - } - - private collectChangedComponents(name: string): void { - if (!this.changedComponentValues.has(name)) { - this.changedComponentValues.add(name); - } - } - - private removeFromChangedComponents(name: string): void { - if (this.changedComponentValues.has(name)) { - this.changedComponentValues.delete(name); - } - } - - private discardParametersTableChanges(): void { - this.changedComponentValues.forEach(v => { - let param = this._parameters.find(p => p.parameterName === v); - if (instanceOfCheckBox(param!.valueComponent)) { - if (param!.originalValue === 'on') { - param!.valueComponent.checked = true; - } else { - param!.valueComponent.checked = false; - } - } else { - param!.valueComponent.value = param!.originalValue; - } - }); - } - - private populateParametersTable(): void { - this._parameters = this.engineSettings.map(parameter => this.createParameterComponents(parameter)); - - this._parametersTable.data = this._parameters.map(p => { - if (p.information) { - // Container to hold input component and information bubble - const valueContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component(); - valueContainer.addItem(p.valueComponent, { CSSStyles: { 'margin-right': '0px' } }); - valueContainer.addItem(p.information, { CSSStyles: { 'margin-left': '5px' } }); - return [p.parameterName, valueContainer, p.description, p.resetButton]; - } else { - return [p.parameterName, p.valueComponent, p.description, p.resetButton]; - } - }); - } - - /** - * Checks if exisiting parameter values needs to be updated. - * Only updates exisiting parameters, will not add/remove parameters from the table. - */ - private refreshParametersTableValues(): void { - this.engineSettings.map(parameter => { - let param = this._parameters.find(p => p.parameterName === parameter.parameterName); - if (param) { - if (parameter.value !== param.originalValue) { - param.originalValue = parameter.value!; - - if (instanceOfCheckBox(param.valueComponent)) { - if (param.originalValue === 'on') { - param.valueComponent.checked = true; - } else { - param.valueComponent.checked = false; - } - } else { - param.valueComponent.value = parameter.value; - } - } - } - }); - } - - private async handleServiceUpdated(): Promise { - if (this._postgresModel.configLastUpdated && !this._postgresModel.engineSettingsLastUpdated) { - this.connectToServerButton!.enabled = true; - this.parametersTableLoading!.loading = false; - } else if (this._postgresModel.engineSettingsLastUpdated) { - await this.callGetEngineSettings(); - } - } -} diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresWorkerNodeParametersPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresWorkerNodeParametersPage.ts deleted file mode 100644 index c29ef4f74e..0000000000 --- a/extensions/arc/src/ui/dashboards/postgres/postgresWorkerNodeParametersPage.ts +++ /dev/null @@ -1,39 +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 azdata from 'azdata'; -import * as loc from '../../../localizedConstants'; -import { IconPathHelper } from '../../../constants'; -import { PostgresParametersPage } from './postgresParameters'; -import { PostgresModel, EngineSettingsModel } from '../../../models/postgresModel'; - -export class PostgresWorkerNodeParametersPage extends PostgresParametersPage { - - constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, postgresModel: PostgresModel) { - super(modelView, dashboard, postgresModel); - } - - protected get title(): string { - return loc.workerNodeParameters; - } - - protected get id(): string { - return 'postgres-worker-node-parameters'; - } - - protected get icon(): { dark: string; light: string; } { - return IconPathHelper.gearBlue; - } - - protected get description(): string { - return loc.workerNodesParametersDescription; - } - - - protected get engineSettings(): EngineSettingsModel[] { - return this._postgresModel.workerNodesEngineSettings; - } - -} diff --git a/extensions/azcli/src/typings/az-ext.d.ts b/extensions/azcli/src/typings/az-ext.d.ts index 2c5a90a763..a6d4336214 100644 --- a/extensions/azcli/src/typings/az-ext.d.ts +++ b/extensions/azcli/src/typings/az-ext.d.ts @@ -450,43 +450,12 @@ declare module 'az-ext' { uid: string, // "26d0f5bb-0c0b-4225-a6b5-5be2bf6feac0" }, spec: { - engine: { - extensions: { - name: string // "citus" - }[], - settings: { - default: { [key: string]: string }, // { "max_connections": "101", "work_mem": "4MB" } - roles: { - coordinator: { [key: string]: string }, - worker: { [key: string]: string } - } - }, - version: string // "12" - }, - scale: { - shards: number, // 1 (shards was renamed to workers, kept here for backwards compatibility) - workers: number // 1 - }, scheduling: { // If no roles are specified, settings will apply to all nodes of the PostgreSQL Hyperscale server group. default: { resources: { requests: SchedulingOptions, limits: SchedulingOptions } - }, - roles: { - coordinator: { - resources: { - requests: SchedulingOptions, - limits: SchedulingOptions - } - }, - worker: { - resources: { - requests: SchedulingOptions, - limits: SchedulingOptions - } - } } }, services: {