Replace FQDN for Sql Synapse Servers (#20581)

* WIP work on rest call

* moved check to resourceTreeDataProviderBase

* some cleanup

* added working check

* placeholder, using filter function in resourceTreeDataProviderBase

* added connectivityEndpoints

* added kind filter

* fixed minor bits

* added wip changes

* added working queries

* added synapse to getSqlServers

* added override

* massive overhaul

* added property check

* made changes

* added clarifying comments

* added comment fixes

* remove async
This commit is contained in:
Alex Ma
2022-09-15 14:24:11 -07:00
committed by GitHub
parent acb22987fa
commit 08a9527314
3 changed files with 123 additions and 21 deletions

View File

@@ -5,7 +5,8 @@
import { ServiceClientCredentials } from '@azure/ms-rest-js';
import { IAzureResourceService } from '../../interfaces';
import { serversQuery, DbServerGraphData } from '../databaseServer/databaseServerService';
import { DbServerGraphData, SynapseWorkspaceGraphData } from '../databaseServer/databaseServerService';
import { synapseWorkspacesQuery, sqlServersQuery } from '../databaseServer/serverQueryStrings';
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore';
@@ -18,19 +19,45 @@ export class AzureResourceDatabaseService implements IAzureResourceService<azure
const databases: azureResource.AzureResourceDatabase[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
// Query servers and databases in parallel (start both promises before waiting on the 1st)
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, serversQuery);
// Query servers, synapse workspaces, and databases in parallel (start all promises before waiting on the 1st)
let servers: DbServerGraphData[];
let synapseWorkspaces: SynapseWorkspaceGraphData[];
let combined: (DbServerGraphData | SynapseWorkspaceGraphData)[] = [];
/**
* We need to get the list of servers minus the Synapse Workspaces,
* then we need to make another query to get them.
*
* This is done because the first query provides invalid endpoints for Synapse Workspaces
* While the second one provides them as one of its properties.
*
* They have to be processed in different ways as their structure differs
* in terms of properties. (See databaseServer/databaseServerService.ts for more info)
*
* Queries must be made separately due to union not being recognized by resourceGraph resource calls
*/
let synapseQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, synapseWorkspacesQuery);
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlServersQuery);
let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, `where type == "${azureResource.AzureResourceType.sqlDatabase}"`);
let servers: DbServerGraphData[] = await serverQueryPromise as DbServerGraphData[];
servers = await serverQueryPromise as DbServerGraphData[];
synapseWorkspaces = await synapseQueryPromise as SynapseWorkspaceGraphData[];
let dbByGraph: DatabaseGraphData[] = await dbQueryPromise as DatabaseGraphData[];
combined = combined.concat(servers).concat(synapseWorkspaces);
// Group servers by resource group, then merge DB results with servers so we
// can get the login name and server fully qualified name to use for connections
let rgMap = new Map<string, DbServerGraphData[]>();
servers.forEach(s => {
let serversForRg = rgMap.get(s.resourceGroup) || [];
serversForRg.push(s);
rgMap.set(s.resourceGroup, serversForRg);
let rgMap = new Map<string, (DbServerGraphData | SynapseWorkspaceGraphData)[]>();
combined.forEach(s => {
if ((s as SynapseWorkspaceGraphData).properties.connectivityEndpoints) {
// If the resource is a Synapse Workspace, we need to use the managedResourceGroupName
// (any SQL pools inside will use this instead of the regular resource group associated with the workspace itself).
let serversForRg = rgMap.get((s as SynapseWorkspaceGraphData).properties.managedResourceGroupName) || [];
serversForRg.push(s as SynapseWorkspaceGraphData);
rgMap.set((s as SynapseWorkspaceGraphData).properties.managedResourceGroupName, serversForRg);
} else {
let serversForRg = rgMap.get(s.resourceGroup) || [];
serversForRg.push(s);
rgMap.set(s.resourceGroup, serversForRg);
}
});
// Match database ID. When calling exec [0] is full match, [1] is resource group name, [2] is server name
@@ -42,14 +69,15 @@ export class AzureResourceDatabaseService implements IAzureResourceService<azure
if (serversForRg && !db.kind.endsWith('system') && svrIdRegExp.test(db.id)) {
const founds = svrIdRegExp.exec(db.id);
const serverName = founds[2];
let server = servers.find(s => s.name === serverName);
let server = combined.find(s => s.name === serverName);
if (server) {
databases.push({
name: db.name,
id: db.id,
serverName: server.name,
serverFullName: server.properties.fullyQualifiedDomainName,
loginName: server.properties.administratorLogin,
// Determine if server object is for Synapse Workspace or not and get the needed property from the correct place.
serverFullName: (server as SynapseWorkspaceGraphData).properties.connectivityEndpoints?.sql ?? (server as DbServerGraphData).properties.fullyQualifiedDomainName,
loginName: (server as SynapseWorkspaceGraphData).properties.sqlAdministratorLogin ?? (server as DbServerGraphData).properties.administratorLogin,
subscription: {
id: db.subscriptionId,
name: (subscriptions.find(sub => sub.id === db.subscriptionId))?.name

View File

@@ -3,9 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
import { azureResource } from 'azurecore';
import { ServiceClientCredentials } from '@azure/ms-rest-js';
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase';
import { azureResource, AzureAccount } from 'azurecore';
import { sqlServersQuery, synapseWorkspacesQuery } from './serverQueryStrings';
import { IAzureResourceService } from '../../interfaces';
export interface DbServerGraphData extends GraphData {
properties: {
@@ -14,20 +17,73 @@ export interface DbServerGraphData extends GraphData {
};
}
export const serversQuery = `where type == "${azureResource.AzureResourceType.sqlServer}"`;
/**
* Properties returned by the Synapse query are different from the server ones and have to be treated differently.
*/
export interface SynapseWorkspaceGraphData extends GraphData {
properties: {
/**
* SQL connectivity endpoint and other endpoints are found here, instead of fullyQualifiedDomainName.
*/
connectivityEndpoints: { sql: string };
/**
* managedResourceGroupName is the resource group used by any SQL pools inside the workspace
* which is different from the resource group of the workspace itself.
*/
managedResourceGroupName: string;
/**
* administratorLogin is called sqlAdministratorLogin here.
*/
sqlAdministratorLogin: string;
};
}
export class AzureResourceDatabaseServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
export class AzureResourceDatabaseServerService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> {
protected get query(): string {
return serversQuery;
return sqlServersQuery;
}
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer {
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
/**
* We need to get the list of servers minus the Synapse Workspaces,
* then we need to make another query to get them.
*
* This is done because the first query provides invalid endpoints for Synapse Workspaces
* While the second one provides them as one of its properties.
*
* They have to be processed in different ways by convertResource as their structure differs
* in terms of properties. (See above)
*
* Queries must be made separately due to union not being recognized by resourceGraph resource calls.
*/
let combinedGraphResources: (DbServerGraphData | SynapseWorkspaceGraphData)[] = [];
let serverGraphResources: DbServerGraphData[] = await queryGraphResources<DbServerGraphData>(resourceClient, subscriptions, this.query);
let synapseGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources<SynapseWorkspaceGraphData>(resourceClient, subscriptions, synapseWorkspacesQuery);
combinedGraphResources = combinedGraphResources.concat(serverGraphResources).concat(synapseGraphResources);
const ids = new Set<string>();
combinedGraphResources.forEach((res) => {
if (!ids.has(res.id)) {
ids.add(res.id);
res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId).name;
const converted = this.convertResource(res);
convertedResources.push(converted);
}
});
return convertedResources;
}
protected convertResource(resource: DbServerGraphData | SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer {
return {
id: resource.id,
name: resource.name,
fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin,
// Determine if resource object is for Synapse Workspace or not and get the needed property from the correct place.
fullName: (resource as SynapseWorkspaceGraphData).properties.connectivityEndpoints?.sql ?? (resource as DbServerGraphData).properties.fullyQualifiedDomainName,
loginName: (resource as SynapseWorkspaceGraphData).properties.sqlAdministratorLogin ?? (resource as DbServerGraphData).properties.administratorLogin,
defaultDatabaseName: 'master',
subscription: {
id: resource.subscriptionId,

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { azureResource } from 'azurecore';
/**
* Get list of Synapse Workspaces with information such as SQL connection endpoints.
*/
export const synapseWorkspacesQuery = `where type == "microsoft.synapse/workspaces"`;
/**
* Lists all Sql Servers except for Synapse Pool Servers
* (they have different properties and need to be handled separately,
* see databaseServerService.ts for more details)
*/
export const sqlServersQuery = `where type == "${azureResource.AzureResourceType.sqlServer}" and kind != "v12.0,analytics"`;