Skip to content

Commit

Permalink
estebanreyl/Unified Azure Utilities (#332)
Browse files Browse the repository at this point in the history
* Added Azure Credentials Manager Singleton

* Small Style Fixes

* Further Style fixes, added getResourceManagementClient

* Added Azure Credentials Manager Singleton (#18)

* Added Azure Credentials Manager Singleton
* Added getResourceManagementClient

* Lazy Initialization Patches

* TSLint updates to match project

* Name Change to Reflect use

* Nit updates
  • Loading branch information
Esteban Rey authored and StephenWeatherford committed Aug 6, 2018
1 parent dd99658 commit 069ed85
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
5 changes: 5 additions & 0 deletions dockerExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { browseDockerHub, dockerHubLogout } from './explorer/utils/dockerHubUtil
import { ext } from "./extensionVariables";
import { initializeTelemetryReporter, reporter } from './telemetry/telemetry';
import { AzureAccount } from './typings/azure-account.api';
import { AzureUtilityManager } from './utils/azureUtilityManager';

export const FROM_DIRECTIVE_PATTERN = /^\s*FROM\s*([\w-\/:]*)(\s*AS\s*[a-z][a-z0-9-_\\.]*)?$/i;
export const COMPOSE_FILE_GLOB_PATTERN = '**/[dD]ocker-[cC]ompose*.{yaml,yml}';
Expand Down Expand Up @@ -148,6 +149,10 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {

ctx.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('docker', new DockerDebugConfigProvider()));

if (azureAccount) {
AzureUtilityManager.getInstance().setAccount(azureAccount);
}

activateLanguageClient(ctx);
}

Expand Down
137 changes: 137 additions & 0 deletions utils/azureUtilityManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { ContainerRegistryManagementClient } from 'azure-arm-containerregistry';
import { ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource';
import { ResourceGroup } from "azure-arm-resource/lib/resource/models";
import { ServiceClientCredentials } from 'ms-rest';
import * as ContainerModels from '../node_modules/azure-arm-containerregistry/lib/models';
import { AzureAccount } from '../typings/azure-account.api';
import { AsyncPool } from '../utils/asyncpool';
import { MAX_CONCURRENT_SUBSCRIPTON_REQUESTS } from './constants';

/* Singleton for facilitating communication with Azure account services by providing extended shared
functionality and extension wide access to azureAccount. Tool for internal use.
Authors: Esteban Rey L, Jackson Stokes
*/

export class AzureUtilityManager {

//SETUP
private static _instance: AzureUtilityManager;
private azureAccount: AzureAccount;

private constructor() { }

public static getInstance(): AzureUtilityManager {
if (!AzureUtilityManager._instance) { // lazy initialization
AzureUtilityManager._instance = new AzureUtilityManager();
}
return AzureUtilityManager._instance;
}

//This function has to be called explicitly before using the singleton.
public setAccount(azureAccount: AzureAccount): void {
this.azureAccount = azureAccount;
}

//GETTERS
public getAccount(): AzureAccount {
if (this.azureAccount) { return this.azureAccount; }
throw new Error('Azure account is not present, you may have forgotten to call setAccount');
}

public getFilteredSubscriptionList(): SubscriptionModels.Subscription[] {
return this.getAccount().filters.map<SubscriptionModels.Subscription>(filter => {
return {
id: filter.subscription.id,
subscriptionId: filter.subscription.subscriptionId,
tenantId: filter.session.tenantId,
displayName: filter.subscription.displayName,
state: filter.subscription.state,
subscriptionPolicies: filter.subscription.subscriptionPolicies,
authorizationSource: filter.subscription.authorizationSource
};
});
}

public getContainerRegistryManagementClient(subscription: SubscriptionModels.Subscription): ContainerRegistryManagementClient {
return new ContainerRegistryManagementClient(this.getCredentialByTenantId(subscription.tenantId), subscription.subscriptionId);
}

public getResourceManagementClient(subscription: SubscriptionModels.Subscription): ResourceManagementClient {
return new ResourceManagementClient(this.getCredentialByTenantId(subscription.tenantId), subscription.subscriptionId);
}

public async getRegistries(subscription?: SubscriptionModels.Subscription, resourceGroup?: string, sortFunction?: (a: ContainerModels.Registry, b: ContainerModels.Registry) => number): Promise<ContainerModels.Registry[]> {
let registries: ContainerModels.Registry[] = [];

if (subscription && resourceGroup) {
//Get all registries under one resourcegroup
const client = this.getContainerRegistryManagementClient(subscription);
registries = await client.registries.listByResourceGroup(resourceGroup);

} else if (subscription) {
//Get all registries under one subscription
const client = this.getContainerRegistryManagementClient(subscription);
registries = await client.registries.list();

} else {
//Get all registries for all subscriptions
const subs: SubscriptionModels.Subscription[] = this.getFilteredSubscriptionList();
const subPool = new AsyncPool(MAX_CONCURRENT_SUBSCRIPTON_REQUESTS);

for (let sub of subs) {
subPool.addTask(async () => {
const client = this.getContainerRegistryManagementClient(sub);
let subscriptionRegistries: ContainerModels.Registry[] = await client.registries.list();
registries = registries.concat(subscriptionRegistries);
});
}
await subPool.runAll();
}

if (sortFunction && registries.length > 1) {
registries.sort(sortFunction);
}

return registries;
}

public async getResourceGroups(subscription?: SubscriptionModels.Subscription): Promise<ResourceGroup[]> {
if (subscription) {
const resourceClient = this.getResourceManagementClient(subscription);
return await resourceClient.resourceGroups.list();
}
const subs = this.getFilteredSubscriptionList();
const subPool = new AsyncPool(MAX_CONCURRENT_SUBSCRIPTON_REQUESTS);
let resourceGroups: ResourceGroup[] = [];
//Acquire each subscription's data simultaneously
for (let sub of subs) {
subPool.addTask(async () => {
const resourceClient = this.getResourceManagementClient(sub);
const internalGroups = await resourceClient.resourceGroups.list();
resourceGroups = resourceGroups.concat(internalGroups);
});
}
await subPool.runAll();
return resourceGroups;
}

public getCredentialByTenantId(tenantId: string): ServiceClientCredentials {

const session = this.getAccount().sessions.find((azureSession) => azureSession.tenantId.toLowerCase() === tenantId.toLowerCase());

if (session) {
return session.credentials;
}

throw new Error(`Failed to get credentials, tenant ${tenantId} not found.`);
}

//CHECKS
//Provides a unified check for login that should be called once before using the rest of the singletons capabilities
public async waitForLogin(): Promise<boolean> {
if (!this.azureAccount) {
return false;
}
return await this.azureAccount.waitForLogin();
}
}

0 comments on commit 069ed85

Please sign in to comment.