diff --git a/extensions/arc/src/localizedConstants.ts b/extensions/arc/src/localizedConstants.ts index 56cca58f7d..c19c283963 100644 --- a/extensions/arc/src/localizedConstants.ts +++ b/extensions/arc/src/localizedConstants.ts @@ -67,6 +67,8 @@ export const selectConnectionString = localize('arc.selectConnectionString', "Se export const addingWorkerNodes = localize('arc.addingWorkerNodes', "adding worker nodes"); export const workerNodesDescription = localize('arc.workerNodesDescription', "Expand your server group and scale your database by adding worker nodes."); export const postgresConfigurationInformation = localize('arc.postgres.configurationInformation', "You can configure the number of CPU cores and storage size that will apply to both worker nodes and coordinator node. Each worker node will have the same configuration. Adjust the number of CPU cores and memory settings for your server group."); +export const workerNodesConfigurationInformation = localize('arc.workerNodesConfigurationInformation', "You can configure the number of CPU cores and storage size that will apply to all worker nodes. Adjust the number of CPU cores and memory settings for your server group."); +export const coordinatorNodeConfigurationInformation = localize('arc.coordinatorNodeConfigurationInformation', "You can configure the number of CPU cores and storage size that will apply to the coordinator node. Adjust the number of CPU cores and memory settings for your server group."); export const workerNodesInformation = localize('arc.workerNodeInformation', "In preview it is not possible to reduce the number of worker nodes. Please refer to documentation linked above for more information."); export const vCores = localize('arc.vCores', "vCores"); export const ram = localize('arc.ram', "RAM"); @@ -150,9 +152,11 @@ export const computeAndStorageDescriptionPartSix = localize('arc.computeAndStora export const node = localize('arc.node', "node"); export const nodes = localize('arc.nodes', "nodes"); export const workerNodes = localize('arc.workerNodes', "Worker Nodes"); +export const coordinatorNode = localize('arc.coordinatorNode', "Coordinator Node"); export const storagePerNode = localize('arc.storagePerNode', "storage per node"); export const workerNodeCount = localize('arc.workerNodeCount', "Worker node count:"); export const configurationPerNode = localize('arc.configurationPerNode', "Configuration (per node)"); +export const configuration = localize('arc.configurationCoordinatorNode', "Configuration"); export const coresLimit = localize('arc.coresLimit', "CPU limit:"); export const coresRequest = localize('arc.coresRequest', "CPU request:"); export const memoryLimit = localize('arc.memoryLimit', "Memory limit (in GB):"); diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts index 37b80f4826..b5216bbdaf 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts @@ -13,18 +13,24 @@ import { PostgresModel } from '../../../models/postgresModel'; import { convertToGibibyteString } from '../../../common/utils'; export class PostgresComputeAndStoragePage extends DashboardPage { - private workerContainer?: azdata.DivContainer; + private workerContainer!: azdata.DivContainer; + private coordinatorContainer!: azdata.DivContainer; - private workerBox?: azdata.InputBoxComponent; - private coresLimitBox?: azdata.InputBoxComponent; - private coresRequestBox?: azdata.InputBoxComponent; - private memoryLimitBox?: azdata.InputBoxComponent; - private memoryRequestBox?: azdata.InputBoxComponent; + private workerBox!: azdata.InputBoxComponent; + private workerCoresLimitBox!: azdata.InputBoxComponent; + private workerCoresRequestBox!: azdata.InputBoxComponent; + private workerMemoryLimitBox!: azdata.InputBoxComponent; + private workerMemoryRequestBox!: azdata.InputBoxComponent; - private discardButton?: azdata.ButtonComponent; - private saveButton?: azdata.ButtonComponent; + private coordinatorCoresLimitBox!: azdata.InputBoxComponent; + private coordinatorCoresRequestBox!: azdata.InputBoxComponent; + private coordinatorMemoryLimitBox!: azdata.InputBoxComponent; + private coordinatorMemoryRequestBox!: azdata.InputBoxComponent; - private saveArgs: { + private discardButton!: azdata.ButtonComponent; + private saveButton!: azdata.ButtonComponent; + + private saveWorkerArgs: { workers?: number, coresLimit?: string, coresRequest?: string, @@ -32,6 +38,13 @@ export class PostgresComputeAndStoragePage extends DashboardPage { memoryRequest?: string } = {}; + private saveCoordinatorArgs: { + coresLimit?: string, + coresRequest?: string, + memoryLimit?: string, + memoryRequest?: string + } = {}; + private readonly _azdataApi: azdataExt.IExtension; constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) { @@ -122,15 +135,26 @@ export class PostgresComputeAndStoragePage extends DashboardPage { .component(); content.addItem(computeInfoAndLinks, { CSSStyles: { 'min-height': '30px' } }); - content.addItem(this.modelView.modelBuilder.text().withProperties({ + // Worker nodes section + this.workerContainer = this.modelView.modelBuilder.divContainer().component(); + this.workerContainer.addItem(this.modelView.modelBuilder.text().withProperties({ value: loc.workerNodes, CSSStyles: { ...cssStyles.title, 'margin-top': '25px' } }).component()); - - this.workerContainer = this.modelView.modelBuilder.divContainer().component(); - this.workerContainer.addItems(this.createUserInputSection(), { CSSStyles: { 'min-height': '30px' } }); + this.workerContainer.addItems(this.createUserInputWorkerSection(), { CSSStyles: { 'min-height': '30px' } }); content.addItem(this.workerContainer, { CSSStyles: { 'min-height': '30px' } }); + // Coordinator node section + this.coordinatorContainer = this.modelView.modelBuilder.divContainer().component(); + this.coordinatorContainer.addItem(this.modelView.modelBuilder.text().withProperties({ + value: loc.coordinatorNode, + CSSStyles: { ...cssStyles.title, 'margin-top': '25px' } + }).component()); + this.coordinatorContainer.addItems(this.createUserInputCoordinatorSection(), { CSSStyles: { 'min-height': '30px' } }); + + // TODO unhide once once ready to make azdata calls + // content.addItem(this.coordinatorContainer, { CSSStyles: { 'min-height': '30px' } }); + this.initialized = true; return root; @@ -160,11 +184,20 @@ export class PostgresComputeAndStoragePage extends DashboardPage { session = await this._postgresModel.controllerModel.acquireAzdataSession(); await this._azdataApi.azdata.arc.postgres.server.edit( this._postgresModel.info.name, - this.saveArgs, + this.saveWorkerArgs, this._postgresModel.engineVersion, this._postgresModel.controllerModel.azdataAdditionalEnvVars, session ); + /* TODO add second edit call for coordinator configuration + await this._azdataApi.azdata.arc.postgres.server.edit( + this._postgresModel.info.name, + this.saveCoordinatorArgs, + this._postgresModel.engineVersion, + this._postgresModel.controllerModel.azdataAdditionalEnvVars, + session + ); + */ } catch (err) { // If an error occurs while editing the instance then re-enable the save button since // the edit wasn't successfully applied @@ -197,9 +230,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage { this.discardButton.onDidClick(async () => { this.discardButton!.enabled = false; try { - this.editWorkerNodeCount(); - this.editCores(); - this.editMemory(); + this.handleServiceUpdated(); } catch (error) { vscode.window.showErrorMessage(loc.pageDiscardFailed(error)); } finally { @@ -213,7 +244,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage { ]).component(); } - private initializeConfigurationBoxes() { + private initializeConfigurationBoxes(): void { this.workerBox = this.modelView.modelBuilder.inputBox().withProperties({ readOnly: false, validationErrorMessage: loc.workerValidationErrorMessage, @@ -224,14 +255,14 @@ export class PostgresComputeAndStoragePage extends DashboardPage { this.disposables.push( this.workerBox.onTextChanged(() => { if (!(this.handleOnTextChanged(this.workerBox!))) { - this.saveArgs.workers = undefined; + this.saveWorkerArgs.workers = undefined; } else { - this.saveArgs.workers = parseInt(this.workerBox!.value!); + this.saveWorkerArgs.workers = parseInt(this.workerBox!.value!); } }) ); - this.coresLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ + this.workerCoresLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ readOnly: false, min: 1, inputType: 'number', @@ -239,16 +270,16 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.coresLimitBox.onTextChanged(() => { - if (!(this.handleOnTextChanged(this.coresLimitBox!))) { - this.saveArgs.coresLimit = undefined; + this.workerCoresLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.workerCoresLimitBox!))) { + this.saveWorkerArgs.coresLimit = undefined; } else { - this.saveArgs.coresLimit = this.coresLimitBox!.value; + this.saveWorkerArgs.coresLimit = this.workerCoresLimitBox!.value; } }) ); - this.coresRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + this.coordinatorCoresLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ readOnly: false, min: 1, inputType: 'number', @@ -256,16 +287,50 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.coresRequestBox.onTextChanged(() => { - if (!(this.handleOnTextChanged(this.coresRequestBox!))) { - this.saveArgs.coresRequest = undefined; + this.coordinatorCoresLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coordinatorCoresLimitBox!))) { + this.saveCoordinatorArgs.coresLimit = undefined; } else { - this.saveArgs.coresRequest = this.coresRequestBox!.value; + this.saveCoordinatorArgs.coresLimit = this.coordinatorCoresLimitBox!.value; } }) ); - this.memoryLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ + this.workerCoresRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 1, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.workerCoresRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.workerCoresRequestBox!))) { + this.saveWorkerArgs.coresRequest = undefined; + } else { + this.saveWorkerArgs.coresRequest = this.workerCoresRequestBox!.value; + } + }) + ); + + this.coordinatorCoresRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 1, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.coordinatorCoresRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coordinatorCoresRequestBox!))) { + this.saveCoordinatorArgs.coresRequest = undefined; + } else { + this.saveCoordinatorArgs.coresRequest = this.coordinatorCoresRequestBox!.value; + } + }) + ); + + this.workerMemoryLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ readOnly: false, min: 0.25, validationErrorMessage: loc.memoryLimitValidationErrorMessage, @@ -274,16 +339,34 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.memoryLimitBox.onTextChanged(() => { - if (!(this.handleOnTextChanged(this.memoryLimitBox!))) { - this.saveArgs.memoryLimit = undefined; + this.workerMemoryLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.workerMemoryLimitBox!))) { + this.saveWorkerArgs.memoryLimit = undefined; } else { - this.saveArgs.memoryLimit = this.memoryLimitBox!.value + 'Gi'; + this.saveWorkerArgs.memoryLimit = this.workerMemoryLimitBox!.value + 'Gi'; } }) ); - this.memoryRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + this.coordinatorMemoryLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 0.25, + validationErrorMessage: loc.memoryLimitValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.coordinatorMemoryLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coordinatorMemoryLimitBox!))) { + this.saveCoordinatorArgs.memoryLimit = undefined; + } else { + this.saveCoordinatorArgs.memoryLimit = this.coordinatorMemoryLimitBox!.value + 'Gi'; + } + }) + ); + + this.workerMemoryRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ readOnly: false, min: 0.25, validationErrorMessage: loc.memoryRequestValidationErrorMessage, @@ -292,31 +375,49 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); this.disposables.push( - this.memoryRequestBox.onTextChanged(() => { - if (!(this.handleOnTextChanged(this.memoryRequestBox!))) { - this.saveArgs.memoryRequest = undefined; + this.workerMemoryRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.workerMemoryRequestBox!))) { + this.saveWorkerArgs.memoryRequest = undefined; } else { - this.saveArgs.memoryRequest = this.memoryRequestBox!.value + 'Gi'; + this.saveWorkerArgs.memoryRequest = this.workerMemoryRequestBox!.value + 'Gi'; + } + }) + ); + + this.coordinatorMemoryRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 0.25, + validationErrorMessage: loc.memoryRequestValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.coordinatorMemoryRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coordinatorMemoryRequestBox!))) { + this.saveCoordinatorArgs.memoryRequest = undefined; + } else { + this.saveCoordinatorArgs.memoryRequest = this.coordinatorMemoryRequestBox!.value + 'Gi'; } }) ); } - private createUserInputSection(): azdata.Component[] { + private createUserInputWorkerSection(): azdata.Component[] { if (this._postgresModel.configLastUpdated) { this.editWorkerNodeCount(); - this.editCores(); - this.editMemory(); + this.editWorkerCores(); + this.editWorkerMemory(); } return [ this.createWorkerNodesSectionContainer(), - this.createCoresMemorySection(), - this.createConfigurationSectionContainer(loc.coresRequest, this.coresRequestBox!), - this.createConfigurationSectionContainer(loc.coresLimit, this.coresLimitBox!), - this.createConfigurationSectionContainer(loc.memoryRequest, this.memoryRequestBox!), - this.createConfigurationSectionContainer(loc.memoryLimit, this.memoryLimitBox!) + this.createCoresMemorySection(loc.configurationPerNode, loc.postgresConfigurationInformation), // use loc.workerNodesConfigurationInformation when coordinator section is included + this.createConfigurationSectionContainer(loc.coresRequest, this.workerCoresRequestBox!), + this.createConfigurationSectionContainer(loc.coresLimit, this.workerCoresLimitBox!), + this.createConfigurationSectionContainer(loc.memoryRequest, this.workerMemoryRequestBox!), + this.createConfigurationSectionContainer(loc.memoryLimit, this.workerMemoryLimitBox!) ]; } @@ -357,6 +458,22 @@ export class PostgresComputeAndStoragePage extends DashboardPage { return flexContainer; } + private createUserInputCoordinatorSection(): azdata.Component[] { + if (this._postgresModel.configLastUpdated) { + this.editCoordinatorCores(); + this.editCoordinatorMemory(); + } + + return [ + this.createCoresMemorySection(loc.configuration, loc.coordinatorNodeConfigurationInformation), + this.createConfigurationSectionContainer(loc.coresRequest, this.coordinatorCoresRequestBox!), + this.createConfigurationSectionContainer(loc.coresLimit, this.coordinatorCoresLimitBox!), + this.createConfigurationSectionContainer(loc.memoryRequest, this.coordinatorMemoryRequestBox!), + this.createConfigurationSectionContainer(loc.memoryLimit, this.coordinatorMemoryLimitBox!) + + ]; + } + private createConfigurationSectionContainer(key: string, input: azdata.Component): azdata.FlexContainer { const inputFlex = { flex: '0 1 150px' }; const keyFlex = { flex: `0 1 250px` }; @@ -400,10 +517,9 @@ export class PostgresComputeAndStoragePage extends DashboardPage { this.discardButton!.enabled = true; return true; } - } - private editWorkerNodeCount() { + private editWorkerNodeCount(): void { // scale.shards was renamed to scale.workers. Check both for backwards compatibility. let scale = this._postgresModel.config?.spec.scale; let currentWorkers = scale?.workers ?? scale?.shards ?? 0; @@ -412,10 +528,10 @@ export class PostgresComputeAndStoragePage extends DashboardPage { this.workerBox!.placeHolder = currentWorkers.toString(); this.workerBox!.value = ''; - this.saveArgs.workers = undefined; + this.saveWorkerArgs.workers = undefined; } - private createCoresMemorySection(): azdata.DivContainer { + private createCoresMemorySection(title: string, description: string): azdata.DivContainer { const titleFlex = { flex: `0 1 250px` }; const flexContainer = this.modelView.modelBuilder.flexContainer().withLayout({ @@ -424,7 +540,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component(); const titleComponent = this.modelView.modelBuilder.text().withProperties({ - value: loc.configurationPerNode, + value: title, CSSStyles: { ...cssStyles.title, 'font-weight': 'bold', 'margin-block-start': '0px', 'margin-block-end': '0px' } }).component(); @@ -433,7 +549,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage { const information = this.modelView.modelBuilder.button().withProperties({ iconPath: IconPathHelper.information, - title: loc.postgresConfigurationInformation, + title: description, width: '15px', height: '15px', enabled: false @@ -448,17 +564,17 @@ export class PostgresComputeAndStoragePage extends DashboardPage { return configurationSection; } - private editCores() { + private editWorkerCores(): void { let currentCPUSize = this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.cpu; if (!currentCPUSize) { currentCPUSize = ''; } - this.coresRequestBox!.validationErrorMessage = loc.validationMin(this.coresRequestBox!.min!); - this.coresRequestBox!.placeHolder = currentCPUSize; - this.coresRequestBox!.value = ''; - this.saveArgs.coresRequest = undefined; + this.workerCoresRequestBox!.validationErrorMessage = loc.validationMin(this.workerCoresRequestBox!.min!); + this.workerCoresRequestBox!.placeHolder = currentCPUSize; + this.workerCoresRequestBox!.value = ''; + this.saveWorkerArgs.coresRequest = undefined; currentCPUSize = this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.cpu; @@ -466,13 +582,39 @@ export class PostgresComputeAndStoragePage extends DashboardPage { currentCPUSize = ''; } - this.coresLimitBox!.validationErrorMessage = loc.validationMin(this.coresLimitBox!.min!); - this.coresLimitBox!.placeHolder = currentCPUSize; - this.coresLimitBox!.value = ''; - this.saveArgs.coresLimit = undefined; + this.workerCoresLimitBox!.validationErrorMessage = loc.validationMin(this.workerCoresLimitBox!.min!); + this.workerCoresLimitBox!.placeHolder = currentCPUSize; + this.workerCoresLimitBox!.value = ''; + this.saveWorkerArgs.coresLimit = undefined; } - private editMemory() { + private editCoordinatorCores(): void { + // TODO get current cpu size for coordinator + let currentCPUSize = this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.cpu; + + if (!currentCPUSize) { + currentCPUSize = ''; + } + + this.coordinatorCoresRequestBox!.validationErrorMessage = loc.validationMin(this.coordinatorCoresRequestBox!.min!); + this.coordinatorCoresRequestBox!.placeHolder = currentCPUSize; + this.coordinatorCoresRequestBox!.value = ''; + this.saveCoordinatorArgs.coresRequest = undefined; + + // TODO get current cpu size for coordinator + currentCPUSize = this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.cpu; + + if (!currentCPUSize) { + currentCPUSize = ''; + } + + this.coordinatorCoresLimitBox!.validationErrorMessage = loc.validationMin(this.coordinatorCoresLimitBox!.min!); + this.coordinatorCoresLimitBox!.placeHolder = currentCPUSize; + this.coordinatorCoresLimitBox!.value = ''; + this.saveCoordinatorArgs.coresLimit = undefined; + } + + private editWorkerMemory(): void { let currentMemSizeConversion: string; let currentMemorySize = this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.memory; @@ -482,10 +624,10 @@ export class PostgresComputeAndStoragePage extends DashboardPage { currentMemSizeConversion = convertToGibibyteString(currentMemorySize); } - this.memoryRequestBox!.placeHolder = currentMemSizeConversion!; - this.memoryRequestBox!.value = ''; + this.workerMemoryRequestBox!.placeHolder = currentMemSizeConversion!; + this.workerMemoryRequestBox!.value = ''; - this.saveArgs.memoryRequest = undefined; + this.saveWorkerArgs.memoryRequest = undefined; currentMemorySize = this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.memory; @@ -495,15 +637,49 @@ export class PostgresComputeAndStoragePage extends DashboardPage { currentMemSizeConversion = convertToGibibyteString(currentMemorySize); } - this.memoryLimitBox!.placeHolder = currentMemSizeConversion!; - this.memoryLimitBox!.value = ''; + this.workerMemoryLimitBox!.placeHolder = currentMemSizeConversion!; + this.workerMemoryLimitBox!.value = ''; - this.saveArgs.memoryLimit = undefined; + this.saveWorkerArgs.memoryLimit = undefined; } - private handleServiceUpdated() { + private editCoordinatorMemory(): void { + let currentMemSizeConversion: string; + // TODO get current memory size for coordinator + let currentMemorySize = this._postgresModel.config?.spec.scheduling?.default?.resources?.requests?.memory; + + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); + } + + this.coordinatorMemoryRequestBox!.placeHolder = currentMemSizeConversion!; + this.coordinatorMemoryRequestBox!.value = ''; + + this.saveCoordinatorArgs.memoryRequest = undefined; + + // TODO get current memory size for coordinator + currentMemorySize = this._postgresModel.config?.spec.scheduling?.default?.resources?.limits?.memory; + + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); + } + + this.coordinatorMemoryLimitBox!.placeHolder = currentMemSizeConversion!; + this.coordinatorMemoryLimitBox!.value = ''; + + this.saveCoordinatorArgs.memoryLimit = undefined; + } + + private handleServiceUpdated(): void { this.editWorkerNodeCount(); - this.editCores(); - this.editMemory(); + this.editWorkerCores(); + this.editWorkerMemory(); + /* TODO perform once Coordinator section is in view + this.editCoordinatorCores(); + this.editCoordinatorMemory(); */ } }