Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
MicroFish91 committed Dec 15, 2022
1 parent 71f017b commit 4b3e056
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 49 deletions.
7 changes: 6 additions & 1 deletion src/commands/deployImage/IDeployImageContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export interface IDeployImageContext extends ISubscriptionActionContext {
repositoryName?: string;
tag?: string;

// fully qualified image name that will be generated by "registy login server:tag" if left undefined
// Fully qualified image name that will be generated by "registy login server:tag" if left undefined
image?: string;
environmentVariables?: EnvironmentVar[];

// Registry credentials
loginServer?: string;
username?: string;
secret?: string;
}
36 changes: 15 additions & 21 deletions src/commands/deployImage/deployImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
import type { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
import { VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { MessageItem, ProgressLocation, window } from "vscode";
import { RevisionConstants, rootFilter, webProvider } from "../../constants";
import { acrDomain, RevisionConstants, rootFilter, webProvider } from "../../constants";
import { ext } from "../../extensionVariables";
import { ContainerAppTreeItem } from "../../tree/ContainerAppTreeItem";
import { createContainerAppsAPIClient } from "../../utils/azureClients";
Expand All @@ -16,9 +16,9 @@ import { nonNullValue } from "../../utils/nonNull";
import { EnvironmentVariablesListStep } from "../createContainerApp/EnvironmentVariablesListStep";
import { getLoginServer } from "../createContainerApp/getLoginServer";
import { showContainerAppCreated } from "../createContainerApp/showContainerAppCreated";
import { listCredentialsFromRegistry } from "./acr/listCredentialsFromRegistry";
import { ContainerRegistryListStep } from "./ContainerRegistryListStep";
import { getContainerNameForImage } from "./getContainerNameForImage";
import { getAcrCredentialsAndSecrets, getThirdPartyCredentialsAndSecrets } from "./getRegistryCredentialsAndSecrets";
import { IDeployImageContext } from "./IDeployImageContext";

export async function deployImage(context: ITreeItemPickerContext & Partial<IDeployImageContext>, node?: ContainerAppTreeItem): Promise<void> {
Expand Down Expand Up @@ -58,24 +58,18 @@ export async function deployImage(context: ITreeItemPickerContext & Partial<IDep

const containerAppEnvelope = await node.getContainerEnvelopeWithSecrets(wizardContext);

// for ACR
if (wizardContext.registry) {
const registry = wizardContext.registry;
const { username, password } = await listCredentialsFromRegistry(wizardContext, registry);
const passwordName = `${wizardContext.registry.name?.toLocaleLowerCase()}-${password?.name}`;
// remove duplicate registry
containerAppEnvelope.configuration.registries = containerAppEnvelope.configuration.registries?.filter(r => r.server !== registry.loginServer);
containerAppEnvelope.configuration.registries?.push(
{
server: registry.loginServer,
username: username,
passwordSecretRef: passwordName
}
)

// remove duplicate secretRef
containerAppEnvelope.configuration.secrets = containerAppEnvelope.configuration.secrets?.filter(s => s.name !== passwordName);
containerAppEnvelope.configuration.secrets?.push({ name: passwordName, value: password.value });
if (wizardContext.registryDomain === acrDomain) {
// ACR
const acrRegistryCredentialsAndSecrets = await getAcrCredentialsAndSecrets(wizardContext, containerAppEnvelope);
containerAppEnvelope.configuration.registries = acrRegistryCredentialsAndSecrets.registries;
containerAppEnvelope.configuration.secrets = acrRegistryCredentialsAndSecrets.secrets;
} else {
// Docker Hub or other...
if (wizardContext.loginServer && wizardContext.username && wizardContext.secret) {
const thirdPartyRegistryCredentialsAndSecrets = getThirdPartyCredentialsAndSecrets(wizardContext, containerAppEnvelope);
containerAppEnvelope.configuration.registries = thirdPartyRegistryCredentialsAndSecrets.registries;
containerAppEnvelope.configuration.secrets = thirdPartyRegistryCredentialsAndSecrets.secrets;
}
}

// we want to replace the old image
Expand Down
39 changes: 23 additions & 16 deletions src/commands/deployImage/deployImageApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { SubscriptionTreeItemBase } from "@microsoft/vscode-azext-azureutils";
import { ISubscriptionContext } from "@microsoft/vscode-azext-dev";
import { IActionContext, ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
import { acrDomain, dockerHubDomain, RegistryTypes } from "../../constants";
import { acrDomain } from "../../constants";
import { ext } from "../../extensionVariables";
import { imageNameUtils } from "../../utils/parseImageNameUtils";
import { deployImage } from "./deployImage";
Expand All @@ -15,8 +15,8 @@ import { IDeployImageContext } from "./IDeployImageContext";
// The interface of the command options passed to the Azure Container Apps extension's deployImageToAca command
// This interface is shared with the Docker extension (https://github.com/microsoft/vscode-docker)
interface DeployImageToAcaOptionsContract {
imageName: string;
loginServer?: string;
imageName: string; // Todo: change `imageName` to `image` or vice versa
loginServer?: string; // Todo: verify formatting with Brandon, esp for docker hub
username?: string;
secret?: string;
}
Expand All @@ -25,21 +25,28 @@ export async function deployImageApi(context: IActionContext & Partial<IDeployIm
const subscription: ISubscriptionContext = (await ext.rgApi.appResourceTree.showTreeItemPicker<SubscriptionTreeItemBase>(SubscriptionTreeItemBase.contextValue, context)).subscription;
Object.assign(context, subscription);

const registryType: RegistryTypes = imageNameUtils.detectRegistryType(deployImageOptions.imageName);

switch (registryType) {
case RegistryTypes.ACR:
context.registryDomain = acrDomain;
context.registry = await imageNameUtils.getRegistryFromAcrName(<ISubscriptionActionContext>context, deployImageOptions.imageName);
break;
case RegistryTypes.DH:
context.registryDomain = dockerHubDomain;
break;
case RegistryTypes.Custom:
default:
// test case for docker hub, will remove later
deployImageOptions.imageName = 'docker.io/' + deployImageOptions.imageName;
deployImageOptions.loginServer = 'index.docker.io';

context.registryDomain = imageNameUtils.detectRegistryDomain(deployImageOptions.imageName);
if (context.registryDomain === acrDomain) {
context.registry = await imageNameUtils.getRegistryFromAcrName(<ISubscriptionActionContext>context, deployImageOptions.imageName);
}

context.image = deployImageOptions.imageName;
// Todo: change contract from imageName to image (or vice versa) and just run `Object.assign(context, deployImageOptions)`
Object.assign(context, {
image: deployImageOptions.imageName,
loginServer: deployImageOptions.loginServer,
username: deployImageOptions.username,
secret: deployImageOptions.secret
});


// Todo: Check how to handle username and secret masking
if (context.secret) {
context.valuesToMask.push(context.secret);
}
context.valuesToMask.push(<string>context.image);

return deployImage(context, undefined);
Expand Down
68 changes: 68 additions & 0 deletions src/commands/deployImage/getRegistryCredentialsAndSecrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { ContainerApp, RegistryCredentials, Secret } from "@azure/arm-appcontainers";
import { nonNullProp } from "@microsoft/vscode-azext-utils";
import { listCredentialsFromRegistry } from "./acr/listCredentialsFromRegistry";
import { IDeployImageContext } from "./IDeployImageContext";

export interface IRegistryCredentialsAndSecrets {
registries: RegistryCredentials[] | undefined;
secrets: Secret[] | undefined;
}

export async function getAcrCredentialsAndSecrets(context: IDeployImageContext, containerAppEnvelope: Required<ContainerApp>): Promise<IRegistryCredentialsAndSecrets> {
const registry = nonNullProp(context, 'registry');
const { username, password } = await listCredentialsFromRegistry(context, registry);
const passwordName = `${registry.name?.toLocaleLowerCase()}-${password?.name}`;

// Remove duplicate registries
const registries: RegistryCredentials[] | undefined = containerAppEnvelope.configuration.registries?.filter(r => r.server !== registry.loginServer);
registries?.push(
{
server: registry.loginServer,
username: username,
passwordSecretRef: passwordName
}
);

// Remove duplicate secrets
const secrets: Secret[] | undefined = containerAppEnvelope.configuration.secrets?.filter(s => s.name !== passwordName);
secrets?.push({ name: passwordName, value: password.value });

return { registries, secrets };
}

export function getThirdPartyCredentialsAndSecrets(context: IDeployImageContext, containerAppEnvelope: Required<ContainerApp>): IRegistryCredentialsAndSecrets {
const loginServer: string = getAcaCompliantLoginServer(nonNullProp(context, 'loginServer'));
const passwordSecretRef: string = `${loginServer.replace(/[\.]+/g, '')}-${context.username}`;

// Remove duplicate registries
const registries: RegistryCredentials[] | undefined = containerAppEnvelope.configuration.registries?.filter(r => r.server !== loginServer);
registries?.push(
{
server: loginServer,
username: context.username,
passwordSecretRef
}
);

// Remove duplicate secrets
const secrets: Secret[] | undefined = containerAppEnvelope.configuration.secrets?.filter(s => s.name !== passwordSecretRef);
secrets?.push(
{
name: passwordSecretRef,
value: context.secret
}
);

return { registries, secrets };
}

// Todo: Revisit and test with different login server formats
// Do we need a fallback for docker hub? 'index.docker.io'
function getAcaCompliantLoginServer(loginServer: string): string {
return loginServer;
}
6 changes: 0 additions & 6 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ export const dockerHubDomainRegExp = new RegExp(dockerHubDomain, 'i');

export type SupportedRegistries = 'azurecr.io' | 'docker.io';

export enum RegistryTypes {
ACR = 'Azure Container Registry',
DH = 'Docker Hub',
Custom = 'Custom'
}

export const loadMoreQp: IAzureQuickPickItem = { label: '$(sync) Load More', data: undefined, suppressPersistence: true };
export type QuickPicksCache = { cache: QuickPickItem[], next: string | null };

Expand Down
10 changes: 5 additions & 5 deletions src/utils/parseImageNameUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@

import type { ContainerRegistryManagementClient, ContainerRegistryManagementModels } from "@azure/arm-containerregistry";
import { ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
import { acrDomainRegExp, dockerHubDomainRegExp, RegistryTypes } from "../constants";
import { acrDomain, acrDomainRegExp, dockerHubDomain, dockerHubDomainRegExp, SupportedRegistries } from "../constants";
import { createContainerRegistryManagementClient } from "./azureClients";
import { localize } from "./localize";

export namespace imageNameUtils {
export function detectRegistryType(imageName: string): RegistryTypes {
export function detectRegistryDomain(imageName: string): SupportedRegistries | undefined {
if (acrDomainRegExp.test(imageName)) {
return RegistryTypes.ACR;
return acrDomain;
} else if (dockerHubDomainRegExp.test(imageName)) {
return RegistryTypes.DH;
return dockerHubDomain;
} else {
return RegistryTypes.Custom;
return undefined;
}
}

Expand Down

0 comments on commit 4b3e056

Please sign in to comment.