From 667cde5d3052e9ca0df2b5169aab1002bc03f4ba Mon Sep 17 00:00:00 2001 From: Chris Dias Date: Mon, 4 Dec 2017 11:02:34 +0100 Subject: [PATCH] Chrisdias/browse azure portal (#187) Add ability to browse to Azure Portal Container Registry --- dockerExtension.ts | 9 +++- explorer/models/azureRegistryNodes.ts | 43 +++++++++++++------- explorer/models/containerNode.ts | 2 +- explorer/models/imageNode.ts | 2 +- explorer/models/registryRootNode.ts | 3 +- explorer/utils/azureUtils.ts | 17 ++++++++ explorer/{models => utils}/dockerHubUtils.ts | 2 +- explorer/{models => utils}/utils.ts | 0 package.json | 20 ++++++++- 9 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 explorer/utils/azureUtils.ts rename explorer/{models => utils}/dockerHubUtils.ts (99%) rename explorer/{models => utils}/utils.ts (100%) diff --git a/dockerExtension.ts b/dockerExtension.ts index bc759dfc1b..c3523ad1a6 100644 --- a/dockerExtension.ts +++ b/dockerExtension.ts @@ -28,14 +28,15 @@ import { DockerExplorerProvider } from './explorer/dockerExplorer'; import { removeContainer } from './commands/remove-container'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Middleware, Proposed, ProposedFeatures, DidChangeConfigurationNotification } from 'vscode-languageclient'; import { WebAppCreator } from './explorer/deploy/webAppCreator'; -import { AzureImageNode } from './explorer/models/azureRegistryNodes'; +import { AzureImageNode, AzureRegistryNode, AzureRepositoryNode } from './explorer/models/azureRegistryNodes'; import { DockerHubImageNode, DockerHubRepositoryNode, DockerHubOrgNode } from './explorer/models/dockerHubNodes'; import { AzureAccountWrapper } from './explorer/deploy/azureAccountWrapper'; import * as util from "./explorer/deploy/util"; -import { dockerHubLogout, browseDockerHub } from './explorer/models/dockerHubUtils'; +import { dockerHubLogout, browseDockerHub } from './explorer/utils/dockerHubUtils'; import { AzureAccount } from './typings/azure-account.api'; import * as opn from 'opn'; import { DockerDebugConfigProvider } from './configureWorkspace/configDebugProvider'; +import { browseAzurePortal } from './explorer/utils/azureUtils'; export const FROM_DIRECTIVE_PATTERN = /^\s*FROM\s*([\w-\/:]*)(\s*AS\s*[a-z][a-z0-9-_\\.]*)?$/i; @@ -127,9 +128,13 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { ctx.subscriptions.push(vscode.commands.registerCommand('vscode-docker.browseDockerHub', async (context?: DockerHubImageNode | DockerHubRepositoryNode | DockerHubOrgNode) => { browseDockerHub(context); })); + ctx.subscriptions.push(vscode.commands.registerCommand('vscode-docker.browseAzurePortal', async (context?: AzureRegistryNode | AzureRepositoryNode | AzureImageNode ) => { + browseAzurePortal(context); + })); ctx.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('docker', new DockerDebugConfigProvider())); + activateLanguageClient(ctx); } diff --git a/explorer/models/azureRegistryNodes.ts b/explorer/models/azureRegistryNodes.ts index c4847edd84..bd9721c6e7 100644 --- a/explorer/models/azureRegistryNodes.ts +++ b/explorer/models/azureRegistryNodes.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as moment from 'moment'; import * as request from 'request-promise'; - +import * as ContainerModels from '../../node_modules/azure-arm-containerregistry/lib/models'; import { NodeBase } from './nodeBase'; import { SubscriptionClient, ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource'; import { AzureAccount, AzureSession } from '../../typings/azure-account.api'; @@ -21,10 +21,11 @@ export class AzureRegistryNode extends NodeBase { this._azureAccount = azureAccount; } - public type: RegistryType; + public password: string; + public registry: ContainerModels.Registry; public subscription: SubscriptionModels.Subscription; + public type: RegistryType; public userName: string; - public password: string; getTreeItem(): vscode.TreeItem { return { @@ -90,13 +91,15 @@ export class AzureRegistryNode extends NodeBase { if (body.length > 0) { const repositories = JSON.parse(body).repositories; for (let i = 0; i < repositories.length; i++) { - node = new AzureRepositoryNode(repositories[i], "azureRepository"); - node.repository = element.label; - node.subscription = element.subscription; + node = new AzureRepositoryNode(repositories[i], "azureRepositoryNode"); node.accessTokenARC = accessTokenARC; + node.azureAccount = element.azureAccount; + node.password = element.password; node.refreshTokenARC = refreshTokenARC; + node.registry = element.registry; + node.repository = element.label; + node.subscription = element.subscription; node.userName = element.userName; - node.password = element.password; repoNodes.push(node); } } @@ -107,6 +110,8 @@ export class AzureRegistryNode extends NodeBase { } } + + export class AzureRepositoryNode extends NodeBase { constructor( @@ -120,12 +125,14 @@ export class AzureRepositoryNode extends NodeBase { super(label); } - public repository: string; - public subscription: any; public accessTokenARC: string; + public azureAccount: AzureAccount + public password: string; public refreshTokenARC: string; + public registry: ContainerModels.Registry; + public repository: string; + public subscription: SubscriptionModels.Subscription; public userName: string; - public password: string; getTreeItem(): vscode.TreeItem { return { @@ -144,7 +151,9 @@ export class AzureRepositoryNode extends NodeBase { let accessTokenARC; let tags; - const { accessToken, refreshToken } = await acquireToken(element.subscription.session); + const tenantId: string = element.subscription.tenantId; + const session: AzureSession = element.azureAccount.sessions.find((s, i, array) => s.tenantId.toLowerCase() === tenantId.toLowerCase()); + const { accessToken, refreshToken } = await acquireToken(session); if (accessToken && refreshToken) { const tenantId = element.subscription.tenantId; @@ -198,10 +207,13 @@ export class AzureRepositoryNode extends NodeBase { })); created = moment(new Date(JSON.parse(manifest.history[0].v1Compatibility).created)).fromNow(); - node = new AzureImageNode(`${element.label}:${tags[i]} (${created})`, 'azureImageTag'); + node = new AzureImageNode(`${element.label}:${tags[i]} (${created})`, 'azureImageNode'); + node.azureAccount = element.azureAccount; + node.password = element.password; + node.registry = element.registry; node.serverUrl = element.repository; + node.subscription = element.subscription; node.userName = element.userName; - node.password = element.password; imageNodes.push(node); } @@ -219,9 +231,12 @@ export class AzureImageNode extends NodeBase { super(label); } + public azureAccount: AzureAccount + public password: string; + public registry: ContainerModels.Registry; public serverUrl: string; + public subscription: SubscriptionModels.Subscription; public userName: string; - public password: string; getTreeItem(): vscode.TreeItem { return { diff --git a/explorer/models/containerNode.ts b/explorer/models/containerNode.ts index c25f3c8f3f..cd64727a9e 100644 --- a/explorer/models/containerNode.ts +++ b/explorer/models/containerNode.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { NodeBase } from './nodeBase'; -import { trimWithElipsis } from './utils'; +import { trimWithElipsis } from '../utils/utils'; export class ContainerNode extends NodeBase { diff --git a/explorer/models/imageNode.ts b/explorer/models/imageNode.ts index ee3e95e2a3..723ec29f2a 100644 --- a/explorer/models/imageNode.ts +++ b/explorer/models/imageNode.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as moment from 'moment'; import { NodeBase } from './nodeBase'; -import { trimWithElipsis } from './utils'; +import { trimWithElipsis } from '../utils/utils'; export class ImageNode extends NodeBase { diff --git a/explorer/models/registryRootNode.ts b/explorer/models/registryRootNode.ts index 607b1f44f7..139a167970 100644 --- a/explorer/models/registryRootNode.ts +++ b/explorer/models/registryRootNode.ts @@ -146,11 +146,12 @@ export class RegistryRootNode extends NodeBase { light: path.join(__filename, '..', '..', '..', '..', 'images', 'light', 'Registry_16x.svg'), dark: path.join(__filename, '..', '..', '..', '..', 'images', 'dark', 'Registry_16x.svg') }; - let node = new AzureRegistryNode(registries[j].loginServer, 'registry', iconPath, this._azureAccount); + let node = new AzureRegistryNode(registries[j].loginServer, 'azureRegistryNode', iconPath, this._azureAccount); node.type = RegistryType.Azure; node.password = creds.passwords[0].value; node.userName = creds.username; node.subscription = subs[i]; + node.registry = registries[j]; azureRegistryNodes.push(node); } } diff --git a/explorer/utils/azureUtils.ts b/explorer/utils/azureUtils.ts new file mode 100644 index 0000000000..f6ae59e2ea --- /dev/null +++ b/explorer/utils/azureUtils.ts @@ -0,0 +1,17 @@ +import * as opn from 'opn'; +import { AzureRepositoryNode, AzureImageNode, AzureRegistryNode } from '../models/azureRegistryNodes'; +import { AzureSession } from '../../typings/azure-account.api'; + +export function browseAzurePortal(context?: AzureRegistryNode | AzureRepositoryNode | AzureImageNode ): void { + + if (context) { + const tenantId: string = context.subscription.tenantId; + const session: AzureSession = context.azureAccount.sessions.find((s, i, array) => s.tenantId.toLowerCase() === tenantId.toLowerCase()); + let url: string = `${session.environment.portalUrl}/${tenantId}/#resource${context.registry.id}`; + if (context.contextValue === 'azureImageNode' || context.contextValue === 'azureRepositoryNode') { + url = `${url}/repository`; + } + opn(url); + } + +} diff --git a/explorer/models/dockerHubUtils.ts b/explorer/utils/dockerHubUtils.ts similarity index 99% rename from explorer/models/dockerHubUtils.ts rename to explorer/utils/dockerHubUtils.ts index f964da1cbf..4428692e9a 100644 --- a/explorer/models/dockerHubUtils.ts +++ b/explorer/utils/dockerHubUtils.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as keytarType from 'keytar'; import * as opn from 'opn'; import request = require('request-promise'); -import { DockerHubRepositoryNode, DockerHubImageNode, DockerHubOrgNode } from './dockerHubNodes'; +import { DockerHubRepositoryNode, DockerHubImageNode, DockerHubOrgNode } from '../models/dockerHubNodes'; let _token: Token; diff --git a/explorer/models/utils.ts b/explorer/utils/utils.ts similarity index 100% rename from explorer/models/utils.ts rename to explorer/utils/utils.ts diff --git a/package.json b/package.json index f6433624c5..3c94781831 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "onCommand:vscode-docker.system.prune", "onCommand:vscode-docker.dockerHubLogout", "onCommand:vscode-docker.browseDockerHub", + "onCommand:vscode-docker.browseAzurePortal", "onView:dockerExplorer", "onDebug" ], @@ -193,7 +194,7 @@ }, { "command": "vscode-docker.createWebApp", - "when": "view == dockerExplorer && viewItem == azureImageTag" + "when": "view == dockerExplorer && viewItem == azureImageNode" }, { "command": "vscode-docker.createWebApp", @@ -214,6 +215,18 @@ { "command": "vscode-docker.browseDockerHub", "when": "view == dockerExplorer && viewItem == dockerHubNamespace" + }, + { + "command": "vscode-docker.browseAzurePortal", + "when": "view == dockerExplorer && viewItem == azureRegistryNode" + }, + { + "command": "vscode-docker.browseAzurePortal", + "when": "view == dockerExplorer && viewItem == azureRepositoryNode" + }, + { + "command": "vscode-docker.browseAzurePortal", + "when": "view == dockerExplorer && viewItem == azureImageNode" } ] }, @@ -514,6 +527,11 @@ "command": "vscode-docker.browseDockerHub", "title": "Browse in DockerHub", "category": "Docker" + }, + { + "command": "vscode-docker.browseAzurePortal", + "title": "Browse in the Azure Portal", + "category": "Docker" } ], "views": {