Skip to content

Commit 35eaafd

Browse files
authored
Add Create Azure Container Registry command (#435)
* Add create Acr command * Minor changes * Requested changes * Split up name and input validation * More changes
1 parent d208636 commit 35eaafd

File tree

8 files changed

+187
-1
lines changed

8 files changed

+187
-1
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@
223223
"command": "containerApps.stopStreamingLogs",
224224
"title": "%containerApps.stopStreamingLogs%",
225225
"category": "Azure Container Apps"
226+
},
227+
{
228+
"command": "containerApps.createAcr",
229+
"title": "%containerApps.createAcr%",
230+
"category": "Azure Container Apps"
226231
}
227232
],
228233
"menus": {

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
"containerApps.disconnectRepo": "Disconnect from Repo",
4141
"containerApps.openGitHubRepo": "Open Repo in GitHub",
4242
"containerApps.startStreamingLogs": "Start Streaming Logs...",
43-
"containerApps.stopStreamingLogs": "Stop Streaming Logs..."
43+
"containerApps.stopStreamingLogs": "Stop Streaming Logs...",
44+
"containerApps.createAcr": "Create Azure Container Registry..."
4445
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { KnownSkuName, Registry } from "@azure/arm-containerregistry";
7+
import { IResourceGroupWizardContext } from "@microsoft/vscode-azext-azureutils";
8+
import { ExecuteActivityContext } from "@microsoft/vscode-azext-utils";
9+
10+
export interface CreateAcrContext extends IResourceGroupWizardContext, ExecuteActivityContext {
11+
newRegistryName?: string;
12+
newRegistrySku?: KnownSkuName;
13+
registry?: Registry;
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ContainerRegistryManagementClient } from "@azure/arm-containerregistry";
7+
import { LocationListStep } from "@microsoft/vscode-azext-azureutils";
8+
import { AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils";
9+
import { createContainerRegistryManagementClient } from "../../../../../../utils/azureClients";
10+
import { CreateAcrContext } from "./CreateAcrContext";
11+
12+
export class RegistryCreateStep extends AzureWizardExecuteStep<CreateAcrContext> {
13+
public priority: number = 150;
14+
15+
public async execute(context: CreateAcrContext): Promise<void> {
16+
const client: ContainerRegistryManagementClient = await createContainerRegistryManagementClient(context);
17+
18+
context.registry = await client.registries.beginCreateAndWait(
19+
nonNullProp(context, 'newResourceGroupName'),
20+
nonNullProp(context, 'newRegistryName'),
21+
{
22+
location: (await LocationListStep.getLocation(context)).name,
23+
sku: { name: nonNullProp(context, 'newRegistrySku') },
24+
adminUserEnabled: true
25+
}
26+
);
27+
}
28+
29+
public shouldExecute(context: CreateAcrContext): boolean {
30+
return !!context.newRegistryName && !!context.newRegistrySku;
31+
}
32+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ContainerRegistryManagementClient, RegistryNameStatus } from "@azure/arm-containerregistry";
7+
import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils";
8+
import { createContainerRegistryManagementClient } from "../../../../../../utils/azureClients";
9+
import { localize } from "../../../../../../utils/localize";
10+
import { CreateAcrContext } from "./CreateAcrContext";
11+
12+
export class RegistryNameStep extends AzureWizardPromptStep<CreateAcrContext> {
13+
public async prompt(context: CreateAcrContext): Promise<void> {
14+
context.newRegistryName = await context.ui.showInputBox({
15+
prompt: localize('registryName', 'Enter a name for the new registry'),
16+
validateInput: this.validateInput,
17+
asyncValidationTask: (value: string): Promise<string | undefined> => this.validateNameAvalability(context, value)
18+
});
19+
}
20+
21+
public shouldPrompt(context: CreateAcrContext): boolean {
22+
return !context.newRegistryName;
23+
}
24+
25+
private validateInput(name: string | undefined): string | undefined {
26+
name = name ? name.trim() : '';
27+
28+
const { minLength, maxLength } = { minLength: 5, maxLength: 50 };
29+
if (name.length < minLength || name.length > maxLength) {
30+
return localize('validationLengthError', 'The name must be between {0} and {1} characters.', minLength, maxLength);
31+
} else if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
32+
return localize('validateInputError', `Connection names can only consist of alphanumeric characters.`);
33+
}
34+
35+
return undefined;
36+
}
37+
38+
private async validateNameAvalability(context: CreateAcrContext, name: string) {
39+
const client: ContainerRegistryManagementClient = await createContainerRegistryManagementClient(context);
40+
const nameResponse: RegistryNameStatus = await client.registries.checkNameAvailability({ name: name, type: "Microsoft.ContainerRegistry/registries" });
41+
if (nameResponse.nameAvailable === false) {
42+
return localize('validateInputError', `The registry name ${name} is already in use.`);
43+
}
44+
45+
return undefined;
46+
}
47+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { KnownSkuName } from "@azure/arm-containerregistry";
7+
import { AzureWizardPromptStep, IAzureQuickPickItem } from "@microsoft/vscode-azext-utils";
8+
import { localize } from "../../../../../../utils/localize";
9+
import { CreateAcrContext } from "./CreateAcrContext";
10+
11+
export class SkuListStep extends AzureWizardPromptStep<CreateAcrContext> {
12+
public async prompt(context: CreateAcrContext): Promise<void> {
13+
const placeHolder: string = localize("sku", "Select a SKU");
14+
const picks: IAzureQuickPickItem<KnownSkuName>[] = [
15+
{ label: KnownSkuName.Basic, data: KnownSkuName.Basic },
16+
{ label: KnownSkuName.Standard, data: KnownSkuName.Standard },
17+
{ label: KnownSkuName.Premium, data: KnownSkuName.Premium },
18+
];
19+
20+
context.newRegistrySku = (await context.ui.showQuickPick(picks, {
21+
placeHolder,
22+
suppressPersistence: true
23+
})).data;
24+
}
25+
26+
public shouldPrompt(context: CreateAcrContext): boolean {
27+
return !context.newRegistrySku;
28+
}
29+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { LocationListStep, ResourceGroupCreateStep } from "@microsoft/vscode-azext-azureutils";
7+
import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, createSubscriptionContext, nonNullValue, subscriptionExperience } from "@microsoft/vscode-azext-utils";
8+
import { AzureSubscription } from "@microsoft/vscode-azureresources-api";
9+
import { ext } from "../../../../../../extensionVariables";
10+
import { createActivityContext } from "../../../../../../utils/activityUtils";
11+
import { localize } from "../../../../../../utils/localize";
12+
import { CreateAcrContext } from "./CreateAcrContext";
13+
import { RegistryCreateStep } from "./RegistryCreateStep";
14+
import { RegistryNameStep } from "./RegistryNameStep";
15+
import { SkuListStep } from "./SkuListStep";
16+
17+
export async function createAcr(context: IActionContext, node?: { subscription: AzureSubscription }): Promise<void> {
18+
const subscription = node?.subscription ?? await subscriptionExperience(context, ext.rgApiV2.resources.azureResourceTreeDataProvider);
19+
20+
const wizardContext: CreateAcrContext = {
21+
...context,
22+
...createSubscriptionContext(subscription),
23+
...(await createActivityContext())
24+
};
25+
26+
const title: string = localize('createAcr', "Create Azure Container Registry");
27+
28+
const promptSteps: AzureWizardPromptStep<CreateAcrContext>[] = [
29+
new RegistryNameStep(),
30+
new SkuListStep(),
31+
];
32+
33+
const executeSteps: AzureWizardExecuteStep<CreateAcrContext>[] = [
34+
new ResourceGroupCreateStep(),
35+
new RegistryCreateStep(),
36+
];
37+
38+
LocationListStep.addStep(wizardContext, promptSteps);
39+
40+
41+
const wizard: AzureWizard<CreateAcrContext> = new AzureWizard(wizardContext, {
42+
title,
43+
promptSteps,
44+
executeSteps,
45+
showLoadingPrompt: true
46+
});
47+
48+
await wizard.prompt();
49+
50+
wizardContext.newResourceGroupName = nonNullValue(wizardContext.newRegistryName);
51+
wizardContext.activityTitle = localize('createAcr', 'Create Azure Container Registry "{0}"', wizardContext.newRegistryName);
52+
53+
await wizard.execute();
54+
}

src/commands/registerCommands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { deleteContainerApp } from './deleteContainerApp/deleteContainerApp';
1111
import { deleteManagedEnvironment } from './deleteManagedEnvironment/deleteManagedEnvironment';
1212
import { deployImage } from './deployImage/deployImage';
1313
import { deployImageApi } from './deployImage/deployImageApi';
14+
import { createAcr } from './deployImage/imageSource/containerRegistry/acr/createAcr/createAcr';
1415
import { editContainerApp } from './editContainerApp';
1516
import { connectToGitHub } from './gitHub/connectToGitHub/connectToGitHub';
1617
import { disconnectRepo } from './gitHub/disconnectRepo/disconnectRepo';
@@ -91,4 +92,7 @@ export function registerCommands(): void {
9192
// Suppress "Report an Issue" button for all errors in favor of the command
9293
registerErrorHandler(c => c.errorHandling.suppressReportIssue = true);
9394
registerReportIssueCommand('containerApps.reportIssue');
95+
96+
// registries
97+
registerCommandWithTreeNodeUnwrapping('containerApps.createAcr', createAcr);
9498
}

0 commit comments

Comments
 (0)