Support for MongoDB clusters / vCore (#22512)

* Initial support for MongoDB clusters / vCore

* Get cluster connection string from arm

* Preserve Azure account for any auth type

When a service has been selected through the Azure
browser, we want to preserve the Azure account
information even if a different authentication has
been selected. This allows doing ARM operations
using the signed in Azure account for any resources
including those with a different login types such
as clusters.
This commit is contained in:
Vsevolod Kukol
2023-06-08 00:38:35 +02:00
committed by GitHub
parent 346b207f4e
commit e0d5cd18b9
8 changed files with 33 additions and 12 deletions

View File

@@ -20,7 +20,8 @@ export enum AzureResourceItemType {
message = 'azure.resource.itemType.message', message = 'azure.resource.itemType.message',
azureMonitor = 'azure.resource.itemType.azureMonitor', azureMonitor = 'azure.resource.itemType.azureMonitor',
azureMonitorContainer = 'azure.resource.itemType.azureMonitorContainer', azureMonitorContainer = 'azure.resource.itemType.azureMonitorContainer',
cosmosDBMongoAccount = 'azure.resource.itemType.cosmosDBMongoAccount' cosmosDBMongoAccount = 'azure.resource.itemType.cosmosDBMongoAccount',
cosmosDBMongoCluster = 'azure.resource.itemType.cosmosDBMongoCluster'
} }
export enum AzureResourceServiceNames { export enum AzureResourceServiceNames {

View File

@@ -50,6 +50,7 @@ export interface DbServerGraphData extends GraphData {
properties: { properties: {
fullyQualifiedDomainName: string; fullyQualifiedDomainName: string;
administratorLogin: string; administratorLogin: string;
connectionString: string;
}; };
} }

View File

@@ -10,15 +10,26 @@ import { cosmosMongoDbQuery } from '../../queryStringConstants';
import { DbServerGraphData } from '../../../interfaces'; import { DbServerGraphData } from '../../../interfaces';
import { COSMOSDB_MONGO_PROVIDER_ID } from '../../../../constants'; import { COSMOSDB_MONGO_PROVIDER_ID } from '../../../../constants';
export interface AzureResourceMongoDatabaseServer extends azureResource.AzureResourceDatabaseServer {
isServer: boolean;
}
export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData> { export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData> {
public override queryFilter: string = cosmosMongoDbQuery; public override queryFilter: string = cosmosMongoDbQuery;
public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { public convertServerResource(resource: DbServerGraphData): AzureResourceMongoDatabaseServer | undefined {
let host = resource.name;
const isServer = resource.type === azureResource.AzureResourceType.cosmosDbCluster;
if (isServer) {
const url = new URL(resource.properties.connectionString);
host = url.hostname;
}
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: COSMOSDB_MONGO_PROVIDER_ID, provider: COSMOSDB_MONGO_PROVIDER_ID,
fullName: resource.properties.fullyQualifiedDomainName, isServer: isServer,
fullName: host,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,
defaultDatabaseName: '', defaultDatabaseName: '',
tenant: resource.tenantId, tenant: resource.tenantId,

View File

@@ -8,6 +8,7 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, AzureResourcePrefixes, cosmosDBProvider } from '../../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, cosmosDBProvider } from '../../../constants';
import { AzureResourceMongoDatabaseServer } from './cosmosDbMongoService';
import { generateGuid } from '../../../utils'; import { generateGuid } from '../../../utils';
import { DbServerGraphData, GraphData } from '../../../interfaces'; import { DbServerGraphData, GraphData } from '../../../interfaces';
import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase';
@@ -25,7 +26,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: azdata.Account): azdata.TreeItem { public getTreeItemForResource(databaseServer: AzureResourceMongoDatabaseServer, account: azdata.Account): azdata.TreeItem {
return { return {
id: `${AzureResourcePrefixes.cosmosdb}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, id: `${AzureResourcePrefixes.cosmosdb}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`,
label: `${databaseServer.name} (CosmosDB Mongo API)`, label: `${databaseServer.name} (CosmosDB Mongo API)`,
@@ -38,16 +39,18 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
payload: { payload: {
id: generateGuid(), id: generateGuid(),
connectionName: databaseServer.name, connectionName: databaseServer.name,
serverName: databaseServer.name, serverName: databaseServer.fullName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: azdata.connection.AuthenticationType.AzureMFA, authenticationType: databaseServer.isServer ? azdata.connection.AuthenticationType.SqlLogin : azdata.connection.AuthenticationType.AzureMFA,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: cosmosDBProvider, providerName: cosmosDBProvider,
saveProfile: false, saveProfile: false,
options: {}, options: {
isServer: databaseServer.isServer,
},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
azureTenantId: databaseServer.tenant, azureTenantId: databaseServer.tenant,
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,

View File

@@ -71,7 +71,7 @@ export const kustoClusterQuery = `type == "${azureResource.AzureResourceType.kus
/** /**
* Lists all Cosmos DB for MongoDB accounts * Lists all Cosmos DB for MongoDB accounts
*/ */
export const cosmosMongoDbQuery = `type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "${Constants.mongoDbKind}"`; export const cosmosMongoDbQuery = `(type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "${Constants.mongoDbKind}") or type == "${azureResource.AzureResourceType.cosmosDbCluster}"`;
/** /**
* Lists all Log Analytics workspaces * Lists all Log Analytics workspaces

View File

@@ -79,7 +79,7 @@ export class AzureResourceUniversalService implements azureResource.IAzureResour
public getProviderFromResourceType(type: string, kind?: string): public getProviderFromResourceType(type: string, kind?: string):
[provider: azureResource.IAzureResourceTreeDataProvider, category: ResourceCategory] { [provider: azureResource.IAzureResourceTreeDataProvider, category: ResourceCategory] {
if (type === azureResource.AzureResourceType.cosmosDbAccount && kind === mongoDbKind) { if ((type === azureResource.AzureResourceType.cosmosDbAccount && kind === mongoDbKind) || type === azureResource.AzureResourceType.cosmosDbCluster) {
return [this.getRegisteredTreeDataProviderInstance(COSMOSDB_MONGO_PROVIDER_ID), ResourceCategory.Server]; return [this.getRegisteredTreeDataProviderInstance(COSMOSDB_MONGO_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.sqlDatabase || type === azureResource.AzureResourceType.sqlSynapseSqlDatabase) { } else if (type === azureResource.AzureResourceType.sqlDatabase || type === azureResource.AzureResourceType.sqlSynapseSqlDatabase) {
return [this.getRegisteredTreeDataProviderInstance(DATABASE_PROVIDER_ID), ResourceCategory.Database]; return [this.getRegisteredTreeDataProviderInstance(DATABASE_PROVIDER_ID), ResourceCategory.Database];

View File

@@ -383,6 +383,7 @@ declare module 'azurecore' {
storageAccount = 'microsoft.storage/storageaccounts', storageAccount = 'microsoft.storage/storageaccounts',
logAnalytics = 'microsoft.operationalinsights/workspaces', logAnalytics = 'microsoft.operationalinsights/workspaces',
cosmosDbAccount = 'microsoft.documentdb/databaseaccounts', cosmosDbAccount = 'microsoft.documentdb/databaseaccounts',
cosmosDbCluster = 'microsoft.documentdb/mongoclusters',
mysqlFlexibleServer = 'microsoft.dbformysql/flexibleservers' mysqlFlexibleServer = 'microsoft.dbformysql/flexibleservers'
} }

View File

@@ -974,9 +974,9 @@ export class ConnectionWidget extends lifecycle.Disposable {
}); });
} }
if (this.authType === AuthenticationType.AzureMFA || this.authType === AuthenticationType.AzureMFAAndUser) { if (this.authType === AuthenticationType.AzureMFA || this.authType === AuthenticationType.AzureMFAAndUser || connectionInfo.azureAccount !== null) {
this.fillInAzureAccountOptions().then(async () => { this.fillInAzureAccountOptions().then(async () => {
let accountName = (this.authType === AuthenticationType.AzureMFA) let accountName = ((this.authType === AuthenticationType.AzureMFA) || connectionInfo.azureAccount !== null)
? connectionInfo.azureAccount : connectionInfo.userName; ? connectionInfo.azureAccount : connectionInfo.userName;
let account: azdata.Account; let account: azdata.Account;
if (accountName) { if (accountName) {
@@ -1262,7 +1262,11 @@ export class ConnectionWidget extends lifecycle.Disposable {
model.userName = this.userName; model.userName = this.userName;
model.password = this.password; model.password = this.password;
model.authenticationType = this.authenticationType; model.authenticationType = this.authenticationType;
model.azureAccount = this.authToken; const azureAccount = this.authToken;
if (azureAccount) {
// set the azureAccount only if one has been selected, otherwise preserve the initial model value
model.azureAccount = azureAccount;
}
model.savePassword = this._rememberPasswordCheckBox.checked; model.savePassword = this._rememberPasswordCheckBox.checked;
model.databaseName = this.databaseName; model.databaseName = this.databaseName;
if (this._customOptionWidgets) { if (this._customOptionWidgets) {