diff --git a/extensions/arc/src/models/miaaModel.ts b/extensions/arc/src/models/miaaModel.ts index 05f3229420..099bd052d9 100644 --- a/extensions/arc/src/models/miaaModel.ts +++ b/extensions/arc/src/models/miaaModel.ts @@ -121,7 +121,7 @@ export class MiaaModel extends ResourceModel { } this._config = result.stdout; this.configLastUpdated = new Date(); - this.rpSettings.retentionDays = this._config?.properties?.k8SRaw?.spec?.backup?.retentionPeriodInDays?.toString() ?? ''; + this.rpSettings.retentionDays = this._config?.spec?.backup?.retentionPeriodInDays?.toString() ?? ''; this._onConfigUpdated.fire(this._config); this._onDatabasesUpdated.fire(this._databases); } catch (err) { @@ -135,7 +135,7 @@ export class MiaaModel extends ResourceModel { } // If we have an external endpoint configured then fetch the databases now - if (this._config.properties?.k8SRaw?.status.primaryEndpoint) { + if (this._config.status.primaryEndpoint) { this.getDatabases(false).catch(_err => { // If an error occurs still fire the event so callers can know to // update (e.g. so dashboards don't show the loading icon forever) @@ -217,7 +217,7 @@ export class MiaaModel extends ResourceModel { } protected createConnectionProfile(): azdata.IConnectionProfile { - const ipAndPort = parseIpAndPort(this.config?.properties?.k8SRaw?.status.primaryEndpoint || ''); + const ipAndPort = parseIpAndPort(this.config?.status.primaryEndpoint || ''); return { serverName: `${ipAndPort.ip},${ipAndPort.port}`, databaseName: '', diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts index 033fff967f..acaf68fe5f 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts @@ -288,7 +288,7 @@ export class MiaaBackupsPage extends DashboardPage { } else { // If we don't have an endpoint then there's no point in showing the connect button - but the logic // to display text informing the user of this is already handled by the handleMiaaConfigUpdated - if (this._miaaModel?.config?.properties?.k8SRaw?.status.primaryEndpoint) { + if (this._miaaModel?.config?.status.primaryEndpoint) { this._connectToServerLoading.loading = false; this._connectToServerButton.enabled = true; } @@ -296,7 +296,7 @@ export class MiaaBackupsPage extends DashboardPage { } private refreshRD(): void { - this._saveArgs.retentionDays = this._miaaModel.config?.properties?.k8SRaw?.spec?.backup?.retentionPeriodInDays.toString() ?? ''; + this._saveArgs.retentionDays = this._miaaModel.config?.spec?.backup?.retentionPeriodInDays.toString() ?? ''; } // Create restore button for every database entry in the database table diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts index 1c19fa9216..b1bb272014 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaComputeAndStoragePage.ts @@ -330,7 +330,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage { } private editCores(): void { - let currentCPUSize = this._miaaModel.config?.properties?.k8SRaw?.spec?.scheduling?.default?.resources?.requests?.cpu; + let currentCPUSize = this._miaaModel.config?.spec?.scheduling?.default?.resources?.requests?.cpu; if (!currentCPUSize) { currentCPUSize = ''; @@ -340,7 +340,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage { this.coresRequestBox!.value = ''; this.saveArgs.coresRequest = undefined; - currentCPUSize = this._miaaModel.config?.properties?.k8SRaw?.spec?.scheduling?.default?.resources?.limits?.cpu; + currentCPUSize = this._miaaModel.config?.spec?.scheduling?.default?.resources?.limits?.cpu; if (!currentCPUSize) { currentCPUSize = ''; @@ -353,7 +353,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage { private editMemory(): void { let currentMemSizeConversion: string; - let currentMemorySize = this._miaaModel.config?.properties?.k8SRaw?.spec?.scheduling?.default?.resources?.requests?.memory; + let currentMemorySize = this._miaaModel.config?.spec?.scheduling?.default?.resources?.requests?.memory; if (!currentMemorySize) { currentMemSizeConversion = ''; @@ -366,7 +366,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage { this.saveArgs.memoryRequest = undefined; - currentMemorySize = this._miaaModel.config?.properties?.k8SRaw?.spec?.scheduling?.default?.resources?.limits?.memory; + currentMemorySize = this._miaaModel.config?.spec?.scheduling?.default?.resources?.limits?.memory; if (!currentMemorySize) { currentMemSizeConversion = ''; diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts index ce48c55c72..08934f9809 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaConnectionStringsPage.ts @@ -74,11 +74,11 @@ export class MiaaConnectionStringsPage extends DashboardPage { private getConnectionStrings(): KeyValue[] { const config = this._miaaModel.config; - if (!config?.properties?.k8SRaw?.status.primaryEndpoint) { + if (!config?.status.primaryEndpoint) { return []; } - const externalEndpoint = parseIpAndPort(config.properties?.k8SRaw?.status.primaryEndpoint); + const externalEndpoint = parseIpAndPort(config.status.primaryEndpoint); const username = this._miaaModel.username ?? '{your_username_here}'; return [ @@ -97,7 +97,7 @@ $conn = sqlsrv_connect($serverName, $connectionInfo);`), } private updateConnectionStrings(): void { - this._connectionStringsMessage.value = !this._miaaModel.config?.properties?.k8SRaw?.status.primaryEndpoint ? loc.noExternalEndpoint : ''; + this._connectionStringsMessage.value = !this._miaaModel.config?.status.primaryEndpoint ? loc.noExternalEndpoint : ''; this._keyValueContainer.refresh(this.getConnectionStrings()); } } diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts index cc4e4bf98f..385e176183 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts @@ -335,11 +335,11 @@ export class MiaaDashboardOverviewPage extends DashboardPage { private handleMiaaConfigUpdated(): void { if (this._miaaModel.config) { - this._instanceProperties.status = this._miaaModel.config.properties?.k8SRaw?.status.state || '-'; - this._instanceProperties.externalEndpoint = this._miaaModel.config.properties?.k8SRaw?.status.primaryEndpoint || loc.notConfigured; - this._instanceProperties.vCores = this._miaaModel.config.properties?.k8SRaw?.spec?.scheduling?.default?.resources?.limits?.cpu?.toString() || ''; - this._databasesMessage.value = !this._miaaModel.config.properties?.k8SRaw?.status.primaryEndpoint ? loc.noExternalEndpoint : ''; - if (!this._miaaModel.config.properties?.k8SRaw?.status.primaryEndpoint) { + this._instanceProperties.status = this._miaaModel.config.status.state || '-'; + this._instanceProperties.externalEndpoint = this._miaaModel.config.status.primaryEndpoint || loc.notConfigured; + this._instanceProperties.vCores = this._miaaModel.config.spec?.scheduling?.default?.resources?.limits?.cpu?.toString() || ''; + this._databasesMessage.value = !this._miaaModel.config.status.primaryEndpoint ? loc.noExternalEndpoint : ''; + if (!this._miaaModel.config.status.primaryEndpoint) { this._databasesContainer.removeItem(this._connectToServerLoading); } } @@ -368,7 +368,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage { } else { // If we don't have an endpoint then there's no point in showing the connect button - but the logic // to display text informing the user of this is already handled by the handleMiaaConfigUpdated - if (this._miaaModel?.config?.properties?.k8SRaw?.status.primaryEndpoint) { + if (this._miaaModel?.config?.status.primaryEndpoint) { this._connectToServerLoading.loading = false; this._connectToServerButton.enabled = true; } @@ -419,12 +419,12 @@ export class MiaaDashboardOverviewPage extends DashboardPage { private refreshDashboardLinks(): void { if (this._miaaModel.config) { - const kibanaUrl = this._miaaModel.config.properties?.k8SRaw?.status.logSearchDashboard ?? ''; + const kibanaUrl = this._miaaModel.config.status.logSearchDashboard ?? ''; this._kibanaLink.label = kibanaUrl; this._kibanaLink.url = kibanaUrl; this._kibanaLoading!.loading = false; - const grafanaUrl = this._miaaModel.config.properties?.k8SRaw?.status.metricsDashboard ?? ''; + const grafanaUrl = this._miaaModel.config.status.metricsDashboard ?? ''; this._grafanaLink.label = grafanaUrl; this._grafanaLink.url = grafanaUrl; this._grafanaLoading!.loading = false; diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts index c9bcc96fa3..99f09dfba4 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts @@ -179,7 +179,7 @@ export class MiaaUpgradeManagementPage extends DashboardPage { this._controllerModel.azAdditionalEnvVars ); } - return miaaShowResult.stdout.properties.k8SRaw.status.runningVersion; + return miaaShowResult.stdout.status.runningVersion; } catch (e) { console.error(loc.showMiaaError, e); return undefined; diff --git a/extensions/arc/src/ui/dialogs/configureRPOSqlDialog.ts b/extensions/arc/src/ui/dialogs/configureRPOSqlDialog.ts index ba90a35dbf..232e46d1e2 100644 --- a/extensions/arc/src/ui/dialogs/configureRPOSqlDialog.ts +++ b/extensions/arc/src/ui/dialogs/configureRPOSqlDialog.ts @@ -26,7 +26,7 @@ export class ConfigureRPOSqlDialog extends InitializingComponent { public showDialog(dialogTitle: string, retentionDays: string | undefined): azdata.window.Dialog { const dialog = azdata.window.createModelViewDialog(dialogTitle); dialog.cancelButton.onClick(() => this.handleCancel()); - retentionDays = (retentionDays === undefined ? this._model.config?.properties?.k8SRaw?.spec?.backup?.retentionPeriodInDays?.toString() : retentionDays); + retentionDays = (retentionDays === undefined ? this._model.config?.spec?.backup?.retentionPeriodInDays?.toString() : retentionDays); dialog.registerContent(async view => { this.modelBuilder = view.modelBuilder; this.retentionDaysInputBox = this.modelBuilder.inputBox() diff --git a/extensions/azcli/src/az.ts b/extensions/azcli/src/az.ts index cdb4d4e695..067d6da47b 100644 --- a/extensions/azcli/src/az.ts +++ b/extensions/azcli/src/az.ts @@ -193,12 +193,14 @@ export class AzTool implements azExt.IAzApi { additionalEnvVars?: azExt.AdditionalEnvVars ): Promise> => { const argsArray = ['sql', 'mi-arc', 'show', '-n', name]; - if (args.resourceGroup) { argsArray.push('--resource-group', args.resourceGroup); } + if (args.resourceGroup) { + argsArray.push('--resource-group', args.resourceGroup); + } if (args.namespace) { argsArray.push('--k8s-namespace', args.namespace); argsArray.push('--use-k8s'); } - return this.executeCommand(argsArray, additionalEnvVars); + return this.executeSqlMiShow(argsArray, additionalEnvVars); }, update: ( name: string, @@ -301,6 +303,64 @@ export class AzTool implements azExt.IAzApi { }; } + /** + * Executes az sql mi-arc show and returns a normalized object, SqlMiShowResult, regardless of the indirect or direct mode raw output shape. + * @param args The args to pass to az + * @param additionalEnvVars Additional environment variables to set for this execution + */ + public async executeSqlMiShow(args: string[], additionalEnvVars?: azExt.AdditionalEnvVars): Promise> { + try { + const result = await executeAzCommand(`"${this._path}"`, args.concat(['--output', 'json']), additionalEnvVars); + + let stdout = result.stdout; + let stderr = result.stderr; + + try { + // Automatically try parsing the JSON. This is expected to fail for some az commands such as resource delete. + stdout = JSON.parse(result.stdout); + } catch (err) { + // If the output was not pure JSON, catch the error and log it here. + Logger.log(loc.azOutputParseErrorCaught(args.concat(['--output', 'json']).toString())); + throw err; + } + + if ((stdout).properties) { + // Then it is direct mode + return { + stdout: { + name: (stdout).name, + spec: (stdout).properties.k8SRaw.spec, + status: (stdout).properties.k8SRaw.status + }, + stderr: stderr + }; + } else { + // It must be indirect mode + return { + stdout: { + name: (stdout).metadata.name, + spec: (stdout).spec, + status: (stdout).status + }, + stderr: stderr + }; + } + } catch (err) { + if (err instanceof ExitCodeError) { + try { + await fs.promises.access(this._path); + //this.path exists + } catch (e) { + // this.path does not exist + await vscode.commands.executeCommand('setContext', azFound, false); + throw new NoAzureCLIError(); + } + } + throw err; + } + } + + /** * Executes the specified az command. * @param args The args to pass to az diff --git a/extensions/azcli/src/typings/az-ext.d.ts b/extensions/azcli/src/typings/az-ext.d.ts index 847096af08..9a0edb2ad4 100644 --- a/extensions/azcli/src/typings/az-ext.d.ts +++ b/extensions/azcli/src/typings/az-ext.d.ts @@ -137,53 +137,89 @@ declare module 'az-ext' { port?: number // 5432 } - // export interface SqlMiShowResult { - // apiVersion: string, // "sql.arcdata.microsoft.com/v1alpha1" - // kind: string, // "sqlmanagedinstance" - // metadata: { - // creationTimestamp: string, // "2020-08-19T17:35:45Z" - // generation: number, // 1 - // name: string, // "miaa-instance" - // namespace: string, // "arc" - // resourceVersion: string, // "202623" - // selfLink: string, // "/apis/sql.arcdata.microsoft.com/v1alpha1/namespaces/arc/sqlmanagedinstances/miaa-instance" - // uid: string // "cea737aa-3f82-4f6a-9bed-2b51c2c33dff" - // }, - // spec: { - // backup?: { - // retentionPeriodInDays: number, // 1 - // } - // scheduling?: { - // default?: { - // resources?: { - // limits?: SchedulingOptions, - // requests?: SchedulingOptions - // } - // } - // } - // services: { - // primary: ServiceSpec - // } - // storage: { - // data: { - // volumes: StorageVolume[] - // }, - // logs: { - // volumes: StorageVolume[] - // } - // } - // }, - // status: { - // readyReplicas: string, // "1/1" - // state: string, // "Ready", - // logSearchDashboard: string, // https://127.0.0.1:30777/kibana/app/kibana#/discover?_a=(query:(language:kuery,query:'custom_resource_name:miaa1')) - // metricsDashboard: string, // https://127.0.0.1:30777/grafana/d/40q72HnGk/sql-managed-instance-metrics?var-hostname=miaa1-0 - // primaryEndpoint?: string // "10.91.86.39:32718" - // runningVersion: string // "v1.5.0_2022-04-05" - // } - // } - export interface SqlMiShowResult { + name: string, // "miaa-instance" + spec: { + backup?: { + retentionPeriodInDays: number, // 1 + } + scheduling?: { + default?: { + resources?: { + limits?: SchedulingOptions, + requests?: SchedulingOptions + } + } + } + services: { + primary: ServiceSpec + } + storage: { + data: { + volumes: StorageVolume[] + }, + logs: { + volumes: StorageVolume[] + } + } + }, + status: { + readyReplicas: string, // "1/1" + state: string, // "Ready", + logSearchDashboard: string, // https://127.0.0.1:30777/kibana/app/kibana#/discover?_a=(query:(language:kuery,query:'custom_resource_name:miaa1')) + metricsDashboard: string, // https://127.0.0.1:30777/grafana/d/40q72HnGk/sql-managed-instance-metrics?var-hostname=miaa1-0 + primaryEndpoint?: string // "10.91.86.39:32718" + runningVersion: string // "v1.5.0_2022-04-05" + } + } + + export interface SqlMiShowResultIndirect { + apiVersion: string, // "sql.arcdata.microsoft.com/v1alpha1" + kind: string, // "sqlmanagedinstance" + metadata: { + creationTimestamp: string, // "2020-08-19T17:35:45Z" + generation: number, // 1 + name: string, // "miaa-instance" + namespace: string, // "arc" + resourceVersion: string, // "202623" + selfLink: string, // "/apis/sql.arcdata.microsoft.com/v1alpha1/namespaces/arc/sqlmanagedinstances/miaa-instance" + uid: string // "cea737aa-3f82-4f6a-9bed-2b51c2c33dff" + }, + spec: { + backup?: { + retentionPeriodInDays: number, // 1 + } + scheduling?: { + default?: { + resources?: { + limits?: SchedulingOptions, + requests?: SchedulingOptions + } + } + } + services: { + primary: ServiceSpec + } + storage: { + data: { + volumes: StorageVolume[] + }, + logs: { + volumes: StorageVolume[] + } + } + }, + status: { + readyReplicas: string, // "1/1" + state: string, // "Ready", + logSearchDashboard: string, // https://127.0.0.1:30777/kibana/app/kibana#/discover?_a=(query:(language:kuery,query:'custom_resource_name:miaa1')) + metricsDashboard: string, // https://127.0.0.1:30777/grafana/d/40q72HnGk/sql-managed-instance-metrics?var-hostname=miaa1-0 + primaryEndpoint?: string // "10.91.86.39:32718" + runningVersion: string // "v1.5.0_2022-04-05" + } + } + + export interface SqlMiShowResultDirect { extendedLocation: { name: string, // /subscriptions/a2382b66-3h2k-3h2k-2gdd-8ef45dgfdc33/resourcegroups/name-rg/providers/microsoft.extendedlocation/customlocations/custom-loc, type: string, // CustomLocation