From 6247fca53456314e027f6275ed92e8d5495896b2 Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:22:13 -0400 Subject: [PATCH 1/6] saving progress --- package-lock.json | 46 +- package.json | 8 +- src/commands/images/buildImage.ts | 12 +- src/commands/images/pushImage.ts | 31 +- src/commands/images/tagImage.ts | 6 +- src/commands/registerCommands.ts | 60 +-- src/commands/registerLocalCommand.ts | 54 +-- .../azure/DockerAssignAcrPullRoleStep.ts | 178 +++---- .../registries/azure/DockerSiteCreateStep.ts | 279 ++++++----- .../azure/DockerWebhookCreateStep.ts | 174 +++---- .../azure/WebSitesPortPromptStep.ts | 56 +-- .../registries/azure/createAzureRegistry.ts | 15 +- .../registries/azure/deleteAzureRegistry.ts | 50 +- .../registries/azure/deleteAzureRepository.ts | 50 +- .../registries/azure/deployImageToAca.ts | 62 ++- .../registries/azure/deployImageToAzure.ts | 138 +++--- .../registries/azure/openInAzurePortal.ts | 35 +- .../azure/tasks/buildImageInAzure.ts | 61 ++- .../registries/azure/tasks/runAzureTask.ts | 27 +- .../azure/tasks/runFileAsAzureTask.ts | 26 +- .../azure/tasks/scheduleRunRequest.ts | 433 +++++++++--------- .../azure/tasks/viewAzureTaskLogs.ts | 48 +- .../registries/azure/untagAzureImage.ts | 62 +-- .../registries/azure/viewAzureProperties.ts | 32 +- src/commands/registries/connectRegistry.ts | 5 +- src/commands/registries/copyRemoteFullTag.ts | 21 +- .../registries/copyRemoteImageDigest.ts | 64 +-- src/commands/registries/deleteRemoteImage.ts | 64 +-- src/commands/registries/disconnectRegistry.ts | 17 +- .../dockerHub/openDockerHubInBrowser.ts | 36 +- src/commands/registries/logInToDockerCli.ts | 33 +- src/commands/registries/logOutOfDockerCli.ts | 48 +- src/commands/registries/pullImages.ts | 28 +- src/commands/registries/reconnectRegistry.ts | 32 +- src/extensionVariables.ts | 8 +- src/tree/RefreshManager.ts | 2 +- src/tree/registerTrees.ts | 26 +- src/tree/registries/Azure/ACROAuthProvider.ts | 110 +++++ .../Azure/AzureRegistryDataProvider.ts | 126 +++++ .../registries/ConnectedRegistriesTreeItem.ts | 31 -- .../registries/ICachedRegistryProvider.ts | 16 - src/tree/registries/IRegistryProvider.ts | 68 --- .../registries/IRegistryProviderTreeItem.ts | 10 - src/tree/registries/RegistriesTreeItem.ts | 233 ---------- .../RegistryConnectErrorTreeItem.ts | 24 - src/tree/registries/RegistryTreeItemBase.ts | 66 --- .../RemoteRepositoryTreeItemBase.ts | 49 -- src/tree/registries/RemoteTagTreeItem.ts | 52 --- .../UnifiedRegistryTreeDataProvider.ts | 132 ++++++ src/tree/registries/all/RegistryApi.ts | 22 - .../registries/all/getRegistryProviders.ts | 19 - .../registries/auth/AzureOAuthProvider.ts | 34 -- .../registries/auth/BasicOAuthProvider.ts | 65 --- src/tree/registries/auth/IAuthProvider.ts | 13 - .../registries/azure/AzureAccountTreeItem.ts | 40 -- .../registries/azure/AzureRegistryTreeItem.ts | 115 ----- .../azure/AzureRepositoryTreeItem.ts | 19 - .../registries/azure/AzureTaskRunTreeItem.ts | 80 ---- .../registries/azure/AzureTaskTreeItem.ts | 90 ---- .../registries/azure/AzureTasksTreeItem.ts | 72 --- .../registries/azure/SubscriptionTreeItem.ts | 75 --- .../registries/azure/azureRegistryProvider.ts | 27 -- .../createWizard/AzureRegistryCreateStep.ts | 57 --- .../createWizard/AzureRegistryNameStep.ts | 50 -- .../createWizard/AzureRegistrySkuStep.ts | 23 - .../IAzureRegistryWizardContext.ts | 13 - .../IConnectRegistryWizardContext.ts | 16 - .../IConnectRegistryWizardOptions.ts | 51 --- .../connectWizard/RegistryPasswordStep.ts | 27 -- .../connectWizard/RegistryUrlStep.ts | 49 -- .../connectWizard/RegistryUsernameStep.ts | 39 -- .../dockerHub/DockerHubAccountTreeItem.ts | 125 ----- .../dockerHub/DockerHubNamespaceTreeItem.ts | 76 --- .../dockerHub/DockerHubRepositoryTreeItem.ts | 52 --- .../dockerHub/dockerHubRegistryProvider.ts | 29 -- .../dockerV2/DockerV2RegistryTreeItemBase.ts | 75 --- .../dockerV2/DockerV2RepositoryTreeItem.ts | 89 ---- .../dockerV2/DockerV2TagTreeItem.ts | 44 -- .../GenericDockerV2RegistryTreeItem.ts | 56 --- .../genericDockerV2RegistryProvider.ts | 31 -- .../getInformationFromRegistryItem.ts | 24 + .../gitLab/GitLabAccountTreeItem.ts | 86 ---- .../gitLab/GitLabProjectTreeItem.ts | 80 ---- .../gitLab/GitLabRepositoryTreeItem.ts | 64 --- .../gitLab/gitLabRegistryProvider.ts | 25 - src/tree/registries/registryContextValues.ts | 64 --- src/tree/registries/registryPasswords.ts | 28 -- src/utils/lazyPackages.ts | 10 - 88 files changed, 1554 insertions(+), 3644 deletions(-) create mode 100644 src/tree/registries/Azure/ACROAuthProvider.ts create mode 100644 src/tree/registries/Azure/AzureRegistryDataProvider.ts delete mode 100644 src/tree/registries/ConnectedRegistriesTreeItem.ts delete mode 100644 src/tree/registries/ICachedRegistryProvider.ts delete mode 100644 src/tree/registries/IRegistryProvider.ts delete mode 100644 src/tree/registries/IRegistryProviderTreeItem.ts delete mode 100644 src/tree/registries/RegistriesTreeItem.ts delete mode 100644 src/tree/registries/RegistryConnectErrorTreeItem.ts delete mode 100644 src/tree/registries/RegistryTreeItemBase.ts delete mode 100644 src/tree/registries/RemoteRepositoryTreeItemBase.ts delete mode 100644 src/tree/registries/RemoteTagTreeItem.ts create mode 100644 src/tree/registries/UnifiedRegistryTreeDataProvider.ts delete mode 100644 src/tree/registries/all/RegistryApi.ts delete mode 100644 src/tree/registries/all/getRegistryProviders.ts delete mode 100644 src/tree/registries/auth/AzureOAuthProvider.ts delete mode 100644 src/tree/registries/auth/BasicOAuthProvider.ts delete mode 100644 src/tree/registries/auth/IAuthProvider.ts delete mode 100644 src/tree/registries/azure/AzureAccountTreeItem.ts delete mode 100644 src/tree/registries/azure/AzureRegistryTreeItem.ts delete mode 100644 src/tree/registries/azure/AzureRepositoryTreeItem.ts delete mode 100644 src/tree/registries/azure/AzureTaskRunTreeItem.ts delete mode 100644 src/tree/registries/azure/AzureTaskTreeItem.ts delete mode 100644 src/tree/registries/azure/AzureTasksTreeItem.ts delete mode 100644 src/tree/registries/azure/SubscriptionTreeItem.ts delete mode 100644 src/tree/registries/azure/azureRegistryProvider.ts delete mode 100644 src/tree/registries/azure/createWizard/AzureRegistryCreateStep.ts delete mode 100644 src/tree/registries/azure/createWizard/AzureRegistryNameStep.ts delete mode 100644 src/tree/registries/azure/createWizard/AzureRegistrySkuStep.ts delete mode 100644 src/tree/registries/azure/createWizard/IAzureRegistryWizardContext.ts delete mode 100644 src/tree/registries/connectWizard/IConnectRegistryWizardContext.ts delete mode 100644 src/tree/registries/connectWizard/IConnectRegistryWizardOptions.ts delete mode 100644 src/tree/registries/connectWizard/RegistryPasswordStep.ts delete mode 100644 src/tree/registries/connectWizard/RegistryUrlStep.ts delete mode 100644 src/tree/registries/connectWizard/RegistryUsernameStep.ts delete mode 100644 src/tree/registries/dockerHub/DockerHubAccountTreeItem.ts delete mode 100644 src/tree/registries/dockerHub/DockerHubNamespaceTreeItem.ts delete mode 100644 src/tree/registries/dockerHub/DockerHubRepositoryTreeItem.ts delete mode 100644 src/tree/registries/dockerHub/dockerHubRegistryProvider.ts delete mode 100644 src/tree/registries/dockerV2/DockerV2RegistryTreeItemBase.ts delete mode 100644 src/tree/registries/dockerV2/DockerV2RepositoryTreeItem.ts delete mode 100644 src/tree/registries/dockerV2/DockerV2TagTreeItem.ts delete mode 100644 src/tree/registries/dockerV2/GenericDockerV2RegistryTreeItem.ts delete mode 100644 src/tree/registries/dockerV2/genericDockerV2RegistryProvider.ts create mode 100644 src/tree/registries/getInformationFromRegistryItem.ts delete mode 100644 src/tree/registries/gitLab/GitLabAccountTreeItem.ts delete mode 100644 src/tree/registries/gitLab/GitLabProjectTreeItem.ts delete mode 100644 src/tree/registries/gitLab/GitLabRepositoryTreeItem.ts delete mode 100644 src/tree/registries/gitLab/gitLabRegistryProvider.ts delete mode 100644 src/tree/registries/registryContextValues.ts delete mode 100644 src/tree/registries/registryPasswords.ts diff --git a/package-lock.json b/package-lock.json index a95cfdef1d..bd81463354 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "@azure/storage-blob": "^12.14.0", "@microsoft/compose-language-service": "^0.2.0", "@microsoft/vscode-azext-azureappservice": "^1.0.2", + "@microsoft/vscode-azext-azureauth": "^1.1.2", "@microsoft/vscode-azext-azureutils": "^1.1.5", "@microsoft/vscode-azext-utils": "^1.2.2", "@microsoft/vscode-container-client": "^0.1.0", + "@microsoft/vscode-docker-registries": "file:../vscode-docker-extensibility/packages/vscode-docker-registries/microsoft-vscode-docker-registries-0.0.1-alpha.tgz", "dayjs": "^1.11.7", "dockerfile-language-server-nodejs": "^0.10.2", "fs-extra": "^11.1.1", @@ -259,6 +261,23 @@ "node": ">=14.0.0" } }, + "node_modules/@azure/arm-subscriptions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@azure/arm-subscriptions/-/arm-subscriptions-5.1.0.tgz", + "integrity": "sha512-6BeOF2eQWNLq22ch7xP9RxYnPjtGev54OUCGggKOWoOvmesK7jUZbIyLk8JeXDT21PEl7iyYnxw78gxJ7zBxQw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.6.1", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.2.0", + "@azure/core-rest-pipeline": "^1.8.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@azure/core-auth": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", @@ -405,8 +424,7 @@ "node_modules/@azure/ms-rest-azure-env": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz", - "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==", - "peer": true + "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" }, "node_modules/@azure/storage-blob": { "version": "12.14.0", @@ -714,6 +732,15 @@ "@microsoft/vscode-azext-utils": "^1.2.1" } }, + "node_modules/@microsoft/vscode-azext-azureauth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureauth/-/vscode-azext-azureauth-1.1.2.tgz", + "integrity": "sha512-cacmiEv1GiofOKaQzgRqXrDqPoFc3HAGiaer+ENNrdeW6elks0o5Z0oXkTIauS26yeYQUUaooB6DezvQU6LC+Q==", + "dependencies": { + "@azure/arm-subscriptions": "^5.1.0", + "@azure/ms-rest-azure-env": "^2.0.0" + } + }, "node_modules/@microsoft/vscode-azext-azureutils": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-1.1.5.tgz", @@ -784,6 +811,15 @@ "tree-kill": "^1.2.2" } }, + "node_modules/@microsoft/vscode-docker-registries": { + "version": "0.0.1-alpha", + "resolved": "file:../vscode-docker-extensibility/packages/vscode-docker-registries/microsoft-vscode-docker-registries-0.0.1-alpha.tgz", + "integrity": "sha512-xUh413coEcazq8xort40952T8OHiShZ+0KRNRdQSfDnbLIL8zmXXIQh4akXPavhUitFf1boVrTrfKmb3PUfeIQ==", + "license": "See LICENSE in the project root for license information.", + "dependencies": { + "node-fetch": "^2.6.11" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4498,9 +4534,9 @@ "optional": true }, "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, diff --git a/package.json b/package.json index d93f461433..86c06d98f8 100644 --- a/package.json +++ b/package.json @@ -501,12 +501,12 @@ }, { "command": "vscode-docker.registries.pullImage", - "when": "view == dockerRegistries && viewItem =~ /Tag;/", + "when": "view == dockerRegistries && viewItem =~ /commontag/i", "group": "regs_tag_1_general@1" }, { "command": "vscode-docker.registries.copyRemoteFullTag", - "when": "view == dockerRegistries && viewItem =~ /(DockerV2|DockerHubV2);Tag;/", + "when": "view == dockerRegistries && viewItem =~ /commontag/i", "group": "regs_tag_1_general@2" }, { @@ -551,7 +551,7 @@ }, { "command": "vscode-docker.registries.disconnectRegistry", - "when": "view == dockerRegistries && viewItem =~ /RegistryProvider;/", + "when": "view == dockerRegistries && viewItem =~ /commonregistryroot/i && !(viewItem =~ /genericregistryrootv2/i)", "group": "regs_yyy_destructive@1" }, { @@ -3031,9 +3031,11 @@ "@azure/storage-blob": "^12.14.0", "@microsoft/compose-language-service": "^0.2.0", "@microsoft/vscode-azext-azureappservice": "^1.0.2", + "@microsoft/vscode-azext-azureauth": "^1.1.2", "@microsoft/vscode-azext-azureutils": "^1.1.5", "@microsoft/vscode-azext-utils": "^1.2.2", "@microsoft/vscode-container-client": "^0.1.0", + "@microsoft/vscode-docker-registries": "file:../vscode-docker-extensibility/packages/vscode-docker-registries/microsoft-vscode-docker-registries-0.0.1-alpha.tgz", "dayjs": "^1.11.7", "dockerfile-language-server-nodejs": "^0.10.2", "fs-extra": "^11.1.1", diff --git a/src/commands/images/buildImage.ts b/src/commands/images/buildImage.ts index db6660c458..34f9e0e9ed 100644 --- a/src/commands/images/buildImage.ts +++ b/src/commands/images/buildImage.ts @@ -14,7 +14,6 @@ import { delay } from "../../utils/promiseUtils"; import { quickPickDockerFileItem } from "../../utils/quickPickFile"; import { quickPickWorkspaceFolder } from "../../utils/quickPickWorkspaceFolder"; import { selectBuildCommand } from "../selectCommandTemplate"; -import { addImageTaggingTelemetry, getTagFromUserInput } from "./tagImage"; const tagRegex: RegExp = /\$\{tag\}/i; @@ -60,12 +59,13 @@ export async function buildImage(context: IActionContext, dockerFileUri: vscode. // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox await delay(500); - addImageTaggingTelemetry(context, suggestedImageName, '.before'); - const imageName: string = await getTagFromUserInput(context, suggestedImageName); - addImageTaggingTelemetry(context, imageName, '.after'); + // addImageTaggingTelemetry(context, suggestedImageName, '.before'); + // const imageName: string = await getTagFromUserInput(context, suggestedImageName); + // addImageTaggingTelemetry(context, imageName, '.after'); - await ext.context.workspaceState.update(dockerFileKey, imageName); - terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); + // await ext.context.workspaceState.update(dockerFileKey, imageName); + // terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); + // TODO: review this later } const client = await ext.runtimeManager.getClient(); diff --git a/src/commands/images/pushImage.ts b/src/commands/images/pushImage.ts index 5ac02c613e..fcf6415176 100644 --- a/src/commands/images/pushImage.ts +++ b/src/commands/images/pushImage.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext, NoResourceFoundError } from '@microsoft/vscode-azext-utils'; +import { IActionContext, NoResourceFoundError, contextValueExperience } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; import { ext } from '../../extensionVariables'; import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; +import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; import { addImageTaggingTelemetry, tagImage } from './tagImage'; export async function pushImage(context: IActionContext, node: ImageTreeItem | undefined): Promise { @@ -21,7 +20,7 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u }); } - let connectedRegistry: RegistryTreeItemBase | undefined; + let connectedRegistry: UnifiedRegistryItem | undefined; if (!node.fullTag.includes('/')) { // The registry to push to is indeterminate--could be Docker Hub, or could need tagging. @@ -30,7 +29,7 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u // If the prompt setting is true, we'll ask; if not we'll assume Docker Hub. if (prompt) { try { - connectedRegistry = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.registry, context); + connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'commonregistry' }); } catch (error) { if (error instanceof NoResourceFoundError) { // Do nothing, move on without a selected registry @@ -41,7 +40,7 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u } } else { // Try to find a connected Docker Hub registry (primarily for login credentials) - connectedRegistry = await tryGetDockerHubRegistry(context); + connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerHubRegistry' }); } } else { // The registry to push to is determinate. If there's a connected registry in the tree view, we'll try to find it, to perform login ahead of time. @@ -53,16 +52,16 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u title: vscode.l10n.t('Fetching login credentials...'), }; - connectedRegistry = await vscode.window.withProgress(progressOptions, async () => await tryGetConnectedRegistryForPath(context, baseImagePath)); + // connectedRegistry = await vscode.window.withProgress(progressOptions, async () => await tryGetConnectedRegistryForPath(context, baseImagePath)); TODO: review this later } // Give the user a chance to modify the tag however they want const finalTag = await tagImage(context, node, connectedRegistry); - if (connectedRegistry && finalTag.startsWith(connectedRegistry.baseImagePath)) { - // If a registry was found/chosen and is still the same as the final tag's registry, try logging in - await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', connectedRegistry); - } + // if (connectedRegistry && finalTag.startsWith(connectedRegistry.baseImagePath)) { + // // If a registry was found/chosen and is still the same as the final tag's registry, try logging in + // await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', connectedRegistry); + // } TODO: review this later addImageTaggingTelemetry(context, finalTag, ''); @@ -77,13 +76,3 @@ export async function pushImage(context: IActionContext, node: ImageTreeItem | u client.pushImage({ imageRef: finalTag }) ); } - -async function tryGetConnectedRegistryForPath(context: IActionContext, baseImagePath: string): Promise { - const allRegistries = await ext.registriesRoot.getAllConnectedRegistries(context); - return allRegistries.find(r => r.baseImagePath === baseImagePath); -} - -async function tryGetDockerHubRegistry(context: IActionContext): Promise { - const allRegistries = await ext.registriesRoot.getAllConnectedRegistries(context); - return allRegistries.find(r => r.contextValue.match(registryExpectedContextValues.dockerHub.registry)); -} diff --git a/src/commands/images/tagImage.ts b/src/commands/images/tagImage.ts index 8f00530ce8..aff407e5bd 100644 --- a/src/commands/images/tagImage.ts +++ b/src/commands/images/tagImage.ts @@ -7,9 +7,9 @@ import { IActionContext, TelemetryProperties } from '@microsoft/vscode-azext-uti import * as vscode from 'vscode'; import { ext } from '../../extensionVariables'; import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; -import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; +import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; -export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: RegistryTreeItemBase): Promise { +export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: UnifiedRegistryItem): Promise { if (!node) { await ext.imagesTree.refresh(context); node = await ext.imagesTree.showTreeItemPicker(ImageTreeItem.contextValue, { @@ -19,7 +19,7 @@ export async function tagImage(context: IActionContext, node?: ImageTreeItem, re } addImageTaggingTelemetry(context, node.fullTag, '.before'); - const newTaggedName: string = await getTagFromUserInput(context, node.fullTag, registry?.baseImagePath); + const newTaggedName: string = await getTagFromUserInput(context, node.fullTag, ''); // TODO: review this later addImageTaggingTelemetry(context, newTaggedName, '.after'); await ext.runWithDefaults(client => diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index 00916b14cd..04ef28b638 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -52,27 +52,29 @@ import { removeNetwork } from "./networks/removeNetwork"; import { pruneSystem } from "./pruneSystem"; import { registerWorkspaceCommand } from "./registerWorkspaceCommand"; import { createAzureRegistry } from "./registries/azure/createAzureRegistry"; -import { deleteAzureRegistry } from "./registries/azure/deleteAzureRegistry"; -import { deleteAzureRepository } from "./registries/azure/deleteAzureRepository"; +// import { deleteAzureRegistry } from "./registries/azure/deleteAzureRegistry"; +// import { deleteAzureRepository } from "./registries/azure/deleteAzureRepository"; import { deployImageToAca } from "./registries/azure/deployImageToAca"; -import { deployImageToAzure } from "./registries/azure/deployImageToAzure"; +// import { deployImageToAzure } from "./registries/azure/deployImageToAzure"; import { openInAzurePortal } from "./registries/azure/openInAzurePortal"; -import { buildImageInAzure } from "./registries/azure/tasks/buildImageInAzure"; +// import { buildImageInAzure } from "./registries/azure/tasks/buildImageInAzure"; import { runAzureTask } from "./registries/azure/tasks/runAzureTask"; -import { runFileAsAzureTask } from "./registries/azure/tasks/runFileAsAzureTask"; -import { viewAzureTaskLogs } from "./registries/azure/tasks/viewAzureTaskLogs"; -import { untagAzureImage } from "./registries/azure/untagAzureImage"; -import { viewAzureProperties } from "./registries/azure/viewAzureProperties"; +// import { runFileAsAzureTask } from "./registries/azure/tasks/runFileAsAzureTask"; +// import { viewAzureTaskLogs } from "./registries/azure/tasks/viewAzureTaskLogs"; +// import { untagAzureImage } from "./registries/azure/untagAzureImage"; +// import { viewAzureProperties } from "./registries/azure/viewAzureProperties"; import { connectRegistry } from "./registries/connectRegistry"; -import { copyRemoteFullTag } from './registries/copyRemoteFullTag'; -import { copyRemoteImageDigest } from "./registries/copyRemoteImageDigest"; -import { deleteRemoteImage } from "./registries/deleteRemoteImage"; -import { disconnectRegistry } from "./registries/disconnectRegistry"; +// import { copyRemoteFullTag } from './registries/copyRemoteFullTag'; +// import { copyRemoteImageDigest } from "./registries/copyRemoteImageDigest"; +// import { deleteRemoteImage } from "./registries/deleteRemoteImage"; import { openDockerHubInBrowser } from "./registries/dockerHub/openDockerHubInBrowser"; +// import { logInToDockerCli } from "./registries/logInToDockerCli"; +// import { logOutOfDockerCli } from "./registries/logOutOfDockerCli"; +// import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; +// import { reconnectRegistry } from "./registries/reconnectRegistry"; +import { copyRemoteFullTag } from "./registries/copyRemoteFullTag"; import { logInToDockerCli } from "./registries/logInToDockerCli"; -import { logOutOfDockerCli } from "./registries/logOutOfDockerCli"; -import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; -import { reconnectRegistry } from "./registries/reconnectRegistry"; +import { pullImageFromRepository } from "./registries/pullImages"; import { registryHelp } from "./registries/registryHelp"; import { openDockerDownloadPage } from "./showDockerLearnMoreNotification"; import { configureVolumesExplorer } from "./volumes/configureVolumesExplorer"; @@ -167,32 +169,32 @@ export function registerCommands(): void { registerCommand('vscode-docker.networks.prune', pruneNetworks); registerCommand('vscode-docker.registries.connectRegistry', connectRegistry); - registerCommand('vscode-docker.registries.copyImageDigest', copyRemoteImageDigest); + // registerCommand('vscode-docker.registries.copyImageDigest', copyRemoteImageDigest); registerCommand('vscode-docker.registries.copyRemoteFullTag', copyRemoteFullTag); - registerCommand('vscode-docker.registries.deleteImage', deleteRemoteImage); - registerCommand('vscode-docker.registries.deployImageToAzure', deployImageToAzure); + // registerCommand('vscode-docker.registries.deleteImage', deleteRemoteImage); + // registerCommand('vscode-docker.registries.deployImageToAzure', deployImageToAzure); registerCommand('vscode-docker.registries.deployImageToAca', deployImageToAca); - registerCommand('vscode-docker.registries.disconnectRegistry', disconnectRegistry); + registerCommand('vscode-docker.registries.disconnectRegistry', (context, item) => ext.registriesRoot.disconnectRegistryProvider(item)); registerCommand('vscode-docker.registries.help', registryHelp); registerWorkspaceCommand('vscode-docker.registries.logInToDockerCli', logInToDockerCli); - registerWorkspaceCommand('vscode-docker.registries.logOutOfDockerCli', logOutOfDockerCli); + // registerWorkspaceCommand('vscode-docker.registries.logOutOfDockerCli', logOutOfDockerCli); registerWorkspaceCommand('vscode-docker.registries.pullImage', pullImageFromRepository); - registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository); - registerCommand('vscode-docker.registries.reconnectRegistry', reconnectRegistry); + // registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository); + // registerCommand('vscode-docker.registries.reconnectRegistry', reconnectRegistry); registerCommand('vscode-docker.registries.dockerHub.openInBrowser', openDockerHubInBrowser); - registerWorkspaceCommand('vscode-docker.registries.azure.buildImage', buildImageInAzure); + // registerWorkspaceCommand('vscode-docker.registries.azure.buildImage', buildImageInAzure); registerCommand('vscode-docker.registries.azure.createRegistry', createAzureRegistry); - registerCommand('vscode-docker.registries.azure.deleteRegistry', deleteAzureRegistry); - registerCommand('vscode-docker.registries.azure.deleteRepository', deleteAzureRepository); + // registerCommand('vscode-docker.registries.azure.deleteRegistry', deleteAzureRegistry); + // registerCommand('vscode-docker.registries.azure.deleteRepository', deleteAzureRepository); registerCommand('vscode-docker.registries.azure.openInPortal', openInAzurePortal); registerCommand('vscode-docker.registries.azure.runTask', runAzureTask); - registerWorkspaceCommand('vscode-docker.registries.azure.runFileAsTask', runFileAsAzureTask); + // registerWorkspaceCommand('vscode-docker.registries.azure.runFileAsTask', runFileAsAzureTask); registerCommand('vscode-docker.registries.azure.selectSubscriptions', () => commands.executeCommand("azure-account.selectSubscriptions")); - registerCommand('vscode-docker.registries.azure.untagImage', untagAzureImage); - registerCommand('vscode-docker.registries.azure.viewProperties', viewAzureProperties); - registerCommand('vscode-docker.registries.azure.viewTaskLogs', viewAzureTaskLogs); + // registerCommand('vscode-docker.registries.azure.untagImage', untagAzureImage); + // registerCommand('vscode-docker.registries.azure.viewProperties', viewAzureProperties); + // registerCommand('vscode-docker.registries.azure.viewTaskLogs', viewAzureTaskLogs); registerCommand('vscode-docker.volumes.configureExplorer', configureVolumesExplorer); registerCommand('vscode-docker.volumes.inspect', inspectVolume); diff --git a/src/commands/registerLocalCommand.ts b/src/commands/registerLocalCommand.ts index 3f2854a49b..be9db187eb 100644 --- a/src/commands/registerLocalCommand.ts +++ b/src/commands/registerLocalCommand.ts @@ -1,31 +1,31 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from '@microsoft/vscode-azext-utils'; -import { l10n } from 'vscode'; -import { DockerExtensionKind, getVSCodeRemoteInfo } from '../utils/getVSCodeRemoteInfo'; -import { registerCommand } from './registerCommands'; +// import { IActionContext } from '@microsoft/vscode-azext-utils'; +// import { l10n } from 'vscode'; +// import { DockerExtensionKind, getVSCodeRemoteInfo } from '../utils/getVSCodeRemoteInfo'; +// import { registerCommand } from './registerCommands'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function registerLocalCommand(commandId: string, callback: (context: IActionContext, ...args: any[]) => any, debounce?: number): void { - registerCommand( - commandId, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async (context, ...args: any[]) => { - await verifyIsRunningLocally(context); - return callback(context, ...args); - }, - debounce - ); -} +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// export function registerLocalCommand(commandId: string, callback: (context: IActionContext, ...args: any[]) => any, debounce?: number): void { +// registerCommand( +// commandId, +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// async (context, ...args: any[]) => { +// await verifyIsRunningLocally(context); +// return callback(context, ...args); +// }, +// debounce +// ); +// } -async function verifyIsRunningLocally(context: IActionContext): Promise { - const remoteInfo = getVSCodeRemoteInfo(context); +// async function verifyIsRunningLocally(context: IActionContext): Promise { +// const remoteInfo = getVSCodeRemoteInfo(context); - if (remoteInfo.extensionKind !== DockerExtensionKind.local) { - context.errorHandling.suppressReportIssue = true; - throw new Error(l10n.t('This command cannot be used in a remote session.')); - } -} +// if (remoteInfo.extensionKind !== DockerExtensionKind.local) { +// context.errorHandling.suppressReportIssue = true; +// throw new Error(l10n.t('This command cannot be used in a remote session.')); +// } +// } diff --git a/src/commands/registries/azure/DockerAssignAcrPullRoleStep.ts b/src/commands/registries/azure/DockerAssignAcrPullRoleStep.ts index 1b310433f0..74095300da 100644 --- a/src/commands/registries/azure/DockerAssignAcrPullRoleStep.ts +++ b/src/commands/registries/azure/DockerAssignAcrPullRoleStep.ts @@ -1,89 +1,89 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy -import { AzureWizardExecuteStep } from "@microsoft/vscode-azext-utils"; -import { randomUUID } from "crypto"; -import { l10n, Progress } from "vscode"; -import { ext } from "../../../extensionVariables"; -import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; -import { getArmAuth, getArmContainerRegistry, getAzExtAppService, getAzExtAzureUtils } from "../../../utils/lazyPackages"; - -export class DockerAssignAcrPullRoleStep extends AzureWizardExecuteStep { - public priority: number = 141; // execute after DockerSiteCreateStep - - public constructor(private readonly tagTreeItem: RemoteTagTreeItem) { - super(); - } - - public async execute(context: IAppServiceWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise { - const message: string = l10n.t('Granting permission for App Service to pull image from ACR...'); - ext.outputChannel.info(message); - progress.report({ message: message }); - - const azExtAzureUtils = await getAzExtAzureUtils(); - const vscAzureAppService = await getAzExtAppService(); - const armAuth = await getArmAuth(); - const armContainerRegistry = await getArmContainerRegistry(); - const authClient = azExtAzureUtils.createAzureClient(context, armAuth.AuthorizationManagementClient); - const crmClient = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); - const appSvcClient = await vscAzureAppService.createWebSiteClient(context); - - // If we're in `execute`, then `shouldExecute` passed and `this.tagTreeItem.parent.parent` is guaranteed to be an AzureRegistryTreeItem - const registryTreeItem: AzureRegistryTreeItem = this.tagTreeItem.parent.parent as unknown as AzureRegistryTreeItem; - - // 1. Get the registry resource. We will need the ID. - const registry = await crmClient.registries.get(registryTreeItem.resourceGroup, registryTreeItem.registryName); - - if (!(registry?.id)) { - throw new Error( - l10n.t('Unable to get details from Container Registry {0}', registryTreeItem.baseUrl) - ); - } - - // 2. Get the role definition for the AcrPull role. We will need the definition ID. This role is built-in and should always exist. - const acrPullRoleDefinition = (await azExtAzureUtils.uiUtils.listAllIterator(authClient.roleDefinitions.list(registry.id, { filter: `roleName eq 'AcrPull'` })))[0]; - - if (!(acrPullRoleDefinition?.id)) { - throw new Error( - l10n.t('Unable to get AcrPull role definition on subscription {0}', context.subscriptionId) - ); - } - - // 3. Get the info for the now-created web site. We will need the principal ID. - const siteInfo = await appSvcClient.webApps.get(context.site.resourceGroup, context.site.name); - - if (!(siteInfo?.identity?.principalId)) { - throw new Error( - l10n.t('Unable to get identity principal ID for web site {0}', context.site.name) - ); - } - - // 4. On the registry, assign the AcrPull role to the principal representing the website - await authClient.roleAssignments.create(registry.id, randomUUID(), { - principalId: siteInfo.identity.principalId, - roleDefinitionId: acrPullRoleDefinition.id, - principalType: 'ServicePrincipal', - }); - - // 5. Set the web app to use the desired ACR image, which was not done in DockerSiteCreateStep. Get the config and then update it. - const config = await appSvcClient.webApps.getConfiguration(context.site.resourceGroup, context.site.name); - - if (!config) { - throw new Error( - l10n.t('Unable to get configuration for web site {0}', context.site.name) - ); - } - - config.linuxFxVersion = `DOCKER|${this.tagTreeItem.fullTag}`; - await appSvcClient.webApps.updateConfiguration(context.site.resourceGroup, context.site.name, config); - } - - public shouldExecute(context: IAppServiceWizardContext): boolean { - return !!(context.site) && !!(this.tagTreeItem?.parent?.parent) && this.tagTreeItem.parent.parent instanceof AzureRegistryTreeItem - && !context.customLocation; - } -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy +// import { AzureWizardExecuteStep } from "@microsoft/vscode-azext-utils"; +// import { randomUUID } from "crypto"; +// import { l10n, Progress } from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; +// import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; +// import { getArmAuth, getArmContainerRegistry, getAzExtAppService, getAzExtAzureUtils } from "../../../utils/lazyPackages"; + +// export class DockerAssignAcrPullRoleStep extends AzureWizardExecuteStep { +// public priority: number = 141; // execute after DockerSiteCreateStep + +// public constructor(private readonly tagTreeItem: RemoteTagTreeItem) { +// super(); +// } + +// public async execute(context: IAppServiceWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise { +// const message: string = l10n.t('Granting permission for App Service to pull image from ACR...'); +// ext.outputChannel.info(message); +// progress.report({ message: message }); + +// const azExtAzureUtils = await getAzExtAzureUtils(); +// const vscAzureAppService = await getAzExtAppService(); +// const armAuth = await getArmAuth(); +// const armContainerRegistry = await getArmContainerRegistry(); +// const authClient = azExtAzureUtils.createAzureClient(context, armAuth.AuthorizationManagementClient); +// const crmClient = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); +// const appSvcClient = await vscAzureAppService.createWebSiteClient(context); + +// // If we're in `execute`, then `shouldExecute` passed and `this.tagTreeItem.parent.parent` is guaranteed to be an AzureRegistryTreeItem +// const registryTreeItem: AzureRegistryTreeItem = this.tagTreeItem.parent.parent as unknown as AzureRegistryTreeItem; + +// // 1. Get the registry resource. We will need the ID. +// const registry = await crmClient.registries.get(registryTreeItem.resourceGroup, registryTreeItem.registryName); + +// if (!(registry?.id)) { +// throw new Error( +// l10n.t('Unable to get details from Container Registry {0}', registryTreeItem.baseUrl) +// ); +// } + +// // 2. Get the role definition for the AcrPull role. We will need the definition ID. This role is built-in and should always exist. +// const acrPullRoleDefinition = (await azExtAzureUtils.uiUtils.listAllIterator(authClient.roleDefinitions.list(registry.id, { filter: `roleName eq 'AcrPull'` })))[0]; + +// if (!(acrPullRoleDefinition?.id)) { +// throw new Error( +// l10n.t('Unable to get AcrPull role definition on subscription {0}', context.subscriptionId) +// ); +// } + +// // 3. Get the info for the now-created web site. We will need the principal ID. +// const siteInfo = await appSvcClient.webApps.get(context.site.resourceGroup, context.site.name); + +// if (!(siteInfo?.identity?.principalId)) { +// throw new Error( +// l10n.t('Unable to get identity principal ID for web site {0}', context.site.name) +// ); +// } + +// // 4. On the registry, assign the AcrPull role to the principal representing the website +// await authClient.roleAssignments.create(registry.id, randomUUID(), { +// principalId: siteInfo.identity.principalId, +// roleDefinitionId: acrPullRoleDefinition.id, +// principalType: 'ServicePrincipal', +// }); + +// // 5. Set the web app to use the desired ACR image, which was not done in DockerSiteCreateStep. Get the config and then update it. +// const config = await appSvcClient.webApps.getConfiguration(context.site.resourceGroup, context.site.name); + +// if (!config) { +// throw new Error( +// l10n.t('Unable to get configuration for web site {0}', context.site.name) +// ); +// } + +// config.linuxFxVersion = `DOCKER|${this.tagTreeItem.fullTag}`; +// await appSvcClient.webApps.updateConfiguration(context.site.resourceGroup, context.site.name, config); +// } + +// public shouldExecute(context: IAppServiceWizardContext): boolean { +// return !!(context.site) && !!(this.tagTreeItem?.parent?.parent) && this.tagTreeItem.parent.parent instanceof AzureRegistryTreeItem +// && !context.customLocation; +// } +// } diff --git a/src/commands/registries/azure/DockerSiteCreateStep.ts b/src/commands/registries/azure/DockerSiteCreateStep.ts index 9da882c4f1..6d05b1f474 100644 --- a/src/commands/registries/azure/DockerSiteCreateStep.ts +++ b/src/commands/registries/azure/DockerSiteCreateStep.ts @@ -1,141 +1,138 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import type { NameValuePair, Site, SiteConfig, WebSiteManagementClient } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy -import type { CustomLocation } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy -import type { AzExtLocation } from '@microsoft/vscode-azext-azureutils'; // These are only dev-time imports so don't need to be lazy -import { AzureWizardExecuteStep, nonNullProp, nonNullValueAndProp } from "@microsoft/vscode-azext-utils"; -import { l10n, Progress } from "vscode"; -import { ext } from "../../../extensionVariables"; -import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { DockerHubNamespaceTreeItem } from '../../../tree/registries/dockerHub/DockerHubNamespaceTreeItem'; -import { DockerV2RegistryTreeItemBase } from '../../../tree/registries/dockerV2/DockerV2RegistryTreeItemBase'; -import { GenericDockerV2RegistryTreeItem } from '../../../tree/registries/dockerV2/GenericDockerV2RegistryTreeItem'; -import { getRegistryPassword } from '../../../tree/registries/registryPasswords'; -import { RegistryTreeItemBase } from '../../../tree/registries/RegistryTreeItemBase'; -import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; -import { getAzExtAppService, getAzExtAzureUtils } from '../../../utils/lazyPackages'; -import { IAppServiceContainerWizardContext } from './deployImageToAzure'; - -export class DockerSiteCreateStep extends AzureWizardExecuteStep { - public priority: number = 140; - - public constructor(private readonly node: RemoteTagTreeItem) { - super(); - } - - public async execute(context: IAppServiceContainerWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise { - const creatingNewApp: string = l10n.t('Creating web app "{0}"...', context.newSiteName); - ext.outputChannel.info(creatingNewApp); - progress.report({ message: creatingNewApp }); - const siteConfig = await this.getNewSiteConfig(context); - - const azExtAzureUtils = await getAzExtAzureUtils(); - const vscAzureAppService = await getAzExtAppService(); - - const location: AzExtLocation = await azExtAzureUtils.LocationListStep.getLocation(context); - const locationName: string = nonNullProp(location, 'name'); - - const client: WebSiteManagementClient = await vscAzureAppService.createWebSiteClient(context); - const siteEnvelope: Site = { - name: context.newSiteName, - location: locationName, - serverFarmId: nonNullValueAndProp(context.plan, 'id'), - siteConfig: siteConfig - }; - - if (context.customLocation) { - // deploying to Azure Arc - siteEnvelope.kind = 'app,linux,kubernetes,container'; - await this.addCustomLocationProperties(siteEnvelope, context.customLocation); - } else { - siteEnvelope.identity = { - type: 'SystemAssigned' - }; - } - - context.site = await client.webApps.beginCreateOrUpdateAndWait(nonNullValueAndProp(context.resourceGroup, 'name'), nonNullProp(context, 'newSiteName'), siteEnvelope); - } - - private async getNewSiteConfig(context: IAppServiceContainerWizardContext): Promise { - const registryTI: RegistryTreeItemBase = this.node.parent.parent; - - let username: string | undefined; - let password: string | undefined; - let registryUrl: string | undefined; - const appSettings: NameValuePair[] = []; - - // Scenarios: - // ACR -> App Service, NOT Arc App Service. Use managed service identity. - if (registryTI instanceof AzureRegistryTreeItem && !context.customLocation) { - appSettings.push({ name: 'DOCKER_ENABLE_CI', value: 'true' }); - - // Don't need an image, username, or password--just create an empty web app to assign permissions and then configure with an image - // `DockerAssignAcrPullRoleStep` handles it after that - return { - acrUseManagedIdentityCreds: true, - appSettings - }; - } - // ACR -> Arc App Service. Use regular auth. Same as any V2 registry but different way of getting auth. - else if (registryTI instanceof AzureRegistryTreeItem && context.customLocation) { - const cred = await registryTI.tryGetAdminCredentials(context); - if (!cred?.username || !cred?.passwords?.[0]?.value) { - throw new Error(l10n.t('Azure App service deployment on Azure Arc only supports running images from Azure Container Registries with admin enabled')); - } - - username = cred.username; - password = cred.passwords[0].value; - registryUrl = registryTI.baseUrl; - } - // Docker Hub -> App Service *OR* Arc App Service - else if (registryTI instanceof DockerHubNamespaceTreeItem) { - username = registryTI.parent.username; - password = await registryTI.parent.getPassword(); - registryUrl = 'https://index.docker.io'; - } - // Generic registry -> App Service *OR* Arc App Service - else if (registryTI instanceof DockerV2RegistryTreeItemBase) { - if (registryTI instanceof GenericDockerV2RegistryTreeItem) { - username = registryTI.cachedProvider.username; - password = await getRegistryPassword(registryTI.cachedProvider); - } else { - throw new RangeError(l10n.t('Unrecognized node type "{0}"', registryTI.constructor.name)); - } - - registryUrl = registryTI.baseUrl; - } else { - throw new RangeError(l10n.t('Unrecognized node type "{0}"', registryTI.constructor.name)); - } - - if (username && password) { - appSettings.push({ name: "DOCKER_REGISTRY_SERVER_USERNAME", value: username }); - appSettings.push({ name: "DOCKER_REGISTRY_SERVER_PASSWORD", value: password }); - } - - if (registryUrl) { - appSettings.push({ name: 'DOCKER_REGISTRY_SERVER_URL', value: registryUrl }); - } - - if (context.webSitesPort) { - appSettings.push({ name: "WEBSITES_PORT", value: context.webSitesPort.toString() }); - } - - const linuxFxVersion = `DOCKER|${this.node.fullTag}`; - - return { - linuxFxVersion, - appSettings - }; - } - - private addCustomLocationProperties(site: Site, customLocation: CustomLocation): void { - site.extendedLocation = { name: customLocation.id, type: 'customLocation' }; - } - - public shouldExecute(context: IAppServiceContainerWizardContext): boolean { - return !context.site; - } -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import type { NameValuePair, Site, SiteConfig, WebSiteManagementClient } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy +// import type { CustomLocation } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy +// import type { AzExtLocation } from '@microsoft/vscode-azext-azureutils'; // These are only dev-time imports so don't need to be lazy +// import { AzureWizardExecuteStep, nonNullProp, nonNullValueAndProp } from "@microsoft/vscode-azext-utils"; +// import { Progress, l10n } from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; +// import { getAzExtAppService, getAzExtAzureUtils } from '../../../utils/lazyPackages'; +// import { IAppServiceContainerWizardContext } from './deployImageToAzure'; + +// export class DockerSiteCreateStep extends AzureWizardExecuteStep { +// public priority: number = 140; + +// public constructor(private readonly node: UnifiedRegistryItem) { +// super(); +// } + +// public async execute(context: IAppServiceContainerWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise { +// const creatingNewApp: string = l10n.t('Creating web app "{0}"...', context.newSiteName); +// ext.outputChannel.info(creatingNewApp); +// progress.report({ message: creatingNewApp }); +// const siteConfig = await this.getNewSiteConfig(context); + +// const azExtAzureUtils = await getAzExtAzureUtils(); +// const vscAzureAppService = await getAzExtAppService(); + +// const location: AzExtLocation = await azExtAzureUtils.LocationListStep.getLocation(context); +// const locationName: string = nonNullProp(location, 'name'); + +// const client: WebSiteManagementClient = await vscAzureAppService.createWebSiteClient(context); +// const siteEnvelope: Site = { +// name: context.newSiteName, +// location: locationName, +// serverFarmId: nonNullValueAndProp(context.plan, 'id'), +// siteConfig: siteConfig +// }; + +// if (context.customLocation) { +// // deploying to Azure Arc +// siteEnvelope.kind = 'app,linux,kubernetes,container'; +// await this.addCustomLocationProperties(siteEnvelope, context.customLocation); +// } else { +// siteEnvelope.identity = { +// type: 'SystemAssigned' +// }; +// } + +// context.site = await client.webApps.beginCreateOrUpdateAndWait(nonNullValueAndProp(context.resourceGroup, 'name'), nonNullProp(context, 'newSiteName'), siteEnvelope); +// } + +// private async getNewSiteConfig(context: IAppServiceContainerWizardContext): Promise { +// const registryTI: UnifiedRegistryItem = this.node.parent.parent; + +// let username: string | undefined; +// let password: string | undefined; +// let registryUrl: string | undefined; +// const appSettings: NameValuePair[] = []; + +// // Scenarios: +// // ACR -> App Service, NOT Arc App Service. Use managed service identity. +// if (registryTI instanceof AzureRegistryTreeItem && !context.customLocation) { +// appSettings.push({ name: 'DOCKER_ENABLE_CI', value: 'true' }); + +// // Don't need an image, username, or password--just create an empty web app to assign permissions and then configure with an image +// // `DockerAssignAcrPullRoleStep` handles it after that +// return { +// acrUseManagedIdentityCreds: true, +// appSettings +// }; +// } +// // ACR -> Arc App Service. Use regular auth. Same as any V2 registry but different way of getting auth. +// else if (registryTI instanceof AzureRegistryTreeItem && context.customLocation) { +// const cred = await registryTI.tryGetAdminCredentials(context); +// if (!cred?.username || !cred?.passwords?.[0]?.value) { +// throw new Error(l10n.t('Azure App service deployment on Azure Arc only supports running images from Azure Container Registries with admin enabled')); +// } + +// username = cred.username; +// password = cred.passwords[0].value; +// registryUrl = registryTI.baseUrl; +// } +// // Docker Hub -> App Service *OR* Arc App Service +// else if (registryTI instanceof DockerHubNamespaceTreeItem) { +// username = registryTI.parent.username; +// password = await registryTI.parent.getPassword(); +// registryUrl = 'https://index.docker.io'; +// } +// // Generic registry -> App Service *OR* Arc App Service +// else if (registryTI instanceof DockerV2RegistryTreeItemBase) { +// if (registryTI instanceof GenericDockerV2RegistryTreeItem) { +// username = registryTI.cachedProvider.username; +// password = await getRegistryPassword(registryTI.cachedProvider); +// } else { +// throw new RangeError(l10n.t('Unrecognized node type "{0}"', registryTI.constructor.name)); +// } + +// registryUrl = registryTI.baseUrl; +// } else { +// throw new RangeError(l10n.t('Unrecognized node type "{0}"', registryTI.constructor.name)); +// } + +// if (username && password) { +// appSettings.push({ name: "DOCKER_REGISTRY_SERVER_USERNAME", value: username }); +// appSettings.push({ name: "DOCKER_REGISTRY_SERVER_PASSWORD", value: password }); +// } + +// if (registryUrl) { +// appSettings.push({ name: 'DOCKER_REGISTRY_SERVER_URL', value: registryUrl }); +// } + +// if (context.webSitesPort) { +// appSettings.push({ name: "WEBSITES_PORT", value: context.webSitesPort.toString() }); +// } + +// const linuxFxVersion = `DOCKER|${this.node.fullTag}`; +// TODO: review this later +// const linuxFxVersion = ''; +// return { +// linuxFxVersion, +// appSettings +// }; +// } + +// private addCustomLocationProperties(site: Site, customLocation: CustomLocation): void { +// site.extendedLocation = { name: customLocation.id, type: 'customLocation' }; +// } + +// public shouldExecute(context: IAppServiceContainerWizardContext): boolean { +// return !context.site; +// } +// } + +// TODO: review this later diff --git a/src/commands/registries/azure/DockerWebhookCreateStep.ts b/src/commands/registries/azure/DockerWebhookCreateStep.ts index c6cb6cc240..25f56c23d1 100644 --- a/src/commands/registries/azure/DockerWebhookCreateStep.ts +++ b/src/commands/registries/azure/DockerWebhookCreateStep.ts @@ -1,96 +1,96 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import type { Site } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy -import type { Webhook, WebhookCreateParameters } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy -import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy -import { AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils"; -import * as vscode from "vscode"; -import { ext } from "../../../extensionVariables"; -import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { AzureRepositoryTreeItem } from '../../../tree/registries/azure/AzureRepositoryTreeItem'; -import { DockerHubRepositoryTreeItem } from '../../../tree/registries/dockerHub/DockerHubRepositoryTreeItem'; -import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; -import { cryptoUtils } from '../../../utils/cryptoUtils'; -import { getArmContainerRegistry, getAzExtAppService, getAzExtAzureUtils } from "../../../utils/lazyPackages"; +// import type { Site } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy +// import type { Webhook, WebhookCreateParameters } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy +// import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy +// import { AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils"; +// import * as vscode from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; +// import { AzureRepositoryTreeItem } from '../../../tree/registries/azure/AzureRepositoryTreeItem'; +// import { DockerHubRepositoryTreeItem } from '../../../tree/registries/dockerHub/DockerHubRepositoryTreeItem'; +// import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; +// import { cryptoUtils } from '../../../utils/cryptoUtils'; +// import { getArmContainerRegistry, getAzExtAppService, getAzExtAzureUtils } from "../../../utils/lazyPackages"; -export class DockerWebhookCreateStep extends AzureWizardExecuteStep { - public priority: number = 142; // execute after DockerAssignAcrPullRoleStep - private _treeItem: RemoteTagTreeItem; - public constructor(treeItem: RemoteTagTreeItem) { - super(); - this._treeItem = treeItem; - } +// export class DockerWebhookCreateStep extends AzureWizardExecuteStep { +// public priority: number = 142; // execute after DockerAssignAcrPullRoleStep +// private _treeItem: RemoteTagTreeItem; +// public constructor(treeItem: RemoteTagTreeItem) { +// super(); +// this._treeItem = treeItem; +// } - public async execute(context: IAppServiceWizardContext, progress: vscode.Progress<{ - message?: string; - increment?: number; - }>): Promise { - const vscAzureAppService = await getAzExtAppService(); - vscAzureAppService.registerAppServiceExtensionVariables(ext); - const site: Site = nonNullProp(context, 'site'); - const parsedSite = new vscAzureAppService.ParsedSite(site, context); - const siteClient = await parsedSite.createClient(context); - const appUri: string = (await siteClient.getWebAppPublishCredential()).scmUri; - if (this._treeItem.parent instanceof AzureRepositoryTreeItem) { - const creatingNewWebhook: string = vscode.l10n.t('Creating webhook for web app "{0}"...', context.newSiteName); - ext.outputChannel.info(creatingNewWebhook); - progress.report({ message: creatingNewWebhook }); - const webhook = await this.createWebhookForApp(context, this._treeItem, context.site, appUri); - ext.outputChannel.info(vscode.l10n.t('Created webhook "{0}" with scope "{1}", id: "{2}" and location: "{3}"', webhook.name, webhook.scope, webhook.id, webhook.location)); - } else if (this._treeItem.parent instanceof DockerHubRepositoryTreeItem) { - // point to dockerhub to create a webhook - // http://cloud.docker.com/repository/docker///webHooks - const dockerhubPrompt: string = vscode.l10n.t('Copy & Open'); - const dockerhubUri: string = `https://cloud.docker.com/repository/docker/${this._treeItem.parent.parent.namespace}/${this._treeItem.parent.repoName}/webHooks`; +// public async execute(context: IAppServiceWizardContext, progress: vscode.Progress<{ +// message?: string; +// increment?: number; +// }>): Promise { +// const vscAzureAppService = await getAzExtAppService(); +// vscAzureAppService.registerAppServiceExtensionVariables(ext); +// const site: Site = nonNullProp(context, 'site'); +// const parsedSite = new vscAzureAppService.ParsedSite(site, context); +// const siteClient = await parsedSite.createClient(context); +// const appUri: string = (await siteClient.getWebAppPublishCredential()).scmUri; +// if (this._treeItem.parent instanceof AzureRepositoryTreeItem) { +// const creatingNewWebhook: string = vscode.l10n.t('Creating webhook for web app "{0}"...', context.newSiteName); +// ext.outputChannel.info(creatingNewWebhook); +// progress.report({ message: creatingNewWebhook }); +// const webhook = await this.createWebhookForApp(context, this._treeItem, context.site, appUri); +// ext.outputChannel.info(vscode.l10n.t('Created webhook "{0}" with scope "{1}", id: "{2}" and location: "{3}"', webhook.name, webhook.scope, webhook.id, webhook.location)); +// } else if (this._treeItem.parent instanceof DockerHubRepositoryTreeItem) { +// // point to dockerhub to create a webhook +// // http://cloud.docker.com/repository/docker///webHooks +// const dockerhubPrompt: string = vscode.l10n.t('Copy & Open'); +// const dockerhubUri: string = `https://cloud.docker.com/repository/docker/${this._treeItem.parent.parent.namespace}/${this._treeItem.parent.repoName}/webHooks`; - // NOTE: The response to the information message is not awaited but handled independently of the wizard steps. - // VS Code will hide such messages in the notifications pane after a period of time; awaiting them risks - // the user never noticing them in the first place, which means the wizard would never complete, and the - // user left with the impression that the action never completes. +// // NOTE: The response to the information message is not awaited but handled independently of the wizard steps. +// // VS Code will hide such messages in the notifications pane after a period of time; awaiting them risks +// // the user never noticing them in the first place, which means the wizard would never complete, and the +// // user left with the impression that the action never completes. - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - vscode.window - .showInformationMessage(vscode.l10n.t('To set up a CI/CD webhook, open the page "{0}" and enter the URI to the created web app in your dockerhub account', dockerhubUri), dockerhubPrompt) - .then(response => { - if (response) { - void vscode.env.clipboard.writeText(appUri); - void vscode.env.openExternal(vscode.Uri.parse(dockerhubUri)); - } - }); - } - } +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// vscode.window +// .showInformationMessage(vscode.l10n.t('To set up a CI/CD webhook, open the page "{0}" and enter the URI to the created web app in your dockerhub account', dockerhubUri), dockerhubPrompt) +// .then(response => { +// if (response) { +// void vscode.env.clipboard.writeText(appUri); +// void vscode.env.openExternal(vscode.Uri.parse(dockerhubUri)); +// } +// }); +// } +// } - public shouldExecute(context: IAppServiceWizardContext): boolean { - return !!context.site && (this._treeItem.parent instanceof AzureRepositoryTreeItem || this._treeItem.parent instanceof DockerHubRepositoryTreeItem); - } +// public shouldExecute(context: IAppServiceWizardContext): boolean { +// return !!context.site && (this._treeItem.parent instanceof AzureRepositoryTreeItem || this._treeItem.parent instanceof DockerHubRepositoryTreeItem); +// } - private async createWebhookForApp(context: IAppServiceWizardContext, node: RemoteTagTreeItem, site: Site, appUri: string): Promise { - const maxLength: number = 50; - const numRandomChars: number = 6; +// private async createWebhookForApp(context: IAppServiceWizardContext, node: RemoteTagTreeItem, site: Site, appUri: string): Promise { +// const maxLength: number = 50; +// const numRandomChars: number = 6; - let webhookName: string = site.name; - // remove disallowed characters - webhookName = webhookName.replace(/[^a-zA-Z0-9]/g, ''); - // trim to max length - webhookName = webhookName.substr(0, maxLength - numRandomChars); - // add random chars for uniqueness and to ensure min length is met - webhookName += cryptoUtils.getRandomHexString(numRandomChars); +// let webhookName: string = site.name; +// // remove disallowed characters +// webhookName = webhookName.replace(/[^a-zA-Z0-9]/g, ''); +// // trim to max length +// webhookName = webhookName.substr(0, maxLength - numRandomChars); +// // add random chars for uniqueness and to ensure min length is met +// webhookName += cryptoUtils.getRandomHexString(numRandomChars); - // variables derived from the container registry - const registryTreeItem: AzureRegistryTreeItem = (node.parent).parent; - const armContainerRegistry = await getArmContainerRegistry(); - const azExtAzureUtils = await getAzExtAzureUtils(); - const crmClient = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); - const webhookCreateParameters: WebhookCreateParameters = { - location: registryTreeItem.registryLocation, - serviceUri: appUri, - scope: `${node.parent.repoName}:${node.tag}`, - actions: ["push"], - status: 'enabled' - }; - return await crmClient.webhooks.beginCreateAndWait(registryTreeItem.resourceGroup, registryTreeItem.registryName, webhookName, webhookCreateParameters); - } -} +// // variables derived from the container registry +// const registryTreeItem: AzureRegistryTreeItem = (node.parent).parent; +// const armContainerRegistry = await getArmContainerRegistry(); +// const azExtAzureUtils = await getAzExtAzureUtils(); +// const crmClient = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); +// const webhookCreateParameters: WebhookCreateParameters = { +// location: registryTreeItem.registryLocation, +// serviceUri: appUri, +// scope: `${node.parent.repoName}:${node.tag}`, +// actions: ["push"], +// status: 'enabled' +// }; +// return await crmClient.webhooks.beginCreateAndWait(registryTreeItem.resourceGroup, registryTreeItem.registryName, webhookName, webhookCreateParameters); +// } +// } diff --git a/src/commands/registries/azure/WebSitesPortPromptStep.ts b/src/commands/registries/azure/WebSitesPortPromptStep.ts index 48667641c0..e16ca26dab 100644 --- a/src/commands/registries/azure/WebSitesPortPromptStep.ts +++ b/src/commands/registries/azure/WebSitesPortPromptStep.ts @@ -1,35 +1,35 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils"; -import { l10n } from 'vscode'; -import { IAppServiceContainerWizardContext } from './deployImageToAzure'; +// import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils"; +// import { l10n } from 'vscode'; +// import { IAppServiceContainerWizardContext } from './deployImageToAzure'; -export class WebSitesPortPromptStep extends AzureWizardPromptStep { +// export class WebSitesPortPromptStep extends AzureWizardPromptStep { - public async prompt(context: IAppServiceContainerWizardContext): Promise { - const prompt: string = l10n.t('What port does your app listen on?'); - const placeHolder: string = '80'; - const value: string = '80'; - const portString: string = await context.ui.showInputBox({ prompt, placeHolder, value, validateInput }); - context.webSitesPort = parseInt(portString, 10); - } +// public async prompt(context: IAppServiceContainerWizardContext): Promise { +// const prompt: string = l10n.t('What port does your app listen on?'); +// const placeHolder: string = '80'; +// const value: string = '80'; +// const portString: string = await context.ui.showInputBox({ prompt, placeHolder, value, validateInput }); +// context.webSitesPort = parseInt(portString, 10); +// } - public shouldPrompt(context: IAppServiceContainerWizardContext): boolean { - return !!context.customLocation; - } -} +// public shouldPrompt(context: IAppServiceContainerWizardContext): boolean { +// return !!context.customLocation; +// } +// } -function validateInput(value: string | undefined): string | undefined { - if (Number(value)) { - const port: number = parseInt(value, 10); - if (port >= 1 && port <= 65535) { - return undefined; - } - } +// function validateInput(value: string | undefined): string | undefined { +// if (Number(value)) { +// const port: number = parseInt(value, 10); +// if (port >= 1 && port <= 65535) { +// return undefined; +// } +// } - return l10n.t('Port must be a positive integer (1 to 65535).'); -} +// return l10n.t('Port must be a positive integer (1 to 65535).'); +// } diff --git a/src/commands/registries/azure/createAzureRegistry.ts b/src/commands/registries/azure/createAzureRegistry.ts index e68cf2c0a4..a569cd5833 100644 --- a/src/commands/registries/azure/createAzureRegistry.ts +++ b/src/commands/registries/azure/createAzureRegistry.ts @@ -4,16 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { IActionContext } from '@microsoft/vscode-azext-utils'; -import { ext } from '../../../extensionVariables'; -import type { SubscriptionTreeItem } from '../../../tree/registries/azure/SubscriptionTreeItem'; // These are only dev-time imports so don't need to be lazy -import { getAzSubTreeItem } from '../../../utils/lazyPackages'; +// import type { SubscriptionTreeItem } from '../../../tree/registries/azure/SubscriptionTreeItem'; // These are only dev-time imports so don't need to be lazy +import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; +// import { getAzSubTreeItem } from '../../../utils/lazyPackages'; -export async function createAzureRegistry(context: IActionContext, node?: SubscriptionTreeItem): Promise { - const azSubTreeItem = await getAzSubTreeItem(); +export async function createAzureRegistry(context: IActionContext, node?: UnifiedRegistryItem): Promise { + // const azSubTreeItem = await getAzSubTreeItem(); if (!node) { - node = await ext.registriesTree.showTreeItemPicker(azSubTreeItem.SubscriptionTreeItem.contextValue, context); + // node = await ext.registriesTree.showTreeItemPicker(azSubTreeItem.SubscriptionTreeItem.contextValue, context); } - await node.createChild(context); + // await node.createChild(context); + // TODO: review this later } diff --git a/src/commands/registries/azure/deleteAzureRegistry.ts b/src/commands/registries/azure/deleteAzureRegistry.ts index 862a3582d6..bfac41cc95 100644 --- a/src/commands/registries/azure/deleteAzureRegistry.ts +++ b/src/commands/registries/azure/deleteAzureRegistry.ts @@ -1,30 +1,30 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; -import { l10n, ProgressLocation, window } from 'vscode'; -import { ext } from '../../../extensionVariables'; -import type { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; +// import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; +// import { l10n, ProgressLocation, window } from 'vscode'; +// import { ext } from '../../../extensionVariables'; +// import type { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; +// import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; -export async function deleteAzureRegistry(context: IActionContext, node?: AzureRegistryTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, { ...context, suppressCreatePick: true }); - } +// export async function deleteAzureRegistry(context: IActionContext, node?: AzureRegistryTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, { ...context, suppressCreatePick: true }); +// } - const confirmDelete: string = l10n.t('Are you sure you want to delete registry "{0}" and its associated images?', node.registryName); - // no need to check result - cancel will throw a UserCancelledError - await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); +// const confirmDelete: string = l10n.t('Are you sure you want to delete registry "{0}" and its associated images?', node.registryName); +// // no need to check result - cancel will throw a UserCancelledError +// await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); - const deleting = l10n.t('Deleting registry "{0}"...', node.registryName); - await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { - await node.deleteTreeItem(context); - }); +// const deleting = l10n.t('Deleting registry "{0}"...', node.registryName); +// await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { +// await node.deleteTreeItem(context); +// }); - const message = l10n.t('Successfully deleted registry "{0}".', node.registryName); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(message); -} +// const message = l10n.t('Successfully deleted registry "{0}".', node.registryName); +// // don't wait +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// window.showInformationMessage(message); +// } diff --git a/src/commands/registries/azure/deleteAzureRepository.ts b/src/commands/registries/azure/deleteAzureRepository.ts index c3be2f59f5..57e8e4de49 100644 --- a/src/commands/registries/azure/deleteAzureRepository.ts +++ b/src/commands/registries/azure/deleteAzureRepository.ts @@ -1,30 +1,30 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; -import { l10n, ProgressLocation, window } from 'vscode'; -import { ext } from '../../../extensionVariables'; -import type { AzureRepositoryTreeItem } from '../../../tree/registries/azure/AzureRepositoryTreeItem'; -import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; +// import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; +// import { l10n, ProgressLocation, window } from 'vscode'; +// import { ext } from '../../../extensionVariables'; +// import type { AzureRepositoryTreeItem } from '../../../tree/registries/azure/AzureRepositoryTreeItem'; +// import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; -export async function deleteAzureRepository(context: IActionContext, node?: AzureRepositoryTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.repository, { ...context, suppressCreatePick: true }); - } +// export async function deleteAzureRepository(context: IActionContext, node?: AzureRepositoryTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.repository, { ...context, suppressCreatePick: true }); +// } - const confirmDelete = l10n.t('Are you sure you want to delete repository "{0}" and its associated images?', node.repoName); - // no need to check result - cancel will throw a UserCancelledError - await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); +// const confirmDelete = l10n.t('Are you sure you want to delete repository "{0}" and its associated images?', node.repoName); +// // no need to check result - cancel will throw a UserCancelledError +// await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); - const deleting = l10n.t('Deleting repository "{0}"...', node.repoName); - await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { - await node.deleteTreeItem(context); - }); +// const deleting = l10n.t('Deleting repository "{0}"...', node.repoName); +// await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { +// await node.deleteTreeItem(context); +// }); - const deleteSucceeded = l10n.t('Successfully deleted repository "{0}".', node.repoName); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(deleteSucceeded); -} +// const deleteSucceeded = l10n.t('Successfully deleted repository "{0}".', node.repoName); +// // don't wait +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// window.showInformationMessage(deleteSucceeded); +// } diff --git a/src/commands/registries/azure/deployImageToAca.ts b/src/commands/registries/azure/deployImageToAca.ts index f31a57c7cd..c2fa541724 100644 --- a/src/commands/registries/azure/deployImageToAca.ts +++ b/src/commands/registries/azure/deployImageToAca.ts @@ -3,16 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext, nonNullProp, UserCancelledError } from '@microsoft/vscode-azext-utils'; -import { parseDockerLikeImageName } from '@microsoft/vscode-container-client'; +import { contextValueExperience, IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; import * as semver from 'semver'; import * as vscode from 'vscode'; import { ext } from '../../../extensionVariables'; -import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { DockerHubNamespaceTreeItem } from '../../../tree/registries/dockerHub/DockerHubNamespaceTreeItem'; -import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; -import { RegistryTreeItemBase } from '../../../tree/registries/RegistryTreeItemBase'; -import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; +import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; import { installExtension } from '../../../utils/installExtension'; import { addImageTaggingTelemetry } from '../../images/tagImage'; @@ -27,7 +22,7 @@ interface DeployImageToAcaOptionsContract { secret?: string; } -export async function deployImageToAca(context: IActionContext, node?: RemoteTagTreeItem): Promise { +export async function deployImageToAca(context: IActionContext, node?: UnifiedRegistryItem): Promise { // Assert installation of the ACA extension if (!isAcaExtensionInstalled()) { // This will always throw a `UserCancelledError` but with the appropriate step name @@ -36,40 +31,41 @@ export async function deployImageToAca(context: IActionContext, node?: RemoteTag } if (!node) { - node = await ext.registriesTree.showTreeItemPicker([registryExpectedContextValues.dockerHub.tag, registryExpectedContextValues.dockerV2.tag], context); + node = await contextValueExperience(context, ext.registriesRoot, { include: 'azurecontainerregistry' }); } const commandOptions: Partial = { - image: node.fullTag, + // image: node.fullTag, }; addImageTaggingTelemetry(context, commandOptions.image, ''); - const registry: RegistryTreeItemBase = node.parent.parent; - if (registry instanceof AzureRegistryTreeItem) { - // No additional work to do; ACA can handle this on its own - } else { - const { auth } = await registry.getDockerCliCredentials() as { auth?: { username?: string, password?: string } }; - - if (!auth?.username || !auth?.password) { - throw new Error(vscode.l10n.t('No credentials found for registry "{0}".', registry.label)); - } - - if (registry instanceof DockerHubNamespaceTreeItem) { - // Ensure Docker Hub images are prefixed with 'docker.io/...' - if (!/^docker\.io\//i.test(commandOptions.image)) { - commandOptions.image = 'docker.io/' + commandOptions.image; - } - } - - commandOptions.username = auth.username; - commandOptions.secret = auth.password; - } + // const registry: RegistryTreeItemBase = node.parent.parent; + // if (registry instanceof AzureRegistryTreeItem) { + // // No additional work to do; ACA can handle this on its own + // } else { + // const { auth } = await registry.getDockerCliCredentials() as { auth?: { username?: string, password?: string } }; + + // if (!auth?.username || !auth?.password) { + // throw new Error(vscode.l10n.t('No credentials found for registry "{0}".', registry.label)); + // } + + // if (registry instanceof DockerHubNamespaceTreeItem) { + // // Ensure Docker Hub images are prefixed with 'docker.io/...' + // if (!/^docker\.io\//i.test(commandOptions.image)) { + // commandOptions.image = 'docker.io/' + commandOptions.image; + // } + // } + + // commandOptions.username = auth.username; + // commandOptions.secret = auth.password; + // } - commandOptions.registryName = nonNullProp(parseDockerLikeImageName(commandOptions.image), 'registry'); + // commandOptions.registryName = nonNullProp(parseDockerLikeImageName(commandOptions.image), 'registry'); - // Don't wait - void vscode.commands.executeCommand('containerApps.deployImageApi', commandOptions); + // // Don't wait + // void vscode.commands.executeCommand('containerApps.deployImageApi', commandOptions); + // TODO: review this later } function isAcaExtensionInstalled(): boolean { diff --git a/src/commands/registries/azure/deployImageToAzure.ts b/src/commands/registries/azure/deployImageToAzure.ts index bbc3cee850..375982c4ea 100644 --- a/src/commands/registries/azure/deployImageToAzure.ts +++ b/src/commands/registries/azure/deployImageToAzure.ts @@ -1,80 +1,80 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import type { Site } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy -import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy -import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { env, l10n, Uri, window } from "vscode"; -import { ext } from "../../../extensionVariables"; -import { RegistryApi } from '../../../tree/registries/all/RegistryApi'; -import { azureRegistryProviderId } from '../../../tree/registries/azure/azureRegistryProvider'; -import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; -import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; -import { getAzActTreeItem, getAzExtAppService, getAzExtAzureUtils } from '../../../utils/lazyPackages'; -import { DockerAssignAcrPullRoleStep } from './DockerAssignAcrPullRoleStep'; -import { DockerSiteCreateStep } from './DockerSiteCreateStep'; -import { DockerWebhookCreateStep } from './DockerWebhookCreateStep'; -import { WebSitesPortPromptStep } from './WebSitesPortPromptStep'; +// import type { Site } from '@azure/arm-appservice'; // These are only dev-time imports so don't need to be lazy +// import type { IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice"; // These are only dev-time imports so don't need to be lazy +// import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; +// import { env, l10n, Uri, window } from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { RegistryApi } from '../../../tree/registries/all/RegistryApi'; +// import { azureRegistryProviderId } from '../../../tree/registries/azure/azureRegistryProvider'; +// import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; +// import { RemoteTagTreeItem } from '../../../tree/registries/RemoteTagTreeItem'; +// import { getAzActTreeItem, getAzExtAppService, getAzExtAzureUtils } from '../../../utils/lazyPackages'; +// import { DockerAssignAcrPullRoleStep } from './DockerAssignAcrPullRoleStep'; +// import { DockerSiteCreateStep } from './DockerSiteCreateStep'; +// import { DockerWebhookCreateStep } from './DockerWebhookCreateStep'; +// import { WebSitesPortPromptStep } from './WebSitesPortPromptStep'; -export interface IAppServiceContainerWizardContext extends IAppServiceWizardContext { - webSitesPort?: number; -} +// export interface IAppServiceContainerWizardContext extends IAppServiceWizardContext { +// webSitesPort?: number; +// } -export async function deployImageToAzure(context: IActionContext, node?: RemoteTagTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker([registryExpectedContextValues.dockerHub.tag, registryExpectedContextValues.dockerV2.tag], context); - } +// export async function deployImageToAzure(context: IActionContext, node?: RemoteTagTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker([registryExpectedContextValues.dockerHub.tag, registryExpectedContextValues.dockerV2.tag], context); +// } - const azExtAzureUtils = await getAzExtAzureUtils(); - const vscAzureAppService = await getAzExtAppService(); - const azActTreeItem = await getAzActTreeItem(); +// const azExtAzureUtils = await getAzExtAzureUtils(); +// const vscAzureAppService = await getAzExtAppService(); +// const azActTreeItem = await getAzActTreeItem(); - const wizardContext: IActionContext & Partial = { - ...context, - newSiteOS: vscAzureAppService.WebsiteOS.linux, - newSiteKind: vscAzureAppService.AppKind.app - }; - const promptSteps: AzureWizardPromptStep[] = []; - // Create a temporary azure account tree item since Azure might not be connected - const azureAccountTreeItem = new azActTreeItem.AzureAccountTreeItem(ext.registriesRoot, { id: azureRegistryProviderId, api: RegistryApi.DockerV2 }); - const subscriptionStep = await azureAccountTreeItem.getSubscriptionPromptStep(wizardContext); - if (subscriptionStep) { - promptSteps.push(subscriptionStep); - } +// const wizardContext: IActionContext & Partial = { +// ...context, +// newSiteOS: vscAzureAppService.WebsiteOS.linux, +// newSiteKind: vscAzureAppService.AppKind.app +// }; +// const promptSteps: AzureWizardPromptStep[] = []; +// // Create a temporary azure account tree item since Azure might not be connected +// const azureAccountTreeItem = new azActTreeItem.AzureAccountTreeItem(ext.registriesRoot, { id: azureRegistryProviderId, api: RegistryApi.DockerV2 }); +// const subscriptionStep = await azureAccountTreeItem.getSubscriptionPromptStep(wizardContext); +// if (subscriptionStep) { +// promptSteps.push(subscriptionStep); +// } - promptSteps.push(new vscAzureAppService.SiteNameStep()); - promptSteps.push(new azExtAzureUtils.ResourceGroupListStep()); - vscAzureAppService.CustomLocationListStep.addStep(wizardContext, promptSteps); - promptSteps.push(new WebSitesPortPromptStep()); - promptSteps.push(new vscAzureAppService.AppServicePlanListStep()); +// promptSteps.push(new vscAzureAppService.SiteNameStep()); +// promptSteps.push(new azExtAzureUtils.ResourceGroupListStep()); +// vscAzureAppService.CustomLocationListStep.addStep(wizardContext, promptSteps); +// promptSteps.push(new WebSitesPortPromptStep()); +// promptSteps.push(new vscAzureAppService.AppServicePlanListStep()); - // Get site config before running the wizard so that any problems with the tag tree item are shown at the beginning of the process - const executeSteps: AzureWizardExecuteStep[] = [ - new DockerSiteCreateStep(node), - new DockerAssignAcrPullRoleStep(node), - new DockerWebhookCreateStep(node), - ]; +// // Get site config before running the wizard so that any problems with the tag tree item are shown at the beginning of the process +// const executeSteps: AzureWizardExecuteStep[] = [ +// new DockerSiteCreateStep(node), +// new DockerAssignAcrPullRoleStep(node), +// new DockerWebhookCreateStep(node), +// ]; - const title = l10n.t('Create new web app'); - const wizard = new AzureWizard(wizardContext, { title, promptSteps, executeSteps }); - await wizard.prompt(); - await wizard.execute(); +// const title = l10n.t('Create new web app'); +// const wizard = new AzureWizard(wizardContext, { title, promptSteps, executeSteps }); +// await wizard.prompt(); +// await wizard.execute(); - const site: Site = nonNullProp(wizardContext, 'site'); - const siteUri: string = `https://${site.defaultHostName}`; - const createdNewWebApp: string = l10n.t('Successfully created web app "{0}": {1}', site.name, siteUri); - ext.outputChannel.info(createdNewWebApp); +// const site: Site = nonNullProp(wizardContext, 'site'); +// const siteUri: string = `https://${site.defaultHostName}`; +// const createdNewWebApp: string = l10n.t('Successfully created web app "{0}": {1}', site.name, siteUri); +// ext.outputChannel.info(createdNewWebApp); - const openSite: string = l10n.t('Open Site'); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(createdNewWebApp, ...[openSite]).then((selection) => { - if (selection === openSite) { - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - env.openExternal(Uri.parse(siteUri)); - } - }); -} +// const openSite: string = l10n.t('Open Site'); +// // don't wait +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// window.showInformationMessage(createdNewWebApp, ...[openSite]).then((selection) => { +// if (selection === openSite) { +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// env.openExternal(Uri.parse(siteUri)); +// } +// }); +// } diff --git a/src/commands/registries/azure/openInAzurePortal.ts b/src/commands/registries/azure/openInAzurePortal.ts index bec849c454..71c284f7d3 100644 --- a/src/commands/registries/azure/openInAzurePortal.ts +++ b/src/commands/registries/azure/openInAzurePortal.ts @@ -4,26 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { IActionContext } from '@microsoft/vscode-azext-utils'; -import { ext } from '../../../extensionVariables'; -import { AzureRegistryTreeItem } from '../../../tree/registries/azure/AzureRegistryTreeItem'; -import { AzureRepositoryTreeItem } from '../../../tree/registries/azure/AzureRepositoryTreeItem'; -import type { SubscriptionTreeItem } from '../../../tree/registries/azure/SubscriptionTreeItem'; // These are only dev-time imports so don't need to be lazy -import { registryExpectedContextValues } from '../../../tree/registries/registryContextValues'; -import { getAzExtAzureUtils, getAzSubTreeItem } from '../../../utils/lazyPackages'; +import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; -export async function openInAzurePortal(context: IActionContext, node?: SubscriptionTreeItem | AzureRegistryTreeItem | AzureRepositoryTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); - } +export async function openInAzurePortal(context: IActionContext, node?: UnifiedRegistryItem): Promise { + // if (!node) { + // node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); + // } - const azSubTreeItem = await getAzSubTreeItem(); - const azExtAzureUtils = await getAzExtAzureUtils(); + // const azSubTreeItem = await getAzSubTreeItem(); + // const azExtAzureUtils = await getAzExtAzureUtils(); - if (node instanceof azSubTreeItem.SubscriptionTreeItem) { - await azExtAzureUtils.openInPortal(node.subscription, node.subscription.subscriptionId); - } else if (node instanceof AzureRegistryTreeItem) { - await azExtAzureUtils.openInPortal(node.parent.subscription, node.registryId); - } else { - await azExtAzureUtils.openInPortal(node.parent.parent.subscription, `${node.parent.registryId}/repository`); - } + // if (node instanceof azSubTreeItem.SubscriptionTreeItem) { + // await azExtAzureUtils.openInPortal(node.subscription, node.subscription.subscriptionId); + // } else if (node instanceof AzureRegistryTreeItem) { + // await azExtAzureUtils.openInPortal(node.parent.subscription, node.registryId); + // } else { + // await azExtAzureUtils.openInPortal(node.parent.parent.subscription, `${node.parent.registryId}/repository`); + // } + + // TODO: review this later } diff --git a/src/commands/registries/azure/tasks/buildImageInAzure.ts b/src/commands/registries/azure/tasks/buildImageInAzure.ts index 346606bde5..f2d25ba469 100644 --- a/src/commands/registries/azure/tasks/buildImageInAzure.ts +++ b/src/commands/registries/azure/tasks/buildImageInAzure.ts @@ -1,37 +1,36 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; -import type { Run } from '@azure/arm-containerregistry'; -import { scheduleRunRequest, RootStrategy } from './scheduleRunRequest'; -import { delay } from '../../../../utils/promiseUtils'; -import { getArmContainerRegistry } from '../../../../utils/lazyPackages'; +// import type { Run } from '@azure/arm-containerregistry'; +// import { IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; +// import * as vscode from 'vscode'; +// import { getArmContainerRegistry } from '../../../../utils/lazyPackages'; +// import { delay } from '../../../../utils/promiseUtils'; -const WAIT_MS = 5000; +// const WAIT_MS = 5000; -export async function buildImageInAzure(context: IActionContext, uri?: vscode.Uri | undefined, rootStrategy?: RootStrategy | undefined): Promise { - if (!vscode.workspace.isTrusted) { - throw new UserCancelledError('enforceTrust'); - } +// export async function buildImageInAzure(context: IActionContext, uri?: vscode.Uri | undefined, rootStrategy?: RootStrategy | undefined): Promise { +// if (!vscode.workspace.isTrusted) { +// throw new UserCancelledError('enforceTrust'); +// } - const getRun = await scheduleRunRequest(context, "DockerBuildRequest", uri, rootStrategy); - - let run = await getRun(); - const { KnownRunStatus } = await getArmContainerRegistry(); - while ( - run.status === KnownRunStatus.Started || - run.status === KnownRunStatus.Queued || - run.status === KnownRunStatus.Running - ) { - await delay(WAIT_MS); - run = await getRun(); - } +// const getRun = await scheduleRunRequest(context, "DockerBuildRequest", uri, rootStrategy); - // we are returning the run so that other extensions can consume this with the vscode.commands.executeCommand - // currently it is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) - return run; -} +// let run = await getRun(); +// const { KnownRunStatus } = await getArmContainerRegistry(); +// while ( +// run.status === KnownRunStatus.Started || +// run.status === KnownRunStatus.Queued || +// run.status === KnownRunStatus.Running +// ) { +// await delay(WAIT_MS); +// run = await getRun(); +// } + +// // we are returning the run so that other extensions can consume this with the vscode.commands.executeCommand +// // currently it is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) +// return run; +// } diff --git a/src/commands/registries/azure/tasks/runAzureTask.ts b/src/commands/registries/azure/tasks/runAzureTask.ts index 0be5e31898..5c1d73865d 100644 --- a/src/commands/registries/azure/tasks/runAzureTask.ts +++ b/src/commands/registries/azure/tasks/runAzureTask.ts @@ -3,22 +3,19 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { TaskRunRequest } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy import { IActionContext } from "@microsoft/vscode-azext-utils"; -import { l10n, window } from "vscode"; -import { ext } from "../../../../extensionVariables"; -import { AzureTaskTreeItem } from "../../../../tree/registries/azure/AzureTaskTreeItem"; +import { UnifiedRegistryItem } from "../../../../tree/registries/UnifiedRegistryTreeDataProvider"; -export async function runAzureTask(context: IActionContext, node?: AzureTaskTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(AzureTaskTreeItem.contextValue, context); - } +export async function runAzureTask(context: IActionContext, node?: UnifiedRegistryItem): Promise { + // if (!node) { + // node = await ext.registriesTree.showTreeItemPicker(AzureTaskTreeItem.contextValue, context); + // } - const registryTI = node.parent.parent; - const runRequest: TaskRunRequest = { type: 'TaskRunRequest', taskId: node.id }; - const run = await (await registryTI.getClient(context)).registries.beginScheduleRunAndWait(registryTI.resourceGroup, registryTI.registryName, runRequest); - await node.parent.refresh(context); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(l10n.t('Successfully scheduled run "{0}" for task "{1}".', run.runId, node.taskName)); + // const registryTI = node.parent.parent; + // const runRequest: TaskRunRequest = { type: 'TaskRunRequest', taskId: node.id }; + // const run = await (await registryTI.getClient(context)).registries.beginScheduleRunAndWait(registryTI.resourceGroup, registryTI.registryName, runRequest); + // await node.parent.refresh(context); + // // don't wait + // /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + // window.showInformationMessage(l10n.t('Successfully scheduled run "{0}" for task "{1}".', run.runId, node.taskName)); } diff --git a/src/commands/registries/azure/tasks/runFileAsAzureTask.ts b/src/commands/registries/azure/tasks/runFileAsAzureTask.ts index 92e5ac28b9..f6ba0f5831 100644 --- a/src/commands/registries/azure/tasks/runFileAsAzureTask.ts +++ b/src/commands/registries/azure/tasks/runFileAsAzureTask.ts @@ -1,16 +1,16 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; -import { scheduleRunRequest } from './scheduleRunRequest'; +// import * as vscode from 'vscode'; +// import { IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; +// import { scheduleRunRequest } from './scheduleRunRequest'; -export async function runFileAsAzureTask(context: IActionContext, uri?: vscode.Uri): Promise { - if (!vscode.workspace.isTrusted) { - throw new UserCancelledError('enforceTrust'); - } +// export async function runFileAsAzureTask(context: IActionContext, uri?: vscode.Uri): Promise { +// if (!vscode.workspace.isTrusted) { +// throw new UserCancelledError('enforceTrust'); +// } - await scheduleRunRequest(context, "FileTaskRunRequest", uri); -} +// await scheduleRunRequest(context, "FileTaskRunRequest", uri); +// } diff --git a/src/commands/registries/azure/tasks/scheduleRunRequest.ts b/src/commands/registries/azure/tasks/scheduleRunRequest.ts index 66f0dfc5f9..e47f9d3460 100644 --- a/src/commands/registries/azure/tasks/scheduleRunRequest.ts +++ b/src/commands/registries/azure/tasks/scheduleRunRequest.ts @@ -1,217 +1,216 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import type { ContainerRegistryManagementClient, DockerBuildRequest as AcrDockerBuildRequest, FileTaskRunRequest as AcrFileTaskRunRequest, OS as AcrOS, Run as AcrRun } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy -import { IActionContext, IAzureQuickPickItem, nonNullProp } from '@microsoft/vscode-azext-utils'; -import * as fse from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import * as readline from 'readline'; -import * as tar from 'tar'; -import * as vscode from 'vscode'; -import { ext } from '../../../../extensionVariables'; -import { AzureRegistryTreeItem } from '../../../../tree/registries/azure/AzureRegistryTreeItem'; -import { registryExpectedContextValues } from "../../../../tree/registries/registryContextValues"; -import { getStorageBlob } from '../../../../utils/lazyPackages'; -import { delay } from '../../../../utils/promiseUtils'; -import { Item, quickPickDockerFileItem, quickPickYamlFileItem } from '../../../../utils/quickPickFile'; -import { quickPickWorkspaceFolder } from '../../../../utils/quickPickWorkspaceFolder'; -import { addImageTaggingTelemetry, getTagFromUserInput } from '../../../images/tagImage'; - -const idPrecision = 6; -const vcsIgnoreList = ['.git', '.gitignore', '.bzr', 'bzrignore', '.hg', '.hgignore', '.svn']; - -// this is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) -export enum RootStrategy { - Default = 'Default', - DockerfileFolder = 'DockerfileFolder', -} - -export async function scheduleRunRequest(context: IActionContext, requestType: 'DockerBuildRequest' | 'FileTaskRunRequest', uri: vscode.Uri | undefined, rootStrategy?: RootStrategy | undefined): Promise<() => Promise> { - // Acquire information. - let rootFolder: vscode.WorkspaceFolder; - let fileItem: Item; - let imageName: string; - if (requestType === 'DockerBuildRequest') { - rootFolder = await quickPickWorkspaceFolder(context, vscode.l10n.t('To quick build Docker files you must first open a folder or workspace in VS Code.')); - fileItem = await quickPickDockerFileItem(context, uri, rootFolder); - imageName = await quickPickImageName(context, rootFolder, fileItem); - } else if (requestType === 'FileTaskRunRequest') { - rootFolder = await quickPickWorkspaceFolder(context, vscode.l10n.t('To run a task from a .yaml file you must first open a folder or workspace in VS Code.')); - fileItem = await quickPickYamlFileItem(context, uri, rootFolder, vscode.l10n.t('To run a task from a .yaml file you must have yaml file in your VS Code workspace.')); - } else { - throw new Error(vscode.l10n.t('Run Request Type Currently not supported.')); - } - - const node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); - - const osPick = ['Linux', 'Windows'].map(item => >{ label: item, data: item }); - const osType: AcrOS = (await context.ui.showQuickPick(osPick, { placeHolder: vscode.l10n.t('Select image base OS') })).data; - - const tarFilePath: string = getTempSourceArchivePath(); - - try { - // Prepare to run. - ext.outputChannel.show(); - - let rootUri = rootFolder.uri; - if (rootStrategy === RootStrategy.DockerfileFolder) { - // changes the root to the folder where the Dockerfile is - // it is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) - rootUri = vscode.Uri.file(path.dirname(fileItem.absoluteFilePath)); - } - - const uploadedSourceLocation: string = await uploadSourceCode(await node.getClient(context), node.registryName, node.resourceGroup, rootUri, tarFilePath); - ext.outputChannel.info(vscode.l10n.t('Uploaded source code from {0}', tarFilePath)); - - let runRequest: AcrDockerBuildRequest | AcrFileTaskRunRequest; - if (requestType === 'DockerBuildRequest') { - runRequest = { - type: requestType, - imageNames: [imageName], - isPushEnabled: true, - sourceLocation: uploadedSourceLocation, - platform: { os: osType }, - dockerFilePath: path.relative(rootUri.fsPath, fileItem.absoluteFilePath) - }; - } else { - runRequest = { - type: 'FileTaskRunRequest', - taskFilePath: path.relative(rootUri.fsPath, fileItem.absoluteFilePath), - sourceLocation: uploadedSourceLocation, - platform: { os: osType } - }; - } - - // Schedule the run and Clean up. - ext.outputChannel.info(vscode.l10n.t('Set up run request')); - - const client = await node.getClient(context); - const run = await client.registries.beginScheduleRunAndWait(node.resourceGroup, node.registryName, runRequest); - ext.outputChannel.info(vscode.l10n.t('Scheduled run {0}', run.runId)); - - void streamLogs(context, node, run); - - // function returns the AcrRun info - return async () => client.runs.get(node.resourceGroup, node.registryName, run.runId); - } finally { - if (await fse.pathExists(tarFilePath)) { - await fse.unlink(tarFilePath); - } - } -} - -async function quickPickImageName(context: IActionContext, rootFolder: vscode.WorkspaceFolder, dockerFileItem: Item | undefined): Promise { - const absFilePath: string = path.join(rootFolder.uri.fsPath, dockerFileItem.relativeFilePath); - const dockerFileKey = `ACR_buildTag_${absFilePath}`; - const prevImageName: string | undefined = ext.context.workspaceState.get(dockerFileKey); - let suggestedImageName: string; - - if (!prevImageName) { - // Get imageName based on name of subfolder containing the Dockerfile, or else workspacefolder - suggestedImageName = path.basename(dockerFileItem.relativeFolderPath).toLowerCase(); - if (suggestedImageName === '.') { - suggestedImageName = path.basename(rootFolder.uri.fsPath).toLowerCase().replace(/\s/g, ''); - } - - suggestedImageName += ":{{.Run.ID}}"; - } else { - suggestedImageName = prevImageName; - } - - // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox - await delay(500); - - addImageTaggingTelemetry(context, suggestedImageName, '.before'); - const imageName: string = await getTagFromUserInput(context, suggestedImageName); - addImageTaggingTelemetry(context, imageName, '.after'); - - await ext.context.workspaceState.update(dockerFileKey, imageName); - return imageName; -} - -async function uploadSourceCode(client: ContainerRegistryManagementClient, registryName: string, resourceGroupName: string, rootFolder: vscode.Uri, tarFilePath: string): Promise { - ext.outputChannel.info(vscode.l10n.t(' Sending source code to temp file')); - const source: string = rootFolder.fsPath; - let items = await fse.readdir(source); - items = items.filter(i => !(i in vcsIgnoreList)); - // tslint:disable-next-line:no-unsafe-any - tar.c({ cwd: source }, items).pipe(fse.createWriteStream(tarFilePath)); - - ext.outputChannel.info(vscode.l10n.t(' Getting build source upload URL')); - const sourceUploadLocation = await client.registries.getBuildSourceUploadUrl(resourceGroupName, registryName); - const uploadUrl: string = sourceUploadLocation.uploadUrl; - const relativePath: string = sourceUploadLocation.relativePath; - - const storageBlob = await getStorageBlob(); - const blobClient = new storageBlob.BlockBlobClient(uploadUrl); - ext.outputChannel.info(vscode.l10n.t(' Creating block blob')); - await blobClient.uploadFile(tarFilePath); - - return relativePath; -} - -const blobCheckInterval = 1000; -const maxBlobChecks = 30; -async function streamLogs(context: IActionContext, node: AzureRegistryTreeItem, run: AcrRun): Promise { - const result = await (await node.getClient(context)).runs.getLogSasUrl(node.resourceGroup, node.registryName, run.runId); - - const storageBlob = await getStorageBlob(); - const blobClient = new storageBlob.BlobClient(nonNullProp(result, 'logLink')); - - // Start streaming the response to the output channel - let byteOffset = 0; - let totalChecks = 0; - let exists = false; - - await new Promise((resolve, reject) => { - const timer = setInterval( - async () => { - try { - if (!exists && !(exists = await blobClient.exists())) { - totalChecks++; - if (totalChecks >= maxBlobChecks) { - clearInterval(timer); - reject('Not found'); - } - } - - const properties = await blobClient.getProperties(); - if (properties.contentLength > byteOffset) { - // New data available - const response = await blobClient.download(byteOffset); - byteOffset += response.contentLength; - - const lineReader = readline.createInterface(response.readableStreamBody); - for await (const line of lineReader) { - const sanitizedLine = line - // eslint-disable-next-line no-control-regex - .replace(/[\x00-\x09\x0B-\x0C\x0E-\x1F]/g, ''); // Remove non-printing control characters - ext.outputChannel.info(sanitizedLine); - } - } - - if (properties.metadata?.complete) { - clearInterval(timer); - resolve(); - } - } catch (err) { - clearInterval(timer); - reject(err); - } - }, - blobCheckInterval - ); - }); -} - -function getTempSourceArchivePath(): string { - /* tslint:disable-next-line:insecure-random */ - const id: number = Math.floor(Math.random() * Math.pow(10, idPrecision)); - const archive = `sourceArchive${id}.tar.gz`; - ext.outputChannel.info(vscode.l10n.t('Setting up temp file with \'{0}\'', archive)); - const tarFilePath: string = path.join(os.tmpdir(), archive); - return tarFilePath; -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import type { DockerBuildRequest as AcrDockerBuildRequest, FileTaskRunRequest as AcrFileTaskRunRequest, OS as AcrOS, Run as AcrRun, ContainerRegistryManagementClient } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy +// import { IActionContext, IAzureQuickPickItem, nonNullProp } from '@microsoft/vscode-azext-utils'; +// import * as fse from 'fs-extra'; +// import * as os from 'os'; +// import * as path from 'path'; +// import * as readline from 'readline'; +// import * as tar from 'tar'; +// import * as vscode from 'vscode'; +// import { ext } from '../../../../extensionVariables'; +// import { getStorageBlob } from '../../../../utils/lazyPackages'; +// import { delay } from '../../../../utils/promiseUtils'; +// import { Item, quickPickDockerFileItem, quickPickYamlFileItem } from '../../../../utils/quickPickFile'; +// import { quickPickWorkspaceFolder } from '../../../../utils/quickPickWorkspaceFolder'; +// import { addImageTaggingTelemetry, getTagFromUserInput } from '../../../images/tagImage'; + +// const idPrecision = 6; +// const vcsIgnoreList = ['.git', '.gitignore', '.bzr', 'bzrignore', '.hg', '.hgignore', '.svn']; + +// // this is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) +// export enum RootStrategy { +// Default = 'Default', +// DockerfileFolder = 'DockerfileFolder', +// } + +// export async function scheduleRunRequest(context: IActionContext, requestType: 'DockerBuildRequest' | 'FileTaskRunRequest', uri: vscode.Uri | undefined, rootStrategy?: RootStrategy | undefined): Promise<() => Promise> { +// // Acquire information. +// let rootFolder: vscode.WorkspaceFolder; +// let fileItem: Item; +// let imageName: string; +// if (requestType === 'DockerBuildRequest') { +// rootFolder = await quickPickWorkspaceFolder(context, vscode.l10n.t('To quick build Docker files you must first open a folder or workspace in VS Code.')); +// fileItem = await quickPickDockerFileItem(context, uri, rootFolder); +// imageName = await quickPickImageName(context, rootFolder, fileItem); +// } else if (requestType === 'FileTaskRunRequest') { +// rootFolder = await quickPickWorkspaceFolder(context, vscode.l10n.t('To run a task from a .yaml file you must first open a folder or workspace in VS Code.')); +// fileItem = await quickPickYamlFileItem(context, uri, rootFolder, vscode.l10n.t('To run a task from a .yaml file you must have yaml file in your VS Code workspace.')); +// } else { +// throw new Error(vscode.l10n.t('Run Request Type Currently not supported.')); +// } + +// const node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); + +// const osPick = ['Linux', 'Windows'].map(item => >{ label: item, data: item }); +// const osType: AcrOS = (await context.ui.showQuickPick(osPick, { placeHolder: vscode.l10n.t('Select image base OS') })).data; + +// const tarFilePath: string = getTempSourceArchivePath(); + +// try { +// // Prepare to run. +// ext.outputChannel.show(); + +// let rootUri = rootFolder.uri; +// if (rootStrategy === RootStrategy.DockerfileFolder) { +// // changes the root to the folder where the Dockerfile is +// // it is used by the ms-kubernetes-tools.aks-devx-tools extension (https://github.com/Azure/aks-devx-tools) +// rootUri = vscode.Uri.file(path.dirname(fileItem.absoluteFilePath)); +// } + +// const uploadedSourceLocation: string = await uploadSourceCode(await node.getClient(context), node.registryName, node.resourceGroup, rootUri, tarFilePath); +// ext.outputChannel.info(vscode.l10n.t('Uploaded source code from {0}', tarFilePath)); + +// let runRequest: AcrDockerBuildRequest | AcrFileTaskRunRequest; +// if (requestType === 'DockerBuildRequest') { +// runRequest = { +// type: requestType, +// imageNames: [imageName], +// isPushEnabled: true, +// sourceLocation: uploadedSourceLocation, +// platform: { os: osType }, +// dockerFilePath: path.relative(rootUri.fsPath, fileItem.absoluteFilePath) +// }; +// } else { +// runRequest = { +// type: 'FileTaskRunRequest', +// taskFilePath: path.relative(rootUri.fsPath, fileItem.absoluteFilePath), +// sourceLocation: uploadedSourceLocation, +// platform: { os: osType } +// }; +// } + +// // Schedule the run and Clean up. +// ext.outputChannel.info(vscode.l10n.t('Set up run request')); + +// const client = await node.getClient(context); +// const run = await client.registries.beginScheduleRunAndWait(node.resourceGroup, node.registryName, runRequest); +// ext.outputChannel.info(vscode.l10n.t('Scheduled run {0}', run.runId)); + +// void streamLogs(context, node, run); + +// // function returns the AcrRun info +// return async () => client.runs.get(node.resourceGroup, node.registryName, run.runId); +// } finally { +// if (await fse.pathExists(tarFilePath)) { +// await fse.unlink(tarFilePath); +// } +// } +// } + +// async function quickPickImageName(context: IActionContext, rootFolder: vscode.WorkspaceFolder, dockerFileItem: Item | undefined): Promise { +// const absFilePath: string = path.join(rootFolder.uri.fsPath, dockerFileItem.relativeFilePath); +// const dockerFileKey = `ACR_buildTag_${absFilePath}`; +// const prevImageName: string | undefined = ext.context.workspaceState.get(dockerFileKey); +// let suggestedImageName: string; + +// if (!prevImageName) { +// // Get imageName based on name of subfolder containing the Dockerfile, or else workspacefolder +// suggestedImageName = path.basename(dockerFileItem.relativeFolderPath).toLowerCase(); +// if (suggestedImageName === '.') { +// suggestedImageName = path.basename(rootFolder.uri.fsPath).toLowerCase().replace(/\s/g, ''); +// } + +// suggestedImageName += ":{{.Run.ID}}"; +// } else { +// suggestedImageName = prevImageName; +// } + +// // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox +// await delay(500); + +// addImageTaggingTelemetry(context, suggestedImageName, '.before'); +// const imageName: string = await getTagFromUserInput(context, suggestedImageName); +// addImageTaggingTelemetry(context, imageName, '.after'); + +// await ext.context.workspaceState.update(dockerFileKey, imageName); +// return imageName; +// } + +// async function uploadSourceCode(client: ContainerRegistryManagementClient, registryName: string, resourceGroupName: string, rootFolder: vscode.Uri, tarFilePath: string): Promise { +// ext.outputChannel.info(vscode.l10n.t(' Sending source code to temp file')); +// const source: string = rootFolder.fsPath; +// let items = await fse.readdir(source); +// items = items.filter(i => !(i in vcsIgnoreList)); +// // tslint:disable-next-line:no-unsafe-any +// tar.c({ cwd: source }, items).pipe(fse.createWriteStream(tarFilePath)); + +// ext.outputChannel.info(vscode.l10n.t(' Getting build source upload URL')); +// const sourceUploadLocation = await client.registries.getBuildSourceUploadUrl(resourceGroupName, registryName); +// const uploadUrl: string = sourceUploadLocation.uploadUrl; +// const relativePath: string = sourceUploadLocation.relativePath; + +// const storageBlob = await getStorageBlob(); +// const blobClient = new storageBlob.BlockBlobClient(uploadUrl); +// ext.outputChannel.info(vscode.l10n.t(' Creating block blob')); +// await blobClient.uploadFile(tarFilePath); + +// return relativePath; +// } + +// const blobCheckInterval = 1000; +// const maxBlobChecks = 30; +// async function streamLogs(context: IActionContext, node: AzureRegistryTreeItem, run: AcrRun): Promise { +// const result = await (await node.getClient(context)).runs.getLogSasUrl(node.resourceGroup, node.registryName, run.runId); + +// const storageBlob = await getStorageBlob(); +// const blobClient = new storageBlob.BlobClient(nonNullProp(result, 'logLink')); + +// // Start streaming the response to the output channel +// let byteOffset = 0; +// let totalChecks = 0; +// let exists = false; + +// await new Promise((resolve, reject) => { +// const timer = setInterval( +// async () => { +// try { +// if (!exists && !(exists = await blobClient.exists())) { +// totalChecks++; +// if (totalChecks >= maxBlobChecks) { +// clearInterval(timer); +// reject('Not found'); +// } +// } + +// const properties = await blobClient.getProperties(); +// if (properties.contentLength > byteOffset) { +// // New data available +// const response = await blobClient.download(byteOffset); +// byteOffset += response.contentLength; + +// const lineReader = readline.createInterface(response.readableStreamBody); +// for await (const line of lineReader) { +// const sanitizedLine = line +// // eslint-disable-next-line no-control-regex +// .replace(/[\x00-\x09\x0B-\x0C\x0E-\x1F]/g, ''); // Remove non-printing control characters +// ext.outputChannel.info(sanitizedLine); +// } +// } + +// if (properties.metadata?.complete) { +// clearInterval(timer); +// resolve(); +// } +// } catch (err) { +// clearInterval(timer); +// reject(err); +// } +// }, +// blobCheckInterval +// ); +// }); +// } + +// function getTempSourceArchivePath(): string { +// /* tslint:disable-next-line:insecure-random */ +// const id: number = Math.floor(Math.random() * Math.pow(10, idPrecision)); +// const archive = `sourceArchive${id}.tar.gz`; +// ext.outputChannel.info(vscode.l10n.t('Setting up temp file with \'{0}\'', archive)); +// const tarFilePath: string = path.join(os.tmpdir(), archive); +// return tarFilePath; +// } +// TODO: review this later diff --git a/src/commands/registries/azure/tasks/viewAzureTaskLogs.ts b/src/commands/registries/azure/tasks/viewAzureTaskLogs.ts index 4ed044abd4..1fd008f1ee 100644 --- a/src/commands/registries/azure/tasks/viewAzureTaskLogs.ts +++ b/src/commands/registries/azure/tasks/viewAzureTaskLogs.ts @@ -1,29 +1,29 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext, nonNullProp, openReadOnlyContent } from "@microsoft/vscode-azext-utils"; -import { l10n } from 'vscode'; -import { ext } from "../../../../extensionVariables"; -import { AzureTaskRunTreeItem } from "../../../../tree/registries/azure/AzureTaskRunTreeItem"; -import { bufferToString } from "../../../../utils/execAsync"; -import { getStorageBlob } from "../../../../utils/lazyPackages"; +// import { IActionContext, nonNullProp, openReadOnlyContent } from "@microsoft/vscode-azext-utils"; +// import { l10n } from 'vscode'; +// import { ext } from "../../../../extensionVariables"; +// import { AzureTaskRunTreeItem } from "../../../../tree/registries/azure/AzureTaskRunTreeItem"; +// import { bufferToString } from "../../../../utils/execAsync"; +// import { getStorageBlob } from "../../../../utils/lazyPackages"; -export async function viewAzureTaskLogs(context: IActionContext, node?: AzureTaskRunTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(AzureTaskRunTreeItem.contextValue, context); - } +// export async function viewAzureTaskLogs(context: IActionContext, node?: AzureTaskRunTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(AzureTaskRunTreeItem.contextValue, context); +// } - const registryTI = node.parent.parent.parent; - await node.runWithTemporaryDescription(context, l10n.t('Retrieving logs...'), async () => { - const result = await (await registryTI.getClient(context)).runs.getLogSasUrl(registryTI.resourceGroup, registryTI.registryName, node.runId); +// const registryTI = node.parent.parent.parent; +// await node.runWithTemporaryDescription(context, l10n.t('Retrieving logs...'), async () => { +// const result = await (await registryTI.getClient(context)).runs.getLogSasUrl(registryTI.resourceGroup, registryTI.registryName, node.runId); - const storageBlob = await getStorageBlob(); - const blobClient = new storageBlob.BlobClient(nonNullProp(result, 'logLink')); - const contentBuffer = await blobClient.downloadToBuffer(); - const content = bufferToString(contentBuffer); +// const storageBlob = await getStorageBlob(); +// const blobClient = new storageBlob.BlobClient(nonNullProp(result, 'logLink')); +// const contentBuffer = await blobClient.downloadToBuffer(); +// const content = bufferToString(contentBuffer); - await openReadOnlyContent(node, content, '.log'); - }); -} +// await openReadOnlyContent(node, content, '.log'); +// }); +// } diff --git a/src/commands/registries/azure/untagAzureImage.ts b/src/commands/registries/azure/untagAzureImage.ts index 4454bf5635..7db25a4dc5 100644 --- a/src/commands/registries/azure/untagAzureImage.ts +++ b/src/commands/registries/azure/untagAzureImage.ts @@ -1,36 +1,36 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from "@microsoft/vscode-azext-utils"; -import { l10n, ProgressLocation, window } from "vscode"; -import { ext } from "../../../extensionVariables"; -import { registryExpectedContextValues } from "../../../tree/registries/registryContextValues"; -import { RemoteTagTreeItem } from "../../../tree/registries/RemoteTagTreeItem"; -import { registryRequest } from "../../../utils/registryRequestUtils"; +// import { IActionContext } from "@microsoft/vscode-azext-utils"; +// import { l10n, ProgressLocation, window } from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { registryExpectedContextValues } from "../../../tree/registries/registryContextValues"; +// import { RemoteTagTreeItem } from "../../../tree/registries/RemoteTagTreeItem"; +// import { registryRequest } from "../../../utils/registryRequestUtils"; -export async function untagAzureImage(context: IActionContext, node?: RemoteTagTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.tag, { - ...context, - suppressCreatePick: true, - noItemFoundErrorMessage: l10n.t('No images are available to untag') - }); - } +// export async function untagAzureImage(context: IActionContext, node?: RemoteTagTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.tag, { +// ...context, +// suppressCreatePick: true, +// noItemFoundErrorMessage: l10n.t('No images are available to untag') +// }); +// } - const confirmUntag: string = l10n.t('Are you sure you want to untag image "{0}"? This does not delete the manifest referenced by the tag.', node.repoNameAndTag); - // no need to check result - cancel will throw a UserCancelledError - await context.ui.showWarningMessage(confirmUntag, { modal: true }, { title: "Untag" }); +// const confirmUntag: string = l10n.t('Are you sure you want to untag image "{0}"? This does not delete the manifest referenced by the tag.', node.repoNameAndTag); +// // no need to check result - cancel will throw a UserCancelledError +// await context.ui.showWarningMessage(confirmUntag, { modal: true }, { title: "Untag" }); - const untagging = l10n.t('Untagging image "{0}"...', node.repoNameAndTag); - const repoTI = node.parent; - await window.withProgress({ location: ProgressLocation.Notification, title: untagging }, async () => { - await registryRequest(repoTI, 'DELETE', `v2/_acr/${repoTI.repoName}/tags/${node.tag}`); - await repoTI.refresh(context); - }); +// const untagging = l10n.t('Untagging image "{0}"...', node.repoNameAndTag); +// const repoTI = node.parent; +// await window.withProgress({ location: ProgressLocation.Notification, title: untagging }, async () => { +// await registryRequest(repoTI, 'DELETE', `v2/_acr/${repoTI.repoName}/tags/${node.tag}`); +// await repoTI.refresh(context); +// }); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(l10n.t('Successfully untagged image "{0}".', node.repoNameAndTag)); -} +// // don't wait +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// window.showInformationMessage(l10n.t('Successfully untagged image "{0}".', node.repoNameAndTag)); +// } diff --git a/src/commands/registries/azure/viewAzureProperties.ts b/src/commands/registries/azure/viewAzureProperties.ts index 67804732ca..4de3cd4a05 100644 --- a/src/commands/registries/azure/viewAzureProperties.ts +++ b/src/commands/registries/azure/viewAzureProperties.ts @@ -1,19 +1,19 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext, openReadOnlyJson } from "@microsoft/vscode-azext-utils"; -import { ext } from "../../../extensionVariables"; -import { AzureRegistryTreeItem } from "../../../tree/registries/azure/AzureRegistryTreeItem"; -import { AzureTaskRunTreeItem } from "../../../tree/registries/azure/AzureTaskRunTreeItem"; -import { AzureTaskTreeItem } from "../../../tree/registries/azure/AzureTaskTreeItem"; -import { registryExpectedContextValues } from "../../../tree/registries/registryContextValues"; +// import { IActionContext, openReadOnlyJson } from "@microsoft/vscode-azext-utils"; +// import { ext } from "../../../extensionVariables"; +// import { AzureRegistryTreeItem } from "../../../tree/registries/azure/AzureRegistryTreeItem"; +// import { AzureTaskRunTreeItem } from "../../../tree/registries/azure/AzureTaskRunTreeItem"; +// import { AzureTaskTreeItem } from "../../../tree/registries/azure/AzureTaskTreeItem"; +// import { registryExpectedContextValues } from "../../../tree/registries/registryContextValues"; -export async function viewAzureProperties(context: IActionContext, node?: AzureRegistryTreeItem | AzureTaskTreeItem | AzureTaskRunTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); - } +// export async function viewAzureProperties(context: IActionContext, node?: AzureRegistryTreeItem | AzureTaskTreeItem | AzureTaskRunTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.azure.registry, context); +// } - await openReadOnlyJson(node, node.properties); -} +// await openReadOnlyJson(node, node.properties); +// } diff --git a/src/commands/registries/connectRegistry.ts b/src/commands/registries/connectRegistry.ts index 93b747c7d8..dd4067a106 100644 --- a/src/commands/registries/connectRegistry.ts +++ b/src/commands/registries/connectRegistry.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from "@microsoft/vscode-azext-utils"; import { ext } from "../../extensionVariables"; -export async function connectRegistry(context: IActionContext): Promise { - await ext.registriesRoot.connectRegistry(context); +export async function connectRegistry(): Promise { + await ext.registriesRoot.connectRegistryProvider(); } diff --git a/src/commands/registries/copyRemoteFullTag.ts b/src/commands/registries/copyRemoteFullTag.ts index 5e557dc43a..a85d67a329 100644 --- a/src/commands/registries/copyRemoteFullTag.ts +++ b/src/commands/registries/copyRemoteFullTag.ts @@ -3,21 +3,18 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from '@microsoft/vscode-azext-utils'; +import { IActionContext, contextValueExperience } from '@microsoft/vscode-azext-utils'; +import { CommonTag } from '@microsoft/vscode-docker-registries'; import * as vscode from 'vscode'; import { ext } from '../../extensionVariables'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -import { RemoteTagTreeItem } from '../../tree/registries/RemoteTagTreeItem'; +import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; +import { getFullImageNameFromRegistryItem } from '../../tree/registries/getInformationFromRegistryItem'; -export async function copyRemoteFullTag(context: IActionContext, node?: RemoteTagTreeItem): Promise { +export async function copyRemoteFullTag(context: IActionContext, node?: UnifiedRegistryItem): Promise { if (!node) { - node = await ext.registriesTree.showTreeItemPicker([registryExpectedContextValues.dockerV2.tag, registryExpectedContextValues.dockerHub.tag], { - ...context, - noItemFoundErrorMessage: vscode.l10n.t('No remote images are available to copy the full tag') - }); + node = await contextValueExperience(context, ext.registriesTree, { include: ['registryV2Tag', 'dockerHubTag'] }); } - // eslint-disable-next-line @typescript-eslint/no-floating-promises - // Don't wait - void vscode.env.clipboard.writeText(node.fullTag); - return node.fullTag; + const fullTag = getFullImageNameFromRegistryItem(node); + void vscode.env.clipboard.writeText(fullTag); + return fullTag; } diff --git a/src/commands/registries/copyRemoteImageDigest.ts b/src/commands/registries/copyRemoteImageDigest.ts index f31ab24f2b..2b7745c49c 100644 --- a/src/commands/registries/copyRemoteImageDigest.ts +++ b/src/commands/registries/copyRemoteImageDigest.ts @@ -1,36 +1,36 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import * as vscode from "vscode"; -import { ext } from "../../extensionVariables"; -import { AzureTaskRunTreeItem } from "../../tree/registries/azure/AzureTaskRunTreeItem"; -import { DockerV2TagTreeItem } from "../../tree/registries/dockerV2/DockerV2TagTreeItem"; -import { registryExpectedContextValues } from "../../tree/registries/registryContextValues"; +// import { IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; +// import * as vscode from "vscode"; +// import { ext } from "../../extensionVariables"; +// import { AzureTaskRunTreeItem } from "../../tree/registries/azure/AzureTaskRunTreeItem"; +// import { DockerV2TagTreeItem } from "../../tree/registries/dockerV2/DockerV2TagTreeItem"; +// import { registryExpectedContextValues } from "../../tree/registries/registryContextValues"; -export async function copyRemoteImageDigest(context: IActionContext, node?: DockerV2TagTreeItem | AzureTaskRunTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerV2.tag, { - ...context, - noItemFoundErrorMessage: vscode.l10n.t('No remote images are available to copy the digest') - }); - } +// export async function copyRemoteImageDigest(context: IActionContext, node?: DockerV2TagTreeItem | AzureTaskRunTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerV2.tag, { +// ...context, +// noItemFoundErrorMessage: vscode.l10n.t('No remote images are available to copy the digest') +// }); +// } - let digest: string; - if (node instanceof AzureTaskRunTreeItem) { - if (node.outputImage) { - digest = nonNullProp(node.outputImage, 'digest'); - } else { - throw new Error(vscode.l10n.t('Failed to find output image for this task run.')); - } - } else { - await node.runWithTemporaryDescription(context, vscode.l10n.t('Getting digest...'), async () => { - digest = await (node).getDigest(); - }); - } +// let digest: string; +// if (node instanceof AzureTaskRunTreeItem) { +// if (node.outputImage) { +// digest = nonNullProp(node.outputImage, 'digest'); +// } else { +// throw new Error(vscode.l10n.t('Failed to find output image for this task run.')); +// } +// } else { +// await node.runWithTemporaryDescription(context, vscode.l10n.t('Getting digest...'), async () => { +// digest = await (node).getDigest(); +// }); +// } - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - vscode.env.clipboard.writeText(digest); -} +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// vscode.env.clipboard.writeText(digest); +// } diff --git a/src/commands/registries/deleteRemoteImage.ts b/src/commands/registries/deleteRemoteImage.ts index ec938b1f68..0495177910 100644 --- a/src/commands/registries/deleteRemoteImage.ts +++ b/src/commands/registries/deleteRemoteImage.ts @@ -1,37 +1,37 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; -import { l10n, ProgressLocation, window } from 'vscode'; -import { ext } from '../../extensionVariables'; -import { DockerV2TagTreeItem } from '../../tree/registries/dockerV2/DockerV2TagTreeItem'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; +// import { DialogResponses, IActionContext } from '@microsoft/vscode-azext-utils'; +// import { l10n, ProgressLocation, window } from 'vscode'; +// import { ext } from '../../extensionVariables'; +// import { DockerV2TagTreeItem } from '../../tree/registries/dockerV2/DockerV2TagTreeItem'; +// import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -export async function deleteRemoteImage(context: IActionContext, node?: DockerV2TagTreeItem): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerV2.tag, { - ...context, - suppressCreatePick: true, - noItemFoundErrorMessage: l10n.t('No remote images are available to delete') - }); - } +// export async function deleteRemoteImage(context: IActionContext, node?: DockerV2TagTreeItem): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerV2.tag, { +// ...context, +// suppressCreatePick: true, +// noItemFoundErrorMessage: l10n.t('No remote images are available to delete') +// }); +// } - const confirmDelete = l10n.t('Are you sure you want to delete image "{0}"? This will delete all images that have the same digest.', node.repoNameAndTag); - // no need to check result - cancel will throw a UserCancelledError - await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); +// const confirmDelete = l10n.t('Are you sure you want to delete image "{0}"? This will delete all images that have the same digest.', node.repoNameAndTag); +// // no need to check result - cancel will throw a UserCancelledError +// await context.ui.showWarningMessage(confirmDelete, { modal: true }, DialogResponses.deleteResponse); - const repoTI = node.parent; - const deleting = l10n.t('Deleting image "{0}"...', node.repoNameAndTag); - await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { - await node.deleteTreeItem(context); - }); +// const repoTI = node.parent; +// const deleting = l10n.t('Deleting image "{0}"...', node.repoNameAndTag); +// await window.withProgress({ location: ProgressLocation.Notification, title: deleting }, async () => { +// await node.deleteTreeItem(context); +// }); - // Other tags that also matched the image may have been deleted, so refresh the whole repository - await repoTI.refresh(context); - const message = l10n.t('Successfully deleted image "{0}".', node.repoNameAndTag); - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(message); -} +// // Other tags that also matched the image may have been deleted, so refresh the whole repository +// await repoTI.refresh(context); +// const message = l10n.t('Successfully deleted image "{0}".', node.repoNameAndTag); +// // don't wait +// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ +// window.showInformationMessage(message); +// } diff --git a/src/commands/registries/disconnectRegistry.ts b/src/commands/registries/disconnectRegistry.ts index 8416735ef9..1f8c6a999e 100644 --- a/src/commands/registries/disconnectRegistry.ts +++ b/src/commands/registries/disconnectRegistry.ts @@ -3,17 +3,10 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext, InvalidTreeItem } from "@microsoft/vscode-azext-utils"; -import { ext } from "../../extensionVariables"; -import { ICachedRegistryProvider } from "../../tree/registries/ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../../tree/registries/IRegistryProviderTreeItem"; +import { IActionContext } from "@microsoft/vscode-azext-utils"; +import { UnifiedRegistryItem } from "../../tree/registries/UnifiedRegistryTreeDataProvider"; -export async function disconnectRegistry(context: IActionContext, node?: InvalidTreeItem | IRegistryProviderTreeItem): Promise { - let cachedProvider: ICachedRegistryProvider | undefined; - if (node instanceof InvalidTreeItem) { - cachedProvider = node.data; - } else if (node) { - cachedProvider = node.cachedProvider; - } - await ext.registriesRoot.disconnectRegistry(context, cachedProvider); + +export async function disconnectRegistry(context: IActionContext, node?: UnifiedRegistryItem): Promise { + await node.provider.onDisconnect?.(); } diff --git a/src/commands/registries/dockerHub/openDockerHubInBrowser.ts b/src/commands/registries/dockerHub/openDockerHubInBrowser.ts index 303d61f9ce..7d6a507377 100644 --- a/src/commands/registries/dockerHub/openDockerHubInBrowser.ts +++ b/src/commands/registries/dockerHub/openDockerHubInBrowser.ts @@ -3,32 +3,28 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from "@microsoft/vscode-azext-utils"; +import { IActionContext, contextValueExperience } from "@microsoft/vscode-azext-utils"; import * as vscode from "vscode"; -import { dockerHubUrl } from "../../../constants"; import { ext } from "../../../extensionVariables"; -import { DockerHubNamespaceTreeItem } from "../../../tree/registries/dockerHub/DockerHubNamespaceTreeItem"; -import { DockerHubRepositoryTreeItem } from "../../../tree/registries/dockerHub/DockerHubRepositoryTreeItem"; -import { registryExpectedContextValues } from "../../../tree/registries/registryContextValues"; -import { RemoteTagTreeItem } from "../../../tree/registries/RemoteTagTreeItem"; +import { UnifiedRegistryItem } from "../../../tree/registries/UnifiedRegistryTreeDataProvider"; -export async function openDockerHubInBrowser(context: IActionContext, node?: DockerHubNamespaceTreeItem | DockerHubRepositoryTreeItem | RemoteTagTreeItem): Promise { +export async function openDockerHubInBrowser(context: IActionContext, node?: UnifiedRegistryItem): Promise { if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerHub.registry, { - ...context, - noItemFoundErrorMessage: vscode.l10n.t('No Docker Hub registries available to browse') - }); + node = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerhubregistry' }); } - let url = dockerHubUrl; - if (node instanceof DockerHubNamespaceTreeItem) { - url += `u/${node.namespace}`; - } else if (node instanceof DockerHubRepositoryTreeItem) { - url += `r/${node.parent.namespace}/${node.repoName}`; - } else { - const repoTI = node.parent; - url += `r/${repoTI.parent.namespace}/${repoTI.repoName}/tags`; - } + // let url = dockerHubUrl; + // if (node instanceof DockerHubNamespaceTreeItem) { + // url += `u/${node.namespace}`; + // } else if (node instanceof DockerHubRepositoryTreeItem) { + // url += `r/${node.parent.namespace}/${node.repoName}`; + // } else { + // const repoTI = node.parent; + // url += `r/${repoTI.parent.namespace}/${repoTI.repoName}/tags`; + // } + + const url = ''; + // TODO: review this later await vscode.env.openExternal(vscode.Uri.parse(url)); } diff --git a/src/commands/registries/logInToDockerCli.ts b/src/commands/registries/logInToDockerCli.ts index b4c367e9e3..a761c8b897 100644 --- a/src/commands/registries/logInToDockerCli.ts +++ b/src/commands/registries/logInToDockerCli.ts @@ -3,33 +3,24 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext, parseError } from '@microsoft/vscode-azext-utils'; +import { IActionContext, contextValueExperience, parseError } from '@microsoft/vscode-azext-utils'; +import { CommonRegistry } from '@microsoft/vscode-docker-registries/lib/clients/Common/models'; // TODO: update this import * as stream from 'stream'; import * as vscode from 'vscode'; -import { NULL_GUID } from '../../constants'; import { ext } from '../../extensionVariables'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; +import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; -export async function logInToDockerCli(context: IActionContext, node?: RegistryTreeItemBase): Promise { +export async function logInToDockerCli(context: IActionContext, node?: UnifiedRegistryItem): Promise { if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.registry, context); + node = await contextValueExperience(context, ext.registriesRoot, { include: 'commonroot' }); } - const creds = await node.getDockerCliCredentials(); - const auth: { username?: string, password?: string, token?: string } = creds.auth || {}; - let username: string | undefined; - let password: string | undefined; - if (auth.token) { - username = NULL_GUID; - password = auth.token; - } else if (auth.password) { - username = auth.username; - password = auth.password; - } + const creds = await node.provider?.getLoginInformation?.(node.wrappedItem); + const username = creds?.username; + const secret = creds?.secret; - if (!username || !password) { - ext.outputChannel.warn(vscode.l10n.t('Skipping login for "{0}" because it does not require authentication.', creds.registryPath)); + if (!username || !secret) { + ext.outputChannel.warn(vscode.l10n.t('Skipping login for "{0}" because it does not require authentication.', node.provider.label)); } else { const progressOptions: vscode.ProgressOptions = { location: vscode.ProgressLocation.Notification, @@ -42,10 +33,10 @@ export async function logInToDockerCli(context: IActionContext, node?: RegistryT client => client.login({ username: username, passwordStdIn: true, - registry: creds.registryPath, + registry: '' // TODO: change this }), { - stdInPipe: stream.Readable.from(password), + stdInPipe: stream.Readable.from(secret), } ); ext.outputChannel.info('Login succeeded.'); diff --git a/src/commands/registries/logOutOfDockerCli.ts b/src/commands/registries/logOutOfDockerCli.ts index 4e828f109e..ccf26ec220 100644 --- a/src/commands/registries/logOutOfDockerCli.ts +++ b/src/commands/registries/logOutOfDockerCli.ts @@ -1,29 +1,29 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from '@microsoft/vscode-azext-utils'; -import { ext } from '../../extensionVariables'; -import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; +// import { IActionContext } from '@microsoft/vscode-azext-utils'; +// import { ext } from '../../extensionVariables'; +// import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; +// import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; +// import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; -export async function logOutOfDockerCli(context: IActionContext, node?: RegistryTreeItemBase): Promise { - if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.registry, context); - } +// export async function logOutOfDockerCli(context: IActionContext, node?: RegistryTreeItemBase): Promise { +// if (!node) { +// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.registry, context); +// } - const creds = await node.getDockerCliCredentials(); +// const creds = await node.getDockerCliCredentials(); - const client = await ext.runtimeManager.getClient(); - const taskCRF = new TaskCommandRunnerFactory( - { - taskName: 'Docker' - } - ); +// const client = await ext.runtimeManager.getClient(); +// const taskCRF = new TaskCommandRunnerFactory( +// { +// taskName: 'Docker' +// } +// ); - await taskCRF.getCommandRunner()( - client.logout({ registry: creds.registryPath }) - ); -} +// await taskCRF.getCommandRunner()( +// client.logout({ registry: creds.registryPath }) +// ); +// } diff --git a/src/commands/registries/pullImages.ts b/src/commands/registries/pullImages.ts index 47dd2eda37..84db7d9b9a 100644 --- a/src/commands/registries/pullImages.ts +++ b/src/commands/registries/pullImages.ts @@ -3,33 +3,33 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from '@microsoft/vscode-azext-utils'; +import { IActionContext, contextValueExperience } from '@microsoft/vscode-azext-utils'; +import { CommonRegistry, CommonRepository, CommonTag } from '@microsoft/vscode-docker-registries/lib/clients/Common/models'; import { ext } from '../../extensionVariables'; import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; -import { registryExpectedContextValues } from '../../tree/registries/registryContextValues'; -import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase'; -import { RemoteRepositoryTreeItemBase } from '../../tree/registries/RemoteRepositoryTreeItemBase'; -import { RemoteTagTreeItem } from '../../tree/registries/RemoteTagTreeItem'; +import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; +import { getImageNameFromRegistryItem } from '../../tree/registries/getInformationFromRegistryItem'; import { logInToDockerCli } from './logInToDockerCli'; -export async function pullRepository(context: IActionContext, node?: RemoteRepositoryTreeItemBase): Promise { +export async function pullRepository(context: IActionContext, node?: UnifiedRegistryItem): Promise { if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.repository, context); + node = await contextValueExperience(context, ext.registriesTree, { include: 'commonrepository' }); } - await pullImages(context, node.parent, node.repoName, true); + await pullImages(context, node.parent, node.wrappedItem.label, true); // TODO: test this } -export async function pullImageFromRepository(context: IActionContext, node?: RemoteTagTreeItem): Promise { +export async function pullImageFromRepository(context: IActionContext, node?: UnifiedRegistryItem): Promise { if (!node) { - node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.all.tag, context); + node = await contextValueExperience(context, ext.registriesTree, { include: 'commontag' }); } - await pullImages(context, node.parent.parent, node.repoNameAndTag, false); + await pullImages(context, node.parent.parent, getImageNameFromRegistryItem(node), false); } -async function pullImages(context: IActionContext, node: RegistryTreeItemBase, imageRequest: string, allTags: boolean): Promise { - await logInToDockerCli(context, node); +async function pullImages(context: IActionContext, node: UnifiedRegistryItem, imageRequest: string, allTags: boolean): Promise { + const registryNode = node as UnifiedRegistryItem; + await logInToDockerCli(context, registryNode); const client = await ext.runtimeManager.getClient(); const taskCRF = new TaskCommandRunnerFactory({ @@ -39,7 +39,7 @@ async function pullImages(context: IActionContext, node: RegistryTreeItemBase, i await taskCRF.getCommandRunner()( client.pullImage( { - imageRef: `${node.baseImagePath}/${imageRequest}`, + imageRef: `${registryNode.wrappedItem.label}/${imageRequest}`, allTags: allTags, } ) diff --git a/src/commands/registries/reconnectRegistry.ts b/src/commands/registries/reconnectRegistry.ts index 31e0c79be7..0537f79960 100644 --- a/src/commands/registries/reconnectRegistry.ts +++ b/src/commands/registries/reconnectRegistry.ts @@ -1,19 +1,19 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from "@microsoft/vscode-azext-utils"; -import { l10n } from 'vscode'; -import { ext } from "../../extensionVariables"; -import { RegistryConnectErrorTreeItem } from "../../tree/registries/RegistryConnectErrorTreeItem"; +// import { IActionContext } from "@microsoft/vscode-azext-utils"; +// import { l10n } from 'vscode'; +// import { ext } from "../../extensionVariables"; +// import { RegistryConnectErrorTreeItem } from "../../tree/registries/RegistryConnectErrorTreeItem"; -export async function reconnectRegistry(context: IActionContext, node?: RegistryConnectErrorTreeItem): Promise { - if (!node?.cachedProvider || !node?.provider) { - // This is not expected ever, so we'll throw an error which can be bubbled up to a Report Issue if it does - throw new Error(l10n.t('Unable to determine provider to re-enter credentials. Please disconnect and connect again.')); - } +// export async function reconnectRegistry(context: IActionContext, node?: RegistryConnectErrorTreeItem): Promise { +// if (!node?.cachedProvider || !node?.provider) { +// // This is not expected ever, so we'll throw an error which can be bubbled up to a Report Issue if it does +// throw new Error(l10n.t('Unable to determine provider to re-enter credentials. Please disconnect and connect again.')); +// } - await ext.registriesRoot.disconnectRegistry(context, node.cachedProvider); - await ext.registriesRoot.connectRegistry(context, node.provider, node.url); -} +// await ext.registriesRoot.disconnectRegistry(context, node.cachedProvider); +// await ext.registriesRoot.connectRegistry(context, node.provider, node.url); +// } diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index e3c403caea..22742fc811 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -13,7 +13,7 @@ import { ContainersTreeItem } from './tree/containers/ContainersTreeItem'; import { ContextsTreeItem } from './tree/contexts/ContextsTreeItem'; import { ImagesTreeItem } from './tree/images/ImagesTreeItem'; import { NetworksTreeItem } from './tree/networks/NetworksTreeItem'; -import { RegistriesTreeItem } from './tree/registries/RegistriesTreeItem'; +import { UnifiedRegistryItem, UnifiedRegistryTreeDataProvider } from './tree/registries/UnifiedRegistryTreeDataProvider'; import { VolumesTreeItem } from './tree/volumes/VolumesTreeItem'; import { AzExtLogOutputChannelWrapper } from './utils/AzExtLogOutputChannelWrapper'; @@ -45,9 +45,9 @@ export namespace ext { export const prefix: string = 'docker'; - export let registriesTree: AzExtTreeDataProvider; - export let registriesTreeView: TreeView; - export let registriesRoot: RegistriesTreeItem; + export let registriesTree: UnifiedRegistryTreeDataProvider; + export let registriesTreeView: TreeView>; + export let registriesRoot: UnifiedRegistryTreeDataProvider; export let volumesTree: AzExtTreeDataProvider; export let volumesTreeView: TreeView; diff --git a/src/tree/RefreshManager.ts b/src/tree/RefreshManager.ts index f3ab4226cd..39485f05c8 100644 --- a/src/tree/RefreshManager.ts +++ b/src/tree/RefreshManager.ts @@ -301,7 +301,7 @@ export class RefreshManager extends vscode.Disposable { callback = () => ext.networksRoot.refresh(context); break; case 'registries': - callback = () => ext.registriesRoot.refresh(context); + ext.registriesRoot.refresh(); //TODO: change this break; case 'volumes': callback = () => ext.volumesRoot.refresh(context); diff --git a/src/tree/registerTrees.ts b/src/tree/registerTrees.ts index c9f53f51a3..27b765a2dc 100644 --- a/src/tree/registerTrees.ts +++ b/src/tree/registerTrees.ts @@ -3,18 +3,20 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; import { AzExtTreeDataProvider, AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; +import { DockerHubRegistryDataProvider, GenericRegistryV2DataProvider, GitHubRegistryDataProvider } from "@microsoft/vscode-docker-registries"; +import * as vscode from 'vscode'; import { registerCommand } from '../commands/registerCommands'; import { ext } from '../extensionVariables'; +import { OpenUrlTreeItem } from './OpenUrlTreeItem'; +import { RefreshManager } from './RefreshManager'; import { ContainersTreeItem } from './containers/ContainersTreeItem'; import { ContextsTreeItem } from './contexts/ContextsTreeItem'; import { HelpsTreeItem } from './help/HelpsTreeItem'; import { ImagesTreeItem } from "./images/ImagesTreeItem"; import { NetworksTreeItem } from "./networks/NetworksTreeItem"; -import { OpenUrlTreeItem } from './OpenUrlTreeItem'; -import { RefreshManager } from './RefreshManager'; -import { RegistriesTreeItem } from "./registries/RegistriesTreeItem"; +import { AzureRegistryDataProvider } from "./registries/Azure/AzureRegistryDataProvider"; +import { UnifiedRegistryTreeDataProvider } from "./registries/UnifiedRegistryTreeDataProvider"; import { VolumesTreeItem } from "./volumes/VolumesTreeItem"; export function registerTrees(): void { @@ -42,13 +44,17 @@ export function registerTrees(): void { /* eslint-disable-next-line @typescript-eslint/promise-function-async */ registerCommand(imagesLoadMore, (context: IActionContext, node: AzExtTreeItem) => ext.imagesTree.loadMore(node, context)); - ext.registriesRoot = new RegistriesTreeItem(); - const registriesLoadMore = 'vscode-docker.registries.loadMore'; - ext.registriesTree = new AzExtTreeDataProvider(ext.registriesRoot, registriesLoadMore); - ext.registriesTreeView = vscode.window.createTreeView('dockerRegistries', { treeDataProvider: ext.registriesTree, showCollapseAll: true, canSelectMany: false }); - ext.context.subscriptions.push(ext.registriesTreeView); + const urtdp = new UnifiedRegistryTreeDataProvider(ext.context.globalState); + const genericRegistryV2DataProvider = new GenericRegistryV2DataProvider(ext.context); + urtdp.registerProvider(new GitHubRegistryDataProvider(ext.context)); + urtdp.registerProvider(new DockerHubRegistryDataProvider(ext.context)); + urtdp.registerProvider(new AzureRegistryDataProvider(ext.context)); + urtdp.registerProvider(genericRegistryV2DataProvider); + ext.registriesRoot = urtdp; + ext.registriesTreeView = vscode.window.createTreeView('dockerRegistries', { treeDataProvider: urtdp }); + ext.registriesTree = urtdp; + // TODO: add gitlab registry provider when the new extension is ready /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - registerCommand(registriesLoadMore, (context: IActionContext, node: AzExtTreeItem) => ext.registriesTree.loadMore(node, context)); ext.volumesRoot = new VolumesTreeItem(undefined); const volumesLoadMore = 'vscode-docker.volumes.loadMore'; diff --git a/src/tree/registries/Azure/ACROAuthProvider.ts b/src/tree/registries/Azure/ACROAuthProvider.ts new file mode 100644 index 0000000000..887811a8ac --- /dev/null +++ b/src/tree/registries/Azure/ACROAuthProvider.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AzureSubscription } from '@microsoft/vscode-azext-azureauth'; +import { httpRequest } from '@microsoft/vscode-docker-registries'; +import { AuthenticationProvider } from "@microsoft/vscode-docker-registries/"; +import * as vscode from 'vscode'; + +// export interface ACROAuthOptions extends BasicOAuthOptions { +// readonly subscription: AzureSubscription; +// } + +export class ACROAuthProvider implements AuthenticationProvider { + private refreshTokenCache = new Map(); + + public constructor(private readonly registryUri: vscode.Uri, private readonly subscription: AzureSubscription) { } + + public async getSession(scopes: string[], options?: vscode.AuthenticationGetSessionOptions): Promise { + const accessToken = await this.getAccessToken(this.subscription); + const registryString = this.registryUri.toString(); + + let refreshToken: string; + if (!options?.forceNewSession && this.refreshTokenCache.has(registryString)) { + refreshToken = this.refreshTokenCache.get(registryString)!; + } else { + refreshToken = await this.getRefreshTokenFromAccessToken(accessToken, this.registryUri, this.subscription); + this.refreshTokenCache.set(registryString, refreshToken); + } + + const oauthToken = await this.getOAuthTokenFromRefreshToken(refreshToken, this.registryUri, scopes.join(' '), this.subscription); + const { sub, jti } = this.parseToken(oauthToken); + + return { + id: jti, + type: 'Bearer', + accessToken: oauthToken, + account: { + label: sub, + id: sub, + }, + scopes: scopes, + }; + } + + private parseToken(accessToken: string): { sub: string, jti: string } { + const tokenParts = accessToken.split('.'); + const tokenBody = JSON.parse(Buffer.from(tokenParts[1], 'base64').toString('utf8')); + return { + sub: tokenBody.sub, + jti: tokenBody.jti, + }; + } + + private async getOAuthTokenFromRefreshToken(refreshToken: string, registryUri: vscode.Uri, scopes: string, subscription: AzureSubscription): Promise { + const requestUrl = registryUri.with({ path: '/oauth2/token' }); + + const requestBody = new URLSearchParams({ + /* eslint-disable @typescript-eslint/naming-convention */ + grant_type: 'refresh_token', + refresh_token: refreshToken, + service: registryUri.authority, + scope: scopes, + }); + + const response = await httpRequest<{ access_token: string }>(requestUrl.toString(), { + method: 'POST', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'content-type': 'application/x-www-form-urlencoded' + }, + body: requestBody, + }); + + return (await response.json()).access_token; + } + + private async getRefreshTokenFromAccessToken(accessToken: string, registryUri: vscode.Uri, subscription: AzureSubscription): Promise { + const requestUrl = registryUri.with({ path: '/oauth2/exchange' }); + + const requestBody = new URLSearchParams({ + /* eslint-disable @typescript-eslint/naming-convention */ + grant_type: 'access_token', + access_token: accessToken, + service: registryUri.authority, + tenant: subscription.tenantId, + /* eslint-enable @typescript-eslint/naming-convention */ + }); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const response = await httpRequest<{ refresh_token: string }>(requestUrl.toString(), { + method: 'POST', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'content-type': 'application/x-www-form-urlencoded' + }, + body: requestBody, + }); + + return (await response.json()).refresh_token; + } + + private async getAccessToken(subscription: AzureSubscription): Promise { + // Registry scopes, i.e. those passed to `getSession()`, are not valid for acquiring this + // access token--instead, those only need to be passed to `getOAuthTokenFromRefreshToken()` + const token = await subscription.credential.getToken([]); + return token!.token; + } +} diff --git a/src/tree/registries/Azure/AzureRegistryDataProvider.ts b/src/tree/registries/Azure/AzureRegistryDataProvider.ts new file mode 100644 index 0000000000..a259abac84 --- /dev/null +++ b/src/tree/registries/Azure/AzureRegistryDataProvider.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Registry as AcrRegistry } from '@azure/arm-containerregistry'; +import { AzureSubscription, VSCodeAzureSubscriptionProvider } from '@microsoft/vscode-azext-azureauth'; +import { RegistryV2DataProvider, V2Registry, V2RegistryItem } from '@microsoft/vscode-docker-registries'; +import { CommonRegistryItem, isRegistryRoot } from '@microsoft/vscode-docker-registries/lib/clients/Common/models'; +import * as vscode from 'vscode'; +import { ACROAuthProvider } from './ACROAuthProvider'; + +interface AzureRegistryItem extends V2RegistryItem { + readonly subscription: AzureSubscription; +} + +interface AzureSubscriptionRegistryItem extends CommonRegistryItem { + readonly subscription: AzureSubscription; + readonly type: 'azuresubscription'; +} + +function isAzureSubscriptionRegistryItem(item: unknown): item is AzureSubscriptionRegistryItem { + return !!item && typeof item === 'object' && (item as AzureSubscriptionRegistryItem).type === 'azuresubscription'; +} + +type AzureRegistry = V2Registry & AzureRegistryItem; + +export class AzureRegistryDataProvider extends RegistryV2DataProvider implements vscode.Disposable { + public readonly id = 'vscode-docker.azureContainerRegistry'; + public readonly label = vscode.l10n.t('Azure'); + public readonly iconPath = new vscode.ThemeIcon('azure'); + public readonly description = vscode.l10n.t('Azure Container Registry'); + + private readonly subscriptionProvider = new VSCodeAzureSubscriptionProvider(); + private readonly authenticationProviders = new Map(); // The tree items are too short-lived to store the associated auth provider so keep a cache + + public constructor(private readonly extensionContext: vscode.ExtensionContext) { + super(); + } + + public override async getChildren(element?: CommonRegistryItem | undefined): Promise { + if (isRegistryRoot(element)) { + if (!await this.subscriptionProvider.isSignedIn()) { + // TODO: show a node for sign in + await this.subscriptionProvider.signIn(); + this.onDidChangeTreeDataEmitter.fire(element); // TODO: this fires too fast, need a "fire soon" analogue + return []; + } + + const subscriptions = await this.subscriptionProvider.getSubscriptions(); + + return subscriptions.map(sub => { + return { + parent: element, + label: sub.name, + type: 'azuresubscription', + subscription: sub, + additionalContextValues: ['azuresubscription'] + } as AzureSubscriptionRegistryItem; + }); + } else if (isAzureSubscriptionRegistryItem(element)) { + return await this.getRegistries(element); + } else { + const children = await super.getChildren(element); + + if ((element as AzureRegistryItem)?.subscription) { + children.forEach(e => { + e.subscription = (element as AzureRegistryItem).subscription; + }); + } + + return children; + } + } + + public dispose(): void { + this.subscriptionProvider.dispose(); + } + + public async getRegistries(subscriptionItem: CommonRegistryItem): Promise { + subscriptionItem = subscriptionItem as AzureSubscriptionRegistryItem; + // TODO: replace this with `createAzureClient` + const acrClient = new (await import('@azure/arm-containerregistry')).ContainerRegistryManagementClient(subscriptionItem.subscription.credential, subscriptionItem.subscription.subscriptionId); + + const registries: AcrRegistry[] = []; + + for await (const registry of acrClient.registries.list()) { + registries.push(registry); + } + + return registries.map(registry => { + return { + parent: subscriptionItem, + type: 'commonregistry', + registryUri: vscode.Uri.parse(`https://${registry.loginServer}`), + label: registry.name!, + iconPath: vscode.Uri.joinPath(this.extensionContext.extensionUri, 'resources', 'azureRegistry.svg'), + subscription: subscriptionItem.subscription, + }; + }); + } + + public override getTreeItem(element: CommonRegistryItem): Promise { + if (isAzureSubscriptionRegistryItem(element)) { + return Promise.resolve({ + label: element.label, + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + contextValue: 'azuresubscription', + iconPath: vscode.Uri.joinPath(this.extensionContext.extensionUri, 'resources', 'azureSubscription.svg'), + }); + } else { + return super.getTreeItem(element); + } + } + + protected override getAuthenticationProvider(item: AzureRegistryItem): ACROAuthProvider { + const registryString = item.registryUri.toString(); + + if (!this.authenticationProviders.has(registryString)) { + const provider = new ACROAuthProvider(item.registryUri, item.subscription); + this.authenticationProviders.set(registryString, provider); + } + + return this.authenticationProviders.get(registryString)!; + } +} diff --git a/src/tree/registries/ConnectedRegistriesTreeItem.ts b/src/tree/registries/ConnectedRegistriesTreeItem.ts deleted file mode 100644 index 1a8a097bf7..0000000000 --- a/src/tree/registries/ConnectedRegistriesTreeItem.ts +++ /dev/null @@ -1,31 +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 { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { l10n, ThemeIcon } from "vscode"; - -export class ConnectedRegistriesTreeItem extends AzExtParentTreeItem { - public contextValue: string = 'connectedRegistries'; - public childTypeLabel: string = 'registry'; - public label: string = l10n.t('Connected Registries'); - public children: AzExtTreeItem[] = []; - - public constructor(parent: AzExtParentTreeItem | undefined) { - super(parent); - this.iconPath = new ThemeIcon('link'); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - return this.children; - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public isAncestorOfImpl(expectedContextValue: string | RegExp): boolean { - return this.children.some(c => c.isAncestorOfImpl && c.isAncestorOfImpl(expectedContextValue)); - } -} diff --git a/src/tree/registries/ICachedRegistryProvider.ts b/src/tree/registries/ICachedRegistryProvider.ts deleted file mode 100644 index aac276b037..0000000000 --- a/src/tree/registries/ICachedRegistryProvider.ts +++ /dev/null @@ -1,16 +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 { RegistryApi } from "./all/RegistryApi"; - -/** - * Basic _non-sensitive_ information that will be cached across sessions - */ -export interface ICachedRegistryProvider { - id: string; - api: RegistryApi; - url?: string; - username?: string; -} diff --git a/src/tree/registries/IRegistryProvider.ts b/src/tree/registries/IRegistryProvider.ts deleted file mode 100644 index bf1100dc81..0000000000 --- a/src/tree/registries/IRegistryProvider.ts +++ /dev/null @@ -1,68 +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 { AzExtParentTreeItem } from "@microsoft/vscode-azext-utils"; -import { RegistryApi } from "./all/RegistryApi"; -import { IConnectRegistryWizardOptions } from "./connectWizard/IConnectRegistryWizardOptions"; -import { ICachedRegistryProvider } from "./ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "./IRegistryProviderTreeItem"; - -export interface IRegistryProvider { - /** - * A unique id for this registry provider - */ - id: string; - - /** - * The api used by this provider - */ - api: RegistryApi; - - /** - * Primary value to display when prompting a user to connect a provider - */ - label: string; - - /** - * Optional secondary value to display when prompting a user to connect a provider - */ - description?: string; - - /** - * Optional tertiary value to display when prompting a user to connect a provider - */ - detail?: string; - - /** - * Set to true if this provider maps to a single registry as opposed to multiple registries - * If it maps to a single registry, it will be grouped under a "Connected Registries" node in the tree. - */ - isSingleRegistry?: boolean; - - /** - * Set to true if only a single instance of this provider can be connected at a time - */ - onlyOneAllowed?: boolean; - - /** - * Describes the wizard to be used when connecting this provider - */ - connectWizardOptions?: IConnectRegistryWizardOptions; - - /** - * The factory method for creating the root tree item - */ - treeItemFactory(parent: AzExtParentTreeItem, cachedProvider: ICachedRegistryProvider): (AzExtParentTreeItem & IRegistryProviderTreeItem) | Promise; - - /** - * Method to call for persisting auth secrets - */ - persistAuth?(cachedProvider: ICachedRegistryProvider, secret: string): Promise; - - /** - * Method to call to remove auth secrets - */ - removeAuth?(cachedProvider: ICachedRegistryProvider): Promise; -} diff --git a/src/tree/registries/IRegistryProviderTreeItem.ts b/src/tree/registries/IRegistryProviderTreeItem.ts deleted file mode 100644 index 5bac896097..0000000000 --- a/src/tree/registries/IRegistryProviderTreeItem.ts +++ /dev/null @@ -1,10 +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 { ICachedRegistryProvider } from "./ICachedRegistryProvider"; - -export interface IRegistryProviderTreeItem { - cachedProvider: ICachedRegistryProvider; -} diff --git a/src/tree/registries/RegistriesTreeItem.ts b/src/tree/registries/RegistriesTreeItem.ts deleted file mode 100644 index b20ff0b8c4..0000000000 --- a/src/tree/registries/RegistriesTreeItem.ts +++ /dev/null @@ -1,233 +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 { AzExtParentTreeItem, AzExtTreeItem, AzureWizard, GenericTreeItem, IActionContext, IAzureQuickPickItem, parseError, UserCancelledError } from "@microsoft/vscode-azext-utils"; -import { l10n, ThemeIcon } from "vscode"; -import { ext } from "../../extensionVariables"; -import { TreePrefix } from "../TreePrefix"; -import { getRegistryProviders } from "./all/getRegistryProviders"; -import { ConnectedRegistriesTreeItem } from "./ConnectedRegistriesTreeItem"; -import { IConnectRegistryWizardContext } from "./connectWizard/IConnectRegistryWizardContext"; -import { RegistryPasswordStep } from "./connectWizard/RegistryPasswordStep"; -import { RegistryUrlStep } from "./connectWizard/RegistryUrlStep"; -import { RegistryUsernameStep } from "./connectWizard/RegistryUsernameStep"; -import { ICachedRegistryProvider } from "./ICachedRegistryProvider"; -import { IRegistryProvider } from "./IRegistryProvider"; -import { IRegistryProviderTreeItem } from "./IRegistryProviderTreeItem"; -import { anyContextValuePart, contextValueSeparator } from "./registryContextValues"; -import { RegistryTreeItemBase } from "./RegistryTreeItemBase"; - -const providersKey = 'docker.registryProviders'; - -export class RegistriesTreeItem extends AzExtParentTreeItem { - public treePrefix: TreePrefix = 'registries'; - public static contextValue: string = 'registries'; - public contextValue: string = RegistriesTreeItem.contextValue; - public label: string = l10n.t('Registries'); - public childTypeLabel: string = 'registry provider'; - public autoSelectInTreeItemPicker: boolean = true; - - private _connectedRegistriesTreeItem: ConnectedRegistriesTreeItem; - private _cachedProviders: ICachedRegistryProvider[]; - - public constructor() { - super(undefined); - this._connectedRegistriesTreeItem = new ConnectedRegistriesTreeItem(this); - this._cachedProviders = ext.context.globalState.get(providersKey, []); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (this._cachedProviders.length === 0) { - return [new GenericTreeItem(this, { - label: l10n.t('Connect Registry...'), - contextValue: 'connectRegistry', - iconPath: new ThemeIcon('plug'), - includeInTreeItemPicker: true, - commandId: 'vscode-docker.registries.connectRegistry' - })]; - } else { - this._connectedRegistriesTreeItem.children = []; - const children: AzExtTreeItem[] = await this.createTreeItemsWithErrorHandling( - this._cachedProviders, - 'invalidRegistryProvider', - async cachedProvider => { - const provider = getRegistryProviders().find(rp => rp.id === cachedProvider.id); - if (!provider) { - throw new Error(l10n.t('Failed to find registry provider with id "{0}".', cachedProvider.id)); - } - - const parent = provider.isSingleRegistry ? this._connectedRegistriesTreeItem : this; - return this.initTreeItem(await Promise.resolve(provider.treeItemFactory(parent, cachedProvider))); - }, - cachedInfo => cachedInfo.id - ); - - this._connectedRegistriesTreeItem.children = children.filter(c => c.parent === this._connectedRegistriesTreeItem); - if (this._connectedRegistriesTreeItem.children.length > 0) { - children.push(this._connectedRegistriesTreeItem); - } - - return children.filter(c => c.parent !== this._connectedRegistriesTreeItem); - } - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public async connectRegistry(context: IActionContext, provider?: IRegistryProvider, url?: string): Promise { - let picks: IAzureQuickPickItem[] = getRegistryProviders().map(rp => { - return { - label: rp.label, - description: rp.description, - detail: rp.detail, - data: rp - }; - }); - picks = picks.sort((p1, p2) => p1.label.localeCompare(p2.label)); - - const placeHolder: string = l10n.t('Select the provider for your registry'); - provider = provider ?? (await context.ui.showQuickPick(picks, { placeHolder, suppressPersistence: true })).data; - if (!provider) { - throw new UserCancelledError(); - } else if (provider.onlyOneAllowed && this._cachedProviders.find(c => c.id === provider.id)) { - // Don't wait, no input to wait for anyway - void context.ui.showWarningMessage(l10n.t('The "{0}" registry provider is already connected.', provider.label)); - throw new UserCancelledError('registryProviderAlreadyAdded'); - } - - context.telemetry.properties.providerId = provider.id; - context.telemetry.properties.providerApi = provider.api; - - const cachedProvider: ICachedRegistryProvider = { - id: provider.id, - api: provider.api, - }; - - if (provider.connectWizardOptions) { - const existingProviders: ICachedRegistryProvider[] = this._cachedProviders.filter(rp => rp.id === provider.id); - const wizardContext: IConnectRegistryWizardContext = { ...context, ...provider.connectWizardOptions, url, existingProviders }; - const wizard = new AzureWizard(wizardContext, { - title: provider.connectWizardOptions.wizardTitle, - promptSteps: [ - new RegistryUrlStep(), - new RegistryUsernameStep(), - new RegistryPasswordStep() - ] - }); - - await wizard.prompt(); - await wizard.execute(); - - cachedProvider.url = wizardContext.url; - cachedProvider.username = wizardContext.username; - - if (wizardContext.secret && provider.persistAuth) { - await provider.persistAuth(cachedProvider, wizardContext.secret); - } - } - - this._cachedProviders.push(cachedProvider); - await this.saveCachedProviders(context); - } - - public async disconnectRegistry(context: IActionContext, cachedProvider: ICachedRegistryProvider | undefined): Promise { - if (!cachedProvider) { - const picks = this._cachedProviders.map(crp => { - const provider = getRegistryProviders().find(rp => rp.id === crp.id); - const label: string = (provider && provider.label) || crp.id; - const descriptions: string[] = []; - if (crp.username) { - descriptions.push(l10n.t('Username: "{0}"', crp.username)); - } - if (crp.url) { - descriptions.push(l10n.t('URL: "{0}"', crp.url)); - } - return { - label, - description: descriptions[0], - detail: descriptions[1], - data: crp - }; - }); - const placeHolder: string = l10n.t('Select the registry to disconnect'); - cachedProvider = (await context.ui.showQuickPick(picks, { placeHolder, suppressPersistence: true })).data; - } - - context.telemetry.properties.providerId = cachedProvider.id; - context.telemetry.properties.providerApi = cachedProvider.api; - - // NOTE: Do not let failure prevent removal of the tree item. - - try { - const provider = getRegistryProviders().find(rp => rp.id === cachedProvider.id); - if (provider?.removeAuth) { - await provider.removeAuth(cachedProvider); - } - } catch (err) { - // Don't wait, no input to wait for anyway - void context.ui.showWarningMessage(l10n.t('The registry password could not be removed from the cache: {0}', parseError(err).message)); - } - - const index = this._cachedProviders.findIndex(n => n === cachedProvider); - if (index !== -1) { - this._cachedProviders.splice(index, 1); - } - - await this.saveCachedProviders(context); - } - - public hasMultiplesOfProvider(cachedProvider: ICachedRegistryProvider): boolean { - return this._cachedProviders.filter(c => c.id === cachedProvider.id).length > 1; - } - - public async getAllConnectedRegistries(context: IActionContext): Promise { - return await recursiveGetAllConnectedRegistries(context, ext.registriesRoot); - } - - private async saveCachedProviders(context: IActionContext): Promise { - await ext.context.globalState.update(providersKey, this._cachedProviders); - await this.refresh(context); - } - - private initTreeItem(node: AzExtParentTreeItem & IRegistryProviderTreeItem): AzExtParentTreeItem & IRegistryProviderTreeItem { - // Forcing all registry providers to have the same `isAncestorOfImpl` so that a provider doesn't show up for another provider's commands - node.isAncestorOfImpl = (expectedContextValue: string | RegExp) => { - expectedContextValue = expectedContextValue instanceof RegExp ? expectedContextValue.source.toString() : expectedContextValue; - - if (!expectedContextValue.includes(contextValueSeparator)) { - // If the expected context value has a non-standard format, just check against the id - // For example 'azureTask' is non-standard since it is unique to azure - return expectedContextValue.startsWith(node.cachedProvider.id); - } else { - const parts = expectedContextValue.split(contextValueSeparator); - if (parts[0] !== anyContextValuePart) { - return parts[0] === node.cachedProvider.id; - } else if (parts[1] !== anyContextValuePart) { - return parts[1] === node.cachedProvider.api; - } else { - // expectedContextValue must not have specificied any particular id or api, so return true - return true; - } - } - }; - - return node; - } -} - -async function recursiveGetAllConnectedRegistries(context: IActionContext, node: AzExtParentTreeItem): Promise { - let results: RegistryTreeItemBase[] = []; - - for (const child of await node.getCachedChildren(context)) { - if (child instanceof RegistryTreeItemBase) { - results.push(child); - } else if (child instanceof AzExtParentTreeItem) { - results = results.concat(await recursiveGetAllConnectedRegistries(context, child)); - } - } - - return results; -} diff --git a/src/tree/registries/RegistryConnectErrorTreeItem.ts b/src/tree/registries/RegistryConnectErrorTreeItem.ts deleted file mode 100644 index f67ef20a92..0000000000 --- a/src/tree/registries/RegistryConnectErrorTreeItem.ts +++ /dev/null @@ -1,24 +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 { AzExtParentTreeItem, GenericTreeItem, parseError } from "@microsoft/vscode-azext-utils"; -import { ThemeColor, ThemeIcon } from "vscode"; -import { getRegistryProviders } from "./all/getRegistryProviders"; -import { ICachedRegistryProvider } from "./ICachedRegistryProvider"; -import { IRegistryProvider } from "./IRegistryProvider"; - -export class RegistryConnectErrorTreeItem extends GenericTreeItem { - public constructor(parent: AzExtParentTreeItem, err: unknown, public readonly cachedProvider: ICachedRegistryProvider, public readonly url?: string) { - super(parent, { - label: parseError(err).message, - contextValue: 'registryConnectError', - iconPath: new ThemeIcon('warning', new ThemeColor('problemsWarningIcon.foreground')), - }); - - this.provider = getRegistryProviders().find(rp => rp.id === this.cachedProvider.id); - } - - public readonly provider: IRegistryProvider; -} diff --git a/src/tree/registries/RegistryTreeItemBase.ts b/src/tree/registries/RegistryTreeItemBase.ts deleted file mode 100644 index 5e482e6ec5..0000000000 --- a/src/tree/registries/RegistryTreeItemBase.ts +++ /dev/null @@ -1,66 +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 { AzExtParentTreeItem } from "@microsoft/vscode-azext-utils"; -import { ThemeIcon } from "vscode"; -import { RequestLike } from "../../utils/httpRequest"; -import { IRegistryAuthTreeItem } from "../../utils/registryRequestUtils"; -import { getRegistryContextValue, registrySuffix } from "./registryContextValues"; - -/** - * Base class for all registries - * NOTE: A registry is loosely defined as anything that contains repositories (e.g. a private registry or a Docker Hub namespace) - */ -export abstract class RegistryTreeItemBase extends AzExtParentTreeItem implements IRegistryAuthTreeItem { - public childTypeLabel: string = 'repository'; - - public constructor(parent: AzExtParentTreeItem | undefined) { - super(parent); - this.iconPath = new ThemeIcon('briefcase'); - } - - public get contextValue(): string { - return getRegistryContextValue(this, registrySuffix); - } - - /** - * Used for an image's full tag - * For example, if the full tag is "example.azurecr.io/hello-world:latest", this would return "example.azurecr.io" - * NOTE: This usually would _not_ include the protocol part of a url - */ - public abstract baseImagePath: string; - - /** - * Used for registry requests - * NOTE: This _should_ include the protocol part of a url - */ - public abstract baseUrl: string; - - /** - * This will be called before each registry request to add authentication - */ - public abstract signRequest(request: RequestLike): Promise; - - /** - * Describes credentials used to log in to the docker cli before pushing or pulling an image - */ - public abstract getDockerCliCredentials(): Promise; -} - -export interface IDockerCliCredentials { - /** - * Return either username/password or token credentials - * Return undefined if this registry doesn't require logging in to the docker cli - * NOTE: This may or may not be the same credentials used for registry requests - */ - auth?: { token: string } | { username: string; password: string }; - - /** - * The registry to log in to - * For central registries, this will usually be at an account level (aka empty for all of Docker Hub) - * For private registries, this will usually be at the registry level (aka the registry url) - */ - registryPath: string; -} diff --git a/src/tree/registries/RemoteRepositoryTreeItemBase.ts b/src/tree/registries/RemoteRepositoryTreeItemBase.ts deleted file mode 100644 index 1750bdf584..0000000000 --- a/src/tree/registries/RemoteRepositoryTreeItemBase.ts +++ /dev/null @@ -1,49 +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 { AzExtParentTreeItem, AzExtTreeItem } from "@microsoft/vscode-azext-utils"; -import { ThemeIcon } from "vscode"; -import { RequestLike } from "../../utils/httpRequest"; -import { IRepositoryAuthTreeItem } from "../../utils/registryRequestUtils"; -import { getRegistryContextValue, repositorySuffix } from "./registryContextValues"; -import { RegistryTreeItemBase } from "./RegistryTreeItemBase"; -import { RemoteTagTreeItem } from "./RemoteTagTreeItem"; - -/** - * Base class for all repositories - */ -export abstract class RemoteRepositoryTreeItemBase extends AzExtParentTreeItem implements IRepositoryAuthTreeItem { - public childTypeLabel: string = 'tag'; - public parent: RegistryTreeItemBase; - public repoName: string; - - public constructor(parent: RegistryTreeItemBase, repoName: string) { - super(parent); - this.repoName = repoName; - this.iconPath = new ThemeIcon('repo'); - } - - public get label(): string { - return this.repoName; - } - - public get contextValue(): string { - return getRegistryContextValue(this, repositorySuffix); - } - - /** - * Optional method to implement if repo-level requests should have different authentication than registry-level requests - * For example, if the registry supports OAuth you might get a token that has just repo-level permissions instead of registry-level permissions - */ - public signRequest?(request: RequestLike): Promise; - - public compareChildrenImpl(ti1: AzExtTreeItem, ti2: AzExtTreeItem): number { - if (ti1 instanceof RemoteTagTreeItem && ti2 instanceof RemoteTagTreeItem) { - return ti2.time.valueOf() - ti1.time.valueOf(); - } else { - return super.compareChildrenImpl(ti1, ti2); - } - } -} diff --git a/src/tree/registries/RemoteTagTreeItem.ts b/src/tree/registries/RemoteTagTreeItem.ts deleted file mode 100644 index be6230349f..0000000000 --- a/src/tree/registries/RemoteTagTreeItem.ts +++ /dev/null @@ -1,52 +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 * as dayjs from 'dayjs'; -import * as relativeTime from 'dayjs/plugin/relativeTime'; -import { AzExtTreeItem } from '@microsoft/vscode-azext-utils'; -import { ThemeIcon } from 'vscode'; -import { getRegistryContextValue, tagSuffix } from './registryContextValues'; -import { RemoteRepositoryTreeItemBase } from './RemoteRepositoryTreeItemBase'; - -dayjs.extend(relativeTime); - -export class RemoteTagTreeItem extends AzExtTreeItem { - public parent: RemoteRepositoryTreeItemBase; - public tag: string; - public time: Date; - - public constructor(parent: RemoteRepositoryTreeItemBase, tag: string, time: string) { - super(parent); - this.tag = tag; - this.time = new Date(time); - } - - public get label(): string { - return this.tag; - } - - public get contextValue(): string { - return getRegistryContextValue(this, tagSuffix); - } - - /** - * The fullTag minus the registry part - */ - public get repoNameAndTag(): string { - return this.parent.repoName + ':' + this.tag; - } - - public get fullTag(): string { - return `${this.parent.parent.baseImagePath}/${this.repoNameAndTag}`; - } - - public get description(): string { - return dayjs(this.time).fromNow(); - } - - public get iconPath(): ThemeIcon { - return new ThemeIcon('bookmark'); - } -} diff --git a/src/tree/registries/UnifiedRegistryTreeDataProvider.ts b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts new file mode 100644 index 0000000000..ffc1fe1635 --- /dev/null +++ b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts @@ -0,0 +1,132 @@ +import { RegistryDataProvider } from '@microsoft/vscode-docker-registries'; +import * as vscode from 'vscode'; + +export interface UnifiedRegistryItem { + provider: RegistryDataProvider; + wrappedItem: T; + parent: UnifiedRegistryItem | undefined; +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +const ConnectedRegistryProvidersKey = 'ConnectedRegistryProviders'; + +export class UnifiedRegistryTreeDataProvider implements vscode.TreeDataProvider> { + private readonly onDidChangeTreeDataEmitter = new vscode.EventEmitter | UnifiedRegistryItem[] | undefined>(); + public readonly onDidChangeTreeData = this.onDidChangeTreeDataEmitter.event; + + private readonly providers = new Map>(); + + public constructor(private readonly storageMemento: vscode.Memento) { + + } + + public getTreeItem(element: UnifiedRegistryItem): vscode.TreeItem | Thenable { + return element.provider.getTreeItem(element.wrappedItem); + } + + public async getChildren(element?: UnifiedRegistryItem | undefined): Promise[]> { + if (element) { + const elements = await element.provider.getChildren(element.wrappedItem); + + if (!elements) { + return []; + } + + return elements.map(e => { + return { + provider: element.provider, + wrappedItem: e, + parent: element + }; + }); + } else { + const unifiedRoots: UnifiedRegistryItem[] = []; + + const connectedProviderIds = this.storageMemento.get(ConnectedRegistryProvidersKey, []); + + for (const provider of this.providers.values()) { + if (!connectedProviderIds.includes(provider.id)) { + continue; + } + + const roots = await provider.getChildren(undefined); + if (!roots) { + continue; + } + + unifiedRoots.push(...roots.map(r => { + return { + provider, + wrappedItem: r, + parent: undefined + }; + })); + } + + return unifiedRoots; + } + } + + public getParent(element: UnifiedRegistryItem): UnifiedRegistryItem | undefined { + return element.parent; + } + + public registerProvider(provider: RegistryDataProvider): vscode.Disposable { + this.providers.set(provider.id, provider); + + return { + dispose: () => { + this.providers.delete(provider.id); + } + }; + } + + public refresh(): void { + this.onDidChangeTreeDataEmitter.fire(undefined); + } + + public async connectRegistryProvider(provider: RegistryDataProvider | undefined = undefined): Promise { + const connectedProviderIds = this.storageMemento.get(ConnectedRegistryProvidersKey, []); + + if (!provider) { + const picks: (vscode.QuickPickItem & { provider: RegistryDataProvider })[] = []; + + for (const currentProvider of this.providers.values()) { + if (connectedProviderIds.includes(currentProvider.id)) { + continue; + } + + picks.push({ + label: currentProvider.label, + description: currentProvider.description, + provider: currentProvider + }); + } + + const picked = await vscode.window.showQuickPick(picks, { placeHolder: vscode.l10n.t('Select a registry provider to use') }); + if (!picked) { + return; + } + + provider = picked.provider; + } + + if (!connectedProviderIds.includes(provider.id)) { + await provider?.onConnect?.(); + const connectedProviderIdsSet: Set = new Set(connectedProviderIds); + connectedProviderIdsSet.add(provider.id); + await this.storageMemento.update(ConnectedRegistryProvidersKey, Array.from(connectedProviderIdsSet)); + } + + this.refresh(); + } + + public async disconnectRegistryProvider(item: UnifiedRegistryItem): Promise { + await item.provider?.onDisconnect?.(); + const newConnectedProviderIds = this.storageMemento + .get(ConnectedRegistryProvidersKey, []) + .filter(cpi => cpi !== item.provider.id); + await this.storageMemento.update(ConnectedRegistryProvidersKey, newConnectedProviderIds); + this.refresh(); + } +} diff --git a/src/tree/registries/all/RegistryApi.ts b/src/tree/registries/all/RegistryApi.ts deleted file mode 100644 index b52cb17815..0000000000 --- a/src/tree/registries/all/RegistryApi.ts +++ /dev/null @@ -1,22 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export enum RegistryApi { - /** - * https://docs.docker.com/registry/spec/api/ - */ - DockerV2 = 'DockerV2', - - /** - * https://docs.gitlab.com/ee/api/README.html - * https://docs.gitlab.com/ee/api/container_registry.html - */ - GitLabV4 = 'GitLabV4', - - /** - * No public docs found - */ - DockerHubV2 = 'DockerHubV2' -} diff --git a/src/tree/registries/all/getRegistryProviders.ts b/src/tree/registries/all/getRegistryProviders.ts deleted file mode 100644 index 5ad27a3133..0000000000 --- a/src/tree/registries/all/getRegistryProviders.ts +++ /dev/null @@ -1,19 +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 { azureRegistryProvider } from "../azure/azureRegistryProvider"; -import { dockerHubRegistryProvider } from "../dockerHub/dockerHubRegistryProvider"; -import { genericDockerV2RegistryProvider } from "../dockerV2/genericDockerV2RegistryProvider"; -import { gitLabRegistryProvider } from "../gitLab/gitLabRegistryProvider"; -import { IRegistryProvider } from "../IRegistryProvider"; - -export function getRegistryProviders(): IRegistryProvider[] { - return [ - azureRegistryProvider, - dockerHubRegistryProvider, - gitLabRegistryProvider, - genericDockerV2RegistryProvider - ]; -} diff --git a/src/tree/registries/auth/AzureOAuthProvider.ts b/src/tree/registries/auth/AzureOAuthProvider.ts deleted file mode 100644 index f65ead5c1b..0000000000 --- a/src/tree/registries/auth/AzureOAuthProvider.ts +++ /dev/null @@ -1,34 +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 { ISubscriptionContext } from '@microsoft/vscode-azext-utils'; -import { acquireAcrAccessToken, acquireAcrRefreshToken } from '../../../utils/azureUtils'; -import { IOAuthContext, RequestLike, bearerAuthHeader } from '../../../utils/httpRequest'; -import { ICachedRegistryProvider } from '../ICachedRegistryProvider'; -import { IDockerCliCredentials } from '../RegistryTreeItemBase'; -import { IAuthProvider } from './IAuthProvider'; - -export interface IAzureOAuthContext extends IOAuthContext { - subscriptionContext: ISubscriptionContext -} - -class AzureOAuthProvider implements IAuthProvider { - - public async signRequest(cachedProvider: ICachedRegistryProvider, request: RequestLike, authContext: IAzureOAuthContext): Promise { - request.headers.set('Authorization', bearerAuthHeader(await acquireAcrAccessToken(authContext.realm.host, authContext.subscriptionContext, authContext.scope))); - return request; - } - - public async getDockerCliCredentials(cachedProvider: ICachedRegistryProvider, authContext?: IAzureOAuthContext): Promise { - return { - registryPath: `https://${authContext.service}`, - auth: { - token: await acquireAcrRefreshToken(authContext.realm.host, authContext.subscriptionContext), - }, - }; - } -} - -export const azureOAuthProvider: IAuthProvider = new AzureOAuthProvider(); diff --git a/src/tree/registries/auth/BasicOAuthProvider.ts b/src/tree/registries/auth/BasicOAuthProvider.ts deleted file mode 100644 index 73aaf5280b..0000000000 --- a/src/tree/registries/auth/BasicOAuthProvider.ts +++ /dev/null @@ -1,65 +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 { HttpResponse, IOAuthContext, RequestLike, RequestOptionsLike, basicAuthHeader, bearerAuthHeader, httpRequest } from '../../../utils/httpRequest'; -import { ICachedRegistryProvider } from '../ICachedRegistryProvider'; -import { getRegistryPassword } from '../registryPasswords'; -import { IDockerCliCredentials } from '../RegistryTreeItemBase'; -import { IAuthProvider } from './IAuthProvider'; - -/** - * Performs basic auth and password-grant-type OAuth - */ -class BasicOAuthProvider implements IAuthProvider { - - public async signRequest(cachedProvider: ICachedRegistryProvider, request: RequestLike, authContext?: IOAuthContext): Promise { - if (!authContext) { - request.headers.set('Authorization', basicAuthHeader(cachedProvider.username, await getRegistryPassword(cachedProvider))); - return request; - } - - const options: RequestOptionsLike = { - form: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'grant_type': 'password', - 'service': authContext.service, - 'scope': authContext.scope - }, - headers: { - Authorization: basicAuthHeader(cachedProvider.username, await getRegistryPassword(cachedProvider)), - }, - }; - - let tokenResponse: HttpResponse<{ token: string }>; - try { - // First try with POST - tokenResponse = await httpRequest(authContext.realm.toString(), { method: 'POST', ...options }); - } catch { - // If that fails, try falling back to GET - // (If that fails we'll just throw) - tokenResponse = await httpRequest(authContext.realm.toString(), { method: 'GET', ...options }); - } - - request.headers.set('Authorization', bearerAuthHeader((await tokenResponse.json()).token)); - return request; - } - - public async getDockerCliCredentials(cachedProvider: ICachedRegistryProvider, authContext?: IOAuthContext): Promise { - const creds: IDockerCliCredentials = { - registryPath: cachedProvider.url - }; - - if (cachedProvider.username) { - creds.auth = { - username: cachedProvider.username, - password: await getRegistryPassword(cachedProvider), - }; - } - - return creds; - } -} - -export const basicOAuthProvider: IAuthProvider = new BasicOAuthProvider(); diff --git a/src/tree/registries/auth/IAuthProvider.ts b/src/tree/registries/auth/IAuthProvider.ts deleted file mode 100644 index 6fa984c305..0000000000 --- a/src/tree/registries/auth/IAuthProvider.ts +++ /dev/null @@ -1,13 +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 { IOAuthContext, RequestLike } from '../../../utils/httpRequest'; -import { ICachedRegistryProvider } from '../ICachedRegistryProvider'; -import { IDockerCliCredentials } from '../RegistryTreeItemBase'; - -export interface IAuthProvider { - signRequest(cachedProvider: ICachedRegistryProvider, request: RequestLike, authContext?: IOAuthContext): Promise; - getDockerCliCredentials(cachedProvider: ICachedRegistryProvider, authContext?: IOAuthContext): Promise; -} diff --git a/src/tree/registries/azure/AzureAccountTreeItem.ts b/src/tree/registries/azure/AzureAccountTreeItem.ts deleted file mode 100644 index 939b0f4d0d..0000000000 --- a/src/tree/registries/azure/AzureAccountTreeItem.ts +++ /dev/null @@ -1,40 +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 { AzureAccountTreeItemBase } from "@microsoft/vscode-azext-azureutils"; // This can't be made lazy, so users of this class must be lazy -import { AzExtParentTreeItem, AzExtTreeItem, IActionContext, ISubscriptionContext } from "@microsoft/vscode-azext-utils"; -import { Disposable } from "vscode"; -import { AzureAccountExtensionListener } from "../../../utils/AzureAccountExtensionListener"; -import { getAzSubTreeItem } from "../../../utils/lazyPackages"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import { getRegistryContextValue, registryProviderSuffix } from "../registryContextValues"; -import type { SubscriptionTreeItem } from "./SubscriptionTreeItem"; - -export class AzureAccountTreeItem extends AzureAccountTreeItemBase implements IRegistryProviderTreeItem { - public constructor(parent: AzExtParentTreeItem, public readonly cachedProvider: ICachedRegistryProvider) { - super(parent); - this.contextValue = getRegistryContextValue(this, registryProviderSuffix); - } - - public async createSubscriptionTreeItem(subContext: ISubscriptionContext): Promise { - const azSubTreeItem = await getAzSubTreeItem(); - return new azSubTreeItem.SubscriptionTreeItem(this, subContext, this.cachedProvider); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - const treeItems: AzExtTreeItem[] = await super.loadMoreChildrenImpl(clearCache, context); - if (treeItems.length === 1 && treeItems[0].commandId === 'extension.open') { - const extensionInstallEventDisposable: Disposable = AzureAccountExtensionListener.onExtensionInstalled(() => { - extensionInstallEventDisposable.dispose(); - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.refresh(context); - }); - } - - return treeItems; - } -} diff --git a/src/tree/registries/azure/AzureRegistryTreeItem.ts b/src/tree/registries/azure/AzureRegistryTreeItem.ts deleted file mode 100644 index d35fe97458..0000000000 --- a/src/tree/registries/azure/AzureRegistryTreeItem.ts +++ /dev/null @@ -1,115 +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 type { ContainerRegistryManagementClient, Registry, RegistryListCredentialsResult } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy -import { AzExtTreeItem, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { URL } from "url"; -import { getResourceGroupFromId } from "../../../utils/azureUtils"; -import { getArmContainerRegistry, getAzExtAzureUtils } from "../../../utils/lazyPackages"; -import { getIconPath } from "../../getThemedIconPath"; -import { IAzureOAuthContext, azureOAuthProvider } from "../auth/AzureOAuthProvider"; -import { DockerV2RegistryTreeItemBase } from "../dockerV2/DockerV2RegistryTreeItemBase"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { AzureRepositoryTreeItem } from "./AzureRepositoryTreeItem"; -import { AzureTasksTreeItem } from "./AzureTasksTreeItem"; -import type { SubscriptionTreeItem } from "./SubscriptionTreeItem"; // These are only dev-time imports so don't need to be lazy - -export class AzureRegistryTreeItem extends DockerV2RegistryTreeItemBase { - public parent: SubscriptionTreeItem; - - protected authContext?: IAzureOAuthContext; - - private _tasksTreeItem: AzureTasksTreeItem; - - public constructor(parent: SubscriptionTreeItem, cachedProvider: ICachedRegistryProvider, private readonly registry: Registry) { - super(parent, cachedProvider, azureOAuthProvider); - this._tasksTreeItem = new AzureTasksTreeItem(this); - this.authContext = { - realm: new URL(`${this.baseUrl}/oauth2/token`), - service: this.host, - subscriptionContext: this.parent.subscription, - scope: 'registry:catalog:*', - }; - - this.id = this.registryId; - this.iconPath = getIconPath('azureRegistry'); - } - - public get registryName(): string { - return nonNullProp(this.registry, 'name'); - } - - public get registryId(): string { - return nonNullProp(this.registry, 'id'); - } - - public get resourceGroup(): string { - return getResourceGroupFromId(this.registryId); - } - - public get registryLocation(): string { - return this.registry.location; - } - - public async getClient(context: IActionContext): Promise { - const azExtAzureUtils = await getAzExtAzureUtils(); - const armContainerRegistry = await getArmContainerRegistry(); - return azExtAzureUtils.createAzureClient({ ...context, ...this.subscription }, armContainerRegistry.ContainerRegistryManagementClient); - } - - public get label(): string { - return this.registryName; - } - - public get properties(): unknown { - return this.registry; - } - - public get baseUrl(): string { - return `https://${nonNullProp(this.registry, 'loginServer')}`; - } - - public createRepositoryTreeItem(name: string): AzureRepositoryTreeItem { - return new AzureRepositoryTreeItem(this, name, this.cachedProvider, this.authHelper, this.authContext); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - const children: AzExtTreeItem[] = await super.loadMoreChildrenImpl(clearCache, context); - if (clearCache) { - children.push(this._tasksTreeItem); - } - return children; - } - - public compareChildrenImpl(ti1: AzExtTreeItem, ti2: AzExtTreeItem): number { - if (ti1 instanceof AzureTasksTreeItem) { - return -1; - } else if (ti2 instanceof AzureTasksTreeItem) { - return 1; - } else { - return super.compareChildrenImpl(ti1, ti2); - } - } - - public async pickTreeItemImpl(expectedContextValues: (string | RegExp)[]): Promise { - if (expectedContextValues.some(v => this._tasksTreeItem.isAncestorOfImpl(v))) { - return this._tasksTreeItem; - } else { - return undefined; - } - } - - public async deleteTreeItemImpl(context: IActionContext): Promise { - await (await this.getClient(context)).registries.beginDeleteAndWait(this.resourceGroup, this.registryName); - } - - public async tryGetAdminCredentials(context: IActionContext): Promise { - if (this.registry.adminUserEnabled) { - return await (await this.getClient(context)).registries.listCredentials(this.resourceGroup, this.registryName); - } else { - return undefined; - } - } -} diff --git a/src/tree/registries/azure/AzureRepositoryTreeItem.ts b/src/tree/registries/azure/AzureRepositoryTreeItem.ts deleted file mode 100644 index 03893ffb78..0000000000 --- a/src/tree/registries/azure/AzureRepositoryTreeItem.ts +++ /dev/null @@ -1,19 +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 { registryRequest } from "../../../utils/registryRequestUtils"; -import { IAzureOAuthContext } from "../auth/AzureOAuthProvider"; -import { DockerV2RepositoryTreeItem } from "../dockerV2/DockerV2RepositoryTreeItem"; -import { AzureRegistryTreeItem } from "./AzureRegistryTreeItem"; - -export class AzureRepositoryTreeItem extends DockerV2RepositoryTreeItem { - public parent: AzureRegistryTreeItem; - - protected authContext?: IAzureOAuthContext; - - public async deleteTreeItemImpl(): Promise { - await registryRequest(this, 'DELETE', `v2/_acr/${this.repoName}/repository`); - } -} diff --git a/src/tree/registries/azure/AzureTaskRunTreeItem.ts b/src/tree/registries/azure/AzureTaskRunTreeItem.ts deleted file mode 100644 index c3d082d0eb..0000000000 --- a/src/tree/registries/azure/AzureTaskRunTreeItem.ts +++ /dev/null @@ -1,80 +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 * as dayjs from 'dayjs'; -import * as relativeTime from 'dayjs/plugin/relativeTime'; -import type { Run as AcrRun, ImageDescriptor } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy -import { AzExtTreeItem, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { ThemeColor, ThemeIcon } from "vscode"; -import { AzureTaskTreeItem } from "./AzureTaskTreeItem"; - -dayjs.extend(relativeTime); - -export class AzureTaskRunTreeItem extends AzExtTreeItem { - public static contextValue: string = 'azureTaskRun'; - public contextValue: string = AzureTaskRunTreeItem.contextValue; - public parent: AzureTaskTreeItem; - - private _run: AcrRun; - - public constructor(parent: AzureTaskTreeItem, run: AcrRun) { - super(parent); - this._run = run; - } - - public get runName(): string { - return nonNullProp(this._run, 'name'); - } - - public get runId(): string { - return nonNullProp(this._run, 'runId'); - } - - public get label(): string { - return this.runName; - } - - public get id(): string { - return this.runId; - } - - public get createTime(): Date | undefined { - return this._run.createTime; - } - - public get outputImage(): ImageDescriptor | undefined { - return this._run.outputImages && this._run.outputImages[0]; - } - - public get iconPath(): ThemeIcon { - switch (this._run.status) { - case 'Succeeded': - return new ThemeIcon('check', new ThemeColor('debugIcon.startForeground')); - case 'Failed': - return new ThemeIcon('error', new ThemeColor('problemsErrorIcon.foreground')); - case 'Running': - return new ThemeIcon('debug-start', new ThemeColor('debugIcon.startForeground')); - default: - return new ThemeIcon('warning', new ThemeColor('problemsWarningIcon.foreground')); - } - } - - public get properties(): unknown { - return this._run; - } - - public get description(): string { - const parts: string[] = []; - if (this.createTime) { - parts.push(dayjs(this.createTime).fromNow()); - } - - if (this._run.status && this._run.status !== 'Succeeded') { - parts.push(this._run.status); - } - - return parts.join(' - '); - } -} diff --git a/src/tree/registries/azure/AzureTaskTreeItem.ts b/src/tree/registries/azure/AzureTaskTreeItem.ts deleted file mode 100644 index 6dfa4aa369..0000000000 --- a/src/tree/registries/azure/AzureTaskTreeItem.ts +++ /dev/null @@ -1,90 +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 type { Task as AcrTask, TaskRun as AcrTaskRun } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy -import { AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, IActionContext, nonNullValue, nonNullValueAndProp } from "@microsoft/vscode-azext-utils"; -import { l10n, ThemeIcon } from "vscode"; -import { getAzExtAzureUtils } from "../../../utils/lazyPackages"; -import { AzureRegistryTreeItem } from "./AzureRegistryTreeItem"; -import { AzureTaskRunTreeItem } from "./AzureTaskRunTreeItem"; -import { AzureTasksTreeItem } from "./AzureTasksTreeItem"; - -export class AzureTaskTreeItem extends AzExtParentTreeItem { - public static contextValue: string = 'azureTask'; - private static _noTaskFilter: string = 'TaskName eq null'; - public childTypeLabel: string = 'task run'; - public parent: AzureTasksTreeItem; - - private _task: AcrTask | undefined; - - public constructor(parent: AzureTasksTreeItem, task: AcrTask | undefined) { - super(parent); - this._task = task; - this.iconPath = new ThemeIcon('tasklist'); - this.id = this._task ? this._task.id : undefined; - } - - public get contextValue(): string { - return this._task ? AzureTaskTreeItem.contextValue : 'azureRunsWithoutTask'; - } - - public get label(): string { - return this._task ? this.taskName : l10n.t('Runs without a task'); - } - - public get taskName(): string { - return nonNullValueAndProp(this._task, 'name'); - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public get properties(): unknown { - return nonNullValue(this._task, '_task'); - } - - public static async hasRunsWithoutTask(context: IActionContext, registryTI: AzureRegistryTreeItem): Promise { - const runListResult = await AzureTaskTreeItem.getTaskRuns(context, registryTI, AzureTaskTreeItem._noTaskFilter); - return runListResult.length > 0; - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - const filter = this._task ? `TaskName eq '${this.taskName}'` : AzureTaskTreeItem._noTaskFilter; - const runListResult = await AzureTaskTreeItem.getTaskRuns(context, this.parent.parent, filter); - - if (clearCache && runListResult.length === 0 && this._task) { - const ti = new GenericTreeItem(this, { - label: l10n.t('Run Task...'), - commandId: 'vscode-docker.registries.azure.runTask', - contextValue: 'runTask' - }); - ti.commandArgs = [this]; - return [ti]; - } else { - return await this.createTreeItemsWithErrorHandling( - runListResult, - 'invalidAzureTaskRun', - async r => new AzureTaskRunTreeItem(this, r), - r => r.name - ); - } - } - - public compareChildrenImpl(ti1: AzExtTreeItem, ti2: AzExtTreeItem): number { - if (ti1 instanceof AzureTaskRunTreeItem && ti2 instanceof AzureTaskRunTreeItem && ti1.createTime && ti2.createTime) { - return ti2.createTime.valueOf() - ti1.createTime.valueOf(); - } else { - return super.compareChildrenImpl(ti1, ti2); - } - } - - private static async getTaskRuns(context: IActionContext, registryTI: AzureRegistryTreeItem, filter: string): Promise { - const azExtAzureUtils = await getAzExtAzureUtils(); - const registryClient = await registryTI.getClient(context); - - return await azExtAzureUtils.uiUtils.listAllIterator(registryClient.runs.list(registryTI.resourceGroup, registryTI.registryName, { filter })); - } -} diff --git a/src/tree/registries/azure/AzureTasksTreeItem.ts b/src/tree/registries/azure/AzureTasksTreeItem.ts deleted file mode 100644 index 82f3417de6..0000000000 --- a/src/tree/registries/azure/AzureTasksTreeItem.ts +++ /dev/null @@ -1,72 +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 type { Task } from "@azure/arm-containerregistry"; // These are only dev-time imports so don't need to be lazy -import { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { l10n, ThemeIcon } from "vscode"; -import { getAzExtAzureUtils } from "../../../utils/lazyPackages"; -import { OpenUrlTreeItem } from "../../OpenUrlTreeItem"; -import { AzureRegistryTreeItem } from "./AzureRegistryTreeItem"; -import { AzureTaskTreeItem } from "./AzureTaskTreeItem"; - -export class AzureTasksTreeItem extends AzExtParentTreeItem { - public static contextValue: string = 'azureTasks'; - public contextValue: string = AzureTasksTreeItem.contextValue; - public label: string = 'Tasks'; - public childTypeLabel: string = 'task'; - public parent: AzureRegistryTreeItem; - - private _nextLink: string | undefined; - - public constructor(parent: AzureRegistryTreeItem) { - super(parent); - this.iconPath = new ThemeIcon('checklist'); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const registryTI = this.parent; - - const azExtAzureUtils = await getAzExtAzureUtils(); - const registryClient = await registryTI.getClient(context); - - const taskListResult: Task[] = await azExtAzureUtils.uiUtils.listAllIterator(registryClient.tasks.list(registryTI.resourceGroup, registryTI.registryName)); - - if (clearCache && taskListResult.length === 0) { - return [new OpenUrlTreeItem(this, l10n.t('Learn how to create a build task...'), 'https://aka.ms/acr/task')]; - } else { - const result: AzExtTreeItem[] = await this.createTreeItemsWithErrorHandling( - taskListResult, - 'invalidAzureTask', - async t => new AzureTaskTreeItem(this, t), - t => t.name - ); - - if (clearCache) { - // If there are any runs _not_ associated with a task (e.g. the user ran a task from a local Dockerfile) add a tree item to display those runs - if (await AzureTaskTreeItem.hasRunsWithoutTask(context, this.parent)) { - result.push(new AzureTaskTreeItem(this, undefined)); - } - } - - return result; - } - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - public isAncestorOfImpl(expectedContextValue: string | RegExp): boolean { - if (expectedContextValue instanceof RegExp) { - expectedContextValue = expectedContextValue.source.toString(); - } - - return expectedContextValue.toLowerCase().includes('task'); - } -} diff --git a/src/tree/registries/azure/SubscriptionTreeItem.ts b/src/tree/registries/azure/SubscriptionTreeItem.ts deleted file mode 100644 index 77769f7ba6..0000000000 --- a/src/tree/registries/azure/SubscriptionTreeItem.ts +++ /dev/null @@ -1,75 +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 type { ContainerRegistryManagementClient, Registry as AcrRegistry } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy -import { SubscriptionTreeItemBase } from '@microsoft/vscode-azext-azureutils'; // This can't be made lazy, so users of this class must be lazy -import { AzExtParentTreeItem, AzExtTreeItem, AzureWizard, IActionContext, ICreateChildImplContext, ISubscriptionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { l10n, window } from 'vscode'; -import { getArmContainerRegistry, getAzExtAzureUtils } from '../../../utils/lazyPackages'; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import type { AzureAccountTreeItem } from './AzureAccountTreeItem'; // These are only dev-time imports so don't need to be lazy -import { AzureRegistryTreeItem } from './AzureRegistryTreeItem'; -import { AzureRegistryCreateStep } from './createWizard/AzureRegistryCreateStep'; -import { AzureRegistryNameStep } from './createWizard/AzureRegistryNameStep'; -import { AzureRegistrySkuStep } from './createWizard/AzureRegistrySkuStep'; -import { IAzureRegistryWizardContext } from './createWizard/IAzureRegistryWizardContext'; - -export class SubscriptionTreeItem extends SubscriptionTreeItemBase implements IRegistryProviderTreeItem { - public childTypeLabel: string = 'registry'; - public parent: AzureAccountTreeItem; - - public constructor(parent: AzExtParentTreeItem, root: ISubscriptionContext, public readonly cachedProvider: ICachedRegistryProvider) { - super(parent, root); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - const armContainerRegistry = await getArmContainerRegistry(); - const azExtAzureUtils = await getAzExtAzureUtils(); - const client: ContainerRegistryManagementClient = azExtAzureUtils.createAzureClient({ ...context, ...this.subscription }, armContainerRegistry.ContainerRegistryManagementClient); - const registryListResult: AcrRegistry[] = await azExtAzureUtils.uiUtils.listAllIterator(client.registries.list()); - - return await this.createTreeItemsWithErrorHandling( - registryListResult, - 'invalidAzureRegistry', - async r => new AzureRegistryTreeItem(this, this.cachedProvider, r), - r => r.name - ); - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public async createChildImpl(context: ICreateChildImplContext): Promise { - const wizardContext: IAzureRegistryWizardContext = { ...context, ...this.subscription }; - const azExtAzureUtils = await getAzExtAzureUtils(); - - const promptSteps = [ - new AzureRegistryNameStep(), - new AzureRegistrySkuStep(), - new azExtAzureUtils.ResourceGroupListStep(), - ]; - azExtAzureUtils.LocationListStep.addStep(wizardContext, promptSteps); - - const wizard = new AzureWizard(wizardContext, { - promptSteps, - executeSteps: [ - new AzureRegistryCreateStep() - ], - title: l10n.t('Create new Azure Container Registry') - }); - - await wizard.prompt(); - const newRegistryName: string = nonNullProp(wizardContext, 'newRegistryName'); - context.showCreatingTreeItem(newRegistryName); - await wizard.execute(); - - // don't wait - /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ - window.showInformationMessage(`Successfully created registry "${newRegistryName}".`); - return new AzureRegistryTreeItem(this, this.cachedProvider, nonNullProp(wizardContext, 'registry')); - } -} diff --git a/src/tree/registries/azure/azureRegistryProvider.ts b/src/tree/registries/azure/azureRegistryProvider.ts deleted file mode 100644 index 3724f25503..0000000000 --- a/src/tree/registries/azure/azureRegistryProvider.ts +++ /dev/null @@ -1,27 +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 { AzExtParentTreeItem } from "@microsoft/vscode-azext-utils"; -import { getAzActTreeItem } from "../../../utils/lazyPackages"; -import { RegistryApi } from "../all/RegistryApi"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProvider } from "../IRegistryProvider"; -import type { AzureAccountTreeItem } from "./AzureAccountTreeItem"; // These are only dev-time imports so don't need to be lazy - -export const azureRegistryProviderId: string = 'azure'; - -export const azureRegistryProvider: IRegistryProvider = { - label: "Azure", - id: azureRegistryProviderId, - api: RegistryApi.DockerV2, - onlyOneAllowed: true, - connectWizardOptions: undefined, - treeItemFactory: async (parent: AzExtParentTreeItem, cachedProvider: ICachedRegistryProvider): Promise => { - const azActTreeItem = await getAzActTreeItem(); - return new azActTreeItem.AzureAccountTreeItem(parent, cachedProvider); - }, - persistAuth: undefined, - removeAuth: undefined, -}; diff --git a/src/tree/registries/azure/createWizard/AzureRegistryCreateStep.ts b/src/tree/registries/azure/createWizard/AzureRegistryCreateStep.ts deleted file mode 100644 index 077bb1aa84..0000000000 --- a/src/tree/registries/azure/createWizard/AzureRegistryCreateStep.ts +++ /dev/null @@ -1,57 +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 type { AzExtLocation } from '@microsoft/vscode-azext-azureutils'; -import { AzureWizardExecuteStep, nonNullProp, parseError } from '@microsoft/vscode-azext-utils'; -import { l10n, Progress } from 'vscode'; -import { ext } from '../../../../extensionVariables'; -import { getArmContainerRegistry, getAzExtAzureUtils } from '../../../../utils/lazyPackages'; -import { IAzureRegistryWizardContext } from './IAzureRegistryWizardContext'; - -export class AzureRegistryCreateStep extends AzureWizardExecuteStep { - public priority: number = 130; - - public async execute(context: IAzureRegistryWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise { - const newRegistryName = nonNullProp(context, 'newRegistryName'); - - const azExtAzureUtils = await getAzExtAzureUtils(); - const armContainerRegistry = await getArmContainerRegistry(); - const client = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); - const creating: string = l10n.t('Creating registry "{0}"...', newRegistryName); - ext.outputChannel.info(creating); - progress.report({ message: creating }); - - const location: AzExtLocation = await azExtAzureUtils.LocationListStep.getLocation(context); - const locationName: string = nonNullProp(location, 'name'); - const resourceGroup = nonNullProp(context, 'resourceGroup'); - try { - context.registry = await client.registries.beginCreateAndWait( - nonNullProp(resourceGroup, 'name'), - newRegistryName, - { - sku: { - name: nonNullProp(context, 'newRegistrySku') - }, - location: locationName - } - ); - } - catch (err) { - const parsedError = parseError(err); - if (parsedError.errorType === 'MissingSubscriptionRegistration') { - context.errorHandling.suppressReportIssue = true; - } - - throw err; - } - - const created = l10n.t('Successfully created registry "{0}".', newRegistryName); - ext.outputChannel.info(created); - } - - public shouldExecute(context: IAzureRegistryWizardContext): boolean { - return !context.registry; - } -} diff --git a/src/tree/registries/azure/createWizard/AzureRegistryNameStep.ts b/src/tree/registries/azure/createWizard/AzureRegistryNameStep.ts deleted file mode 100644 index 14843f6b4b..0000000000 --- a/src/tree/registries/azure/createWizard/AzureRegistryNameStep.ts +++ /dev/null @@ -1,50 +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 type { ContainerRegistryManagementClient } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy -import { AzureNameStep } from '@microsoft/vscode-azext-utils'; -import { l10n } from 'vscode'; -import { getArmContainerRegistry, getAzExtAzureUtils } from '../../../../utils/lazyPackages'; -import { IAzureRegistryWizardContext } from './IAzureRegistryWizardContext'; - -export class AzureRegistryNameStep extends AzureNameStep { - protected async isRelatedNameAvailable(context: IAzureRegistryWizardContext, name: string): Promise { - const azExtAzureUtils = await getAzExtAzureUtils(); - return await azExtAzureUtils.ResourceGroupListStep.isNameAvailable(context, name); - } - - public async prompt(context: IAzureRegistryWizardContext): Promise { - const azExtAzureUtils = await getAzExtAzureUtils(); - const armContainerRegistry = await getArmContainerRegistry(); - const client = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient); - context.newRegistryName = (await context.ui.showInputBox({ - placeHolder: l10n.t('Registry name'), - prompt: l10n.t('Provide a registry name'), - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ - validateInput: (name: string) => validateRegistryName(name, client) - })).trim(); - - context.relatedNameTask = this.generateRelatedName(context, context.newRegistryName, azExtAzureUtils.resourceGroupNamingRules); - } - - public shouldPrompt(context: IAzureRegistryWizardContext): boolean { - return !context.newRegistryName; - } -} - -async function validateRegistryName(name: string, client: ContainerRegistryManagementClient): Promise { - name = name ? name.trim() : ''; - - const min = 5; - const max = 50; - if (name.length < min || name.length > max) { - return l10n.t('The name must be between {0} and {1} characters.', min, max); - } else if (name.match(/[^a-z0-9]/i)) { - return l10n.t('The name can only contain alphanumeric characters.'); - } else { - const nameStatus = await client.registries.checkNameAvailability({ name, type: 'Microsoft.ContainerRegistry/registries' }); - return nameStatus.message; - } -} diff --git a/src/tree/registries/azure/createWizard/AzureRegistrySkuStep.ts b/src/tree/registries/azure/createWizard/AzureRegistrySkuStep.ts deleted file mode 100644 index bbfc3684c1..0000000000 --- a/src/tree/registries/azure/createWizard/AzureRegistrySkuStep.ts +++ /dev/null @@ -1,23 +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 type { SkuName as AcrSkuName } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy -import { AzureWizardPromptStep, IAzureQuickPickItem } from '@microsoft/vscode-azext-utils'; -import { l10n } from 'vscode'; -import { IAzureRegistryWizardContext } from './IAzureRegistryWizardContext'; - -export class AzureRegistrySkuStep extends AzureWizardPromptStep { - public async prompt(context: IAzureRegistryWizardContext): Promise { - const skus: AcrSkuName[] = ["Basic", "Standard", "Premium"]; - const picks: IAzureQuickPickItem[] = skus.map(s => { return { label: s, data: s }; }); - - const placeHolder: string = l10n.t('Select a SKU'); - context.newRegistrySku = (await context.ui.showQuickPick(picks, { placeHolder })).data; - } - - public shouldPrompt(context: IAzureRegistryWizardContext): boolean { - return !context.newRegistrySku; - } -} diff --git a/src/tree/registries/azure/createWizard/IAzureRegistryWizardContext.ts b/src/tree/registries/azure/createWizard/IAzureRegistryWizardContext.ts deleted file mode 100644 index 7d0109f2b0..0000000000 --- a/src/tree/registries/azure/createWizard/IAzureRegistryWizardContext.ts +++ /dev/null @@ -1,13 +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 type { Registry as AcrRegistry, SkuName as AcrSkuName } from '@azure/arm-containerregistry'; // These are only dev-time imports so don't need to be lazy -import type { IResourceGroupWizardContext } from '@microsoft/vscode-azext-azureutils'; // These are only dev-time imports so don't need to be lazy - -export interface IAzureRegistryWizardContext extends IResourceGroupWizardContext { - newRegistryName?: string; - newRegistrySku?: AcrSkuName; - registry?: AcrRegistry; -} diff --git a/src/tree/registries/connectWizard/IConnectRegistryWizardContext.ts b/src/tree/registries/connectWizard/IConnectRegistryWizardContext.ts deleted file mode 100644 index 360441afd8..0000000000 --- a/src/tree/registries/connectWizard/IConnectRegistryWizardContext.ts +++ /dev/null @@ -1,16 +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 { IActionContext } from '@microsoft/vscode-azext-utils'; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IConnectRegistryWizardOptions } from './IConnectRegistryWizardOptions'; - -export interface IConnectRegistryWizardContext extends IActionContext, IConnectRegistryWizardOptions { - existingProviders: ICachedRegistryProvider[]; - - username?: string; - secret?: string; - url?: string; -} diff --git a/src/tree/registries/connectWizard/IConnectRegistryWizardOptions.ts b/src/tree/registries/connectWizard/IConnectRegistryWizardOptions.ts deleted file mode 100644 index 2ca525ac0c..0000000000 --- a/src/tree/registries/connectWizard/IConnectRegistryWizardOptions.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface IConnectRegistryWizardOptions { - /** - * The title for the wizard (e.g. "Sign In To Docker Hub") - */ - wizardTitle: string; - - /** - * Set to true to prompt for a url - */ - includeUrl?: boolean; - - /** - * Optional value to overwrite the default text displayed underneath the url input box - */ - urlPrompt?: string; - - /** - * Set to true to prompt for a username - */ - includeUsername?: boolean; - - /** - * Optional value to overwrite the default prompt text displayed underneath the username input box - */ - usernamePrompt?: string; - - /** - * Optional value to overwrite the default "ghost" text displayed within the username input box - */ - usernamePlaceholder?: string; - - /** - * Set to true if the username is optional - */ - isUsernameOptional?: boolean; - - /** - * Set to true to prompt for a password - */ - includePassword?: boolean; - - /** - * Optional value to overwrite the default text displayed underneath the password input box - */ - passwordPrompt?: string; -} diff --git a/src/tree/registries/connectWizard/RegistryPasswordStep.ts b/src/tree/registries/connectWizard/RegistryPasswordStep.ts deleted file mode 100644 index a70e6e76ce..0000000000 --- a/src/tree/registries/connectWizard/RegistryPasswordStep.ts +++ /dev/null @@ -1,27 +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 { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils'; -import { l10n } from 'vscode'; -import { IConnectRegistryWizardContext } from './IConnectRegistryWizardContext'; - -export class RegistryPasswordStep extends AzureWizardPromptStep { - public async prompt(context: IConnectRegistryWizardContext): Promise { - const prompt: string = context.passwordPrompt || l10n.t('Enter your password'); - context.secret = await context.ui.showInputBox({ prompt, validateInput, password: true }); - } - - public shouldPrompt(context: IConnectRegistryWizardContext): boolean { - return !!context.includePassword && !context.secret; - } -} - -function validateInput(value: string | undefined): string | undefined { - if (!value) { - return l10n.t('Password cannot be empty.'); - } else { - return undefined; - } -} diff --git a/src/tree/registries/connectWizard/RegistryUrlStep.ts b/src/tree/registries/connectWizard/RegistryUrlStep.ts deleted file mode 100644 index b576bf70e3..0000000000 --- a/src/tree/registries/connectWizard/RegistryUrlStep.ts +++ /dev/null @@ -1,49 +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 { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils'; -import { URL } from 'url'; -import { l10n } from 'vscode'; -import { IConnectRegistryWizardContext } from './IConnectRegistryWizardContext'; - -export class RegistryUrlStep extends AzureWizardPromptStep { - public async prompt(context: IConnectRegistryWizardContext): Promise { - const prompt: string = context.urlPrompt || l10n.t('Enter the URL for the registry provider'); - const placeHolder: string = l10n.t('Example: http://localhost:5000'); - context.url = (await context.ui.showInputBox({ - prompt, - placeHolder, - validateInput: v => this.validateUrl(context, v) - })); - } - - public shouldPrompt(context: IConnectRegistryWizardContext): boolean { - return !!context.includeUrl && !context.url; - } - - private validateUrl(context: IConnectRegistryWizardContext, value: string): string | undefined { - if (!value) { - return l10n.t('URL cannot be empty.'); - } else { - let protocol: string | undefined; - let host: string | undefined; - try { - const uri = new URL(value); - protocol = uri.protocol; - host = uri.host; - } catch { - // ignore - } - - if (!protocol || !host) { - return l10n.t('Please enter a valid URL'); - } else if (context.existingProviders.find(rp => rp.url === value)) { - return l10n.t('URL "{0}" is already connected.', value); - } else { - return undefined; - } - } - } -} diff --git a/src/tree/registries/connectWizard/RegistryUsernameStep.ts b/src/tree/registries/connectWizard/RegistryUsernameStep.ts deleted file mode 100644 index 846db40e0d..0000000000 --- a/src/tree/registries/connectWizard/RegistryUsernameStep.ts +++ /dev/null @@ -1,39 +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 { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils'; -import { InputBoxOptions, l10n } from 'vscode'; -import { IConnectRegistryWizardContext } from './IConnectRegistryWizardContext'; - -export class RegistryUsernameStep extends AzureWizardPromptStep { - public async prompt(context: IConnectRegistryWizardContext): Promise { - const prompt: string = context.usernamePrompt || (context.isUsernameOptional ? l10n.t('Enter your username, or press \'Enter\' for none') : l10n.t('Enter your username')); - const options: InputBoxOptions = { - prompt, - placeHolder: context.usernamePlaceholder, - validateInput: (value: string | undefined) => this.validateInput(context, value) - }; - - context.username = await context.ui.showInputBox(options); - - if (!context.username) { - context.includePassword = false; - } - } - - public shouldPrompt(context: IConnectRegistryWizardContext): boolean { - return !!context.includeUsername && !context.username; - } - - private validateInput(context: IConnectRegistryWizardContext, value: string | undefined): string | undefined { - if (!context.isUsernameOptional && !value) { - return l10n.t('Username cannot be empty.'); - } else if (context.existingProviders.find(rp => rp.url === context.url && rp.username === value)) { - return l10n.t('Username "{0}" is already connected.', value); - } else { - return undefined; - } - } -} diff --git a/src/tree/registries/dockerHub/DockerHubAccountTreeItem.ts b/src/tree/registries/dockerHub/DockerHubAccountTreeItem.ts deleted file mode 100644 index 663455ae19..0000000000 --- a/src/tree/registries/dockerHub/DockerHubAccountTreeItem.ts +++ /dev/null @@ -1,125 +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 { AzExtParentTreeItem, AzExtTreeItem, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { dockerHubUrl } from "../../../constants"; -import { ext } from "../../../extensionVariables"; -import { RequestLike, bearerAuthHeader } from "../../../utils/httpRequest"; -import { registryRequest } from "../../../utils/registryRequestUtils"; -import { getThemedIconPath } from "../../getThemedIconPath"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import { RegistryConnectErrorTreeItem } from "../RegistryConnectErrorTreeItem"; -import { getRegistryContextValue, registryProviderSuffix } from "../registryContextValues"; -import { getRegistryPassword } from "../registryPasswords"; -import { DockerHubNamespaceTreeItem } from "./DockerHubNamespaceTreeItem"; - -export class DockerHubAccountTreeItem extends AzExtParentTreeItem implements IRegistryProviderTreeItem { - public label: string = 'Docker Hub'; - public childTypeLabel: string = 'namespace'; - public baseUrl: string = dockerHubUrl; - public cachedProvider: ICachedRegistryProvider; - - private _token?: string; - - public constructor(parent: AzExtParentTreeItem, cachedProvider: ICachedRegistryProvider) { - super(parent); - this.cachedProvider = cachedProvider; - this.id = this.cachedProvider.id + this.username; - this.iconPath = getThemedIconPath('docker'); - this.description = ext.registriesRoot.hasMultiplesOfProvider(this.cachedProvider) ? this.username : undefined; - } - - public get contextValue(): string { - return getRegistryContextValue(this, registryProviderSuffix); - } - - public get username(): string { - return nonNullProp(this.cachedProvider, 'username'); - } - - public async getPassword(): Promise { - return await getRegistryPassword(this.cachedProvider); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - try { - await this.refreshToken(); - } catch (err) { - // If creds are invalid, the above refreshToken will fail - return [new RegistryConnectErrorTreeItem(this, err, this.cachedProvider)]; - } - } - - const orgsAndNamespaces = new Set(); - - for (const orgs of await this.getOrganizations()) { - orgsAndNamespaces.add(orgs); - } - - for (const namespaces of await this.getNamespaces()) { - orgsAndNamespaces.add(namespaces); - } - - return this.createTreeItemsWithErrorHandling( - Array.from(orgsAndNamespaces), - 'invalidDockerHubNamespace', - n => new DockerHubNamespaceTreeItem(this, n.toLowerCase()), - n => n - ); - } - - public hasMoreChildrenImpl(): boolean { - return false; - } - - public async signRequest(request: RequestLike): Promise { - if (this._token) { - request.headers.set('Authorization', bearerAuthHeader(this._token)); - } - - return request; - } - - private async refreshToken(): Promise { - this._token = undefined; - const url = 'v2/users/login'; - const body = { username: this.username, password: await this.getPassword() }; - // eslint-disable-next-line @typescript-eslint/naming-convention - const response = await registryRequest(this, 'POST', url, { body: JSON.stringify(body), headers: { 'Content-Type': 'application/json' } }); - this._token = response.body.token; - } - - private async getNamespaces(): Promise { - const url: string = `v2/repositories/namespaces`; - const response = await registryRequest(this, 'GET', url); - return response.body.namespaces; - } - - private async getOrganizations(): Promise { - const url: string = `v2/user/orgs`; - const response = await registryRequest(this, 'GET', url); - return response.body.results?.map(o => o.orgname) ?? []; - } -} - -interface IToken { - token: string -} - -interface INamespaces { - namespaces: string[]; - next?: string; -} - -interface IOrganizations { - results: [ - { - orgname: string - } - ], - next?: string; -} diff --git a/src/tree/registries/dockerHub/DockerHubNamespaceTreeItem.ts b/src/tree/registries/dockerHub/DockerHubNamespaceTreeItem.ts deleted file mode 100644 index a28f2c45c8..0000000000 --- a/src/tree/registries/dockerHub/DockerHubNamespaceTreeItem.ts +++ /dev/null @@ -1,76 +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 { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE, dockerHubUrl } from "../../../constants"; -import { RequestLike } from "../../../utils/httpRequest"; -import { registryRequest } from "../../../utils/registryRequestUtils"; -import { IDockerCliCredentials, RegistryTreeItemBase } from "../RegistryTreeItemBase"; -import { DockerHubAccountTreeItem } from "./DockerHubAccountTreeItem"; -import { DockerHubRepositoryTreeItem } from "./DockerHubRepositoryTreeItem"; - -export class DockerHubNamespaceTreeItem extends RegistryTreeItemBase { - public parent: DockerHubAccountTreeItem; - public baseUrl: string = dockerHubUrl; - public namespace: string; - - private _nextLink: string | undefined; - - public constructor(parent: DockerHubAccountTreeItem, namespace: string) { - super(parent); - this.namespace = namespace; - } - - public get label(): string { - return this.namespace; - } - - public get baseImagePath(): string { - return this.namespace; - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `v2/repositories/${this.namespace}?page_size=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = response.body.next; - return await this.createTreeItemsWithErrorHandling( - response.body.results, - 'invalidRepository', - r => new DockerHubRepositoryTreeItem(this, r.name), - r => r.name - ); - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - public async signRequest(request: RequestLike): Promise { - return this.parent.signRequest(request); - } - - public async getDockerCliCredentials(): Promise { - return { - registryPath: '', - auth: { - username: this.parent.username, - password: await this.parent.getPassword() - } - }; - } -} - -interface IRepositories { - results: IRepository[]; - next?: string; -} - -interface IRepository { - name: string; -} diff --git a/src/tree/registries/dockerHub/DockerHubRepositoryTreeItem.ts b/src/tree/registries/dockerHub/DockerHubRepositoryTreeItem.ts deleted file mode 100644 index 3040ac7287..0000000000 --- a/src/tree/registries/dockerHub/DockerHubRepositoryTreeItem.ts +++ /dev/null @@ -1,52 +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 { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE } from "../../../constants"; -import { registryRequest } from "../../../utils/registryRequestUtils"; -import { RemoteRepositoryTreeItemBase } from "../RemoteRepositoryTreeItemBase"; -import { RemoteTagTreeItem } from "../RemoteTagTreeItem"; -import { DockerHubNamespaceTreeItem } from "./DockerHubNamespaceTreeItem"; - -export class DockerHubRepositoryTreeItem extends RemoteRepositoryTreeItemBase { - public parent: DockerHubNamespaceTreeItem; - - private _nextLink: string | undefined; - - public constructor(parent: DockerHubNamespaceTreeItem, name: string) { - super(parent, name); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `v2/repositories/${this.parent.namespace}/${this.repoName}/tags?page_size=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = response.body.next; - return await this.createTreeItemsWithErrorHandling( - response.body.results, - 'invalidTag', - async t => new RemoteTagTreeItem(this, t.name, t.last_updated), - t => t.name - ); - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } -} - -interface ITags { - next?: string; - results: ITag[]; -} - -interface ITag { - name: string; - // eslint-disable-next-line @typescript-eslint/naming-convention - last_updated: string; -} diff --git a/src/tree/registries/dockerHub/dockerHubRegistryProvider.ts b/src/tree/registries/dockerHub/dockerHubRegistryProvider.ts deleted file mode 100644 index 15cf22dcb0..0000000000 --- a/src/tree/registries/dockerHub/dockerHubRegistryProvider.ts +++ /dev/null @@ -1,29 +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 { l10n } from 'vscode'; -import { RegistryApi } from "../all/RegistryApi"; -import { IRegistryProvider } from "../IRegistryProvider"; -import { deleteRegistryPassword, setRegistryPassword } from '../registryPasswords'; -import { DockerHubAccountTreeItem } from "./DockerHubAccountTreeItem"; - -export const dockerHubRegistryProviderId: string = 'dockerHub'; - -export const dockerHubRegistryProvider: IRegistryProvider = { - label: "Docker Hub", - id: dockerHubRegistryProviderId, - api: RegistryApi.DockerHubV2, - connectWizardOptions: { - wizardTitle: l10n.t('Sign in to Docker Hub'), - includeUsername: true, - usernamePrompt: l10n.t('Visit hub.docker.com to sign up for a Docker ID'), - usernamePlaceholder: l10n.t('Enter your Docker ID'), - passwordPrompt: l10n.t('Enter your password or personal access token'), - includePassword: true, - }, - treeItemFactory: (parent, cachedProvider) => new DockerHubAccountTreeItem(parent, cachedProvider), - persistAuth: async (cachedProvider, secret) => await setRegistryPassword(cachedProvider, secret), - removeAuth: async (cachedProvider) => await deleteRegistryPassword(cachedProvider), -}; diff --git a/src/tree/registries/dockerV2/DockerV2RegistryTreeItemBase.ts b/src/tree/registries/dockerV2/DockerV2RegistryTreeItemBase.ts deleted file mode 100644 index 0266d53c99..0000000000 --- a/src/tree/registries/dockerV2/DockerV2RegistryTreeItemBase.ts +++ /dev/null @@ -1,75 +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 { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { URL } from "url"; -import { PAGE_SIZE } from "../../../constants"; -import { IOAuthContext, RequestLike } from "../../../utils/httpRequest"; -import { getNextLinkFromHeaders, registryRequest } from "../../../utils/registryRequestUtils"; -import { IAuthProvider } from "../auth/IAuthProvider"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import { IDockerCliCredentials, RegistryTreeItemBase } from "../RegistryTreeItemBase"; -import { RemoteRepositoryTreeItemBase } from "../RemoteRepositoryTreeItemBase"; - -export abstract class DockerV2RegistryTreeItemBase extends RegistryTreeItemBase implements IRegistryProviderTreeItem { - protected authContext?: IOAuthContext; - - private _nextLink: string | undefined; - - public constructor(parent: AzExtParentTreeItem, public readonly cachedProvider: ICachedRegistryProvider, protected readonly authHelper: IAuthProvider) { - super(parent); - } - - public get baseImagePath(): string { - return this.host.toLowerCase(); - } - - public get host(): string { - return new URL(this.baseUrl).host; - } - - public abstract createRepositoryTreeItem(name: string): RemoteRepositoryTreeItemBase; - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `v2/_catalog?n=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = getNextLinkFromHeaders(response); - return await this.createTreeItemsWithErrorHandling( - response.body.repositories, - 'invalidRepository', - r => this.createRepositoryTreeItem(r), - r => r - ); - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - public async signRequest(request: RequestLike): Promise { - if (this.authHelper) { - return this.authHelper.signRequest(this.cachedProvider, request, this.authContext); - } - - return request; - } - - public async getDockerCliCredentials(): Promise { - if (this.authHelper) { - return await this.authHelper.getDockerCliCredentials(this.cachedProvider, this.authContext); - } - - return undefined; - } -} - -interface IRepositories { - repositories: string[]; -} diff --git a/src/tree/registries/dockerV2/DockerV2RepositoryTreeItem.ts b/src/tree/registries/dockerV2/DockerV2RepositoryTreeItem.ts deleted file mode 100644 index 63dd0448a6..0000000000 --- a/src/tree/registries/dockerV2/DockerV2RepositoryTreeItem.ts +++ /dev/null @@ -1,89 +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 { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE } from "../../../constants"; -import { ErrorHandling, HttpErrorResponse, HttpStatusCode, IOAuthContext, RequestLike } from "../../../utils/httpRequest"; -import { getNextLinkFromHeaders, registryRequest } from "../../../utils/registryRequestUtils"; -import { IAuthProvider } from "../auth/IAuthProvider"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import { RemoteRepositoryTreeItemBase } from "../RemoteRepositoryTreeItemBase"; -import { DockerV2RegistryTreeItemBase } from "./DockerV2RegistryTreeItemBase"; -import { DockerV2TagTreeItem } from "./DockerV2TagTreeItem"; - -export class DockerV2RepositoryTreeItem extends RemoteRepositoryTreeItemBase implements IRegistryProviderTreeItem { - public parent: DockerV2RegistryTreeItemBase; - - private _nextLink: string | undefined; - - public constructor(parent: DockerV2RegistryTreeItemBase, repoName: string, public readonly cachedProvider: ICachedRegistryProvider, protected readonly authHelper: IAuthProvider, protected readonly authContext?: IOAuthContext) { - super(parent, repoName); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `v2/${this.repoName}/tags/list?n=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url, undefined, ErrorHandling.ReturnErrorResponse); - if (response.status === HttpStatusCode.NotFound) { - // Some registries return 404 when all tags have been removed and the repository becomes effectively unavailable. - void this.deleteTreeItem(context); - return []; - } - else if (!response.ok) { - throw new HttpErrorResponse(response); - } - - this._nextLink = getNextLinkFromHeaders(response); - return await this.createTreeItemsWithErrorHandling( - response.body.tags, - 'invalidTag', - async t => { - const time = await this.getTagTime(t); - return new DockerV2TagTreeItem(this, t, time); - }, - t => t - ); - } - - public async signRequest(request: RequestLike): Promise { - if (this.authHelper) { - const authContext: IOAuthContext | undefined = this.authContext ? { ...this.authContext, scope: `repository:${this.repoName}:${request.method === 'DELETE' ? '*' : 'pull'}` } : undefined; - return this.authHelper.signRequest(this.cachedProvider, request, authContext); - } - - return request; - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - private async getTagTime(tag: string): Promise { - const manifestUrl: string = `v2/${this.repoName}/manifests/${tag}`; - const manifestResponse = await registryRequest(this, 'GET', manifestUrl); - const history = JSON.parse(manifestResponse.body.history[0].v1Compatibility); - return history.created; - } -} - -interface ITags { - tags: string[]; -} - -interface IManifestHistory { - v1Compatibility: string; // stringified ManifestHistoryV1Compatibility -} - -interface IManifestHistoryV1Compatibility { - created: string; -} - -interface IManifest { - history: IManifestHistory[]; -} diff --git a/src/tree/registries/dockerV2/DockerV2TagTreeItem.ts b/src/tree/registries/dockerV2/DockerV2TagTreeItem.ts deleted file mode 100644 index 1ee3516e62..0000000000 --- a/src/tree/registries/dockerV2/DockerV2TagTreeItem.ts +++ /dev/null @@ -1,44 +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 { IActionContext, UserCancelledError, parseError } from '@microsoft/vscode-azext-utils'; -import { registryRequest } from '../../../utils/registryRequestUtils'; -import { RemoteTagTreeItem } from '../RemoteTagTreeItem'; - -export class DockerV2TagTreeItem extends RemoteTagTreeItem { - public async getDigest(): Promise { - const digestOptions = { - headers: { - // According to https://docs.docker.com/registry/spec/api/ - // When deleting a manifest from a registry version 2.3 or later, the following header must be used when HEAD or GET-ing the manifest to obtain the correct digest to delete - accept: 'application/vnd.docker.distribution.manifest.v2+json' - } - }; - - const url = `v2/${this.parent.repoName}/manifests/${this.tag}`; - const response = await registryRequest(this.parent, 'GET', url, digestOptions); - const digest = response.headers.get('docker-content-digest') as string; - return digest; - } - - public async deleteTreeItemImpl(context: IActionContext): Promise { - const digest = await this.getDigest(); - const url = `v2/${this.parent.repoName}/manifests/${digest}`; - - try { - await registryRequest(this.parent, 'DELETE', url); - } catch (error) { - const errorType: string = parseError(error).errorType.toLowerCase(); - if (errorType === '405' || errorType === 'unsupported') { - // Don't wait - // eslint-disable-next-line @typescript-eslint/no-floating-promises - context.ui.showWarningMessage('Deleting remote images is not supported on this registry. It may need to be enabled.', { learnMoreLink: 'https://aka.ms/AA7jsql' }); - throw new UserCancelledError(); - } else { - throw error; - } - } - } -} diff --git a/src/tree/registries/dockerV2/GenericDockerV2RegistryTreeItem.ts b/src/tree/registries/dockerV2/GenericDockerV2RegistryTreeItem.ts deleted file mode 100644 index 9b6336ee37..0000000000 --- a/src/tree/registries/dockerV2/GenericDockerV2RegistryTreeItem.ts +++ /dev/null @@ -1,56 +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 { AzExtParentTreeItem, AzExtTreeItem, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; -import { HttpErrorResponse, getWwwAuthenticateContext } from "../../../utils/httpRequest"; -import { registryRequest } from "../../../utils/registryRequestUtils"; -import { IAuthProvider } from "../auth/IAuthProvider"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { getRegistryContextValue, registryProviderSuffix, registrySuffix } from "../registryContextValues"; -import { DockerV2RegistryTreeItemBase } from "./DockerV2RegistryTreeItemBase"; -import { DockerV2RepositoryTreeItem } from "./DockerV2RepositoryTreeItem"; - -export class GenericDockerV2RegistryTreeItem extends DockerV2RegistryTreeItemBase { - public constructor(parent: AzExtParentTreeItem, cachedProvider: ICachedRegistryProvider, authHelper: IAuthProvider) { - super(parent, cachedProvider, authHelper); - this.id = this.baseUrl; - } - - public get contextValue(): string { - return getRegistryContextValue(this, registrySuffix, registryProviderSuffix); - } - - public get label(): string { - return this.host; - } - - public get baseUrl(): string { - return nonNullProp(this.cachedProvider, 'url'); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - try { - // If the call succeeds, it's a V2 registry (https://docs.docker.com/registry/spec/api/#api-version-check) - // NOTE: Trailing slash is necessary (https://github.com/microsoft/vscode-docker/issues/1142) - await registryRequest(this, 'GET', 'v2/'); - } catch (error) { - if (error instanceof HttpErrorResponse && - (this.authContext = getWwwAuthenticateContext(error))) { - // We got authentication context successfully--set scope and move on to requesting the items - this.authContext.scope = 'registry:catalog:*'; - } else { - throw error; - } - } - } - - return super.loadMoreChildrenImpl(clearCache, context); - } - - public createRepositoryTreeItem(name: string): DockerV2RepositoryTreeItem { - return new DockerV2RepositoryTreeItem(this, name, this.cachedProvider, this.authHelper, this.authContext); - } -} diff --git a/src/tree/registries/dockerV2/genericDockerV2RegistryProvider.ts b/src/tree/registries/dockerV2/genericDockerV2RegistryProvider.ts deleted file mode 100644 index 9054b212c7..0000000000 --- a/src/tree/registries/dockerV2/genericDockerV2RegistryProvider.ts +++ /dev/null @@ -1,31 +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 { l10n } from 'vscode'; -import { RegistryApi } from "../all/RegistryApi"; -import { basicOAuthProvider } from '../auth/BasicOAuthProvider'; -import { IRegistryProvider } from "../IRegistryProvider"; -import { deleteRegistryPassword, setRegistryPassword } from '../registryPasswords'; -import { GenericDockerV2RegistryTreeItem } from "./GenericDockerV2RegistryTreeItem"; - -export const genericDockerV2RegistryProvider: IRegistryProvider = { - label: l10n.t('Generic Docker Registry'), - description: l10n.t('(Preview)'), - detail: l10n.t('Connect any generic private registry that supports the "Docker V2" api.'), - id: 'genericDockerV2', - api: RegistryApi.DockerV2, - isSingleRegistry: true, - connectWizardOptions: { - wizardTitle: l10n.t('Connect Docker Registry'), - includeUrl: true, - urlPrompt: l10n.t('Enter the URL for the registry'), - includeUsername: true, - isUsernameOptional: true, - includePassword: true, - }, - treeItemFactory: (parent, cachedProvider) => new GenericDockerV2RegistryTreeItem(parent, cachedProvider, basicOAuthProvider), - persistAuth: async (cachedProvider, secret) => await setRegistryPassword(cachedProvider, secret), - removeAuth: async (cachedProvider) => await deleteRegistryPassword(cachedProvider), -}; diff --git a/src/tree/registries/getInformationFromRegistryItem.ts b/src/tree/registries/getInformationFromRegistryItem.ts new file mode 100644 index 0000000000..d1d87ddc18 --- /dev/null +++ b/src/tree/registries/getInformationFromRegistryItem.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CommonRegistry, CommonRepository, CommonTag, isRegistry, isRepository, isTag } from "@microsoft/vscode-docker-registries"; +import { UnifiedRegistryItem } from "./UnifiedRegistryTreeDataProvider"; + +export function getImageNameFromRegistryItem(node: UnifiedRegistryItem): string { + if (!isTag(node.wrappedItem) || !isRepository(node.parent.wrappedItem)) { + throw new Error('Unable to get image name'); + } + + return `${(node.parent.wrappedItem as CommonRepository).label}:${node.wrappedItem.label}`; +} + +export function getFullImageNameFromRegistryItem(node: UnifiedRegistryItem): string { + const imageName = getImageNameFromRegistryItem(node); + if (!isRegistry(node.parent.parent?.wrappedItem)) { + throw new Error('Unable to get full image name'); + } + + return `${(node.parent.parent.wrappedItem as CommonRegistry).label}/${imageName}`; +} diff --git a/src/tree/registries/gitLab/GitLabAccountTreeItem.ts b/src/tree/registries/gitLab/GitLabAccountTreeItem.ts deleted file mode 100644 index 4db3a4a873..0000000000 --- a/src/tree/registries/gitLab/GitLabAccountTreeItem.ts +++ /dev/null @@ -1,86 +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 { AzExtParentTreeItem, AzExtTreeItem, IActionContext, nonNullProp, parseError } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE } from "../../../constants"; -import { ext } from "../../../extensionVariables"; -import { RequestLike } from "../../../utils/httpRequest"; -import { getNextLinkFromHeaders, registryRequest } from "../../../utils/registryRequestUtils"; -import { getIconPath } from "../../getThemedIconPath"; -import { ICachedRegistryProvider } from "../ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "../IRegistryProviderTreeItem"; -import { RegistryConnectErrorTreeItem } from "../RegistryConnectErrorTreeItem"; -import { getRegistryContextValue, registryProviderSuffix } from "../registryContextValues"; -import { getRegistryPassword } from "../registryPasswords"; -import { GitLabProjectTreeItem } from "./GitLabProjectTreeItem"; - -export class GitLabAccountTreeItem extends AzExtParentTreeItem implements IRegistryProviderTreeItem { - public label: string = 'GitLab'; - public childTypeLabel: string = 'project'; - public baseUrl: string = 'https://gitlab.com/'; - public cachedProvider: ICachedRegistryProvider; - - private _nextLink?: string; - - public constructor(parent: AzExtParentTreeItem, provider: ICachedRegistryProvider) { - super(parent); - this.cachedProvider = provider; - this.id = this.cachedProvider.id + this.username; - this.iconPath = getIconPath('gitlab'); - this.description = ext.registriesRoot.hasMultiplesOfProvider(this.cachedProvider) ? this.username : undefined; - } - - public get contextValue(): string { - return getRegistryContextValue(this, registryProviderSuffix); - } - - public get username(): string { - return nonNullProp(this.cachedProvider, 'username'); - } - - public async getPassword(): Promise { - return await getRegistryPassword(this.cachedProvider); - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - try { - const url: string = this._nextLink || `api/v4/projects?per_page=${PAGE_SIZE}&simple=true&membership=true`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = getNextLinkFromHeaders(response); - return this.createTreeItemsWithErrorHandling( - response.body, - 'invalidGitLabProject', - n => new GitLabProjectTreeItem(this, n.id.toString(), n.path_with_namespace.toLowerCase()), - n => n.path_with_namespace - ); - } catch (err) { - const errorType: string = parseError(err).errorType.toLowerCase(); - if (errorType === '401' || errorType === 'unauthorized') { - return [new RegistryConnectErrorTreeItem(this, err, this.cachedProvider)]; - } else { - throw err; - } - } - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - public async signRequest(request: RequestLike): Promise { - request.headers.set('PRIVATE-TOKEN', await this.getPassword()); - return request; - } -} - -interface IProject { - id: number; - // eslint-disable-next-line @typescript-eslint/naming-convention - path_with_namespace: string; -} diff --git a/src/tree/registries/gitLab/GitLabProjectTreeItem.ts b/src/tree/registries/gitLab/GitLabProjectTreeItem.ts deleted file mode 100644 index dfb917fe54..0000000000 --- a/src/tree/registries/gitLab/GitLabProjectTreeItem.ts +++ /dev/null @@ -1,80 +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 { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE } from "../../../constants"; -import { RequestLike } from "../../../utils/httpRequest"; -import { getNextLinkFromHeaders, registryRequest } from "../../../utils/registryRequestUtils"; -import { IDockerCliCredentials, RegistryTreeItemBase } from "../RegistryTreeItemBase"; -import { GitLabAccountTreeItem } from "./GitLabAccountTreeItem"; -import { GitLabRepositoryTreeItem } from "./GitLabRepositoryTreeItem"; - -const gitLabRegistryUrl: string = 'registry.gitlab.com'; - -export class GitLabProjectTreeItem extends RegistryTreeItemBase { - public parent: GitLabAccountTreeItem; - public readonly projectId: string; - public pathWithNamespace: string; - - private _nextLink?: string; - - public constructor(parent: GitLabAccountTreeItem, id: string, pathWithNamespace: string) { - super(parent); - this.projectId = id; - this.pathWithNamespace = pathWithNamespace; - this.id = this.projectId; - } - - public get baseUrl(): string { - return this.parent.baseUrl; - } - - public get label(): string { - return this.pathWithNamespace; - } - - public get baseImagePath(): string { - return gitLabRegistryUrl + '/' + this.pathWithNamespace; - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `api/v4/projects/${this.projectId}/registry/repositories?per_page=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = getNextLinkFromHeaders(response); - return await this.createTreeItemsWithErrorHandling( - response.body, - 'invalidRepository', - r => new GitLabRepositoryTreeItem(this, r.id.toString(), r.name), - r => r.name - ); - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - public async signRequest(request: RequestLike): Promise { - return this.parent.signRequest(request); - } - - public async getDockerCliCredentials(): Promise { - return { - registryPath: gitLabRegistryUrl, - auth: { - username: this.parent.username, - password: await this.parent.getPassword() - } - }; - } -} - -interface IRepository { - name: string; - id: number; -} diff --git a/src/tree/registries/gitLab/GitLabRepositoryTreeItem.ts b/src/tree/registries/gitLab/GitLabRepositoryTreeItem.ts deleted file mode 100644 index b0fe1ced00..0000000000 --- a/src/tree/registries/gitLab/GitLabRepositoryTreeItem.ts +++ /dev/null @@ -1,64 +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 { AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils"; -import { PAGE_SIZE } from "../../../constants"; -import { getNextLinkFromHeaders, registryRequest } from "../../../utils/registryRequestUtils"; -import { RemoteRepositoryTreeItemBase } from "../RemoteRepositoryTreeItemBase"; -import { RemoteTagTreeItem } from "../RemoteTagTreeItem"; -import { GitLabProjectTreeItem } from "./GitLabProjectTreeItem"; - -export class GitLabRepositoryTreeItem extends RemoteRepositoryTreeItemBase { - public parent: GitLabProjectTreeItem; - public readonly repoId: string; - - private _nextLink?: string; - - public constructor(parent: GitLabProjectTreeItem, id: string, name: string) { - // GitLab returns an empty repository name, - // if the project's namespace is the same as the repository - super(parent, name || parent.label); - this.repoId = id; - this.id = this.repoId; - } - - public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise { - if (clearCache) { - this._nextLink = undefined; - } - - const url = this._nextLink || `api/v4/projects/${this.parent.projectId}/registry/repositories/${this.repoId}/tags?per_page=${PAGE_SIZE}`; - const response = await registryRequest(this, 'GET', url); - this._nextLink = getNextLinkFromHeaders(response); - return await this.createTreeItemsWithErrorHandling( - response.body, - 'invalidTag', - async t => { - const details = await this.getTagDetails(t.name); - return new RemoteTagTreeItem(this, t.name, details.created_at); - }, - t => t.name - ); - } - - public hasMoreChildrenImpl(): boolean { - return !!this._nextLink; - } - - private async getTagDetails(tag: string): Promise { - const url = `api/v4/projects/${this.parent.projectId}/registry/repositories/${this.repoId}/tags/${tag}`; - const response = await registryRequest(this, 'GET', url); - return response.body; - } -} - -interface ITag { - name: string; -} - -interface ITagDetails { - // eslint-disable-next-line @typescript-eslint/naming-convention - created_at: string; -} diff --git a/src/tree/registries/gitLab/gitLabRegistryProvider.ts b/src/tree/registries/gitLab/gitLabRegistryProvider.ts deleted file mode 100644 index 827ef762b7..0000000000 --- a/src/tree/registries/gitLab/gitLabRegistryProvider.ts +++ /dev/null @@ -1,25 +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 { l10n } from 'vscode'; -import { RegistryApi } from "../all/RegistryApi"; -import { IRegistryProvider } from "../IRegistryProvider"; -import { deleteRegistryPassword, setRegistryPassword } from '../registryPasswords'; -import { GitLabAccountTreeItem } from "./GitLabAccountTreeItem"; - -export const gitLabRegistryProvider: IRegistryProvider = { - label: "GitLab", - id: 'gitLab', - api: RegistryApi.GitLabV4, - connectWizardOptions: { - wizardTitle: l10n.t('Sign in to GitLab'), - includeUsername: true, - includePassword: true, - passwordPrompt: l10n.t('GitLab Personal Access Token (requires `api` or `read_api` scope)'), - }, - treeItemFactory: (parent, cachedProvider) => new GitLabAccountTreeItem(parent, cachedProvider), - persistAuth: async (cachedProvider, secret) => await setRegistryPassword(cachedProvider, secret), - removeAuth: async (cachedProvider) => await deleteRegistryPassword(cachedProvider), -}; diff --git a/src/tree/registries/registryContextValues.ts b/src/tree/registries/registryContextValues.ts deleted file mode 100644 index 57f10dcdc4..0000000000 --- a/src/tree/registries/registryContextValues.ts +++ /dev/null @@ -1,64 +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 { AzExtTreeItem } from "@microsoft/vscode-azext-utils"; -import { l10n } from 'vscode'; -import { RegistryApi } from "./all/RegistryApi"; -import { azureRegistryProviderId } from "./azure/azureRegistryProvider"; -import { dockerHubRegistryProviderId } from "./dockerHub/dockerHubRegistryProvider"; -import { ICachedRegistryProvider } from "./ICachedRegistryProvider"; -import { IRegistryProviderTreeItem } from "./IRegistryProviderTreeItem"; - -export const registryProviderSuffix = 'RegistryProvider'; -export const registrySuffix = 'Registry'; -export const repositorySuffix = 'Repository'; -export const tagSuffix = 'Tag'; - -export const contextValueSeparator = ';'; -export const anyContextValuePart = '.*'; - -export function getRegistryContextValue(node: AzExtTreeItem & Partial, ...suffixes: string[]): string { - const cachedProvider = getCachedProvider(node); - const parts = [cachedProvider.id, cachedProvider.api, ...suffixes]; - return parts.join(contextValueSeparator) + contextValueSeparator; -} - -/** - * Regular expressions used for the Tree Item Picker (which is used when a command is called from the command palette) - * Registry providers only need to add an entry here if they support commands unique to their provider - */ -export const registryExpectedContextValues = { - all: getRegistryExpectedContextValues({}), - azure: getRegistryExpectedContextValues({ id: azureRegistryProviderId }), - dockerHub: getRegistryExpectedContextValues({ id: dockerHubRegistryProviderId }), - dockerV2: getRegistryExpectedContextValues({ api: RegistryApi.DockerV2 }), -}; - -function getRegistryExpectedContextValues(provider: Partial): { registryProvider: RegExp, registry: RegExp, repository: RegExp, tag: RegExp } { - return { - registryProvider: convertToRegExp(provider, registryProviderSuffix), - registry: convertToRegExp(provider, registrySuffix), - repository: convertToRegExp(provider, repositorySuffix), - tag: convertToRegExp(provider, tagSuffix) - }; -} - -function convertToRegExp(provider: Partial, suffix: string): RegExp { - const parts = [provider.id, provider.api, suffix].map(p => p || anyContextValuePart); - const value = parts.join(contextValueSeparator) + contextValueSeparator; - return new RegExp(value.replace(/undefined/g, anyContextValuePart), 'i'); -} - -function getCachedProvider(node: AzExtTreeItem & Partial): ICachedRegistryProvider { - while (!node.cachedProvider) { - if (!node.parent) { - throw new Error(l10n.t('Failed to find cachedProvider')); - } else { - node = node.parent; - } - } - - return node.cachedProvider; -} diff --git a/src/tree/registries/registryPasswords.ts b/src/tree/registries/registryPasswords.ts deleted file mode 100644 index d02f3b279c..0000000000 --- a/src/tree/registries/registryPasswords.ts +++ /dev/null @@ -1,28 +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 * as crypto from 'crypto'; -import { ext } from '../../extensionVariables'; -import { ICachedRegistryProvider } from "./ICachedRegistryProvider"; - -export async function getRegistryPassword(cached: ICachedRegistryProvider): Promise { - return ext.context.secrets.get(getRegistryPasswordKey(cached)); -} - -export async function setRegistryPassword(cached: ICachedRegistryProvider, password: string): Promise { - return ext.context.secrets.store(getRegistryPasswordKey(cached), password); -} - -export async function deleteRegistryPassword(cached: ICachedRegistryProvider): Promise { - return ext.context.secrets.delete(getRegistryPasswordKey(cached)); -} - -function getRegistryPasswordKey(cached: ICachedRegistryProvider): string { - return getPseudononymousStringHash(cached.id + cached.api + (cached.url || '') + (cached.username || '')); -} - -function getPseudononymousStringHash(s: string): string { - return crypto.createHash('sha256').update(s).digest('hex'); -} diff --git a/src/utils/lazyPackages.ts b/src/utils/lazyPackages.ts index ab02bb6912..11edd3fe73 100644 --- a/src/utils/lazyPackages.ts +++ b/src/utils/lazyPackages.ts @@ -34,13 +34,3 @@ export async function getAzExtAppService(): Promise { - return await import('../tree/registries/azure/AzureAccountTreeItem'); -} - -export async function getAzSubTreeItem(): Promise { - return await import('../tree/registries/azure/SubscriptionTreeItem'); -} From 5ff2055c1132e5e10604f325a188236cc8857863 Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:22:41 -0400 Subject: [PATCH 2/6] added context values for copy full tag --- package-lock.json | 2 +- package.json | 4 ++-- src/commands/images/buildImage.ts | 12 +++++----- src/commands/registerCommands.ts | 4 ++-- .../registries/copyRemoteImageDigest.ts | 16 ++++++------- src/commands/registries/pullImages.ts | 2 +- .../getInformationFromRegistryItem.ts | 23 +++++++++++++++++++ 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index bd81463354..1cd2906092 100644 --- a/package-lock.json +++ b/package-lock.json @@ -814,7 +814,7 @@ "node_modules/@microsoft/vscode-docker-registries": { "version": "0.0.1-alpha", "resolved": "file:../vscode-docker-extensibility/packages/vscode-docker-registries/microsoft-vscode-docker-registries-0.0.1-alpha.tgz", - "integrity": "sha512-xUh413coEcazq8xort40952T8OHiShZ+0KRNRdQSfDnbLIL8zmXXIQh4akXPavhUitFf1boVrTrfKmb3PUfeIQ==", + "integrity": "sha512-m7XuVX8quDJqEKK7RqCakk8uz4QEFuGn4rG9CAHAodYD1GUDE6csUPnHU4kb9RRvbe6Z8bSFFJ6MgZ+8J8eEZQ==", "license": "See LICENSE in the project root for license information.", "dependencies": { "node-fetch": "^2.6.11" diff --git a/package.json b/package.json index 86c06d98f8..3119ea1d8e 100644 --- a/package.json +++ b/package.json @@ -491,7 +491,7 @@ }, { "command": "vscode-docker.registries.pullRepository", - "when": "view == dockerRegistries && viewItem =~ /Repository;/", + "when": "view == dockerRegistries && viewItem =~ /commonrepository/", "group": "regs_repo_1_general@1" }, { @@ -506,7 +506,7 @@ }, { "command": "vscode-docker.registries.copyRemoteFullTag", - "when": "view == dockerRegistries && viewItem =~ /commontag/i", + "when": "view == dockerRegistries && viewItem =~ /(dockerHubTag|registryV2Tag)/i", "group": "regs_tag_1_general@2" }, { diff --git a/src/commands/images/buildImage.ts b/src/commands/images/buildImage.ts index 34f9e0e9ed..db6660c458 100644 --- a/src/commands/images/buildImage.ts +++ b/src/commands/images/buildImage.ts @@ -14,6 +14,7 @@ import { delay } from "../../utils/promiseUtils"; import { quickPickDockerFileItem } from "../../utils/quickPickFile"; import { quickPickWorkspaceFolder } from "../../utils/quickPickWorkspaceFolder"; import { selectBuildCommand } from "../selectCommandTemplate"; +import { addImageTaggingTelemetry, getTagFromUserInput } from "./tagImage"; const tagRegex: RegExp = /\$\{tag\}/i; @@ -59,13 +60,12 @@ export async function buildImage(context: IActionContext, dockerFileUri: vscode. // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox await delay(500); - // addImageTaggingTelemetry(context, suggestedImageName, '.before'); - // const imageName: string = await getTagFromUserInput(context, suggestedImageName); - // addImageTaggingTelemetry(context, imageName, '.after'); + addImageTaggingTelemetry(context, suggestedImageName, '.before'); + const imageName: string = await getTagFromUserInput(context, suggestedImageName); + addImageTaggingTelemetry(context, imageName, '.after'); - // await ext.context.workspaceState.update(dockerFileKey, imageName); - // terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); - // TODO: review this later + await ext.context.workspaceState.update(dockerFileKey, imageName); + terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); } const client = await ext.runtimeManager.getClient(); diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index 04ef28b638..040486a3d4 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -74,7 +74,7 @@ import { openDockerHubInBrowser } from "./registries/dockerHub/openDockerHubInBr // import { reconnectRegistry } from "./registries/reconnectRegistry"; import { copyRemoteFullTag } from "./registries/copyRemoteFullTag"; import { logInToDockerCli } from "./registries/logInToDockerCli"; -import { pullImageFromRepository } from "./registries/pullImages"; +import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; import { registryHelp } from "./registries/registryHelp"; import { openDockerDownloadPage } from "./showDockerLearnMoreNotification"; import { configureVolumesExplorer } from "./volumes/configureVolumesExplorer"; @@ -179,7 +179,7 @@ export function registerCommands(): void { registerWorkspaceCommand('vscode-docker.registries.logInToDockerCli', logInToDockerCli); // registerWorkspaceCommand('vscode-docker.registries.logOutOfDockerCli', logOutOfDockerCli); registerWorkspaceCommand('vscode-docker.registries.pullImage', pullImageFromRepository); - // registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository); + registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository); // registerCommand('vscode-docker.registries.reconnectRegistry', reconnectRegistry); registerCommand('vscode-docker.registries.dockerHub.openInBrowser', openDockerHubInBrowser); diff --git a/src/commands/registries/copyRemoteImageDigest.ts b/src/commands/registries/copyRemoteImageDigest.ts index 2b7745c49c..7f1e67fae5 100644 --- a/src/commands/registries/copyRemoteImageDigest.ts +++ b/src/commands/registries/copyRemoteImageDigest.ts @@ -3,19 +3,17 @@ // * Licensed under the MIT License. See LICENSE.md in the project root for license information. // *--------------------------------------------------------------------------------------------*/ -// import { IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils"; +// import { IActionContext, contextValueExperience, nonNullProp } from "@microsoft/vscode-azext-utils"; +// import { CommonTag } from "@microsoft/vscode-docker-registries"; // import * as vscode from "vscode"; // import { ext } from "../../extensionVariables"; -// import { AzureTaskRunTreeItem } from "../../tree/registries/azure/AzureTaskRunTreeItem"; -// import { DockerV2TagTreeItem } from "../../tree/registries/dockerV2/DockerV2TagTreeItem"; -// import { registryExpectedContextValues } from "../../tree/registries/registryContextValues"; +// import { UnifiedRegistryItem } from "../../tree/registries/UnifiedRegistryTreeDataProvider"; -// export async function copyRemoteImageDigest(context: IActionContext, node?: DockerV2TagTreeItem | AzureTaskRunTreeItem): Promise { +// export async function copyRemoteImageDigest(context: IActionContext, node?: UnifiedRegistryItem): Promise { // if (!node) { -// node = await ext.registriesTree.showTreeItemPicker(registryExpectedContextValues.dockerV2.tag, { -// ...context, -// noItemFoundErrorMessage: vscode.l10n.t('No remote images are available to copy the digest') -// }); +// if (!node) { +// node = await contextValueExperience(context, ext.registriesTree, { include: ['registryV2Tag'] }); +// } // } // let digest: string; diff --git a/src/commands/registries/pullImages.ts b/src/commands/registries/pullImages.ts index 84db7d9b9a..d77620b4c5 100644 --- a/src/commands/registries/pullImages.ts +++ b/src/commands/registries/pullImages.ts @@ -16,7 +16,7 @@ export async function pullRepository(context: IActionContext, node?: UnifiedRegi node = await contextValueExperience(context, ext.registriesTree, { include: 'commonrepository' }); } - await pullImages(context, node.parent, node.wrappedItem.label, true); // TODO: test this + await pullImages(context, node.parent, node.wrappedItem.label, true); } export async function pullImageFromRepository(context: IActionContext, node?: UnifiedRegistryItem): Promise { diff --git a/src/tree/registries/getInformationFromRegistryItem.ts b/src/tree/registries/getInformationFromRegistryItem.ts index d1d87ddc18..e7fa5fb4c0 100644 --- a/src/tree/registries/getInformationFromRegistryItem.ts +++ b/src/tree/registries/getInformationFromRegistryItem.ts @@ -22,3 +22,26 @@ export function getFullImageNameFromRegistryItem(node: UnifiedRegistryItem): Promise { +// if (!isTag(node.wrappedItem) || !isRepository(node.parent.wrappedItem)) { +// throw new Error('Unable to get image name'); +// } +// const digestOptions = { +// headers: { +// // According to https://docs.docker.com/registry/spec/api/ +// // When deleting a manifest from a registry version 2.3 or later, the following header must be used when HEAD or GET-ing the manifest to obtain the correct digest to delete +// accept: 'application/vnd.docker.distribution.manifest.v2+json' +// } +// }; +// const url = `v2/${(node.parent.wrappedItem as CommonRepository).label}/manifests/${node.wrappedItem.label}`; +// const response = await httpRequest<{ results: [{ name: string; }] }>(requestUrl.toString(), { +// method: 'GET', +// headers: { +// Authorization: `Bearer ${(await this.authenticationProvider.getSession([], {})).accessToken}`, +// } +// }); +// const response = await htt; +// const digest = response.headers.get('docker-content-digest') as string; +// return digest; +// } From c5eedb1ff3de30f52263bd6b866a3769b6c6364b Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Fri, 4 Aug 2023 16:33:20 -0400 Subject: [PATCH 3/6] added back copy remote image digest --- package-lock.json | 2 +- package.json | 2 +- src/commands/registerCommands.ts | 3 +- .../registries/copyRemoteImageDigest.ts | 48 +++++++------------ .../Azure/AzureRegistryDataProvider.ts | 2 +- .../getInformationFromRegistryItem.ts | 35 +++++--------- 6 files changed, 34 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1cd2906092..d9409d10aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -814,7 +814,7 @@ "node_modules/@microsoft/vscode-docker-registries": { "version": "0.0.1-alpha", "resolved": "file:../vscode-docker-extensibility/packages/vscode-docker-registries/microsoft-vscode-docker-registries-0.0.1-alpha.tgz", - "integrity": "sha512-m7XuVX8quDJqEKK7RqCakk8uz4QEFuGn4rG9CAHAodYD1GUDE6csUPnHU4kb9RRvbe6Z8bSFFJ6MgZ+8J8eEZQ==", + "integrity": "sha512-b7VTChrnCksWeobO7jgCqdoobtjJpAJKKVJg+dzF3fdw6ypxAXsmG70aiqKZGdkJaJJGq5rIn1lYc8JcCGlEJQ==", "license": "See LICENSE in the project root for license information.", "dependencies": { "node-fetch": "^2.6.11" diff --git a/package.json b/package.json index 3119ea1d8e..4490647747 100644 --- a/package.json +++ b/package.json @@ -511,7 +511,7 @@ }, { "command": "vscode-docker.registries.copyImageDigest", - "when": "view == dockerRegistries && viewItem =~ /DockerV2;Tag;/", + "when": "view == dockerRegistries && viewItem =~ /registryV2Tag/", "group": "regs_tag_1_general@3" }, { diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index 040486a3d4..ae23d8492d 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -73,6 +73,7 @@ import { openDockerHubInBrowser } from "./registries/dockerHub/openDockerHubInBr // import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; // import { reconnectRegistry } from "./registries/reconnectRegistry"; import { copyRemoteFullTag } from "./registries/copyRemoteFullTag"; +import { copyRemoteImageDigest } from "./registries/copyRemoteImageDigest"; import { logInToDockerCli } from "./registries/logInToDockerCli"; import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; import { registryHelp } from "./registries/registryHelp"; @@ -169,7 +170,7 @@ export function registerCommands(): void { registerCommand('vscode-docker.networks.prune', pruneNetworks); registerCommand('vscode-docker.registries.connectRegistry', connectRegistry); - // registerCommand('vscode-docker.registries.copyImageDigest', copyRemoteImageDigest); + registerCommand('vscode-docker.registries.copyImageDigest', copyRemoteImageDigest); registerCommand('vscode-docker.registries.copyRemoteFullTag', copyRemoteFullTag); // registerCommand('vscode-docker.registries.deleteImage', deleteRemoteImage); // registerCommand('vscode-docker.registries.deployImageToAzure', deployImageToAzure); diff --git a/src/commands/registries/copyRemoteImageDigest.ts b/src/commands/registries/copyRemoteImageDigest.ts index 7f1e67fae5..1e022c75cf 100644 --- a/src/commands/registries/copyRemoteImageDigest.ts +++ b/src/commands/registries/copyRemoteImageDigest.ts @@ -1,34 +1,22 @@ -// /*--------------------------------------------------------------------------------------------- -// * Copyright (c) Microsoft Corporation. All rights reserved. -// * Licensed under the MIT License. See LICENSE.md in the project root for license information. -// *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ -// import { IActionContext, contextValueExperience, nonNullProp } from "@microsoft/vscode-azext-utils"; -// import { CommonTag } from "@microsoft/vscode-docker-registries"; -// import * as vscode from "vscode"; -// import { ext } from "../../extensionVariables"; -// import { UnifiedRegistryItem } from "../../tree/registries/UnifiedRegistryTreeDataProvider"; +import { IActionContext, contextValueExperience } from "@microsoft/vscode-azext-utils"; +import { CommonTag, RegistryV2DataProvider, V2Tag } from "@microsoft/vscode-docker-registries"; +import * as vscode from "vscode"; +import { ext } from "../../extensionVariables"; +import { UnifiedRegistryItem } from "../../tree/registries/UnifiedRegistryTreeDataProvider"; -// export async function copyRemoteImageDigest(context: IActionContext, node?: UnifiedRegistryItem): Promise { -// if (!node) { -// if (!node) { -// node = await contextValueExperience(context, ext.registriesTree, { include: ['registryV2Tag'] }); -// } -// } +export async function copyRemoteImageDigest(context: IActionContext, node?: UnifiedRegistryItem): Promise { + if (!node) { + node = await contextValueExperience(context, ext.registriesTree, { include: ['registryV2Tag'] }); + } -// let digest: string; -// if (node instanceof AzureTaskRunTreeItem) { -// if (node.outputImage) { -// digest = nonNullProp(node.outputImage, 'digest'); -// } else { -// throw new Error(vscode.l10n.t('Failed to find output image for this task run.')); -// } -// } else { -// await node.runWithTemporaryDescription(context, vscode.l10n.t('Getting digest...'), async () => { -// digest = await (node).getDigest(); -// }); -// } + const v2DataProvider = node.provider as unknown as RegistryV2DataProvider; + const digest = await v2DataProvider.getImageDigest(node.wrappedItem as V2Tag); -// /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ -// vscode.env.clipboard.writeText(digest); -// } + /* eslint-disable-next-line @typescript-eslint/no-floating-promises */ + vscode.env.clipboard.writeText(digest); +} diff --git a/src/tree/registries/Azure/AzureRegistryDataProvider.ts b/src/tree/registries/Azure/AzureRegistryDataProvider.ts index a259abac84..9e04b6edcc 100644 --- a/src/tree/registries/Azure/AzureRegistryDataProvider.ts +++ b/src/tree/registries/Azure/AzureRegistryDataProvider.ts @@ -92,7 +92,7 @@ export class AzureRegistryDataProvider extends RegistryV2DataProvider implements return { parent: subscriptionItem, type: 'commonregistry', - registryUri: vscode.Uri.parse(`https://${registry.loginServer}`), + baseUrl: vscode.Uri.parse(`https://${registry.loginServer}`), label: registry.name!, iconPath: vscode.Uri.joinPath(this.extensionContext.extensionUri, 'resources', 'azureRegistry.svg'), subscription: subscriptionItem.subscription, diff --git a/src/tree/registries/getInformationFromRegistryItem.ts b/src/tree/registries/getInformationFromRegistryItem.ts index e7fa5fb4c0..e7dbf341ff 100644 --- a/src/tree/registries/getInformationFromRegistryItem.ts +++ b/src/tree/registries/getInformationFromRegistryItem.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CommonRegistry, CommonRepository, CommonTag, isRegistry, isRepository, isTag } from "@microsoft/vscode-docker-registries"; +import { Uri } from "vscode"; import { UnifiedRegistryItem } from "./UnifiedRegistryTreeDataProvider"; export function getImageNameFromRegistryItem(node: UnifiedRegistryItem): string { @@ -20,28 +21,14 @@ export function getFullImageNameFromRegistryItem(node: UnifiedRegistryItem): Promise { -// if (!isTag(node.wrappedItem) || !isRepository(node.parent.wrappedItem)) { -// throw new Error('Unable to get image name'); -// } -// const digestOptions = { -// headers: { -// // According to https://docs.docker.com/registry/spec/api/ -// // When deleting a manifest from a registry version 2.3 or later, the following header must be used when HEAD or GET-ing the manifest to obtain the correct digest to delete -// accept: 'application/vnd.docker.distribution.manifest.v2+json' -// } -// }; -// const url = `v2/${(node.parent.wrappedItem as CommonRepository).label}/manifests/${node.wrappedItem.label}`; -// const response = await httpRequest<{ results: [{ name: string; }] }>(requestUrl.toString(), { -// method: 'GET', -// headers: { -// Authorization: `Bearer ${(await this.authenticationProvider.getSession([], {})).accessToken}`, -// } -// }); -// const response = await htt; -// const digest = response.headers.get('docker-content-digest') as string; -// return digest; -// } From 3d3d98c11d14af45544462a97242381ee70e802d Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Mon, 7 Aug 2023 09:54:25 -0400 Subject: [PATCH 4/6] comment out commands that are not implemented yet --- src/commands/images/buildImage.ts | 160 +++++++-------- src/commands/images/pushImage.ts | 132 ++++++------ src/commands/images/tagImage.ts | 194 +++++++++--------- src/commands/registerCommands.ts | 16 +- .../registries/azure/deployImageToAca.ts | 188 ++++++++--------- .../dockerHub/openDockerHubInBrowser.ts | 50 ++--- 6 files changed, 368 insertions(+), 372 deletions(-) diff --git a/src/commands/images/buildImage.ts b/src/commands/images/buildImage.ts index db6660c458..a51eb7a44b 100644 --- a/src/commands/images/buildImage.ts +++ b/src/commands/images/buildImage.ts @@ -1,80 +1,80 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IActionContext, UserCancelledError } from "@microsoft/vscode-azext-utils"; -import * as path from "path"; -import * as vscode from "vscode"; -import { ext } from "../../extensionVariables"; -import { TaskCommandRunnerFactory } from "../../runtimes/runners/TaskCommandRunnerFactory"; -import { getOfficialBuildTaskForDockerfile } from "../../tasks/TaskHelper"; -import { getValidImageNameFromPath } from "../../utils/getValidImageName"; -import { delay } from "../../utils/promiseUtils"; -import { quickPickDockerFileItem } from "../../utils/quickPickFile"; -import { quickPickWorkspaceFolder } from "../../utils/quickPickWorkspaceFolder"; -import { selectBuildCommand } from "../selectCommandTemplate"; -import { addImageTaggingTelemetry, getTagFromUserInput } from "./tagImage"; - -const tagRegex: RegExp = /\$\{tag\}/i; - -export async function buildImage(context: IActionContext, dockerFileUri: vscode.Uri | undefined): Promise { - if (!vscode.workspace.isTrusted) { - throw new UserCancelledError('enforceTrust'); - } - - const configOptions: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('docker'); - const defaultContextPath = configOptions.get('imageBuildContextPath', ''); - - let rootFolder: vscode.WorkspaceFolder; - if (dockerFileUri) { - rootFolder = vscode.workspace.getWorkspaceFolder(dockerFileUri); - } - - rootFolder = rootFolder || await quickPickWorkspaceFolder(context, vscode.l10n.t('To build Docker files you must first open a folder or workspace in VS Code.')); - - const dockerFileItem = await quickPickDockerFileItem(context, dockerFileUri, rootFolder); - const task = await getOfficialBuildTaskForDockerfile(context, dockerFileItem.absoluteFilePath, rootFolder); - - if (task) { - await vscode.tasks.executeTask(task); - } else { - const contextPath: string = defaultContextPath || dockerFileItem.relativeFolderPath; - - const terminalCommand = await selectBuildCommand( - context, - rootFolder, - dockerFileItem.relativeFilePath, - contextPath - ); - - // Replace '${tag}' if needed. Tag is a special case because we don't want to prompt unless necessary, so must manually replace it. - if (tagRegex.test(terminalCommand.command)) { - const absFilePath: string = path.join(rootFolder.uri.fsPath, dockerFileItem.relativeFilePath); - const dockerFileKey = `buildTag_${absFilePath}`; - const prevImageName: string | undefined = ext.context.workspaceState.get(dockerFileKey); - - // Get imageName based previous entries, else on name of subfolder containing the Dockerfile - const suggestedImageName = prevImageName || getValidImageNameFromPath(dockerFileItem.absoluteFolderPath, 'latest'); - - // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox - await delay(500); - - addImageTaggingTelemetry(context, suggestedImageName, '.before'); - const imageName: string = await getTagFromUserInput(context, suggestedImageName); - addImageTaggingTelemetry(context, imageName, '.after'); - - await ext.context.workspaceState.update(dockerFileKey, imageName); - terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); - } - - const client = await ext.runtimeManager.getClient(); - const taskCRF = new TaskCommandRunnerFactory({ - taskName: client.displayName, - workspaceFolder: rootFolder, - focus: true, - }); - - await taskCRF.getCommandRunner()(terminalCommand); - } -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import { IActionContext, UserCancelledError } from "@microsoft/vscode-azext-utils"; +// import * as path from "path"; +// import * as vscode from "vscode"; +// import { ext } from "../../extensionVariables"; +// import { TaskCommandRunnerFactory } from "../../runtimes/runners/TaskCommandRunnerFactory"; +// import { getOfficialBuildTaskForDockerfile } from "../../tasks/TaskHelper"; +// import { getValidImageNameFromPath } from "../../utils/getValidImageName"; +// import { delay } from "../../utils/promiseUtils"; +// import { quickPickDockerFileItem } from "../../utils/quickPickFile"; +// import { quickPickWorkspaceFolder } from "../../utils/quickPickWorkspaceFolder"; +// import { selectBuildCommand } from "../selectCommandTemplate"; +// import { addImageTaggingTelemetry, getTagFromUserInput } from "./tagImage"; + +// const tagRegex: RegExp = /\$\{tag\}/i; + +// export async function buildImage(context: IActionContext, dockerFileUri: vscode.Uri | undefined): Promise { +// if (!vscode.workspace.isTrusted) { +// throw new UserCancelledError('enforceTrust'); +// } + +// const configOptions: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('docker'); +// const defaultContextPath = configOptions.get('imageBuildContextPath', ''); + +// let rootFolder: vscode.WorkspaceFolder; +// if (dockerFileUri) { +// rootFolder = vscode.workspace.getWorkspaceFolder(dockerFileUri); +// } + +// rootFolder = rootFolder || await quickPickWorkspaceFolder(context, vscode.l10n.t('To build Docker files you must first open a folder or workspace in VS Code.')); + +// const dockerFileItem = await quickPickDockerFileItem(context, dockerFileUri, rootFolder); +// const task = await getOfficialBuildTaskForDockerfile(context, dockerFileItem.absoluteFilePath, rootFolder); + +// if (task) { +// await vscode.tasks.executeTask(task); +// } else { +// const contextPath: string = defaultContextPath || dockerFileItem.relativeFolderPath; + +// const terminalCommand = await selectBuildCommand( +// context, +// rootFolder, +// dockerFileItem.relativeFilePath, +// contextPath +// ); + +// // Replace '${tag}' if needed. Tag is a special case because we don't want to prompt unless necessary, so must manually replace it. +// if (tagRegex.test(terminalCommand.command)) { +// const absFilePath: string = path.join(rootFolder.uri.fsPath, dockerFileItem.relativeFilePath); +// const dockerFileKey = `buildTag_${absFilePath}`; +// const prevImageName: string | undefined = ext.context.workspaceState.get(dockerFileKey); + +// // Get imageName based previous entries, else on name of subfolder containing the Dockerfile +// const suggestedImageName = prevImageName || getValidImageNameFromPath(dockerFileItem.absoluteFolderPath, 'latest'); + +// // Temporary work-around for vscode bug where valueSelection can be messed up if a quick pick is followed by a showInputBox +// await delay(500); + +// addImageTaggingTelemetry(context, suggestedImageName, '.before'); +// const imageName: string = await getTagFromUserInput(context, suggestedImageName); +// addImageTaggingTelemetry(context, imageName, '.after'); + +// await ext.context.workspaceState.update(dockerFileKey, imageName); +// terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName); +// } + +// const client = await ext.runtimeManager.getClient(); +// const taskCRF = new TaskCommandRunnerFactory({ +// taskName: client.displayName, +// workspaceFolder: rootFolder, +// focus: true, +// }); + +// await taskCRF.getCommandRunner()(terminalCommand); +// } +// } diff --git a/src/commands/images/pushImage.ts b/src/commands/images/pushImage.ts index fcf6415176..114748bb41 100644 --- a/src/commands/images/pushImage.ts +++ b/src/commands/images/pushImage.ts @@ -1,78 +1,78 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext, NoResourceFoundError, contextValueExperience } from '@microsoft/vscode-azext-utils'; -import * as vscode from 'vscode'; -import { ext } from '../../extensionVariables'; -import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; -import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; -import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; -import { addImageTaggingTelemetry, tagImage } from './tagImage'; +// import { IActionContext, NoResourceFoundError, contextValueExperience } from '@microsoft/vscode-azext-utils'; +// import * as vscode from 'vscode'; +// import { ext } from '../../extensionVariables'; +// import { TaskCommandRunnerFactory } from '../../runtimes/runners/TaskCommandRunnerFactory'; +// import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; +// import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; +// import { addImageTaggingTelemetry, tagImage } from './tagImage'; -export async function pushImage(context: IActionContext, node: ImageTreeItem | undefined): Promise { - if (!node) { - await ext.imagesTree.refresh(context); - node = await ext.imagesTree.showTreeItemPicker(ImageTreeItem.contextValue, { - ...context, - noItemFoundErrorMessage: vscode.l10n.t('No images are available to push'), - }); - } +// export async function pushImage(context: IActionContext, node: ImageTreeItem | undefined): Promise { +// if (!node) { +// await ext.imagesTree.refresh(context); +// node = await ext.imagesTree.showTreeItemPicker(ImageTreeItem.contextValue, { +// ...context, +// noItemFoundErrorMessage: vscode.l10n.t('No images are available to push'), +// }); +// } - let connectedRegistry: UnifiedRegistryItem | undefined; +// let connectedRegistry: UnifiedRegistryItem | undefined; - if (!node.fullTag.includes('/')) { - // The registry to push to is indeterminate--could be Docker Hub, or could need tagging. - const prompt: boolean = vscode.workspace.getConfiguration('docker').get('promptForRegistryWhenPushingImages', true); +// if (!node.fullTag.includes('/')) { +// // The registry to push to is indeterminate--could be Docker Hub, or could need tagging. +// const prompt: boolean = vscode.workspace.getConfiguration('docker').get('promptForRegistryWhenPushingImages', true); - // If the prompt setting is true, we'll ask; if not we'll assume Docker Hub. - if (prompt) { - try { - connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'commonregistry' }); - } catch (error) { - if (error instanceof NoResourceFoundError) { - // Do nothing, move on without a selected registry - } else { - // Rethrow - throw error; - } - } - } else { - // Try to find a connected Docker Hub registry (primarily for login credentials) - connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerHubRegistry' }); - } - } else { - // The registry to push to is determinate. If there's a connected registry in the tree view, we'll try to find it, to perform login ahead of time. - // Registry path is everything up to the last slash. - const baseImagePath = node.fullTag.substring(0, node.fullTag.lastIndexOf('/')); +// // If the prompt setting is true, we'll ask; if not we'll assume Docker Hub. +// if (prompt) { +// try { +// connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'commonregistry' }); +// } catch (error) { +// if (error instanceof NoResourceFoundError) { +// // Do nothing, move on without a selected registry +// } else { +// // Rethrow +// throw error; +// } +// } +// } else { +// // Try to find a connected Docker Hub registry (primarily for login credentials) +// connectedRegistry = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerHubRegistry' }); +// } +// } else { +// // The registry to push to is determinate. If there's a connected registry in the tree view, we'll try to find it, to perform login ahead of time. +// // Registry path is everything up to the last slash. +// const baseImagePath = node.fullTag.substring(0, node.fullTag.lastIndexOf('/')); - const progressOptions: vscode.ProgressOptions = { - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t('Fetching login credentials...'), - }; +// const progressOptions: vscode.ProgressOptions = { +// location: vscode.ProgressLocation.Notification, +// title: vscode.l10n.t('Fetching login credentials...'), +// }; - // connectedRegistry = await vscode.window.withProgress(progressOptions, async () => await tryGetConnectedRegistryForPath(context, baseImagePath)); TODO: review this later - } +// // connectedRegistry = await vscode.window.withProgress(progressOptions, async () => await tryGetConnectedRegistryForPath(context, baseImagePath)); TODO: review this later +// } - // Give the user a chance to modify the tag however they want - const finalTag = await tagImage(context, node, connectedRegistry); +// // Give the user a chance to modify the tag however they want +// const finalTag = await tagImage(context, node, connectedRegistry); - // if (connectedRegistry && finalTag.startsWith(connectedRegistry.baseImagePath)) { - // // If a registry was found/chosen and is still the same as the final tag's registry, try logging in - // await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', connectedRegistry); - // } TODO: review this later +// // if (connectedRegistry && finalTag.startsWith(connectedRegistry.baseImagePath)) { +// // // If a registry was found/chosen and is still the same as the final tag's registry, try logging in +// // await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', connectedRegistry); +// // } TODO: review this later - addImageTaggingTelemetry(context, finalTag, ''); +// addImageTaggingTelemetry(context, finalTag, ''); - const client = await ext.runtimeManager.getClient(); - const taskCRF = new TaskCommandRunnerFactory( - { - taskName: finalTag - } - ); +// const client = await ext.runtimeManager.getClient(); +// const taskCRF = new TaskCommandRunnerFactory( +// { +// taskName: finalTag +// } +// ); - await taskCRF.getCommandRunner()( - client.pushImage({ imageRef: finalTag }) - ); -} +// await taskCRF.getCommandRunner()( +// client.pushImage({ imageRef: finalTag }) +// ); +// } diff --git a/src/commands/images/tagImage.ts b/src/commands/images/tagImage.ts index aff407e5bd..dda54f7e1c 100644 --- a/src/commands/images/tagImage.ts +++ b/src/commands/images/tagImage.ts @@ -1,97 +1,97 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IActionContext, TelemetryProperties } from '@microsoft/vscode-azext-utils'; -import * as vscode from 'vscode'; -import { ext } from '../../extensionVariables'; -import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; -import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; - -export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: UnifiedRegistryItem): Promise { - if (!node) { - await ext.imagesTree.refresh(context); - node = await ext.imagesTree.showTreeItemPicker(ImageTreeItem.contextValue, { - ...context, - noItemFoundErrorMessage: vscode.l10n.t('No images are available to tag') - }); - } - - addImageTaggingTelemetry(context, node.fullTag, '.before'); - const newTaggedName: string = await getTagFromUserInput(context, node.fullTag, ''); // TODO: review this later - addImageTaggingTelemetry(context, newTaggedName, '.after'); - - await ext.runWithDefaults(client => - client.tagImage({ fromImageRef: node.imageId, toImageRef: newTaggedName }) - ); - - return newTaggedName; -} - -export async function getTagFromUserInput(context: IActionContext, fullTag: string, baseImagePath?: string): Promise { - const opt: vscode.InputBoxOptions = { - ignoreFocusOut: true, - prompt: vscode.l10n.t('Tag image as...'), - }; - - if (fullTag.includes('/')) { - opt.valueSelection = [0, fullTag.lastIndexOf('/')]; - } else if (baseImagePath) { - fullTag = `${baseImagePath}/${fullTag}`; - opt.valueSelection = [0, fullTag.lastIndexOf('/')]; - } - - opt.value = fullTag; - - return await context.ui.showInputBox(opt); -} - -const KnownRegistries: { type: string, regex: RegExp }[] = [ - // Like username/path - { type: 'dockerhub-namespace', regex: /^[^.:]+\/[^.:]+$/ }, - - { type: 'dockerhub-dockerio', regex: /^docker.io.*\// }, - { type: 'github', regex: /ghcr\.io.*\// }, - { type: 'gitlab', regex: /gitlab.*\// }, - { type: 'ACR', regex: /azurecr\.io.*\// }, - { type: 'GCR', regex: /gcr\.io.*\// }, - { type: 'ECR', regex: /\.ecr\..*\// }, - { type: 'localhost', regex: /localhost:.*\// }, - - // Has a port, probably a private registry - { type: 'privateWithPort', regex: /:[0-9]+\// }, - - // Match anything remaining - { type: 'other', regex: /\// }, // has a slash - { type: 'none', regex: /./ } // no slash -]; - -export function addImageTaggingTelemetry(context: IActionContext, fullImageName: string, propertyPostfix: '.before' | '.after' | ''): void { - try { - const properties: TelemetryProperties = {}; - - const [, repository, tag] = /^(.*):(.*)$/.exec(fullImageName) ?? [undefined, fullImageName, '']; - - if (!!tag.match(/^[0-9.-]*(|alpha|beta|latest|edge|v|version)?[0-9.-]*$/)) { - properties.safeTag = tag; - } - properties.hasTag = String(!!tag); - properties.numSlashes = String(numberMatches(repository.match(/\//g))); - - const knownRegistry = KnownRegistries.find(kr => !!repository.match(kr.regex)); - if (knownRegistry) { - properties.registryType = knownRegistry.type; - } - - for (const propertyName of Object.keys(properties)) { - context.telemetry.properties[propertyName + propertyPostfix] = properties[propertyName]; - } - } catch (error) { - console.error(error); - } -} - -function numberMatches(matches: RegExpMatchArray | null): number { - return matches ? matches.length : 0; -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import { IActionContext, TelemetryProperties } from '@microsoft/vscode-azext-utils'; +// import * as vscode from 'vscode'; +// import { ext } from '../../extensionVariables'; +// import { ImageTreeItem } from '../../tree/images/ImageTreeItem'; +// import { UnifiedRegistryItem } from '../../tree/registries/UnifiedRegistryTreeDataProvider'; + +// export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: UnifiedRegistryItem): Promise { +// if (!node) { +// await ext.imagesTree.refresh(context); +// node = await ext.imagesTree.showTreeItemPicker(ImageTreeItem.contextValue, { +// ...context, +// noItemFoundErrorMessage: vscode.l10n.t('No images are available to tag') +// }); +// } + +// addImageTaggingTelemetry(context, node.fullTag, '.before'); +// const newTaggedName: string = await getTagFromUserInput(context, node.fullTag, ''); // TODO: review this later +// addImageTaggingTelemetry(context, newTaggedName, '.after'); + +// await ext.runWithDefaults(client => +// client.tagImage({ fromImageRef: node.imageId, toImageRef: newTaggedName }) +// ); + +// return newTaggedName; +// } + +// export async function getTagFromUserInput(context: IActionContext, fullTag: string, baseImagePath?: string): Promise { +// const opt: vscode.InputBoxOptions = { +// ignoreFocusOut: true, +// prompt: vscode.l10n.t('Tag image as...'), +// }; + +// if (fullTag.includes('/')) { +// opt.valueSelection = [0, fullTag.lastIndexOf('/')]; +// } else if (baseImagePath) { +// fullTag = `${baseImagePath}/${fullTag}`; +// opt.valueSelection = [0, fullTag.lastIndexOf('/')]; +// } + +// opt.value = fullTag; + +// return await context.ui.showInputBox(opt); +// } + +// const KnownRegistries: { type: string, regex: RegExp }[] = [ +// // Like username/path +// { type: 'dockerhub-namespace', regex: /^[^.:]+\/[^.:]+$/ }, + +// { type: 'dockerhub-dockerio', regex: /^docker.io.*\// }, +// { type: 'github', regex: /ghcr\.io.*\// }, +// { type: 'gitlab', regex: /gitlab.*\// }, +// { type: 'ACR', regex: /azurecr\.io.*\// }, +// { type: 'GCR', regex: /gcr\.io.*\// }, +// { type: 'ECR', regex: /\.ecr\..*\// }, +// { type: 'localhost', regex: /localhost:.*\// }, + +// // Has a port, probably a private registry +// { type: 'privateWithPort', regex: /:[0-9]+\// }, + +// // Match anything remaining +// { type: 'other', regex: /\// }, // has a slash +// { type: 'none', regex: /./ } // no slash +// ]; + +// export function addImageTaggingTelemetry(context: IActionContext, fullImageName: string, propertyPostfix: '.before' | '.after' | ''): void { +// try { +// const properties: TelemetryProperties = {}; + +// const [, repository, tag] = /^(.*):(.*)$/.exec(fullImageName) ?? [undefined, fullImageName, '']; + +// if (!!tag.match(/^[0-9.-]*(|alpha|beta|latest|edge|v|version)?[0-9.-]*$/)) { +// properties.safeTag = tag; +// } +// properties.hasTag = String(!!tag); +// properties.numSlashes = String(numberMatches(repository.match(/\//g))); + +// const knownRegistry = KnownRegistries.find(kr => !!repository.match(kr.regex)); +// if (knownRegistry) { +// properties.registryType = knownRegistry.type; +// } + +// for (const propertyName of Object.keys(properties)) { +// context.telemetry.properties[propertyName + propertyPostfix] = properties[propertyName]; +// } +// } catch (error) { +// console.error(error); +// } +// } + +// function numberMatches(matches: RegExpMatchArray | null): number { +// return matches ? matches.length : 0; +// } diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index ae23d8492d..4bc7b1aa77 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -31,19 +31,17 @@ import { inspectDockerContext } from "./context/inspectDockerContext"; import { removeDockerContext } from "./context/removeDockerContext"; import { useDockerContext } from "./context/useDockerContext"; import { help } from "./help"; -import { buildImage } from "./images/buildImage"; +// import { buildImage } from "./images/buildImage"; import { configureImagesExplorer } from "./images/configureImagesExplorer"; import { copyFullTag } from "./images/copyFullTag"; import { inspectImage } from "./images/inspectImage"; import { pruneImages } from "./images/pruneImages"; import { pullImage } from "./images/pullImage"; -import { pushImage } from "./images/pushImage"; import { removeImage } from "./images/removeImage"; import { removeImageGroup } from "./images/removeImageGroup"; import { runAzureCliImage } from "./images/runAzureCliImage"; import { runImage, runImageInteractive } from "./images/runImage"; import { hideDanglingImages, setInitialDanglingContextValue, showDanglingImages } from "./images/showDanglingImages"; -import { tagImage } from "./images/tagImage"; import { configureNetworksExplorer } from "./networks/configureNetworksExplorer"; import { createNetwork } from "./networks/createNetwork"; import { inspectNetwork } from "./networks/inspectNetwork"; @@ -54,7 +52,6 @@ import { registerWorkspaceCommand } from "./registerWorkspaceCommand"; import { createAzureRegistry } from "./registries/azure/createAzureRegistry"; // import { deleteAzureRegistry } from "./registries/azure/deleteAzureRegistry"; // import { deleteAzureRepository } from "./registries/azure/deleteAzureRepository"; -import { deployImageToAca } from "./registries/azure/deployImageToAca"; // import { deployImageToAzure } from "./registries/azure/deployImageToAzure"; import { openInAzurePortal } from "./registries/azure/openInAzurePortal"; // import { buildImageInAzure } from "./registries/azure/tasks/buildImageInAzure"; @@ -67,7 +64,6 @@ import { connectRegistry } from "./registries/connectRegistry"; // import { copyRemoteFullTag } from './registries/copyRemoteFullTag'; // import { copyRemoteImageDigest } from "./registries/copyRemoteImageDigest"; // import { deleteRemoteImage } from "./registries/deleteRemoteImage"; -import { openDockerHubInBrowser } from "./registries/dockerHub/openDockerHubInBrowser"; // import { logInToDockerCli } from "./registries/logInToDockerCli"; // import { logOutOfDockerCli } from "./registries/logOutOfDockerCli"; // import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; @@ -146,7 +142,7 @@ export function registerCommands(): void { registerWorkspaceCommand('vscode-docker.containers.composeGroup.restart', composeGroupRestart); registerWorkspaceCommand('vscode-docker.containers.composeGroup.down', composeGroupDown); - registerWorkspaceCommand('vscode-docker.images.build', buildImage); + // registerWorkspaceCommand('vscode-docker.images.build', buildImage); registerCommand('vscode-docker.images.configureExplorer', configureImagesExplorer); registerCommand('vscode-docker.images.inspect', inspectImage); registerCommand('vscode-docker.images.prune', pruneImages); @@ -154,13 +150,13 @@ export function registerCommands(): void { registerCommand('vscode-docker.images.hideDangling', hideDanglingImages); setInitialDanglingContextValue(); registerWorkspaceCommand('vscode-docker.images.pull', pullImage); - registerWorkspaceCommand('vscode-docker.images.push', pushImage); + // registerWorkspaceCommand('vscode-docker.images.push', pushImage); registerCommand('vscode-docker.images.remove', removeImage); registerCommand('vscode-docker.images.group.remove', removeImageGroup); registerWorkspaceCommand('vscode-docker.images.run', runImage); registerWorkspaceCommand('vscode-docker.images.runAzureCli', runAzureCliImage); registerWorkspaceCommand('vscode-docker.images.runInteractive', runImageInteractive); - registerCommand('vscode-docker.images.tag', tagImage); + // registerCommand('vscode-docker.images.tag', tagImage); registerCommand('vscode-docker.images.copyFullTag', copyFullTag); registerCommand('vscode-docker.networks.configureExplorer', configureNetworksExplorer); @@ -174,7 +170,7 @@ export function registerCommands(): void { registerCommand('vscode-docker.registries.copyRemoteFullTag', copyRemoteFullTag); // registerCommand('vscode-docker.registries.deleteImage', deleteRemoteImage); // registerCommand('vscode-docker.registries.deployImageToAzure', deployImageToAzure); - registerCommand('vscode-docker.registries.deployImageToAca', deployImageToAca); + // registerCommand('vscode-docker.registries.deployImageToAca', deployImageToAca); registerCommand('vscode-docker.registries.disconnectRegistry', (context, item) => ext.registriesRoot.disconnectRegistryProvider(item)); registerCommand('vscode-docker.registries.help', registryHelp); registerWorkspaceCommand('vscode-docker.registries.logInToDockerCli', logInToDockerCli); @@ -183,7 +179,7 @@ export function registerCommands(): void { registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository); // registerCommand('vscode-docker.registries.reconnectRegistry', reconnectRegistry); - registerCommand('vscode-docker.registries.dockerHub.openInBrowser', openDockerHubInBrowser); + // registerCommand('vscode-docker.registries.dockerHub.openInBrowser', openDockerHubInBrowser); // registerWorkspaceCommand('vscode-docker.registries.azure.buildImage', buildImageInAzure); registerCommand('vscode-docker.registries.azure.createRegistry', createAzureRegistry); diff --git a/src/commands/registries/azure/deployImageToAca.ts b/src/commands/registries/azure/deployImageToAca.ts index c2fa541724..da4d9a495a 100644 --- a/src/commands/registries/azure/deployImageToAca.ts +++ b/src/commands/registries/azure/deployImageToAca.ts @@ -1,94 +1,94 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { contextValueExperience, IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; -import * as semver from 'semver'; -import * as vscode from 'vscode'; -import { ext } from '../../../extensionVariables'; -import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; -import { installExtension } from '../../../utils/installExtension'; -import { addImageTaggingTelemetry } from '../../images/tagImage'; - -const acaExtensionId = 'ms-azuretools.vscode-azurecontainerapps'; -const minimumAcaExtensionVersion = '0.4.0'; - -// The interface of the command options passed to the Azure Container Apps extension's deployImageToAca command -interface DeployImageToAcaOptionsContract { - image: string; - registryName: string; - username?: string; - secret?: string; -} - -export async function deployImageToAca(context: IActionContext, node?: UnifiedRegistryItem): Promise { - // Assert installation of the ACA extension - if (!isAcaExtensionInstalled()) { - // This will always throw a `UserCancelledError` but with the appropriate step name - // based on user choice about installation - await openAcaInstallPage(context); - } - - if (!node) { - node = await contextValueExperience(context, ext.registriesRoot, { include: 'azurecontainerregistry' }); - } - - const commandOptions: Partial = { - // image: node.fullTag, - }; - - addImageTaggingTelemetry(context, commandOptions.image, ''); - - // const registry: RegistryTreeItemBase = node.parent.parent; - // if (registry instanceof AzureRegistryTreeItem) { - // // No additional work to do; ACA can handle this on its own - // } else { - // const { auth } = await registry.getDockerCliCredentials() as { auth?: { username?: string, password?: string } }; - - // if (!auth?.username || !auth?.password) { - // throw new Error(vscode.l10n.t('No credentials found for registry "{0}".', registry.label)); - // } - - // if (registry instanceof DockerHubNamespaceTreeItem) { - // // Ensure Docker Hub images are prefixed with 'docker.io/...' - // if (!/^docker\.io\//i.test(commandOptions.image)) { - // commandOptions.image = 'docker.io/' + commandOptions.image; - // } - // } - - // commandOptions.username = auth.username; - // commandOptions.secret = auth.password; - // } - - // commandOptions.registryName = nonNullProp(parseDockerLikeImageName(commandOptions.image), 'registry'); - - // // Don't wait - // void vscode.commands.executeCommand('containerApps.deployImageApi', commandOptions); - // TODO: review this later -} - -function isAcaExtensionInstalled(): boolean { - const acaExtension = vscode.extensions.getExtension(acaExtensionId); - - if (!acaExtension?.packageJSON?.version) { - // If the ACA extension is not present, or the package JSON didn't come through, or the version is not present, then it's not installed - return false; - } - - const acaVersion = semver.coerce(acaExtension.packageJSON.version); - const minVersion = semver.coerce(minimumAcaExtensionVersion); - - return semver.gte(acaVersion, minVersion); -} - -async function openAcaInstallPage(context: IActionContext): Promise { - const message = vscode.l10n.t( - 'Version {0} or higher of the Azure Container Apps extension is required to deploy to Azure Container Apps. Would you like to install it now?', - minimumAcaExtensionVersion - ); - - await installExtension(context, acaExtensionId, message); - - throw new UserCancelledError('installAcaExtensionAccepted'); -} +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +// import { contextValueExperience, IActionContext, UserCancelledError } from '@microsoft/vscode-azext-utils'; +// import * as semver from 'semver'; +// import * as vscode from 'vscode'; +// import { ext } from '../../../extensionVariables'; +// import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider'; +// import { installExtension } from '../../../utils/installExtension'; +// import { addImageTaggingTelemetry } from '../../images/tagImage'; + +// const acaExtensionId = 'ms-azuretools.vscode-azurecontainerapps'; +// const minimumAcaExtensionVersion = '0.4.0'; + +// // The interface of the command options passed to the Azure Container Apps extension's deployImageToAca command +// interface DeployImageToAcaOptionsContract { +// image: string; +// registryName: string; +// username?: string; +// secret?: string; +// } + +// export async function deployImageToAca(context: IActionContext, node?: UnifiedRegistryItem): Promise { +// // Assert installation of the ACA extension +// if (!isAcaExtensionInstalled()) { +// // This will always throw a `UserCancelledError` but with the appropriate step name +// // based on user choice about installation +// await openAcaInstallPage(context); +// } + +// if (!node) { +// node = await contextValueExperience(context, ext.registriesRoot, { include: 'azurecontainerregistry' }); +// } + +// const commandOptions: Partial = { +// // image: node.fullTag, +// }; + +// addImageTaggingTelemetry(context, commandOptions.image, ''); + +// // const registry: RegistryTreeItemBase = node.parent.parent; +// // if (registry instanceof AzureRegistryTreeItem) { +// // // No additional work to do; ACA can handle this on its own +// // } else { +// // const { auth } = await registry.getDockerCliCredentials() as { auth?: { username?: string, password?: string } }; + +// // if (!auth?.username || !auth?.password) { +// // throw new Error(vscode.l10n.t('No credentials found for registry "{0}".', registry.label)); +// // } + +// // if (registry instanceof DockerHubNamespaceTreeItem) { +// // // Ensure Docker Hub images are prefixed with 'docker.io/...' +// // if (!/^docker\.io\//i.test(commandOptions.image)) { +// // commandOptions.image = 'docker.io/' + commandOptions.image; +// // } +// // } + +// // commandOptions.username = auth.username; +// // commandOptions.secret = auth.password; +// // } + +// // commandOptions.registryName = nonNullProp(parseDockerLikeImageName(commandOptions.image), 'registry'); + +// // // Don't wait +// // void vscode.commands.executeCommand('containerApps.deployImageApi', commandOptions); +// // TODO: review this later +// } + +// function isAcaExtensionInstalled(): boolean { +// const acaExtension = vscode.extensions.getExtension(acaExtensionId); + +// if (!acaExtension?.packageJSON?.version) { +// // If the ACA extension is not present, or the package JSON didn't come through, or the version is not present, then it's not installed +// return false; +// } + +// const acaVersion = semver.coerce(acaExtension.packageJSON.version); +// const minVersion = semver.coerce(minimumAcaExtensionVersion); + +// return semver.gte(acaVersion, minVersion); +// } + +// async function openAcaInstallPage(context: IActionContext): Promise { +// const message = vscode.l10n.t( +// 'Version {0} or higher of the Azure Container Apps extension is required to deploy to Azure Container Apps. Would you like to install it now?', +// minimumAcaExtensionVersion +// ); + +// await installExtension(context, acaExtensionId, message); + +// throw new UserCancelledError('installAcaExtensionAccepted'); +// } diff --git a/src/commands/registries/dockerHub/openDockerHubInBrowser.ts b/src/commands/registries/dockerHub/openDockerHubInBrowser.ts index 7d6a507377..f2ab582bc5 100644 --- a/src/commands/registries/dockerHub/openDockerHubInBrowser.ts +++ b/src/commands/registries/dockerHub/openDockerHubInBrowser.ts @@ -1,30 +1,30 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See LICENSE.md in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ -import { IActionContext, contextValueExperience } from "@microsoft/vscode-azext-utils"; -import * as vscode from "vscode"; -import { ext } from "../../../extensionVariables"; -import { UnifiedRegistryItem } from "../../../tree/registries/UnifiedRegistryTreeDataProvider"; +// import { IActionContext, contextValueExperience } from "@microsoft/vscode-azext-utils"; +// import * as vscode from "vscode"; +// import { ext } from "../../../extensionVariables"; +// import { UnifiedRegistryItem } from "../../../tree/registries/UnifiedRegistryTreeDataProvider"; -export async function openDockerHubInBrowser(context: IActionContext, node?: UnifiedRegistryItem): Promise { - if (!node) { - node = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerhubregistry' }); - } +// export async function openDockerHubInBrowser(context: IActionContext, node?: UnifiedRegistryItem): Promise { +// if (!node) { +// node = await contextValueExperience(context, ext.registriesRoot, { include: 'dockerhubregistry' }); +// } - // let url = dockerHubUrl; - // if (node instanceof DockerHubNamespaceTreeItem) { - // url += `u/${node.namespace}`; - // } else if (node instanceof DockerHubRepositoryTreeItem) { - // url += `r/${node.parent.namespace}/${node.repoName}`; - // } else { - // const repoTI = node.parent; - // url += `r/${repoTI.parent.namespace}/${repoTI.repoName}/tags`; - // } +// let url = dockerHubUrl; +// if (node instanceof DockerHubNamespaceTreeItem) { +// url += `u/${node.namespace}`; +// } else if (node instanceof DockerHubRepositoryTreeItem) { +// url += `r/${node.parent.namespace}/${node.repoName}`; +// } else { +// const repoTI = node.parent; +// url += `r/${repoTI.parent.namespace}/${repoTI.repoName}/tags`; +// } - const url = ''; - // TODO: review this later +// const url = ''; +// // TODO: review this later - await vscode.env.openExternal(vscode.Uri.parse(url)); -} +// await vscode.env.openExternal(vscode.Uri.parse(url)); +// } From a82da2eb7a9c63ed22f60b1cf1842cd2bcc3b276 Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Mon, 7 Aug 2023 13:24:44 -0400 Subject: [PATCH 5/6] added disconnext registry function back --- src/commands/registerCommands.ts | 3 +- src/commands/registerLocalCommand.ts | 54 +++++++++---------- src/commands/registries/disconnectRegistry.ts | 10 ++-- .../UnifiedRegistryTreeDataProvider.ts | 2 +- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index 4bc7b1aa77..ee3350847a 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -70,6 +70,7 @@ import { connectRegistry } from "./registries/connectRegistry"; // import { reconnectRegistry } from "./registries/reconnectRegistry"; import { copyRemoteFullTag } from "./registries/copyRemoteFullTag"; import { copyRemoteImageDigest } from "./registries/copyRemoteImageDigest"; +import { disconnectRegistry } from "./registries/disconnectRegistry"; import { logInToDockerCli } from "./registries/logInToDockerCli"; import { pullImageFromRepository, pullRepository } from "./registries/pullImages"; import { registryHelp } from "./registries/registryHelp"; @@ -171,7 +172,7 @@ export function registerCommands(): void { // registerCommand('vscode-docker.registries.deleteImage', deleteRemoteImage); // registerCommand('vscode-docker.registries.deployImageToAzure', deployImageToAzure); // registerCommand('vscode-docker.registries.deployImageToAca', deployImageToAca); - registerCommand('vscode-docker.registries.disconnectRegistry', (context, item) => ext.registriesRoot.disconnectRegistryProvider(item)); + registerCommand('vscode-docker.registries.disconnectRegistry', disconnectRegistry); registerCommand('vscode-docker.registries.help', registryHelp); registerWorkspaceCommand('vscode-docker.registries.logInToDockerCli', logInToDockerCli); // registerWorkspaceCommand('vscode-docker.registries.logOutOfDockerCli', logOutOfDockerCli); diff --git a/src/commands/registerLocalCommand.ts b/src/commands/registerLocalCommand.ts index be9db187eb..3f2854a49b 100644 --- a/src/commands/registerLocalCommand.ts +++ b/src/commands/registerLocalCommand.ts @@ -1,31 +1,31 @@ -// /*--------------------------------------------------------------------------------------------- -// * Copyright (c) Microsoft Corporation. All rights reserved. -// * Licensed under the MIT License. See LICENSE.md in the project root for license information. -// *--------------------------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ -// import { IActionContext } from '@microsoft/vscode-azext-utils'; -// import { l10n } from 'vscode'; -// import { DockerExtensionKind, getVSCodeRemoteInfo } from '../utils/getVSCodeRemoteInfo'; -// import { registerCommand } from './registerCommands'; +import { IActionContext } from '@microsoft/vscode-azext-utils'; +import { l10n } from 'vscode'; +import { DockerExtensionKind, getVSCodeRemoteInfo } from '../utils/getVSCodeRemoteInfo'; +import { registerCommand } from './registerCommands'; -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// export function registerLocalCommand(commandId: string, callback: (context: IActionContext, ...args: any[]) => any, debounce?: number): void { -// registerCommand( -// commandId, -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// async (context, ...args: any[]) => { -// await verifyIsRunningLocally(context); -// return callback(context, ...args); -// }, -// debounce -// ); -// } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function registerLocalCommand(commandId: string, callback: (context: IActionContext, ...args: any[]) => any, debounce?: number): void { + registerCommand( + commandId, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async (context, ...args: any[]) => { + await verifyIsRunningLocally(context); + return callback(context, ...args); + }, + debounce + ); +} -// async function verifyIsRunningLocally(context: IActionContext): Promise { -// const remoteInfo = getVSCodeRemoteInfo(context); +async function verifyIsRunningLocally(context: IActionContext): Promise { + const remoteInfo = getVSCodeRemoteInfo(context); -// if (remoteInfo.extensionKind !== DockerExtensionKind.local) { -// context.errorHandling.suppressReportIssue = true; -// throw new Error(l10n.t('This command cannot be used in a remote session.')); -// } -// } + if (remoteInfo.extensionKind !== DockerExtensionKind.local) { + context.errorHandling.suppressReportIssue = true; + throw new Error(l10n.t('This command cannot be used in a remote session.')); + } +} diff --git a/src/commands/registries/disconnectRegistry.ts b/src/commands/registries/disconnectRegistry.ts index 1f8c6a999e..e158c6b49d 100644 --- a/src/commands/registries/disconnectRegistry.ts +++ b/src/commands/registries/disconnectRegistry.ts @@ -3,10 +3,14 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IActionContext } from "@microsoft/vscode-azext-utils"; +import { IActionContext, contextValueExperience } from "@microsoft/vscode-azext-utils"; +import { ext } from "../../extensionVariables"; import { UnifiedRegistryItem } from "../../tree/registries/UnifiedRegistryTreeDataProvider"; - export async function disconnectRegistry(context: IActionContext, node?: UnifiedRegistryItem): Promise { - await node.provider.onDisconnect?.(); + if (!node) { + node = await contextValueExperience(context, ext.registriesTree, { include: 'commonregistryroot', exclude: 'genericRegistryV2Root' }); + } + + await ext.registriesTree.disconnectRegistryProvider(node); } diff --git a/src/tree/registries/UnifiedRegistryTreeDataProvider.ts b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts index ffc1fe1635..573b3d7db5 100644 --- a/src/tree/registries/UnifiedRegistryTreeDataProvider.ts +++ b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts @@ -121,7 +121,7 @@ export class UnifiedRegistryTreeDataProvider implements vscode.TreeDataProvider< this.refresh(); } - public async disconnectRegistryProvider(item: UnifiedRegistryItem): Promise { + public async disconnectRegistryProvider(item: UnifiedRegistryItem): Promise { await item.provider?.onDisconnect?.(); const newConnectedProviderIds = this.storageMemento .get(ConnectedRegistryProvidersKey, []) From 4a69692ce3a783068da55fad0a8395d0c801a467 Mon Sep 17 00:00:00 2001 From: alexyaang <59073590+alexyaang@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:45:21 -0400 Subject: [PATCH 6/6] small fixes to adjust to new changes --- src/commands/registries/logInToDockerCli.ts | 2 +- src/extensionVariables.ts | 2 ++ src/tree/RefreshManager.ts | 2 +- src/tree/registerTrees.ts | 3 +-- .../registries/Azure/AzureRegistryDataProvider.ts | 4 ++-- .../registries/UnifiedRegistryTreeDataProvider.ts | 3 +-- .../registries/getInformationFromRegistryItem.ts | 12 +++++++----- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/commands/registries/logInToDockerCli.ts b/src/commands/registries/logInToDockerCli.ts index a761c8b897..2b7fd92b20 100644 --- a/src/commands/registries/logInToDockerCli.ts +++ b/src/commands/registries/logInToDockerCli.ts @@ -33,7 +33,7 @@ export async function logInToDockerCli(context: IActionContext, node?: UnifiedRe client => client.login({ username: username, passwordStdIn: true, - registry: '' // TODO: change this + registry: node.wrappedItem.baseUrl?.toString() ?? '' }), { stdInPipe: stream.Readable.from(secret), diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index 22742fc811..93c3fad0db 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AzExtTreeDataProvider, AzExtTreeItem, IExperimentationServiceAdapter } from '@microsoft/vscode-azext-utils'; +import { GenericRegistryV2DataProvider } from '@microsoft/vscode-docker-registries'; import { ExtensionContext, StatusBarItem, TreeView } from 'vscode'; import { ContainerRuntimeManager } from './runtimes/ContainerRuntimeManager'; import { OrchestratorRuntimeManager } from './runtimes/OrchestratorRuntimeManager'; @@ -48,6 +49,7 @@ export namespace ext { export let registriesTree: UnifiedRegistryTreeDataProvider; export let registriesTreeView: TreeView>; export let registriesRoot: UnifiedRegistryTreeDataProvider; + export let genericRegistryV2DataProvider: GenericRegistryV2DataProvider; export let volumesTree: AzExtTreeDataProvider; export let volumesTreeView: TreeView; diff --git a/src/tree/RefreshManager.ts b/src/tree/RefreshManager.ts index 39485f05c8..502ac49604 100644 --- a/src/tree/RefreshManager.ts +++ b/src/tree/RefreshManager.ts @@ -301,7 +301,7 @@ export class RefreshManager extends vscode.Disposable { callback = () => ext.networksRoot.refresh(context); break; case 'registries': - ext.registriesRoot.refresh(); //TODO: change this + callback = () => ext.registriesRoot.refresh(); break; case 'volumes': callback = () => ext.volumesRoot.refresh(context); diff --git a/src/tree/registerTrees.ts b/src/tree/registerTrees.ts index 27b765a2dc..e55991df7f 100644 --- a/src/tree/registerTrees.ts +++ b/src/tree/registerTrees.ts @@ -53,8 +53,7 @@ export function registerTrees(): void { ext.registriesRoot = urtdp; ext.registriesTreeView = vscode.window.createTreeView('dockerRegistries', { treeDataProvider: urtdp }); ext.registriesTree = urtdp; - // TODO: add gitlab registry provider when the new extension is ready - /* eslint-disable-next-line @typescript-eslint/promise-function-async */ + ext.genericRegistryV2DataProvider = genericRegistryV2DataProvider; ext.volumesRoot = new VolumesTreeItem(undefined); const volumesLoadMore = 'vscode-docker.volumes.loadMore'; diff --git a/src/tree/registries/Azure/AzureRegistryDataProvider.ts b/src/tree/registries/Azure/AzureRegistryDataProvider.ts index 9e04b6edcc..3ec224bc34 100644 --- a/src/tree/registries/Azure/AzureRegistryDataProvider.ts +++ b/src/tree/registries/Azure/AzureRegistryDataProvider.ts @@ -114,10 +114,10 @@ export class AzureRegistryDataProvider extends RegistryV2DataProvider implements } protected override getAuthenticationProvider(item: AzureRegistryItem): ACROAuthProvider { - const registryString = item.registryUri.toString(); + const registryString = item.baseUrl.toString(); if (!this.authenticationProviders.has(registryString)) { - const provider = new ACROAuthProvider(item.registryUri, item.subscription); + const provider = new ACROAuthProvider(item.baseUrl, item.subscription); this.authenticationProviders.set(registryString, provider); } diff --git a/src/tree/registries/UnifiedRegistryTreeDataProvider.ts b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts index 573b3d7db5..9df806fd6d 100644 --- a/src/tree/registries/UnifiedRegistryTreeDataProvider.ts +++ b/src/tree/registries/UnifiedRegistryTreeDataProvider.ts @@ -7,7 +7,6 @@ export interface UnifiedRegistryItem { parent: UnifiedRegistryItem | undefined; } -// eslint-disable-next-line @typescript-eslint/naming-convention const ConnectedRegistryProvidersKey = 'ConnectedRegistryProviders'; export class UnifiedRegistryTreeDataProvider implements vscode.TreeDataProvider> { @@ -81,7 +80,7 @@ export class UnifiedRegistryTreeDataProvider implements vscode.TreeDataProvider< }; } - public refresh(): void { + public async refresh(): Promise { this.onDidChangeTreeDataEmitter.fire(undefined); } diff --git a/src/tree/registries/getInformationFromRegistryItem.ts b/src/tree/registries/getInformationFromRegistryItem.ts index e7dbf341ff..eccab72abf 100644 --- a/src/tree/registries/getInformationFromRegistryItem.ts +++ b/src/tree/registries/getInformationFromRegistryItem.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { CommonRegistry, CommonRepository, CommonTag, isRegistry, isRepository, isTag } from "@microsoft/vscode-docker-registries"; -import { Uri } from "vscode"; import { UnifiedRegistryItem } from "./UnifiedRegistryTreeDataProvider"; export function getImageNameFromRegistryItem(node: UnifiedRegistryItem): string { @@ -12,7 +11,9 @@ export function getImageNameFromRegistryItem(node: UnifiedRegistryItem): string { @@ -21,14 +22,15 @@ export function getFullImageNameFromRegistryItem(node: UnifiedRegistryItem