Skip to content

Commit

Permalink
Merge with main
Browse files Browse the repository at this point in the history
  • Loading branch information
MicroFish91 committed Dec 17, 2024
2 parents 11840be + 874ff43 commit 7f912d5
Show file tree
Hide file tree
Showing 46 changed files with 1,295 additions and 304 deletions.
18 changes: 18 additions & 0 deletions .azure-pipelines/compliance/tsaoptions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"tsaVersion": "TsaV2",
"codeBase": "NewOrUpdate",
"codeBaseName": "vscode-azurecontainerapps",
"tsaStamp": "DevDiv",
"notificationAliases": [
"[email protected]"
],
"codebaseAdmins": [
"REDMOND\\jinglou",
"REDMOND\\AzCode"
],
"instanceUrl": "https://devdiv.visualstudio.com",
"projectName": "DevDiv",
"areaPath": "DevDiv\\VS Azure Tools\\AzCode Extensions",
"iterationPath": "DevDiv",
"allTools": true
}
1 change: 1 addition & 0 deletions extension.bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export * from './src/commands/ingress/tryGetDockerfileExposePorts';
export { activate, deactivate } from './src/extension';
export * from './src/extensionVariables';
export * from './src/utils/azureClients';
export * from './src/utils/imageNameUtils';
export * from './src/utils/settingUtils';
export * from './src/utils/validateUtils';

Expand Down
51 changes: 31 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@
"title": "%containerApps.editContainerApp%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.updateImage",
"title": "%containerApps.updateImage%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.deployImageApi",
"title": "%containerApps.deployImageApi%",
Expand Down Expand Up @@ -203,6 +198,22 @@
"title": "%containerApps.openConsoleInPortal%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.editContainer",
"title": "%containerApps.editContainer%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.editContainerImage",
"title": "%containerApps.editContainerImage.title%",
"shortTitle": "%containerApps.editContainerImage.shortTitle%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.addEnvironmentVariable",
"title": "%containerApps.addEnvironmentVariable%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.editScaleRange",
"title": "%containerApps.editScaleRange%",
Expand Down Expand Up @@ -373,11 +384,6 @@
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem(.*)revisionMode:single(.*)unsavedChanges:true/i",
"group": "3@2"
},
{
"command": "containerApps.updateImage",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem(.*)revisionMode:single/i",
"group": "4@1"
},
{
"command": "containerApps.editContainerApp",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem(.*)revisionMode:single/i",
Expand Down Expand Up @@ -438,11 +444,6 @@
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionItem(.*)revisionState:active/i",
"group": "2@2"
},
{
"command": "containerApps.updateImage",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionDraft:false(.*)revisionItem/i",
"group": "3@1"
},
{
"command": "containerApps.deployRevisionDraft",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionDraftItem(.*)unsavedChanges:true/i",
Expand All @@ -463,16 +464,26 @@
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionDraftItem/i",
"group": "1@2"
},
{
"command": "containerApps.updateImage",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionDraftItem/i",
"group": "2@1"
},
{
"command": "containerApps.editRevisionDraft",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /revisionDraftItem/i",
"group": "3@1"
},
{
"command": "containerApps.editContainer",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerItem/i",
"group": "1@1"
},
{
"command": "containerApps.editContainerImage",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /imageItem/i",
"group": "1@1"
},
{
"command": "containerApps.addEnvironmentVariable",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /environmentVariablesItem/i",
"group": "1@1"
},
{
"command": "containerApps.editScaleRange",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /scaleItem/i",
Expand Down
5 changes: 4 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"containerApps.createContainerApp": "Create Container App...",
"containerApps.createContainerAppFromWorkspace": "Create Container App from Workspace...",
"containerApps.editContainerApp": "Edit Container App (Advanced)...",
"containerApps.updateImage": "Update Container Image...",
"containerApps.editContainer": "Edit Container...",
"containerApps.editContainerImage.title": "Edit Container Image...",
"containerApps.editContainerImage.shortTitle": "Edit Image...",
"containerApps.addEnvironmentVariable": "Add Environment Variable...",
"containerApps.deployImageApi": "Deploy Image to Container App (API)...",
"containerApps.deployWorkspaceProject": "Deploy Project from Workspace...",
"containerApps.deployWorkspaceProjectApi": "Deploy Project from Workspace (API)...",
Expand Down
16 changes: 16 additions & 0 deletions src/commands/editContainer/ContainerEditContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type ExecuteActivityContext } from "@microsoft/vscode-azext-utils";
import { type SetTelemetryProps } from "../../telemetry/SetTelemetryProps";
import { type ContainerUpdateTelemetryProps as TelemetryProps } from "../../telemetry/commandTelemetryProps";
import { type IContainerAppContext } from "../IContainerAppContext";
import { type ImageSourceBaseContext } from "../image/imageSource/ImageSourceContext";

export interface ContainerEditBaseContext extends IContainerAppContext, ImageSourceBaseContext, ExecuteActivityContext {
containersIdx: number;
}

export type ContainerEditContext = ContainerEditBaseContext & SetTelemetryProps<TelemetryProps>;
74 changes: 74 additions & 0 deletions src/commands/editContainer/ContainerEditDraftStep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type Container, type Revision } from "@azure/arm-appcontainers";
import { activityFailContext, activityFailIcon, activityProgressContext, activityProgressIcon, activitySuccessContext, activitySuccessIcon, createUniversallyUniqueContextValue, GenericParentTreeItem, GenericTreeItem, nonNullProp, type ExecuteActivityOutput } from "@microsoft/vscode-azext-utils";
import { type Progress } from "vscode";
import { type ContainerAppItem, type ContainerAppModel } from "../../tree/ContainerAppItem";
import { type RevisionsItemModel } from "../../tree/revisionManagement/RevisionItem";
import { localize } from "../../utils/localize";
import { getParentResourceFromItem } from "../../utils/revisionDraftUtils";
import { getContainerNameForImage } from "../image/imageSource/containerRegistry/getContainerNameForImage";
import { RevisionDraftUpdateBaseStep } from "../revisionDraft/RevisionDraftUpdateBaseStep";
import { type ContainerEditContext } from "./ContainerEditContext";

export class ContainerEditDraftStep<T extends ContainerEditContext> extends RevisionDraftUpdateBaseStep<T> {
public priority: number = 590;

constructor(baseItem: ContainerAppItem | RevisionsItemModel) {
super(baseItem);
}

public async execute(context: T, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
progress.report({ message: localize('editingContainer', 'Editing container (draft)...') });
this.revisionDraftTemplate.containers ??= [];

const container: Container = this.revisionDraftTemplate.containers[context.containersIdx] ?? {};
container.name = getContainerNameForImage(nonNullProp(context, 'image'));
container.image = context.image;
container.env = context.environmentVariables;

await this.updateRevisionDraftWithTemplate(context);
}

public shouldExecute(context: T): boolean {
return context.containersIdx !== undefined && !!context.image;
}

public createSuccessOutput(): ExecuteActivityOutput {
const parentResource: ContainerAppModel | Revision = getParentResourceFromItem(this.baseItem);
return {
item: new GenericTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['containerEditDraftStepSuccessItem', activitySuccessContext]),
label: localize('editContainer', 'Edit container profile for container app "{0}" (draft)', parentResource.name),
iconPath: activitySuccessIcon,
}),
message: localize('editContainerSuccess', 'Successfully edited container profile for container app "{0}" (draft).', parentResource.name),
};
}

public createProgressOutput(): ExecuteActivityOutput {
const parentResource: ContainerAppModel | Revision = getParentResourceFromItem(this.baseItem);
return {
item: new GenericTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['containerEditDraftStepProgressItem', activityProgressContext]),
label: localize('editContainer', 'Edit container profile for container app "{0}" (draft)', parentResource.name),
iconPath: activityProgressIcon,
}),
};
}

public createFailOutput(): ExecuteActivityOutput {
const parentResource: ContainerAppModel | Revision = getParentResourceFromItem(this.baseItem);
return {
item: new GenericParentTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['containerEditDraftStepFailItem', activityFailContext]),
label: localize('editContainer', 'Edit container profile for container app "{0}" (draft)', parentResource.name),
iconPath: activityFailIcon,
}),
message: localize('editContainerFail', 'Failed to edit container profile for container app "{0}" (draft).', parentResource.name),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { type RegistryCredentials, type Secret } from "@azure/arm-appcontainers"
import { AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils";
import * as deepEqual from "deep-eql";
import { type Progress } from "vscode";
import { ext } from "../../../extensionVariables";
import { getContainerEnvelopeWithSecrets, type ContainerAppModel } from "../../../tree/ContainerAppItem";
import { localize } from "../../../utils/localize";
import { updateContainerApp } from "../../updateContainerApp";
import { type UpdateImageContext } from "./updateImage";
import { ext } from "../../extensionVariables";
import { getContainerEnvelopeWithSecrets, type ContainerAppModel } from "../../tree/ContainerAppItem";
import { localize } from "../../utils/localize";
import { updateContainerApp } from "../updateContainerApp";
import { type ContainerEditContext } from "./ContainerEditContext";

export class UpdateRegistryAndSecretsStep extends AzureWizardExecuteStep<UpdateImageContext> {
export class RegistryAndSecretsUpdateStep<T extends ContainerEditContext> extends AzureWizardExecuteStep<T> {
public priority: number = 580;

public async execute(context: UpdateImageContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
public async execute(context: T, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const containerApp: ContainerAppModel = nonNullProp(context, 'containerApp');
const containerAppEnvelope = await getContainerEnvelopeWithSecrets(context, context.subscription, containerApp);

Expand All @@ -28,20 +28,17 @@ export class UpdateRegistryAndSecretsStep extends AzureWizardExecuteStep<UpdateI
context.telemetry.properties.skippedRegistryCredentialUpdate = 'true';
return;
}

context.telemetry.properties.skippedRegistryCredentialUpdate = 'false';

progress.report({ message: localize('configuringSecrets', 'Configuring registry secrets...') });

containerAppEnvelope.configuration.secrets = context.secrets;
containerAppEnvelope.configuration.registries = context.registryCredentials;

await updateContainerApp(context, context.subscription, containerAppEnvelope);

ext.outputChannel.appendLog(localize('updatedSecrets', 'Updated container app "{0}" with new registry secrets.', containerApp.name));
}

public shouldExecute(context: UpdateImageContext): boolean {
public shouldExecute(context: T): boolean {
return !!context.registryCredentials && !!context.secrets;
}

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

import { type Revision } from "@azure/arm-appcontainers";
import { AzureWizard, createSubscriptionContext, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { type ContainerAppModel } from "../../tree/ContainerAppItem";
import { type ContainerItem } from "../../tree/containers/ContainerItem";
import { ContainersItem } from "../../tree/containers/ContainersItem";
import { createActivityContext } from "../../utils/activityUtils";
import { getManagedEnvironmentFromContainerApp } from "../../utils/getResourceUtils";
import { getVerifyProvidersStep } from "../../utils/getVerifyProvidersStep";
import { localize } from "../../utils/localize";
import { pickContainer } from "../../utils/pickItem/pickContainer";
import { getParentResourceFromItem, isTemplateItemEditable, TemplateItemNotEditableError } from "../../utils/revisionDraftUtils";
import { ImageSourceListStep } from "../image/imageSource/ImageSourceListStep";
import { RevisionDraftDeployPromptStep } from "../revisionDraft/RevisionDraftDeployPromptStep";
import { type ContainerEditContext } from "./ContainerEditContext";
import { ContainerEditDraftStep } from "./ContainerEditDraftStep";
import { RegistryAndSecretsUpdateStep } from "./RegistryAndSecretsUpdateStep";

// Edits both the 'image' and 'environmentVariables' portion of the container profile (draft)
export async function editContainer(context: IActionContext, node?: ContainersItem | ContainerItem): Promise<void> {
const item: ContainerItem | ContainersItem = node ?? await pickContainer(context, { autoSelectDraft: true });
const { containerApp, subscription } = item;

if (!isTemplateItemEditable(item)) {
throw new TemplateItemNotEditableError(item);
}

const subscriptionContext: ISubscriptionContext = createSubscriptionContext(subscription);
const parentResource: ContainerAppModel | Revision = getParentResourceFromItem(item);

let containersIdx: number;
if (ContainersItem.isContainersItem(item)) {
// The 'editContainer' command should only show up on a 'ContainersItem' when it only has one container, else the command would show up on the 'ContainerItem'
containersIdx = 0;
} else {
containersIdx = item.containersIdx;
}

const wizardContext: ContainerEditContext = {
...context,
...subscriptionContext,
...await createActivityContext(true),
subscription,
managedEnvironment: await getManagedEnvironmentFromContainerApp({ ...context, ...subscriptionContext }, containerApp),
containerApp,
containersIdx,
};
wizardContext.telemetry.properties.revisionMode = containerApp.revisionsMode;

const wizard: AzureWizard<ContainerEditContext> = new AzureWizard(wizardContext, {
title: localize('editContainer', 'Edit container profile for "{0}" (draft)', parentResource.name),
promptSteps: [
new ImageSourceListStep(),
new RevisionDraftDeployPromptStep(),
],
executeSteps: [
getVerifyProvidersStep<ContainerEditContext>(),
new RegistryAndSecretsUpdateStep(),
new ContainerEditDraftStep(item),
],
showLoadingPrompt: true,
});

await wizard.prompt();
await wizard.execute();
}
Loading

0 comments on commit 7f912d5

Please sign in to comment.