diff --git a/extensions/arc/src/models/controllerModel.ts b/extensions/arc/src/models/controllerModel.ts index 132add20c0..570c4dee63 100644 --- a/extensions/arc/src/models/controllerModel.ts +++ b/extensions/arc/src/models/controllerModel.ts @@ -6,6 +6,7 @@ import { ControllerInfo, ResourceType } from 'arc'; import * as azExt from 'az-ext'; import * as vscode from 'vscode'; +import { ConnectionMode } from '../constants'; import * as loc from '../localizedConstants'; import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider'; @@ -63,10 +64,19 @@ export class ControllerModel { if (node) { this.treeDataProvider.refreshNode(node); } else { - await this.refresh(false, this.info.namespace); + await this.refresh(false, this.info.resourceGroup, this.info.namespace); } } - public async refresh(showErrors: boolean = true, namespace: string): Promise { + + public async refresh(showErrors: boolean = true, resourceGroup: string, namespace: string): Promise { + if (this.info.connectionMode === ConnectionMode.direct) { + this.refreshDirectMode(showErrors, resourceGroup, namespace); + } else { + this.refreshIndirectMode(showErrors, namespace); + } + } + + public async refreshDirectMode(showErrors: boolean = true, resourceGroup: string, namespace: string): Promise { const newRegistrations: Registration[] = []; await Promise.all([ this._azApi.az.arcdata.dc.config.show(namespace, this.azAdditionalEnvVars).then(result => { @@ -107,7 +117,66 @@ export class ControllerModel { }; })); }), - this._azApi.az.sql.miarc.list(namespace, this.azAdditionalEnvVars).then(result => { + this._azApi.az.sql.miarc.list({ resourceGroup: resourceGroup, namespace: undefined }, this.azAdditionalEnvVars).then(result => { + newRegistrations.push(...result.stdout.map(r => { + return { + instanceName: r.name, + state: r.state, + instanceType: ResourceType.sqlManagedInstances + }; + })); + + }) + ]).then(() => { + this._registrations = newRegistrations; + this.registrationsLastUpdated = new Date(); + this._onRegistrationsUpdated.fire(this._registrations); + }) + ]); + } + + public async refreshIndirectMode(showErrors: boolean = true, namespace: string): Promise { + const newRegistrations: Registration[] = []; + await Promise.all([ + this._azApi.az.arcdata.dc.config.show(namespace, this.azAdditionalEnvVars).then(result => { + this._controllerConfig = result.stdout; + this.configLastUpdated = new Date(); + this._onConfigUpdated.fire(this._controllerConfig); + }).catch(err => { + // If an error occurs show a message so the user knows something failed but still + // fire the event so callers hooking into this can handle the error (e.g. so dashboards don't show the + // loading icon forever) + if (showErrors) { + vscode.window.showErrorMessage(loc.fetchConfigFailed(this.info.name, err)); + } + this._onConfigUpdated.fire(this._controllerConfig); + throw err; + }), + this._azApi.az.arcdata.dc.endpoint.list(namespace, this.azAdditionalEnvVars).then(result => { + this._endpoints = result.stdout; + this.endpointsLastUpdated = new Date(); + this._onEndpointsUpdated.fire(this._endpoints); + }).catch(err => { + // If an error occurs show a message so the user knows something failed but still + // fire the event so callers can know to update (e.g. so dashboards don't show the + // loading icon forever) + if (showErrors) { + vscode.window.showErrorMessage(loc.fetchEndpointsFailed(this.info.name, err)); + } + this._onEndpointsUpdated.fire(this._endpoints); + throw err; + }), + Promise.all([ + this._azApi.az.postgres.arcserver.list(namespace, this.azAdditionalEnvVars).then(result => { + newRegistrations.push(...result.stdout.map(r => { + return { + instanceName: r.name, + state: r.state, + instanceType: ResourceType.postgresInstances + }; + })); + }), + this._azApi.az.sql.miarc.list({ resourceGroup: undefined, namespace: namespace }, this.azAdditionalEnvVars).then(result => { newRegistrations.push(...result.stdout.map(r => { return { instanceName: r.name, diff --git a/extensions/arc/src/ui/dashboards/controller/controllerDashboard.ts b/extensions/arc/src/ui/dashboards/controller/controllerDashboard.ts index e04289eae2..f4186e5270 100644 --- a/extensions/arc/src/ui/dashboards/controller/controllerDashboard.ts +++ b/extensions/arc/src/ui/dashboards/controller/controllerDashboard.ts @@ -19,7 +19,7 @@ export class ControllerDashboard extends Dashboard { public override async showDashboard(): Promise { await super.showDashboard(); // Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways - this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing Controller dashboard ${err}`)); + this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing Controller dashboard ${err}`)); } protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> { diff --git a/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts b/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts index db2c54290a..c27ee2fb14 100644 --- a/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/controller/controllerDashboardOverviewPage.ts @@ -57,7 +57,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage { } protected async refresh(): Promise { - await this._controllerModel.refresh(false, this._controllerModel.info.namespace); + await this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace); } public get container(): azdata.Component { diff --git a/extensions/arc/src/ui/dashboards/controller/controllerUpgrades.ts b/extensions/arc/src/ui/dashboards/controller/controllerUpgrades.ts index 0f133b82c7..be46207c39 100644 --- a/extensions/arc/src/ui/dashboards/controller/controllerUpgrades.ts +++ b/extensions/arc/src/ui/dashboards/controller/controllerUpgrades.ts @@ -35,7 +35,7 @@ export class ControllerUpgradesPage extends DashboardPage { return IconPathHelper.upgrade; } protected async refresh(): Promise { - await Promise.resolve(this._controllerModel.refresh(false, this._controllerModel.info.namespace)); + await Promise.resolve(this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace)); this.handleTableUpdated(); } @@ -252,7 +252,7 @@ export class ControllerUpgradesPage extends DashboardPage { } try { - await this._controllerModel.refresh(false, this._controllerModel.info.namespace); + await this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); } diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts index acaf68fe5f..9e598baa13 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaBackupsPage.ts @@ -56,7 +56,7 @@ export class MiaaBackupsPage extends DashboardPage { return IconPathHelper.pitr; } protected async refresh(): Promise { - await Promise.all([this._controllerModel.refresh(false, this._controllerModel.info.namespace), this._miaaModel.refresh()]); + await Promise.all([this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace), this._miaaModel.refresh()]); } public get container(): azdata.Component { diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts b/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts index 097e1ee2d1..a515b669e7 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaDashboard.ts @@ -23,7 +23,7 @@ export class MiaaDashboard extends Dashboard { public override async showDashboard(): Promise { await super.showDashboard(); // Kick off the model refreshes but don't wait on it since that's all handled with callbacks anyways - this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`)); + this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`)); this._miaaModel.refresh().catch(err => console.log(`Error refreshing MIAA model for MIAA dashboard ${err}`)); } diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts index 385e176183..5597db43ec 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaDashboardOverviewPage.ts @@ -74,7 +74,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage { } protected async refresh(): Promise { - await Promise.all([this._controllerModel.refresh(false, this._controllerModel.info.namespace), this._miaaModel.refresh()]); + await Promise.all([this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace), this._miaaModel.refresh()]); } public get container(): azdata.Component { diff --git a/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts b/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts index 99f09dfba4..d861e85e91 100644 --- a/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts +++ b/extensions/arc/src/ui/dashboards/miaa/miaaUpgradeManagementPage.ts @@ -36,7 +36,7 @@ export class MiaaUpgradeManagementPage extends DashboardPage { return IconPathHelper.upgrade; } protected async refresh(): Promise { - await Promise.resolve(this._controllerModel.refresh(false, this._controllerModel.info.namespace)); + await Promise.resolve(this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace)); this.handleTableUpdated(); } @@ -289,7 +289,7 @@ export class MiaaUpgradeManagementPage extends DashboardPage { } try { - await this._controllerModel.refresh(false, this._controllerModel.info.namespace); + await this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts b/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts index 0bfd5b69a2..e206f90113 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts @@ -29,7 +29,7 @@ export class PostgresDashboard extends Dashboard { await super.showDashboard(); // Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways - this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for Postgres dashboard ${err}`)); + this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for Postgres dashboard ${err}`)); this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres model for Postgres dashboard ${err}`)); } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts index fe57fb3089..d9c8e793b2 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts @@ -295,7 +295,7 @@ export class PostgresOverviewPage extends DashboardPage { await Promise.all([ this._postgresModel.refresh(), - this._controllerModel.refresh(false, this._controllerModel.info.namespace) + this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace) ]); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts index 5b5a7e640d..90cf9938ff 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts @@ -77,7 +77,7 @@ export class PostgresPropertiesPage extends DashboardPage { this.loading!.loading = true; await Promise.all([ this._postgresModel.refresh(), - this._controllerModel.refresh(false, this._controllerModel.info.namespace) + this._controllerModel.refresh(false, this._controllerModel.info.resourceGroup, this._controllerModel.info.namespace) ]); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); diff --git a/extensions/arc/src/ui/dialogs/connectControllerDialog.ts b/extensions/arc/src/ui/dialogs/connectControllerDialog.ts index b0f76760a0..bef2452219 100644 --- a/extensions/arc/src/ui/dialogs/connectControllerDialog.ts +++ b/extensions/arc/src/ui/dialogs/connectControllerDialog.ts @@ -196,7 +196,7 @@ export class ConnectToControllerDialog extends ControllerDialogBase { const controllerModel = new ControllerModel(this.treeDataProvider, controllerInfo); try { // Validate that we can connect to the controller, this also populates the controllerRegistration from the connection response. - await controllerModel.refresh(false, this.namespaceInputBox.value); + await controllerModel.refresh(false, this.resourceGroup, this.namespaceInputBox.value); // default info.name to the name of the controller instance if the user did not specify their own and to a pre-canned default if for some weird reason controller endpoint returned instanceName is also not a valid value controllerModel.info.name = controllerModel.info.name || controllerModel.controllerConfig?.metadata.name || loc.defaultControllerName; controllerModel.info.resourceGroup = controllerModel.controllerConfig?.spec.settings.azure.resourceGroup; diff --git a/extensions/arc/src/ui/tree/controllerTreeNode.ts b/extensions/arc/src/ui/tree/controllerTreeNode.ts index 2b8bd67549..32d1d8da98 100644 --- a/extensions/arc/src/ui/tree/controllerTreeNode.ts +++ b/extensions/arc/src/ui/tree/controllerTreeNode.ts @@ -39,12 +39,12 @@ export class ControllerTreeNode extends TreeNode { public override async getChildren(): Promise { try { - await this.model.refresh(false, this.model.info.namespace); + await this.model.refresh(false, this.model.info.resourceGroup, this.model.info.namespace); this.updateChildren(this.model.registrations); } catch (err) { vscode.window.showErrorMessage(loc.errorConnectingToController(err)); try { - await this.model.refresh(false, this.model.info.namespace); + await this.model.refresh(false, this.model.info.resourceGroup, this.model.info.namespace); this.updateChildren(this.model.registrations); } catch (err) { if (!(err instanceof UserCancelledError)) { diff --git a/extensions/azcli/src/api.ts b/extensions/azcli/src/api.ts index 49d0165c09..53429f883e 100644 --- a/extensions/azcli/src/api.ts +++ b/extensions/azcli/src/api.ts @@ -123,10 +123,18 @@ export function getAzApi(localAzDiscovered: Promise, azTool validateAz(azToolService.localAz); return azToolService.localAz!.sql.miarc.delete(name, namespace, additionalEnvVars); }, - list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => { + list: async ( + args: { + // Direct mode arguments + resourceGroup?: string; + // Indirect mode arguments + namespace?: string; + }, + additionalEnvVars?: azExt.AdditionalEnvVars + ) => { await localAzDiscovered; validateAz(azToolService.localAz); - return azToolService.localAz!.sql.miarc.list(namespace, additionalEnvVars); + return azToolService.localAz!.sql.miarc.list(args, additionalEnvVars); }, show: async ( name: string, diff --git a/extensions/azcli/src/az.ts b/extensions/azcli/src/az.ts index bb71085ae9..b8051e6e5d 100644 --- a/extensions/azcli/src/az.ts +++ b/extensions/azcli/src/az.ts @@ -178,8 +178,25 @@ export class AzTool implements azExt.IAzApi { delete: (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise> => { return this.executeCommand(['sql', 'mi-arc', 'delete', '-n', name, '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars); }, - list: (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise> => { - return this.executeCommand(['sql', 'mi-arc', 'list', '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars); + list: ( + args: { + // Direct mode arguments + resourceGroup?: string, + // Indirect mode arguments + namespace?: string + // Additional arguments + }, + additionalEnvVars?: azExt.AdditionalEnvVars + ): Promise> => { + const argsArray = ['sql', 'mi-arc', 'list']; + 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); }, show: ( name: string, diff --git a/extensions/azcli/src/test/az.test.ts b/extensions/azcli/src/test/az.test.ts index 2c8bbef1ac..2e9b358caf 100644 --- a/extensions/azcli/src/test/az.test.ts +++ b/extensions/azcli/src/test/az.test.ts @@ -116,7 +116,7 @@ describe('az', function () { }); it('list', async function (): Promise { // Assume indirect mode - await azTool.sql.miarc.list(namespace); + await azTool.sql.miarc.list({resourceGroup: undefined, namespace: namespace}); verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'list', '--k8s-namespace', namespace, '--use-k8s']); }); it('show', async function (): Promise { diff --git a/extensions/azcli/src/typings/az-ext.d.ts b/extensions/azcli/src/typings/az-ext.d.ts index 9a0edb2ad4..6f74334bfc 100644 --- a/extensions/azcli/src/typings/az-ext.d.ts +++ b/extensions/azcli/src/typings/az-ext.d.ts @@ -567,7 +567,16 @@ declare module 'az-ext' { sql: { miarc: { delete(name: string, namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise>, - list(namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise>, + list( + args: { + // Direct mode arguments + resourceGroup?: string, + // Indirect mode arguments + namespace?: string + }, + // Additional arguments + additionalEnvVars?: AdditionalEnvVars + ): Promise>, show( name: string, args: {