Skip to content

Commit 71b4358

Browse files
authored
Suggest values when deploying an image from a third party registry (#331)
1 parent a341eb1 commit 71b4358

File tree

4 files changed

+71
-11
lines changed

4 files changed

+71
-11
lines changed

src/commands/imageSource/containerRegistry/ContainerRegistryListStep.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class ContainerRegistryListStep extends AzureWizardPromptStep<IContainerR
2323
const placeHolder: string = localize('selectTag', 'Select a container registry');
2424
const picks: IAzureQuickPickItem<SupportedRegistries | undefined>[] = [];
2525

26-
picks.push({ label: 'Azure Container Registries', data: acrDomain });
26+
picks.push({ label: 'Azure Container Registry', data: acrDomain });
2727
if (env.uiKind === UIKind.Desktop) {
2828
// this will fails in vscode.dev due to browser CORS access policies
2929
picks.push({ label: 'Docker Hub Registry', data: dockerHubDomain });
@@ -35,7 +35,7 @@ export class ContainerRegistryListStep extends AzureWizardPromptStep<IContainerR
3535
}
3636

3737
public shouldPrompt(context: IContainerRegistryImageContext): boolean {
38-
return !context.tag && !context.image;
38+
return !context.image && !context.registryDomain;
3939
}
4040

4141
public async getSubWizard(context: IContainerRegistryImageContext): Promise<IWizardOptions<IContainerRegistryImageContext> | undefined> {

src/commands/imageSource/containerRegistry/RegistryImageInputStep.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,32 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils";
7+
import { acrDomain, quickStartImageName } from "../../../constants";
8+
import { parseImageName } from "../../../utils/imageNameUtils";
79
import { localize } from "../../../utils/localize";
810
import { IContainerRegistryImageContext } from "./IContainerRegistryImageContext";
11+
import { getLatestContainerAppImage } from "./getLatestContainerImage";
912

1013
export class RegistryImageInputStep extends AzureWizardPromptStep<IContainerRegistryImageContext> {
1114
public async prompt(context: IContainerRegistryImageContext): Promise<void> {
1215
const prompt: string = localize('registryImagePrompt', 'Enter the container image with tag');
13-
const placeHolder: string = localize('registryImagePlaceHolder', 'For example: `mcr.microsoft.com/azuredocs/containerapps-helloworld:latest`')
16+
const placeHolder: string = localize('registryImagePlaceHolder', 'For example: `mcr.microsoft.com/azuredocs/containerapps-helloworld:latest`');
17+
18+
// Try to suggest an image name only when the user is deploying to a Container App
19+
let value: string | undefined;
20+
if (context.targetContainer) {
21+
const { registryDomain, imageNameReference } = parseImageName(getLatestContainerAppImage(context.targetContainer));
22+
23+
// Only bother carrying over the suggestion if the old image was from a third party registry
24+
if (registryDomain !== acrDomain && imageNameReference !== quickStartImageName) {
25+
value = imageNameReference;
26+
}
27+
}
28+
1429
context.image = (await context.ui.showInputBox({
1530
prompt,
1631
placeHolder,
32+
value
1733
})).trim();
1834

1935
context.valuesToMask.push(context.image);

src/commands/imageSource/containerRegistry/dockerHub/DockerHubContainerRepositoryListStep.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { QuickPickItem } from "vscode";
7-
import { loadMoreQp, QuickPicksCache } from "../../../../constants";
6+
import type { QuickPickItem } from "vscode";
7+
import { currentlyDeployed, dockerHubDomain, loadMoreQp, QuickPicksCache, quickStartImageName } from "../../../../constants";
8+
import { parseImageName } from "../../../../utils/imageNameUtils";
89
import { localize } from "../../../../utils/localize";
910
import { nonNullProp } from "../../../../utils/nonNull";
11+
import { getLatestContainerAppImage } from "../getLatestContainerImage";
1012
import { IContainerRegistryImageContext } from "../IContainerRegistryImageContext";
1113
import { RegistryRepositoriesListStepBase } from "../RegistryRepositoriesListBaseStep";
1214
import { getReposForNamespace } from "./DockerHubV2ApiCalls";
15+
import type { DockerHubV2Repository } from "./DockerHubV2Types";
1316

1417
export class DockerHubContainerRepositoryListStep extends RegistryRepositoriesListStepBase {
1518
public async getPicks(context: IContainerRegistryImageContext, cachedPicks: QuickPicksCache): Promise<QuickPickItem[]> {
@@ -19,7 +22,33 @@ export class DockerHubContainerRepositoryListStep extends RegistryRepositoriesLi
1922
await context.ui.showWarningMessage(localize('noRepos', 'Unable to find any repositories associated to namespace "{0}"', context.dockerHubNamespace), { modal: true });
2023
}
2124

22-
cachedPicks.cache.push(...response.results.map((r) => { return { label: r.name, description: r.description } }));
25+
// Try to suggest a repository only when the user is deploying to a Container App
26+
let suggestedRepository: string | undefined;
27+
let srExists: boolean = false;
28+
if (context.targetContainer) {
29+
const { registryDomain, repositoryName, imageNameReference } = parseImageName(getLatestContainerAppImage(context.targetContainer));
30+
31+
// If the image is not the default quickstart image, then we can try to suggest a repository based on the latest Container App image
32+
if (registryDomain === dockerHubDomain && imageNameReference !== quickStartImageName) {
33+
suggestedRepository = repositoryName;
34+
}
35+
36+
// Does the suggested repositoryName exist in the list of pulled repositories? If so, move it to the front of the list
37+
const srIndex: number = response.results.findIndex((r) => !!suggestedRepository && r.name === suggestedRepository);
38+
srExists = srIndex !== -1;
39+
if (srExists) {
40+
const sr: DockerHubV2Repository = response.results.splice(srIndex, 1)[0];
41+
response.results.unshift(sr);
42+
}
43+
}
44+
45+
// Preferring 'suppressPersistence: true' over 'priority: highest' to avoid the possibility of a double parenthesis appearing in the description
46+
const quickPicks: QuickPickItem[] = response.results.map((r) => {
47+
return !!suggestedRepository && r.name === suggestedRepository ?
48+
{ label: r.name, description: r.description ? `${r.description} ${currentlyDeployed}` : currentlyDeployed, suppressPersistence: true } :
49+
{ label: r.name, description: r.description, suppressPersistence: srExists }
50+
});
51+
cachedPicks.cache.push(...quickPicks);
2352

2453
if (response.next) {
2554
cachedPicks.next = response.next;

src/commands/imageSource/containerRegistry/dockerHub/DockerHubNamespaceInputStep.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils";
7+
import { dockerHubDomain, quickStartImageName } from "../../../../constants";
8+
import { parseImageName } from "../../../../utils/imageNameUtils";
79
import { localize } from "../../../../utils/localize";
810
import { IContainerRegistryImageContext } from "../IContainerRegistryImageContext";
11+
import { getLatestContainerAppImage } from "../getLatestContainerImage";
912

10-
let checkNameLength: boolean = false;
1113
export class DockerHubNamespaceInputStep extends AzureWizardPromptStep<IContainerRegistryImageContext> {
1214
public async prompt(context: IContainerRegistryImageContext): Promise<void> {
1315
const prompt: string = localize('dockerHubNamespacePrompt', 'Enter a Docker Hub namespace');
1416
context.dockerHubNamespace = (await context.ui.showInputBox({
1517
prompt,
16-
value: 'library',
18+
value: this.getSuggestedNamespace(context),
1719
validateInput: async (value: string | undefined): Promise<string | undefined> => await this.validateInput(value)
1820
})).toLowerCase();
1921

@@ -26,16 +28,29 @@ export class DockerHubNamespaceInputStep extends AzureWizardPromptStep<IContaine
2628

2729
private async validateInput(name: string | undefined): Promise<string | undefined> {
2830
name = name ? name.trim() : '';
29-
// to prevent showing an error when the character types the first letter
30-
checkNameLength = checkNameLength || name.length > 1;
3131

3232
const { minLength, maxLength } = { minLength: 4, maxLength: 30 };
3333
if (/\W/.test(name)) {
3434
return localize('invalidNamespace', `A namespace name should only contain letters and/or numbers.`);
35-
} else if ((checkNameLength && name.length < minLength) || name.length > maxLength) {
35+
} else if (name.length < minLength || name.length > maxLength) {
3636
return localize('invalidLength', 'The name must be between {0} and {1} characters.', minLength, maxLength);
3737
}
3838

3939
return undefined;
4040
}
41+
42+
private getSuggestedNamespace(context: IContainerRegistryImageContext): string {
43+
// Try to suggest a namespace only when the user is deploying to a Container App
44+
let suggestedNamespace: string | undefined;
45+
if (context.targetContainer) {
46+
const { registryDomain, namespace, imageNameReference } = parseImageName(getLatestContainerAppImage(context.targetContainer));
47+
48+
// If the image is not the default quickstart image, then we can try to suggest a namespace based on the latest Container App image
49+
if (registryDomain === dockerHubDomain && imageNameReference !== quickStartImageName) {
50+
suggestedNamespace = namespace;
51+
}
52+
}
53+
54+
return suggestedNamespace || 'library';
55+
}
4156
}

0 commit comments

Comments
 (0)