From 32ac586431599709525cff7b31e5356eec3e5991 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Fri, 13 Nov 2020 07:20:46 -0800 Subject: [PATCH] Add MIAA Compute+Storage page (#13367) * Add MIAA Compute+Storage page * update min memory limits * update strings * feedback --- extensions/arc/src/localizedConstants.ts | 7 +- .../arc/src/test/mocks/fakeAzdataApi.ts | 11 +- .../miaa/miaaComputeAndStoragePage.ts | 369 ++++++++++++++++++ .../src/ui/dashboards/miaa/miaaDashboard.ts | 5 +- .../postgres/postgresComputeAndStoragePage.ts | 39 +- extensions/azdata/src/api.ts | 13 + extensions/azdata/src/azdata.ts | 33 +- extensions/azdata/src/typings/azdata-ext.d.ts | 19 +- 8 files changed, 467 insertions(+), 29 deletions(-) create mode 100644 extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts diff --git a/extensions/arc/src/localizedConstants.ts b/extensions/arc/src/localizedConstants.ts index 9628ffc5a0..5037b1ef3a 100644 --- a/extensions/arc/src/localizedConstants.ts +++ b/extensions/arc/src/localizedConstants.ts @@ -63,7 +63,7 @@ export const feedback = localize('arc.feedback', "Feedback"); export const selectConnectionString = localize('arc.selectConnectionString', "Select from available client connection strings below."); export const addingWokerNodes = localize('arc.addingWokerNodes', "adding worker nodes"); export const workerNodesDescription = localize('arc.workerNodesDescription', "Expand your server group and scale your database by adding worker nodes."); -export const configurationInformation = localize('arc.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 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 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"); @@ -121,8 +121,9 @@ export const enterNewPassword = localize('arc.enterNewPassword', "Enter a new pa export const confirmNewPassword = localize('arc.confirmNewPassword', "Confirm the new password"); export const learnAboutPostgresClients = localize('arc.learnAboutPostgresClients', "Learn more about Azure PostgreSQL Hyperscale client interfaces"); export const scalingCompute = localize('arc.scalingCompute', "scaling compute vCores and memory."); -export const computeAndStorageDescriptionPartOne = localize('arc.computeAndStorageDescriptionPartOne', "You can scale your Azure Arc enabled"); -export const computeAndStorageDescriptionPartTwo = localize('arc.computeAndStorageDescriptionPartTwo', "PostgreSQL Hyperscale server group by"); +export const postgresComputeAndStorageDescriptionPartOne = localize('arc.postgresComputeAndStorageDescriptionPartOne', "You can scale your Azure Arc enabled"); +export const miaaComputeAndStorageDescriptionPartOne = localize('arc.miaaComputeAndStorageDescriptionPartOne', "You can scale your Azure SQL managed instance - Azure Arc by"); +export const postgresComputeAndStorageDescriptionPartTwo = localize('arc.postgres.computeAndStorageDescriptionPartTwo', "PostgreSQL Hyperscale server group by"); export const computeAndStorageDescriptionPartThree = localize('arc.computeAndStorageDescriptionPartThree', "without downtime and by"); export const computeAndStorageDescriptionPartFour = localize('arc.computeAndStorageDescriptionPartFour', "Before doing so, you need to ensure"); export const computeAndStorageDescriptionPartFive = localize('arc.computeAndStorageDescriptionPartFive', "there are sufficient resources available"); diff --git a/extensions/arc/src/test/mocks/fakeAzdataApi.ts b/extensions/arc/src/test/mocks/fakeAzdataApi.ts index 7312e948f5..45ee220a9f 100644 --- a/extensions/arc/src/test/mocks/fakeAzdataApi.ts +++ b/extensions/arc/src/test/mocks/fakeAzdataApi.ts @@ -56,7 +56,16 @@ export class FakeAzdataApi implements azdataExt.IAzdataApi { mi: { delete(_name: string): Promise> { throw new Error('Method not implemented.'); }, async list(): Promise> { return { result: self.miaaInstances }; }, - show(_name: string): Promise> { throw new Error('Method not implemented.'); } + show(_name: string): Promise> { throw new Error('Method not implemented.'); }, + edit( + _name: string, + _args: { + coresLimit?: string, + coresRequest?: string, + memoryLimit?: string, + memoryRequest?: string, + noWait?: boolean + }): Promise> { throw new Error('Method not implemented.'); } } } }; diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts new file mode 100644 index 0000000000..f8d54eb0e0 --- /dev/null +++ b/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * 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 azdataExt from 'azdata-ext'; +import * as loc from '../../../localizedConstants'; +import { IconPathHelper, cssStyles } from '../../../constants'; +import { DashboardPage } from '../../components/dashboardPage'; +import { convertToGibibyteString } from '../../../common/utils'; +import { MiaaModel } from '../../../models/miaaModel'; + +export class MiaaComputeAndStoragePage extends DashboardPage { + + private configurationContainer?: azdata.DivContainer; + private coresLimitBox?: azdata.InputBoxComponent; + private coresRequestBox?: azdata.InputBoxComponent; + private memoryLimitBox?: azdata.InputBoxComponent; + private memoryRequestBox?: azdata.InputBoxComponent; + + private discardButton?: azdata.ButtonComponent; + private saveButton?: azdata.ButtonComponent; + + private saveArgs: { + coresLimit?: string, + coresRequest?: string, + memoryLimit?: string, + memoryRequest?: string + } = {}; + + private readonly _azdataApi: azdataExt.IExtension; + + constructor(protected modelView: azdata.ModelView, private _miaaModel: MiaaModel) { + super(modelView); + this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports; + + this.initializeConfigurationBoxes(); + + this.disposables.push(this._miaaModel.onConfigUpdated( + () => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated()))); + } + + protected get title(): string { + return loc.computeAndStorage; + } + + protected get id(): string { + return 'miaa-compute-and-storage'; + } + + protected get icon(): { dark: string; light: string; } { + return IconPathHelper.computeStorage; + } + + protected get container(): azdata.Component { + const root = this.modelView.modelBuilder.divContainer().component(); + const content = this.modelView.modelBuilder.divContainer().component(); + root.addItem(content, { CSSStyles: { 'margin': '20px' } }); + + content.addItem(this.modelView.modelBuilder.text().withProperties({ + value: loc.computeAndStorage, + CSSStyles: { ...cssStyles.title } + }).component()); + + const infoComputeStorage_p1 = this.modelView.modelBuilder.text().withProperties({ + value: loc.miaaComputeAndStorageDescriptionPartOne, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' } + }).component(); + + const memoryVCoreslink = this.modelView.modelBuilder.hyperlink().withProperties({ + label: loc.scalingCompute, + url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-managed-instance', + CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + const infoComputeStorage_p4 = this.modelView.modelBuilder.text().withProperties({ + value: loc.computeAndStorageDescriptionPartFour, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + const infoComputeStorage_p5 = this.modelView.modelBuilder.text().withProperties({ + value: loc.computeAndStorageDescriptionPartFive, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + const infoComputeStorage_p6 = this.modelView.modelBuilder.text().withProperties({ + value: loc.computeAndStorageDescriptionPartSix, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + const computeInfoAndLinks = this.modelView.modelBuilder.flexContainer() + .withLayout({ flexWrap: 'wrap' }) + .withItems([ + infoComputeStorage_p1, + memoryVCoreslink, + infoComputeStorage_p4, + infoComputeStorage_p5, + infoComputeStorage_p6 + ], { CSSStyles: { 'margin-right': '5px' } }).component(); + content.addItem(computeInfoAndLinks, { CSSStyles: { 'min-height': '30px' } }); + + this.configurationContainer = this.modelView.modelBuilder.divContainer().component(); + this.configurationContainer.addItems(this.createUserInputSection(), { CSSStyles: { 'min-height': '30px' } }); + content.addItem(this.configurationContainer, { CSSStyles: { 'margin-top': '30px' } }); + + this.initialized = true; + + return root; + } + + protected get toolbarContainer(): azdata.ToolbarContainer { + // Save Edits + this.saveButton = this.modelView.modelBuilder.button().withProperties({ + label: loc.saveText, + iconPath: IconPathHelper.save, + enabled: false + }).component(); + + this.disposables.push( + this.saveButton.onDidClick(async () => { + this.saveButton!.enabled = false; + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: loc.updatingInstance(this._miaaModel.info.name), + cancellable: false + }, + async (_progress, _token): Promise => { + try { + await this._azdataApi.azdata.arc.sql.mi.edit( + this._miaaModel.info.name, this.saveArgs); + } catch (err) { + this.saveButton!.enabled = true; + throw err; + } + + await this._miaaModel.refresh(); + } + ); + + vscode.window.showInformationMessage(loc.instanceUpdated(this._miaaModel.info.name)); + + this.discardButton!.enabled = false; + } catch (error) { + vscode.window.showErrorMessage(loc.instanceUpdateFailed(this._miaaModel.info.name, error)); + } + })); + + // Discard + this.discardButton = this.modelView.modelBuilder.button().withProperties({ + label: loc.discardText, + iconPath: IconPathHelper.discard, + enabled: false + }).component(); + + this.disposables.push( + this.discardButton.onDidClick(async () => { + this.discardButton!.enabled = false; + try { + this.editCores(); + this.editMemory(); + } catch (error) { + vscode.window.showErrorMessage(loc.pageDiscardFailed(error)); + } finally { + this.saveButton!.enabled = false; + } + })); + + return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([ + { component: this.saveButton }, + { component: this.discardButton } + ]).component(); + } + + private initializeConfigurationBoxes() { + this.coresLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 1, + validationErrorMessage: loc.coresValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.coresLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coresLimitBox!))) { + this.saveArgs.coresLimit = undefined; + } else { + this.saveArgs.coresLimit = this.coresLimitBox!.value; + } + }) + ); + + this.coresRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 1, + validationErrorMessage: loc.coresValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.coresRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.coresRequestBox!))) { + this.saveArgs.coresRequest = undefined; + } else { + this.saveArgs.coresRequest = this.coresRequestBox!.value; + } + }) + ); + + this.memoryLimitBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 2, + validationErrorMessage: loc.memoryLimitValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.memoryLimitBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.memoryLimitBox!))) { + this.saveArgs.memoryLimit = undefined; + } else { + this.saveArgs.memoryLimit = this.memoryLimitBox!.value + 'Gi'; + } + }) + ); + + this.memoryRequestBox = this.modelView.modelBuilder.inputBox().withProperties({ + readOnly: false, + min: 2, + validationErrorMessage: loc.memoryRequestValidationErrorMessage, + inputType: 'number', + placeHolder: loc.loading + }).component(); + + this.disposables.push( + this.memoryRequestBox.onTextChanged(() => { + if (!(this.handleOnTextChanged(this.memoryRequestBox!))) { + this.saveArgs.memoryRequest = undefined; + } else { + this.saveArgs.memoryRequest = this.memoryRequestBox!.value + 'Gi'; + } + }) + ); + + } + + private createUserInputSection(): azdata.Component[] { + if (this._miaaModel.configLastUpdated) { + this.editCores(); + this.editMemory(); + } + + return [ + this.createConfigurationSectionContainer(loc.coresRequest, this.coresRequestBox!), + this.createConfigurationSectionContainer(loc.coresLimit, this.coresLimitBox!), + this.createConfigurationSectionContainer(loc.memoryRequest, this.memoryRequestBox!), + this.createConfigurationSectionContainer(loc.memoryLimit, this.memoryLimitBox!) + + ]; + } + + private createConfigurationSectionContainer(key: string, input: azdata.Component): azdata.FlexContainer { + const inputFlex = { flex: '0 1 150px' }; + const keyFlex = { flex: `0 1 250px` }; + + const flexContainer = this.modelView.modelBuilder.flexContainer().withLayout({ + flexWrap: 'wrap', + alignItems: 'center' + }).component(); + + const keyComponent = this.modelView.modelBuilder.text().withProperties({ + value: key, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + const keyContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component(); + keyContainer.addItem(keyComponent, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } }); + flexContainer.addItem(keyContainer, keyFlex); + + const inputContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component(); + inputContainer.addItem(input, { CSSStyles: { 'margin-bottom': '15px', 'min-width': '50px', 'max-width': '225px' } }); + + flexContainer.addItem(inputContainer, inputFlex); + + return flexContainer; + } + + 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)) { + // 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 { + // 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 editCores(): void { + let currentCPUSize = this._miaaModel.config?.spec?.requests?.vcores; + + if (!currentCPUSize) { + currentCPUSize = ''; + } + + this.coresRequestBox!.placeHolder = currentCPUSize; + this.coresRequestBox!.value = ''; + this.saveArgs.coresRequest = undefined; + + currentCPUSize = this._miaaModel.config?.spec?.limits?.vcores; + + if (!currentCPUSize) { + currentCPUSize = ''; + } + + this.coresLimitBox!.placeHolder = currentCPUSize; + this.coresLimitBox!.value = ''; + this.saveArgs.coresLimit = undefined; + } + + private editMemory(): void { + let currentMemSizeConversion: string; + let currentMemorySize = this._miaaModel.config?.spec?.requests?.memory; + + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); + } + + this.memoryRequestBox!.placeHolder = currentMemSizeConversion!; + this.memoryRequestBox!.value = ''; + + this.saveArgs.memoryRequest = undefined; + + currentMemorySize = this._miaaModel.config?.spec?.limits?.memory; + + if (!currentMemorySize) { + currentMemSizeConversion = ''; + } else { + currentMemSizeConversion = convertToGibibyteString(currentMemorySize); + } + + this.memoryLimitBox!.placeHolder = currentMemSizeConversion!; + this.memoryLimitBox!.value = ''; + + this.saveArgs.memoryLimit = undefined; + } + + private handleServiceUpdated() { + this.editCores(); + this.editMemory(); + } +} diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts b/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts index 8f52b2c15d..b2edee930f 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts @@ -10,6 +10,7 @@ import { ControllerModel } from '../../../models/controllerModel'; import * as loc from '../../../localizedConstants'; import { MiaaConnectionStringsPage } from './miaaConnectionStringsPage'; import { MiaaModel } from '../../../models/miaaModel'; +import { MiaaComputeAndStoragePage } from './miaaComputeAndStoragePage'; export class MiaaDashboard extends Dashboard { @@ -27,12 +28,14 @@ export class MiaaDashboard extends Dashboard { protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> { const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel, this._miaaModel); const connectionStringsPage = new MiaaConnectionStringsPage(modelView, this._controllerModel, this._miaaModel); + const computeAndStoragePage = new MiaaComputeAndStoragePage(modelView, this._miaaModel); return [ overviewPage.tab, { title: loc.settings, tabs: [ - connectionStringsPage.tab + connectionStringsPage.tab, + computeAndStoragePage.tab ] }, ]; diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts index b57c7fd611..bf195adc32 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresComputeAndStoragePage.ts @@ -67,11 +67,11 @@ export class PostgresComputeAndStoragePage extends DashboardPage { }).component()); const infoComputeStorage_p1 = this.modelView.modelBuilder.text().withProperties({ - value: loc.computeAndStorageDescriptionPartOne, + value: loc.postgresComputeAndStorageDescriptionPartOne, CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' } }).component(); const infoComputeStorage_p2 = this.modelView.modelBuilder.text().withProperties({ - value: loc.computeAndStorageDescriptionPartTwo, + value: loc.postgresComputeAndStorageDescriptionPartTwo, CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } }).component(); @@ -107,15 +107,19 @@ export class PostgresComputeAndStoragePage extends DashboardPage { CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } }).component(); - const computeInfoAndLinks = this.modelView.modelBuilder.flexContainer().withLayout({ flexWrap: 'wrap' }).component(); - computeInfoAndLinks.addItem(infoComputeStorage_p1, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(infoComputeStorage_p2, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(workerNodeslink, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(infoComputeStorage_p3, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(memoryVCoreslink, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(infoComputeStorage_p4, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(infoComputeStorage_p5, { CSSStyles: { 'margin-right': '5px' } }); - computeInfoAndLinks.addItem(infoComputeStorage_p6, { CSSStyles: { 'margin-right': '5px' } }); + const computeInfoAndLinks = this.modelView.modelBuilder.flexContainer() + .withLayout({ flexWrap: 'wrap' }) + .withItems([ + infoComputeStorage_p1, + infoComputeStorage_p2, + workerNodeslink, + infoComputeStorage_p3, + memoryVCoreslink, + infoComputeStorage_p4, + infoComputeStorage_p5, + infoComputeStorage_p6 + ], { CSSStyles: { 'margin-right': '5px' } }) + .component(); content.addItem(computeInfoAndLinks, { CSSStyles: { 'min-height': '30px' } }); content.addItem(this.modelView.modelBuilder.text().withProperties({ @@ -151,8 +155,15 @@ export class PostgresComputeAndStoragePage extends DashboardPage { cancellable: false }, async (_progress, _token): Promise => { - await this._azdataApi.azdata.arc.postgres.server.edit( - this._postgresModel.info.name, this.saveArgs); + try { + await this._azdataApi.azdata.arc.postgres.server.edit( + this._postgresModel.info.name, this.saveArgs); + } 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; + } await this._postgresModel.refresh(); } ); @@ -415,7 +426,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage { const information = this.modelView.modelBuilder.button().withProperties({ iconPath: IconPathHelper.information, - title: loc.configurationInformation, + title: loc.postgresConfigurationInformation, width: '12px', height: '12px', enabled: false diff --git a/extensions/azdata/src/api.ts b/extensions/azdata/src/api.ts index 70b3d0fe4f..0d41f981d8 100644 --- a/extensions/azdata/src/api.ts +++ b/extensions/azdata/src/api.ts @@ -125,6 +125,19 @@ export function getAzdataApi(localAzdataDiscovered: Promise { + await localAzdataDiscovered; + throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento)); + return azdataToolService.localAzdata.arc.sql.mi.edit(name, args); } } } diff --git a/extensions/azdata/src/azdata.ts b/extensions/azdata/src/azdata.ts index a943900b3e..50436f668e 100644 --- a/extensions/azdata/src/azdata.ts +++ b/extensions/azdata/src/azdata.ts @@ -121,16 +121,16 @@ export class AzdataTool implements azdataExt.IAzdataApi { additionalEnvVars?: { [key: string]: string }): Promise> => { const argsArray = ['arc', 'postgres', 'server', 'edit', '-n', name]; if (args.adminPassword) { argsArray.push('--admin-password'); } - if (args.coresLimit !== undefined) { argsArray.push('--cores-limit', args.coresLimit); } - if (args.coresRequest !== undefined) { argsArray.push('--cores-request', args.coresRequest); } - if (args.engineSettings !== undefined) { argsArray.push('--engine-settings', args.engineSettings); } - if (args.extensions !== undefined) { argsArray.push('--extensions', args.extensions); } - if (args.memoryLimit !== undefined) { argsArray.push('--memory-limit', args.memoryLimit); } - if (args.memoryRequest !== undefined) { argsArray.push('--memory-request', args.memoryRequest); } + if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); } + if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); } + if (args.engineSettings) { argsArray.push('--engine-settings', args.engineSettings); } + if (args.extensions) { argsArray.push('--extensions', args.extensions); } + if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); } + if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); } if (args.noWait) { argsArray.push('--no-wait'); } - if (args.port !== undefined) { argsArray.push('--port', args.port.toString()); } + if (args.port) { argsArray.push('--port', args.port.toString()); } if (args.replaceEngineSettings) { argsArray.push('--replace-engine-settings'); } - if (args.workers !== undefined) { argsArray.push('--workers', args.workers.toString()); } + if (args.workers) { argsArray.push('--workers', args.workers.toString()); } return this.executeCommand(argsArray, additionalEnvVars); } } @@ -145,6 +145,23 @@ export class AzdataTool implements azdataExt.IAzdataApi { }, show: (name: string): Promise> => { return this.executeCommand(['arc', 'sql', 'mi', 'show', '-n', name]); + }, + edit: ( + name: string, + args: { + coresLimit?: string, + coresRequest?: string, + memoryLimit?: string, + memoryRequest?: string, + noWait?: boolean, + }): Promise> => { + const argsArray = ['arc', 'sql', 'mi', 'edit', '-n', name]; + if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); } + if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); } + if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); } + if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); } + if (args.noWait) { argsArray.push('--no-wait'); } + return this.executeCommand(argsArray); } } } diff --git a/extensions/azdata/src/typings/azdata-ext.d.ts b/extensions/azdata/src/typings/azdata-ext.d.ts index 5f660793e9..40be276e0c 100644 --- a/extensions/azdata/src/typings/azdata-ext.d.ts +++ b/extensions/azdata/src/typings/azdata-ext.d.ts @@ -125,7 +125,12 @@ declare module 'azdata-ext' { }, spec: { limits?: { - vcores?: number // 4 + memory?: string // "10Gi" + vcores?: string // "4" + }, + requests?: { + memory?: string // "10Gi" + vcores?: string // "4" } service: { type: string // "NodePort" @@ -264,7 +269,17 @@ declare module 'azdata-ext' { mi: { delete(name: string): Promise>, list(): Promise>, - show(name: string): Promise> + show(name: string): Promise>, + edit( + name: string, + args: { + coresLimit?: string, + coresRequest?: string, + memoryLimit?: string, + memoryRequest?: string, + noWait?: boolean, + } + ): Promise> } } },