diff --git a/src/commands/api/CHANGELOG.md b/src/commands/api/CHANGELOG.md index 38148abc9..190c5d386 100644 --- a/src/commands/api/CHANGELOG.md +++ b/src/commands/api/CHANGELOG.md @@ -2,6 +2,9 @@ ### 0.0.3 +### Added +* [[817]](https://github.com/microsoft/vscode-azurecontainerapps/pull/817) Added an API entry-point and compat wrapper for existing `deployImageApi` command + ### Changed * [[816]](https://github.com/microsoft/vscode-azurecontainerapps/pull/816) Added backward compatibility to ensure existing functionality remains unaffected by new managed identity features. diff --git a/src/commands/api/deployImageApi.ts b/src/commands/api/deployImageApi.ts new file mode 100644 index 000000000..be83c7247 --- /dev/null +++ b/src/commands/api/deployImageApi.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { callWithMaskHandling, callWithTelemetryAndErrorHandling, createSubscriptionContext, type ExecuteActivityContext, type IActionContext, type ISubscriptionActionContext } from "@microsoft/vscode-azext-utils"; +import { ImageSource, acrDomain } from "../../constants"; +import { type DeployImageApiTelemetryProps as TelemetryProps } from "../../telemetry/commandTelemetryProps"; +import { type SetTelemetryProps } from "../../telemetry/SetTelemetryProps"; +import { getDomainFromRegistryName, getRegistryFromAcrName } from "../../utils/imageNameUtils"; +import { pickContainer } from "../../utils/pickItem/pickContainer"; +import { deployImage } from "../deployImage/deployImage"; +import { type ContainerRegistryImageSourceContext } from "../image/imageSource/containerRegistry/ContainerRegistryImageSourceContext"; +import { type ImageSourceBaseContext } from "../image/imageSource/ImageSourceContext"; +import { type DeployImageToAcaOptionsContract } from "./vscode-azurecontainerapps.api"; + +export type DeployImageApiContext = ImageSourceBaseContext & ExecuteActivityContext & SetTelemetryProps; + +export async function deployImageApi(deployImageOptions: DeployImageToAcaOptionsContract): Promise { + return await callWithTelemetryAndErrorHandling('containerApps.api.deployImage', async (context: IActionContext & Partial) => { + const node = await pickContainer(context); + const { subscription } = node; + + Object.assign(context, { ...createSubscriptionContext(subscription), imageSource: ImageSource.ContainerRegistry }, deployImageOptions); + + context.registryDomain = getDomainFromRegistryName(deployImageOptions.registryName); + if (context.registryDomain === acrDomain) { + context.registry = await getRegistryFromAcrName(context, deployImageOptions.registryName); + } + + // Mask sensitive data + if (deployImageOptions.secret) { + context.valuesToMask.push(deployImageOptions.secret); + } + if (deployImageOptions.username) { + context.valuesToMask.push(deployImageOptions.username); + } + context.valuesToMask.push(deployImageOptions.image); + + if (deployImageOptions.secret) { + context.telemetry.properties.hasRegistrySecrets = 'true'; + return callWithMaskHandling(() => deployImage(context, node), deployImageOptions.secret); + } else { + context.telemetry.properties.hasRegistrySecrets = 'false'; + return deployImage(context, node); + } + }); +} + +/** + * A compatibility wrapper for the legacy entrypoint utilizing `deployImageApi` + */ +export async function deployImageApiCompat(_: IActionContext & Partial, deployImageOptions: DeployImageToAcaOptionsContract): Promise { + return await deployImageApi(deployImageOptions); +} diff --git a/src/commands/api/getAzureContainerAppsApiProvider.ts b/src/commands/api/getAzureContainerAppsApiProvider.ts index 8d39da0ac..7e127d804 100644 --- a/src/commands/api/getAzureContainerAppsApiProvider.ts +++ b/src/commands/api/getAzureContainerAppsApiProvider.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createApiProvider, type apiUtils } from "@microsoft/vscode-azext-utils"; +import { deployImageApi } from "./deployImageApi"; import { deployWorkspaceProjectApi } from "./deployWorkspaceProjectApi"; import type * as api from "./vscode-azurecontainerapps.api"; @@ -12,6 +13,7 @@ export function getAzureContainerAppsApiProvider(): apiUtils.AzureExtensionApiPr // Todo: Change this to 0.0.3 later. 0.0.3 is backwards compatible anyway so this change should be fine either way. // For some reason it's causing a block on Function side, so just keep it at 0.0.1 until we figure out why apiVersion: '0.0.1', - deployWorkspaceProject: deployWorkspaceProjectApi + deployImage: deployImageApi, + deployWorkspaceProject: deployWorkspaceProjectApi, }]); } diff --git a/src/commands/api/vscode-azurecontainerapps.api.d.ts b/src/commands/api/vscode-azurecontainerapps.api.d.ts index 87d11f6ab..ecde702df 100644 --- a/src/commands/api/vscode-azurecontainerapps.api.d.ts +++ b/src/commands/api/vscode-azurecontainerapps.api.d.ts @@ -8,6 +8,15 @@ export interface AzureContainerAppsExtensionApi { deployWorkspaceProject(options: DeployWorkspaceProjectOptionsContract): Promise; } +// 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) +export interface DeployImageToAcaOptionsContract { + image: string; + registryName: string; + username?: string; + secret?: string; +} + export interface DeployWorkspaceProjectOptionsContract { // Existing resources subscriptionId?: string; diff --git a/src/commands/deployImage/DeployImageContext.ts b/src/commands/deployImage/DeployImageContext.ts new file mode 100644 index 000000000..ebc2fbb29 --- /dev/null +++ b/src/commands/deployImage/DeployImageContext.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.md in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { type Template } from "@azure/arm-appcontainers"; +import { type DeployImageApiContext } from "../api/deployImageApi"; + +export interface DeployImageContext extends DeployImageApiContext { + containersIdx: number; + template: Template; +} diff --git a/src/commands/deployImage/deployImage.ts b/src/commands/deployImage/deployImage.ts new file mode 100644 index 000000000..dcb5cb9b0 --- /dev/null +++ b/src/commands/deployImage/deployImage.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.md in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { type ResourceGroup } from "@azure/arm-resources"; +import { LocationListStep, ResourceGroupListStep } from "@microsoft/vscode-azext-azureutils"; +import { activityInfoIcon, activitySuccessContext, AzureWizard, createSubscriptionContext, createUniversallyUniqueContextValue, GenericTreeItem, nonNullValue, nonNullValueAndProp, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils"; +import { ext } from "../../extensionVariables"; +import { type ContainerItem } from "../../tree/containers/ContainerItem"; +import { createActivityContext } from "../../utils/activityUtils"; +import { isAzdExtensionInstalled } from "../../utils/azdUtils"; +import { getManagedEnvironmentFromContainerApp } from "../../utils/getResourceUtils"; +import { getVerifyProvidersStep } from "../../utils/getVerifyProvidersStep"; +import { localize } from "../../utils/localize"; +import { getParentResource } from "../../utils/revisionDraftUtils"; +import { ContainerAppOverwriteConfirmStep } from "../ContainerAppOverwriteConfirmStep"; +import { showContainerAppNotification } from "../createContainerApp/showContainerAppNotification"; +import { ContainerAppUpdateStep } from "../image/imageSource/ContainerAppUpdateStep"; +import { ImageSourceListStep } from "../image/imageSource/ImageSourceListStep"; +import { type ContainerRegistryImageSourceContext } from "../image/imageSource/containerRegistry/ContainerRegistryImageSourceContext"; +import { type DeployImageContext } from "./DeployImageContext"; + +export async function deployImage(context: IActionContext & Partial, node: ContainerItem): Promise { + const { subscription, containerApp } = node; + const subscriptionContext: ISubscriptionContext = createSubscriptionContext(subscription); + + const wizardContext: DeployImageContext = { + ...context, + ...subscriptionContext, + ...await createActivityContext(true), + subscription, + managedEnvironment: await getManagedEnvironmentFromContainerApp({ ...context, ...subscriptionContext }, containerApp), + containerApp, + containersIdx: node.containersIdx, + template: nonNullValueAndProp(getParentResource(containerApp, node.revision), 'template'), + }; + + if (isAzdExtensionInstalled()) { + wizardContext.telemetry.properties.isAzdExtensionInstalled = 'true'; + } + + const resourceGroups: ResourceGroup[] = await ResourceGroupListStep.getResourceGroups(wizardContext); + wizardContext.resourceGroup = nonNullValue( + resourceGroups.find(rg => rg.name === containerApp.resourceGroup), + localize('containerAppResourceGroup', 'Expected to find the container app\'s resource group.'), + ); + + // Log resource group + wizardContext.activityChildren?.push( + new GenericTreeItem(undefined, { + contextValue: createUniversallyUniqueContextValue(['useExistingResourceGroupInfoItem', activitySuccessContext]), + label: localize('useResourceGroup', 'Using resource group "{0}"', wizardContext.resourceGroup.name), + iconPath: activityInfoIcon + }) + ); + ext.outputChannel.appendLog(localize('usingResourceGroup', 'Using resource group "{0}".', wizardContext.resourceGroup.name)); + + // Log container app + wizardContext.activityChildren?.push( + new GenericTreeItem(undefined, { + contextValue: createUniversallyUniqueContextValue(['useExistingContainerAppInfoItem', activitySuccessContext]), + label: localize('useContainerApp', 'Using container app "{0}"', wizardContext.containerApp?.name), + iconPath: activityInfoIcon + }) + ); + ext.outputChannel.appendLog(localize('usingContainerApp', 'Using container app "{0}".', wizardContext.containerApp?.name)); + + await LocationListStep.setLocation(wizardContext, containerApp.location); + + const parentResourceName: string = getParentResource(containerApp, node.revision).name ?? containerApp.name; + const wizard: AzureWizard = new AzureWizard(wizardContext, { + title: localize('deployImageTitle', 'Deploy image to "{0}"', parentResourceName), + promptSteps: [ + new ImageSourceListStep(), + new ContainerAppOverwriteConfirmStep(), + ], + executeSteps: [ + getVerifyProvidersStep(), + new ContainerAppUpdateStep(), + ], + showLoadingPrompt: true + }); + + await wizard.prompt(); + await wizard.execute(); + + if (!wizardContext.suppressNotification) { + void showContainerAppNotification(containerApp, true /** isUpdate */); + } +} diff --git a/src/commands/image/deployImageApi/deployImage.ts b/src/commands/image/deployImageApi/deployImage.ts deleted file mode 100644 index ceb03288f..000000000 --- a/src/commands/image/deployImageApi/deployImage.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.md in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import { AzureWizard, createSubscriptionContext, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils"; -import { type ContainerAppItem } from "../../../tree/ContainerAppItem"; -import { createActivityContext } from "../../../utils/activityUtils"; -import { getManagedEnvironmentFromContainerApp } from "../../../utils/getResourceUtils"; -import { getVerifyProvidersStep } from "../../../utils/getVerifyProvidersStep"; -import { localize } from "../../../utils/localize"; -import { ContainerAppOverwriteConfirmStep } from "../../ContainerAppOverwriteConfirmStep"; -import { showContainerAppNotification } from "../../createContainerApp/showContainerAppNotification"; -import { ContainerAppUpdateStep } from "../imageSource/ContainerAppUpdateStep"; -import { ImageSourceListStep } from "../imageSource/ImageSourceListStep"; -import { type ContainerRegistryImageSourceContext } from "../imageSource/containerRegistry/ContainerRegistryImageSourceContext"; -import { type DeployImageApiContext } from "./deployImageApi"; - -export async function deployImage(context: IActionContext & Partial, node: ContainerAppItem): Promise { - const { subscription, containerApp } = node; - const subscriptionContext: ISubscriptionContext = createSubscriptionContext(subscription); - - const wizardContext: DeployImageApiContext = { - ...context, - ...subscriptionContext, - ...await createActivityContext(), - subscription, - managedEnvironment: await getManagedEnvironmentFromContainerApp({ ...context, ...subscriptionContext }, containerApp), - containerApp, - }; - - wizardContext.telemetry.properties.revisionMode = containerApp.revisionsMode; - - const wizard: AzureWizard = new AzureWizard(wizardContext, { - title: localize('deploy', 'Deploy image to container app "{0}"', containerApp.name), - promptSteps: [ - new ImageSourceListStep(), - new ContainerAppOverwriteConfirmStep(), - ], - executeSteps: [ - getVerifyProvidersStep(), - new ContainerAppUpdateStep(), - ], - showLoadingPrompt: true - }); - - await wizard.prompt(); - await wizard.execute(); - - if (!wizardContext.suppressNotification) { - void showContainerAppNotification(containerApp, true /** isUpdate */); - } -} diff --git a/src/commands/image/deployImageApi/deployImageApi.ts b/src/commands/image/deployImageApi/deployImageApi.ts deleted file mode 100644 index 75ab0210e..000000000 --- a/src/commands/image/deployImageApi/deployImageApi.ts +++ /dev/null @@ -1,59 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { callWithMaskHandling, createSubscriptionContext, type ExecuteActivityContext, type IActionContext, type ISubscriptionActionContext } from "@microsoft/vscode-azext-utils"; -import { ImageSource, acrDomain } from "../../../constants"; -import { type SetTelemetryProps } from "../../../telemetry/SetTelemetryProps"; -import { type DeployImageApiTelemetryProps as TelemetryProps } from "../../../telemetry/commandTelemetryProps"; -import { getDomainFromRegistryName, getRegistryFromAcrName } from "../../../utils/imageNameUtils"; -import { pickContainerApp } from "../../../utils/pickItem/pickContainerApp"; -import { type ImageSourceBaseContext } from "../imageSource/ImageSourceContext"; -import { type ContainerRegistryImageSourceContext } from "../imageSource/containerRegistry/ContainerRegistryImageSourceContext"; -import { deployImage } from "./deployImage"; - -// 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 { - image: string; - registryName: string; - username?: string; - secret?: string; -} - -export type DeployImageApiContext = ImageSourceBaseContext & ExecuteActivityContext & SetTelemetryProps; - -/** - * A command shared with the `vscode-docker` extension. - * It uses our old `deployImage` command flow which immediately tries to deploy the image to a container app without creating a draft. - * This command cannot be used to bundle template changes. - */ -export async function deployImageApi(context: IActionContext & Partial, deployImageOptions: DeployImageToAcaOptionsContract): Promise { - const node = await pickContainerApp(context); - const { subscription } = node; - - Object.assign(context, { ...createSubscriptionContext(subscription), imageSource: ImageSource.ContainerRegistry }, deployImageOptions); - - context.registryDomain = getDomainFromRegistryName(deployImageOptions.registryName); - if (context.registryDomain === acrDomain) { - context.registry = await getRegistryFromAcrName(context, deployImageOptions.registryName); - } - - // Mask sensitive data - if (deployImageOptions.secret) { - context.valuesToMask.push(deployImageOptions.secret); - } - if (deployImageOptions.username) { - context.valuesToMask.push(deployImageOptions.username); - } - context.valuesToMask.push(deployImageOptions.image); - - if (deployImageOptions.secret) { - context.telemetry.properties.hasRegistrySecrets = 'true'; - return callWithMaskHandling(() => deployImage(context, node), deployImageOptions.secret); - } else { - context.telemetry.properties.hasRegistrySecrets = 'false'; - return deployImage(context, node); - } -} diff --git a/src/commands/image/imageSource/ContainerAppUpdateStep.ts b/src/commands/image/imageSource/ContainerAppUpdateStep.ts index 274da923a..3939843ce 100644 --- a/src/commands/image/imageSource/ContainerAppUpdateStep.ts +++ b/src/commands/image/imageSource/ContainerAppUpdateStep.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { type Ingress } from "@azure/arm-appcontainers"; +import { type Container, type Ingress } from "@azure/arm-appcontainers"; import { activityFailContext, activityFailIcon, activitySuccessContext, activitySuccessIcon, AzureWizardExecuteStep, createUniversallyUniqueContextValue, GenericParentTreeItem, GenericTreeItem, nonNullProp, type ExecuteActivityOutput } from "@microsoft/vscode-azext-utils"; import * as retry from "p-retry"; import { type Progress } from "vscode"; @@ -57,15 +57,19 @@ export class ContainerAppUpdateStep extends AzureWizardPr } public async prompt(context: T): Promise { - const existingData = context.containerApp?.template?.containers?.[context.containersIdx ?? 0].env; + const existingData = context.template?.containers?.[context.containersIdx ?? 0].env ?? context.containerApp?.template?.containers?.[context.containersIdx ?? 0].env; context.envPath ??= await this.promptForEnvPath(context, !!existingData /** showHasExistingData */); if (!context.envPath && existingData) { diff --git a/src/commands/image/imageSource/ImageSourceContext.ts b/src/commands/image/imageSource/ImageSourceContext.ts index 8af0e2967..66a91f4af 100644 --- a/src/commands/image/imageSource/ImageSourceContext.ts +++ b/src/commands/image/imageSource/ImageSourceContext.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { type EnvironmentVar } from "@azure/arm-appcontainers"; +import { type EnvironmentVar, type Template } from "@azure/arm-appcontainers"; import { type ExecuteActivityContext } from "@microsoft/vscode-azext-utils"; import { type ImageSource } from "../../../constants"; import { type ImageSourceTelemetryProps as TelemetryProps } from "../../../telemetry/ImageSourceTelemetryProps"; @@ -12,6 +12,10 @@ import { type IContainerAppContext } from "../../IContainerAppContext"; import { type RegistryCredentialsContext } from "../../registryCredentials/RegistryCredentialsContext"; export interface ImageSourceBaseContext extends RegistryCredentialsContext, IContainerAppContext, ExecuteActivityContext { + // If a template is provided, prioritize data from this, else prioritize the latest container app template + // Todo: In the future, we should migrate to always requiring a template be supplied in advance by individual commands + template?: Template; + // ImageSourceListStep imageSource?: ImageSource; showQuickStartImage?: boolean; diff --git a/src/commands/image/imageSource/containerRegistry/ContainerRegistryImageConfigureStep.ts b/src/commands/image/imageSource/containerRegistry/ContainerRegistryImageConfigureStep.ts index c78ca880b..b4f21e4f1 100644 --- a/src/commands/image/imageSource/containerRegistry/ContainerRegistryImageConfigureStep.ts +++ b/src/commands/image/imageSource/containerRegistry/ContainerRegistryImageConfigureStep.ts @@ -46,7 +46,8 @@ export class ContainerRegistryImageConfigureStep { +export class DockerLoginRegistryCredentialsAddConfigurationStep extends AzureWizardActivityOutputExecuteStep { public priority: number = 470; + public stepName: string = 'dockerLoginRegistryCredentialsAddConfigurationStep'; + protected getSuccessString = (context: T) => localize('createRegistryCredentialSuccess', 'Successfully added registry credential for "{0}" (Docker login).', context.newRegistryCredential?.server); + protected getFailString = () => localize('createRegistryCredentialFail', 'Failed to add registry credential (Docker login).'); + protected getTreeItemLabel = (context: T) => localize('createRegistryCredentialLabel', 'Add registry credential for "{0}" (Docker login)', context.newRegistryCredential?.server); constructor(private readonly supportedRegistryDomain: SupportedRegistries | undefined) { super(); } - public async execute(context: DockerLoginRegistryCredentialsContext): Promise { + public async execute(context: T): Promise { if (this.supportedRegistryDomain === acrDomain) { // ACR const acrRegistryCredentialAndSecret: RegistryCredentialAndSecret = await this.getAcrCredentialAndSecret(context); @@ -37,11 +43,11 @@ export class DockerLoginRegistryCredentialsAddConfigurationStep extends AzureWiz } } - public shouldExecute(context: DockerLoginRegistryCredentialsContext): boolean { + public shouldExecute(context: T): boolean { return !context.newRegistryCredential || !context.newRegistrySecret; } - private async getAcrCredentialAndSecret(context: DockerLoginRegistryCredentialsContext): Promise { + private async getAcrCredentialAndSecret(context: T): Promise { const registry = nonNullProp(context, 'registry'); const { username, password } = await listCredentialsFromAcr(context); const passwordName = `${registry.name?.toLocaleLowerCase()}-${password?.name}`; @@ -60,7 +66,7 @@ export class DockerLoginRegistryCredentialsAddConfigurationStep extends AzureWiz }; } - private getThirdPartyRegistryCredentialAndSecret(context: DockerLoginRegistryCredentialsContext): RegistryCredentialAndSecret { + private getThirdPartyRegistryCredentialAndSecret(context: T): RegistryCredentialAndSecret { // If 'docker.io', convert to 'index.docker.io', else use registryName as loginServer const loginServer: string = DockerLoginRegistryCredentialsAddConfigurationStep.getThirdPartyLoginServer( this.supportedRegistryDomain as typeof dockerHubDomain | undefined, diff --git a/src/commands/registryCredentials/identity/ManagedIdentityRegistryCredentialsAddConfigurationStep.ts b/src/commands/registryCredentials/identity/ManagedIdentityRegistryCredentialsAddConfigurationStep.ts index 4af0fe11d..792d2a827 100644 --- a/src/commands/registryCredentials/identity/ManagedIdentityRegistryCredentialsAddConfigurationStep.ts +++ b/src/commands/registryCredentials/identity/ManagedIdentityRegistryCredentialsAddConfigurationStep.ts @@ -3,19 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils"; +import { nonNullProp } from "@microsoft/vscode-azext-utils"; import { acrDomain, type SupportedRegistries } from "../../../constants"; import { localize } from "../../../utils/localize"; +import { AzureWizardActivityOutputExecuteStep } from "../../AzureWizardActivityOutputExecuteStep"; import { type ManagedIdentityRegistryCredentialsContext } from "./ManagedIdentityRegistryCredentialsContext"; -export class ManagedIdentityRegistryCredentialsAddConfigurationStep extends AzureWizardExecuteStep { +export class ManagedIdentityRegistryCredentialsAddConfigurationStep extends AzureWizardActivityOutputExecuteStep { public priority: number = 470; + public stepName: string = 'managedIdentityRegistryCredentialsAddConfigurationStep'; + protected getSuccessString = (context: T) => localize('createRegistryCredentialSuccess', 'Successfully added registry credential for "{0}" (system-assigned identity).', context.newRegistryCredential?.server); + protected getFailString = () => localize('createRegistryCredentialFail', 'Failed to add registry credential (system-assigned identity).'); + protected getTreeItemLabel = (context: T) => localize('createRegistryCredentialLabel', 'Add registry credential for "{0}" (system-assigned identity)', context.newRegistryCredential?.server); constructor(private readonly supportedRegistryDomain: SupportedRegistries | undefined) { super(); } - public async execute(context: ManagedIdentityRegistryCredentialsContext): Promise { + public async execute(context: T): Promise { if (this.supportedRegistryDomain !== acrDomain) { throw new Error(localize('domainNotSupported', 'The provided registry domain does not have managed identity connection support.')); } @@ -29,7 +34,7 @@ export class ManagedIdentityRegistryCredentialsAddConfigurationStep extends Azur }; } - public shouldExecute(context: ManagedIdentityRegistryCredentialsContext): boolean { + public shouldExecute(context: T): boolean { return !context.newRegistryCredential; } }