Skip to content

Commit c170a85

Browse files
authored
Improve registry detection logic (#752)
1 parent 21a6074 commit c170a85

File tree

6 files changed

+54
-24
lines changed

6 files changed

+54
-24
lines changed

src/commands/image/deployImageApi/deployImageApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { callWithMaskHandling, createSubscriptionContext, type ExecuteActivityCo
77
import { ImageSource, acrDomain } from "../../../constants";
88
import { type SetTelemetryProps } from "../../../telemetry/SetTelemetryProps";
99
import { type DeployImageApiTelemetryProps as TelemetryProps } from "../../../telemetry/commandTelemetryProps";
10-
import { detectRegistryDomain, getRegistryFromAcrName } from "../../../utils/imageNameUtils";
10+
import { getDomainFromRegistryName, getRegistryFromAcrName } from "../../../utils/imageNameUtils";
1111
import { pickContainerApp } from "../../../utils/pickItem/pickContainerApp";
1212
import { type ImageSourceBaseContext } from "../imageSource/ImageSourceContext";
1313
import { type ContainerRegistryImageSourceContext } from "../imageSource/containerRegistry/ContainerRegistryImageSourceContext";
@@ -35,7 +35,7 @@ export async function deployImageApi(context: IActionContext & Partial<Container
3535

3636
Object.assign(context, { ...createSubscriptionContext(subscription), imageSource: ImageSource.ContainerRegistry }, deployImageOptions);
3737

38-
context.registryDomain = detectRegistryDomain(deployImageOptions.registryName);
38+
context.registryDomain = getDomainFromRegistryName(deployImageOptions.registryName);
3939
if (context.registryDomain === acrDomain) {
4040
context.registry = await getRegistryFromAcrName(<ISubscriptionActionContext>context, deployImageOptions.registryName);
4141
}

src/commands/registryCredentials/RegistryCredentialsAddConfigurationListStep.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { AzureWizardPromptStep, nonNullProp, type AzureWizardExecuteStep, type IAzureQuickPickItem, type IWizardOptions } from "@microsoft/vscode-azext-utils";
77
import { acrDomain, type SupportedRegistries } from "../../constants";
8-
import { detectRegistryDomain } from "../../utils/imageNameUtils";
8+
import { getRegistryDomainFromContext } from "../../utils/imageNameUtils";
99
import { localize } from "../../utils/localize";
1010
import { AcrEnableAdminUserConfirmStep } from "./dockerLogin/AcrEnableAdminUserConfirmStep";
1111
import { AcrEnableAdminUserStep } from "./dockerLogin/AcrEnableAdminUserStep";
@@ -23,24 +23,34 @@ export enum RegistryCredentialType {
2323
}
2424

2525
export class RegistryCredentialsAddConfigurationListStep extends AzureWizardPromptStep<RegistryCredentialsContext> {
26-
private hasExistingRegistry?: boolean;
26+
private requiresRegistryConfiguration: boolean;
2727

2828
public async configureBeforePrompt(context: RegistryCredentialsContext): Promise<void> {
29-
this.hasExistingRegistry = !!context.containerApp?.configuration?.registries?.some(r => {
29+
const registryDomain: SupportedRegistries | undefined = getRegistryDomainFromContext(context);
30+
const hasExistingConfiguration: boolean = !!context.containerApp?.configuration?.registries?.some(r => {
3031
if (!r.server) {
3132
return false;
3233
}
3334

34-
const registryDomain: SupportedRegistries | undefined = this.getRegistryDomain(context);
3535
if (registryDomain === acrDomain) {
3636
return r.server === context.registry?.loginServer;
37-
} else {
37+
} else if (context.registryName) {
3838
return r.server === DockerLoginRegistryCredentialsAddConfigurationStep.getThirdPartyLoginServer(
3939
registryDomain,
4040
nonNullProp(context, 'registryName'),
4141
);
4242
}
43-
})
43+
44+
// At this point, there is the small possibility of an existing configuration existing that we don't have
45+
// enough information to match. This edge case usually happens for public repositories that are under the same
46+
// private account configuration. The good news is that these public repositories don't actually need a configuration
47+
// to be registered because they are public, so the 'requiresRegistryConfiguration' check should be sufficient to handle these cases.
48+
return false;
49+
});
50+
51+
// Rule 1: If we're configuring a new ACR and don't have an existing configuration, we need to create one
52+
// Rule 2: If we're configuring a new third party registry and don't have an existing configuration -- only do so if it's not a public repository. We can detect this with the registryName, username, and secret
53+
this.requiresRegistryConfiguration = (registryDomain === acrDomain || (!!context.registryName && !!context.username && !!context.secret)) && !hasExistingConfiguration;
4454
}
4555

4656
public async prompt(context: RegistryCredentialsContext): Promise<void> {
@@ -51,14 +61,14 @@ export class RegistryCredentialsAddConfigurationListStep extends AzureWizardProm
5161
}
5262

5363
public shouldPrompt(context: RegistryCredentialsContext): boolean {
54-
return !this.hasExistingRegistry && !context.newRegistryCredentialType;
64+
return this.requiresRegistryConfiguration && !context.newRegistryCredentialType;
5565
}
5666

5767
public async getSubWizard(context: RegistryCredentialsContext): Promise<IWizardOptions<RegistryCredentialsContext> | undefined> {
5868
const promptSteps: AzureWizardPromptStep<RegistryCredentialsContext>[] = [];
5969
const executeSteps: AzureWizardExecuteStep<RegistryCredentialsContext>[] = [];
6070

61-
const registryDomain: SupportedRegistries | undefined = this.getRegistryDomain(context);
71+
const registryDomain: SupportedRegistries | undefined = getRegistryDomainFromContext(context);
6272
switch (context.newRegistryCredentialType) {
6373
case RegistryCredentialType.SystemAssigned:
6474
executeSteps.push(
@@ -86,18 +96,11 @@ export class RegistryCredentialsAddConfigurationListStep extends AzureWizardProm
8696
};
8797
}
8898

89-
private getRegistryDomain(context: RegistryCredentialsContext): SupportedRegistries | undefined {
90-
if (context.registry?.loginServer || context.registryName) {
91-
return detectRegistryDomain(context.registry?.loginServer || nonNullProp(context, 'registryName'));
92-
} else {
93-
// If no registries exist, we can assume we're creating a new ACR
94-
return acrDomain;
95-
}
96-
}
99+
97100

98101
public async getPicks(context: RegistryCredentialsContext): Promise<IAzureQuickPickItem<RegistryCredentialType>[]> {
99102
const picks: IAzureQuickPickItem<RegistryCredentialType>[] = [];
100-
const registryDomain = this.getRegistryDomain(context);
103+
const registryDomain = getRegistryDomainFromContext(context);
101104

102105
if (registryDomain === acrDomain) {
103106
picks.push({

src/commands/registryCredentials/dockerLogin/AcrEnableAdminUserConfirmStep.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils";
7+
import { acrDomain } from "../../../constants";
8+
import { getRegistryDomainFromContext } from "../../../utils/imageNameUtils";
79
import { localize } from "../../../utils/localize";
810
import { type DockerLoginRegistryCredentialsContext } from "./DockerLoginRegistryCredentialsContext";
911

@@ -14,7 +16,6 @@ export class AcrEnableAdminUserConfirmStep extends AzureWizardPromptStep<DockerL
1416
}
1517

1618
public shouldPrompt(context: DockerLoginRegistryCredentialsContext): boolean {
17-
// For further clarification, see: https://github.com/microsoft/vscode-azurecontainerapps/pull/723/files#r1712152541
18-
return !context.registryName && !context.registry?.adminUserEnabled;
19+
return getRegistryDomainFromContext(context) === acrDomain && !context.registry?.adminUserEnabled;
1920
}
2021
}

src/commands/registryCredentials/dockerLogin/DockerLoginRegistryCredentialsContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { type RegistryCredentials, type Secret } from "@azure/arm-appcontainers";
7+
import { type SupportedRegistries } from "../../../constants";
78
import { type IContainerAppContext } from "../../IContainerAppContext";
89
import { type CreateAcrContext } from "../../image/imageSource/containerRegistry/acr/createAcr/CreateAcrContext";
910

1011
export interface DockerLoginRegistryCredentialsContext extends CreateAcrContext, IContainerAppContext {
1112
// These values are often pre-populated from the Docker extension via the deployImage API layer
13+
registryDomain?: SupportedRegistries;
1214
registryName?: string;
1315
username?: string;
1416
secret?: string;

src/commands/registryCredentials/identity/ManagedIdentityRegistryCredentialsContext.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { type RegistryCredentials } from "@azure/arm-appcontainers";
7+
import { type SupportedRegistries } from "../../../constants";
78
import { type IContainerAppContext } from "../../IContainerAppContext";
89
import { type CreateAcrContext } from "../../image/imageSource/containerRegistry/acr/createAcr/CreateAcrContext";
910
import { type ManagedEnvironmentRequiredContext } from "../../ManagedEnvironmentContext";
1011

1112
export interface ManagedIdentityRegistryCredentialsContext extends CreateAcrContext, ManagedEnvironmentRequiredContext, IContainerAppContext {
13+
registryDomain?: SupportedRegistries;
14+
1215
hasAcrPullRole?: boolean;
1316
newRegistryCredential?: RegistryCredentials;
1417
}

src/utils/imageNameUtils.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import { type ContainerRegistryManagementClient, type Registry } from "@azure/arm-containerregistry";
77
import { uiUtils } from "@microsoft/vscode-azext-azureutils";
8-
import { type ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
8+
import { nonNullProp, type ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
9+
import { type ContainerRegistryImageSourceContext } from "../commands/image/imageSource/containerRegistry/ContainerRegistryImageSourceContext";
910
import { acrDomain, dockerHubDomain, type SupportedRegistries } from "../constants";
1011
import { createContainerRegistryManagementClient } from "./azureClients";
1112

@@ -42,7 +43,7 @@ export function parseImageName(imageName?: string): ParsedImageName {
4243
const match: RegExpMatchArray | null = imageName.match(/^(?:(?<registryName>[^/]+)\/)?(?:(?<namespace>[^/]+(?:\/[^/]+)*)\/)?(?<repositoryName>[^/:]+)(?::(?<tag>[^/]+))?$/);
4344
return {
4445
imageNameReference: imageName,
45-
registryDomain: match?.groups?.registryName ? detectRegistryDomain(match.groups.registryName) : undefined,
46+
registryDomain: match?.groups?.registryName ? getDomainFromRegistryName(match.groups.registryName) : undefined,
4647
registryName: match?.groups?.registryName,
4748
namespace: match?.groups?.namespace,
4849
repositoryName: match?.groups?.repositoryName,
@@ -53,7 +54,7 @@ export function parseImageName(imageName?: string): ParsedImageName {
5354
/**
5455
* @param registryName When parsed from a full image name, everything before the first slash
5556
*/
56-
export function detectRegistryDomain(registryName: string): SupportedRegistries | undefined {
57+
export function getDomainFromRegistryName(registryName: string): SupportedRegistries | undefined {
5758
if (/\.azurecr\.io$/i.test(registryName)) {
5859
return acrDomain;
5960
} else if (/^docker\.io$/i.test(registryName)) {
@@ -63,6 +64,26 @@ export function detectRegistryDomain(registryName: string): SupportedRegistries
6364
}
6465
}
6566

67+
/**
68+
* A best effort attempt to obtain the registry domain using all available information on the context.
69+
* This function should only be called when we expect to have the full set of inputs necessary to make an informed decision.
70+
* It assumes that any missing or ambiguous information has already been addressed prior to the call.
71+
*/
72+
export function getRegistryDomainFromContext(context: Partial<ContainerRegistryImageSourceContext>): SupportedRegistries | undefined {
73+
switch (true) {
74+
case !!context.registryDomain:
75+
return context.registryDomain;
76+
case !!context.image:
77+
const registryName: string | undefined = parseImageName(context.image).registryName;
78+
return registryName ? getDomainFromRegistryName(registryName) : undefined;
79+
case !!context.registry?.loginServer || !!context.registryName:
80+
return getDomainFromRegistryName(context.registry?.loginServer || nonNullProp(context, 'registryName'));
81+
default:
82+
// If no image by this point, assume we're creating a new ACR
83+
return acrDomain;
84+
}
85+
}
86+
6687
/**
6788
* @param acrName When parsed from a full ACR image name, everything before the first slash
6889
*/

0 commit comments

Comments
 (0)