Fixed PG update (#20484)

Co-authored-by: Candice Ye <canye@microsoft.com>
This commit is contained in:
Candice Ye
2022-08-28 17:00:32 -07:00
committed by GitHub
parent aba0d249a1
commit 96b7335cdd
7 changed files with 97 additions and 1247 deletions

View File

@@ -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<void> {
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 || '');

View File

@@ -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 <role>=<value>.
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();
}
}

View File

@@ -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;
}
}

View File

@@ -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<void> => {
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<void> {
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<void> => {
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();
}
}
}

View File

@@ -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<string> = new Set();
private parameterUpdates: Map<string, string> = 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<void> => {
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<void> => {
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<void> => {
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<void> {
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<void> => {
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<void> {
if (this._postgresModel.configLastUpdated && !this._postgresModel.engineSettingsLastUpdated) {
this.connectToServerButton!.enabled = true;
this.parametersTableLoading!.loading = false;
} else if (this._postgresModel.engineSettingsLastUpdated) {
await this.callGetEngineSettings();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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: {