-
Notifications
You must be signed in to change notification settings - Fork 517
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added Azure Credentials Manager Singleton (#18) * Added Azure Credentials Manager Singleton * Added getResourceManagementClient * Sorted Existing Create Registry ready for code review * Added acquiring telemetry data for create registry * broke up createnewresourcegroup method and fixed client use Added try catch loop and awaited for resource group list again to check for duplicates with ResourceManagementClient * Jackson esteban/unified client nit Fix (#24) * Added Azure Credentials Manager Singleton * Small Style Fixes * Further Style fixes, added getResourceManagementClient * Lazy Initialization Patches * Enabled location selection * Location request fixes -Changed order of questions asked to user for better UX (location of new res group & location of new registry) -Placeholder of location is display name view * Refactor while loop for new res group * Added SKU selection * Quick fix- initializing array syntax * Added specific error messages and comments * Julia/delete image (#29) * first fully functional version of delete through input bar AND right click * refactored code to make it prettier! * comments * comments, added subscription function * fixed to style guide * style fixes, refactoring * delete image after reviews put my functions from azureCredentialsManager into two new files: utils/azure/acrTools.ts, and commands/utils/quick-pick-azure.ts Edited code based on Esteban's and Bin's reviews * One last little change to delete image * moved repository, azureimage, and getsubscriptions to the correct places within deleteImage * changes from PR reviews on delete image * fixed authentication issue, got rid of azureAccount property for repository and image **on constructor for repository, azurecredentialsmanager was being recreated and thus couldn't find the azureAccount. For this reason, I got rid of the azureAccount property of the classes Repository and AzureImage. This bug may lead to future problems (Esteban and I couldn't see why it was happening) * minor fixes deleteImage * delete a parentheses * Estebanreyl/dev merge fixes (#43) * Merge fixes to acquire latest telemetry items * Updated to master AzureUtilityManager * Rutusamai/list build tasks for each registry (#37) * tslint updates, transfered from old branch * updated icon * Addressed PR comments- mostly styling/code efficiency * Changed url to aka.ms link * changed Error window to Info message for no build tasks in your registry * Changed default sku and unified parsing resource group into a new method, getResourceGroup in acrTools.ts * Changed build task icon * Julia/delete repository final (#49) * deleteRepo moved over to branch off dev * Got rid of unnecessary code, fully functioning! * deleteRepo moved over to branch off dev * Got rid of unnecessary code, fully functioning! * spacing * final commit * Cleaned code * Added Telemetry * Julia/delete registry final (#47) Delete azure registry functionality added Delete azure registry moved to branch off dev Reorganized stye * began updating * Reorganized create registry and delete azure image * continued improvements * Began updating login * being credentials update * further updates * Finished updating, need to test functionality now * Updated requests, things all work now * Applied some nit fixes * Updates to naming * maintain UtilityManager standards * Updated Prompts * Updated imports and naming / standarized telemetry * Added explorer refresh capabilities on delete/add * Remove build task features from this branch This reverts commit 126c01e. * Merge bugfixes and name specification * updated weird naming issue * Deleted deprecated telemetry, added copyright comment and updated quick picks * Updated casing * Updated resource group and registry validity checking and other nit fixes * Updated Azure Utility Manager to by default sort registries alphabetically * Updated azureRegistryNodes and registryRootNode to use shared functions * Corrected resourcegroup name test * added delete button when deleting an image * Small changes in variables for better prompts and success notifications
- Loading branch information
1 parent
f935cb7
commit bc29acd
Showing
17 changed files
with
770 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
import { ContainerRegistryManagementClient } from 'azure-arm-containerregistry'; | ||
import { Registry, RegistryNameStatus } from "azure-arm-containerregistry/lib/models"; | ||
import { SubscriptionModels } from 'azure-arm-resource'; | ||
import { ResourceGroup } from "azure-arm-resource/lib/resource/models"; | ||
import * as vscode from "vscode"; | ||
import { dockerExplorerProvider } from '../../dockerExtension'; | ||
import { ext } from '../../extensionVariables'; | ||
import { isValidAzureName } from '../../utils/Azure/common'; | ||
import { AzureUtilityManager } from '../../utils/azureUtilityManager'; | ||
import { quickPickLocation, quickPickResourceGroup, quickPickSKU, quickPickSubscription } from '../utils/quick-pick-azure'; | ||
|
||
/* Creates a new Azure container registry based on user input/selection of features */ | ||
export async function createRegistry(): Promise<Registry> { | ||
const subscription: SubscriptionModels.Subscription = await quickPickSubscription(); | ||
const resourceGroup: ResourceGroup = await quickPickResourceGroup(true, subscription); | ||
const client = AzureUtilityManager.getInstance().getContainerRegistryManagementClient(subscription); | ||
const registryName: string = await acquireRegistryName(client); | ||
const sku: string = await quickPickSKU(); | ||
const location = await quickPickLocation(subscription); | ||
|
||
const registry = await client.registries.beginCreate(resourceGroup.name, registryName, { | ||
'sku': { 'name': sku }, | ||
'location': location | ||
}); | ||
vscode.window.showInformationMessage(registry.name + ' has been created succesfully!'); | ||
dockerExplorerProvider.refreshRegistries(); | ||
return registry; | ||
} | ||
|
||
/** Acquires a new registry name from a user, validating that the name is unique */ | ||
async function acquireRegistryName(client: ContainerRegistryManagementClient): Promise<string> { | ||
let opt: vscode.InputBoxOptions = { | ||
validateInput: async (value: string) => { return await checkForValidName(value, client) }, | ||
ignoreFocusOut: false, | ||
prompt: 'Enter the new registry name? ' | ||
}; | ||
let registryName: string = await ext.ui.showInputBox(opt); | ||
|
||
return registryName; | ||
} | ||
|
||
async function checkForValidName(registryName: string, client: ContainerRegistryManagementClient): Promise<string> { | ||
let check = isValidAzureName(registryName); | ||
if (!check.isValid) { return check.message; } | ||
let registryStatus: RegistryNameStatus = await client.registries.checkNameAvailability({ 'name': registryName }); | ||
if (registryStatus.message) { | ||
return registryStatus.message; | ||
} | ||
return undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
import { Registry } from "azure-arm-containerregistry/lib/models"; | ||
import * as vscode from "vscode"; | ||
import { DialogResponses } from "vscode-azureextensionui"; | ||
import { dockerExplorerProvider } from '../../dockerExtension'; | ||
import { AzureImageTagNode } from '../../explorer/models/azureRegistryNodes'; | ||
import { ext } from "../../extensionVariables"; | ||
import * as acrTools from '../../utils/Azure/acrTools'; | ||
import { AzureImage } from "../../utils/Azure/models/image"; | ||
import { Repository } from "../../utils/Azure/models/repository"; | ||
import * as quickPicks from '../utils/quick-pick-azure'; | ||
|
||
/** Function to delete an Azure hosted image | ||
* @param context : if called through right click on AzureImageNode, the node object will be passed in. See azureRegistryNodes.ts for more info | ||
*/ | ||
export async function deleteAzureImage(context?: AzureImageTagNode): Promise<void> { | ||
let registry: Registry; | ||
let repoName: string; | ||
let tag: string; | ||
|
||
if (!context) { | ||
registry = await quickPicks.quickPickACRRegistry(); | ||
const repository: Repository = await quickPicks.quickPickACRRepository(registry, 'Select the repository of the image you want to delete'); | ||
repoName = repository.name; | ||
const image: AzureImage = await quickPicks.quickPickACRImage(repository, 'Select the image you want to delete'); | ||
tag = image.tag; | ||
|
||
} else { | ||
registry = context.registry; | ||
let wholeName: string[] = context.label.split(':'); | ||
repoName = wholeName[0]; | ||
tag = wholeName[1]; | ||
} | ||
|
||
const shouldDelete = await ext.ui.showWarningMessage(`Are you sure you want to delete ${repoName}:${tag}? `, { modal: true }, DialogResponses.deleteResponse, DialogResponses.cancel); | ||
if (shouldDelete === DialogResponses.deleteResponse) { | ||
const { acrAccessToken } = await acrTools.acquireACRAccessTokenFromRegistry(registry, `repository:${repoName}:*`); | ||
const path = `/v2/_acr/${repoName}/tags/${tag}`; | ||
await acrTools.sendRequestToRegistry('delete', registry.loginServer, path, acrAccessToken); | ||
vscode.window.showInformationMessage(`Successfully deleted image ${tag}`); | ||
if (context) { | ||
dockerExplorerProvider.refreshNode(context.parent); | ||
} else { | ||
dockerExplorerProvider.refreshRegistries(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
import { Registry } from "azure-arm-containerregistry/lib/models"; | ||
import { SubscriptionModels } from "azure-arm-resource"; | ||
import * as vscode from "vscode"; | ||
import { dockerExplorerProvider } from '../../dockerExtension'; | ||
import { AzureRegistryNode } from '../../explorer/models/azureRegistryNodes'; | ||
import * as acrTools from '../../utils/Azure/acrTools'; | ||
import { AzureUtilityManager } from '../../utils/azureUtilityManager'; | ||
import { confirmUserIntent, quickPickACRRegistry } from '../utils/quick-pick-azure'; | ||
|
||
/** Delete a registry and all it's associated nested items | ||
* @param context : the AzureRegistryNode the user right clicked on to delete | ||
*/ | ||
export async function deleteAzureRegistry(context?: AzureRegistryNode): Promise<void> { | ||
let registry: Registry; | ||
if (context) { | ||
registry = context.registry; | ||
} else { | ||
registry = await quickPickACRRegistry(false, 'Select the registry you want to delete'); | ||
} | ||
const shouldDelete = await confirmUserIntent(`Are you sure you want to delete ${registry.name} and its associated images?`); | ||
if (shouldDelete) { | ||
let subscription: SubscriptionModels.Subscription = acrTools.getSubscriptionFromRegistry(registry); | ||
let resourceGroup: string = acrTools.getResourceGroupName(registry); | ||
const client = AzureUtilityManager.getInstance().getContainerRegistryManagementClient(subscription); | ||
await client.registries.beginDeleteMethod(resourceGroup, registry.name); | ||
vscode.window.showInformationMessage(`Successfully deleted registry ${registry.name}`); | ||
dockerExplorerProvider.refreshRegistries(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
import { Registry } from "azure-arm-containerregistry/lib/models"; | ||
import * as vscode from "vscode"; | ||
import { dockerExplorerProvider } from '../../dockerExtension'; | ||
import { AzureRepositoryNode } from '../../explorer/models/azureRegistryNodes'; | ||
import * as acrTools from '../../utils/Azure/acrTools'; | ||
import { Repository } from "../../utils/Azure/models/repository"; | ||
import { AzureUtilityManager } from "../../utils/azureUtilityManager"; | ||
import { confirmUserIntent, quickPickACRRegistry, quickPickACRRepository } from '../utils/quick-pick-azure'; | ||
|
||
/** | ||
* function to delete an Azure repository and its associated images | ||
* @param context : if called through right click on AzureRepositoryNode, the node object will be passed in. See azureRegistryNodes.ts for more info | ||
*/ | ||
export async function deleteRepository(context?: AzureRepositoryNode): Promise<void> { | ||
let registry: Registry; | ||
let repoName: string; | ||
|
||
if (context) { | ||
repoName = context.label; | ||
registry = context.registry; | ||
} else { | ||
registry = await quickPickACRRegistry(); | ||
const repository: Repository = await quickPickACRRepository(registry, 'Select the repository you want to delete'); | ||
repoName = repository.name; | ||
} | ||
const shouldDelete = await confirmUserIntent(`Are you sure you want to delete ${repoName} and its associated images? Enter yes to continue: `); | ||
if (shouldDelete) { | ||
const { acrAccessToken } = await acrTools.acquireACRAccessTokenFromRegistry(registry, `repository:${repoName}:*`); | ||
const path = `/v2/_acr/${repoName}/repository`; | ||
await acrTools.sendRequestToRegistry('delete', registry.loginServer, path, acrAccessToken); | ||
vscode.window.showInformationMessage(`Successfully deleted repository ${repoName}`); | ||
if (context) { | ||
dockerExplorerProvider.refreshNode(context.parent); | ||
} else { | ||
dockerExplorerProvider.refreshRegistries(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE.md in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
import { Registry } from 'azure-arm-containerregistry/lib/models'; | ||
import { ResourceGroup } from 'azure-arm-resource/lib/resource/models'; | ||
import { Location, Subscription } from 'azure-arm-resource/lib/subscription/models'; | ||
import * as opn from 'opn'; | ||
import * as vscode from "vscode"; | ||
import { IAzureQuickPickItem, UserCancelledError } from 'vscode-azureextensionui'; | ||
import { skus } from '../../constants' | ||
import { ext } from '../../extensionVariables'; | ||
import { ResourceManagementClient } from '../../node_modules/azure-arm-resource'; | ||
import * as acrTools from '../../utils/Azure/acrTools'; | ||
import { isValidAzureName } from '../../utils/Azure/common'; | ||
import { AzureImage } from "../../utils/Azure/models/image"; | ||
import { Repository } from "../../utils/Azure/models/repository"; | ||
import { AzureUtilityManager } from '../../utils/azureUtilityManager'; | ||
|
||
export async function quickPickACRImage(repository: Repository, prompt?: string): Promise<AzureImage> { | ||
const placeHolder = prompt ? prompt : 'Select image to use'; | ||
const repoImages: AzureImage[] = await acrTools.getImagesByRepository(repository); | ||
const imageListNames = repoImages.map(img => <IAzureQuickPickItem<AzureImage>>{ label: img.tag, data: img }); | ||
let desiredImage = await ext.ui.showQuickPick(imageListNames, { 'canPickMany': false, 'placeHolder': placeHolder }); | ||
return desiredImage.data; | ||
} | ||
|
||
export async function quickPickACRRepository(registry: Registry, prompt?: string): Promise<Repository> { | ||
const placeHolder = prompt ? prompt : 'Select repository to use'; | ||
const repositories: Repository[] = await acrTools.getRepositoriesByRegistry(registry); | ||
const quickPickRepoList = repositories.map(repo => <IAzureQuickPickItem<Repository>>{ label: repo.name, data: repo }); | ||
let desiredRepo = await ext.ui.showQuickPick(quickPickRepoList, { 'canPickMany': false, 'placeHolder': placeHolder }); | ||
return desiredRepo.data; | ||
} | ||
|
||
export async function quickPickACRRegistry(canCreateNew: boolean = false, prompt?: string): Promise<Registry> { | ||
const placeHolder = prompt ? prompt : 'Select registry to use'; | ||
let registries = await AzureUtilityManager.getInstance().getRegistries(); | ||
let quickPickRegList = registries.map(reg => <IAzureQuickPickItem<Registry | undefined>>{ label: reg.name, data: reg }); | ||
|
||
let createNewItem: IAzureQuickPickItem<Registry | undefined> = { label: '+ Create new registry', data: undefined }; | ||
if (canCreateNew) { quickPickRegList.unshift(createNewItem); } | ||
|
||
let desiredReg: IAzureQuickPickItem<Registry | undefined> = await ext.ui.showQuickPick(quickPickRegList, { | ||
'canPickMany': false, | ||
'placeHolder': placeHolder | ||
}); | ||
let registry: Registry; | ||
if (desiredReg === createNewItem) { | ||
registry = <Registry>await vscode.commands.executeCommand("vscode-docker.create-ACR-Registry"); | ||
} else { | ||
registry = desiredReg.data; | ||
} | ||
return registry; | ||
} | ||
|
||
export async function quickPickSKU(): Promise<string> { | ||
const quickPickSkuList = skus.map(sk => <IAzureQuickPickItem<string>>{ label: sk, data: sk }); | ||
let desiredSku: IAzureQuickPickItem<string> = await ext.ui.showQuickPick(quickPickSkuList, { | ||
'canPickMany': false, | ||
'placeHolder': 'Choose a SKU to use' | ||
}); | ||
return desiredSku.data; | ||
} | ||
|
||
export async function quickPickSubscription(): Promise<Subscription> { | ||
const subscriptions = AzureUtilityManager.getInstance().getFilteredSubscriptionList(); | ||
if (subscriptions.length === 0) { | ||
vscode.window.showErrorMessage("You do not have any subscriptions. You can create one in your Azure portal", "Open Portal").then(val => { | ||
if (val === "Open Portal") { | ||
opn('https://portal.azure.com/'); | ||
} | ||
}); | ||
} | ||
if (subscriptions.length === 1) { return subscriptions[0]; } | ||
|
||
let quickPickSubList = subscriptions.map(sub => <IAzureQuickPickItem<Subscription>>{ label: sub.displayName, data: sub }); | ||
let desiredSub = await ext.ui.showQuickPick(quickPickSubList, { | ||
'canPickMany': false, | ||
'placeHolder': 'Select a subscription to use' | ||
}); | ||
return desiredSub.data; | ||
} | ||
|
||
export async function quickPickLocation(subscription: Subscription): Promise<string> { | ||
let locations: Location[] = await AzureUtilityManager.getInstance().getLocationsBySubscription(subscription); | ||
let quickPickLocList = locations.map(loc => <IAzureQuickPickItem<Location>>{ label: loc.displayName, data: loc }); | ||
|
||
quickPickLocList.sort((loc1, loc2): number => { | ||
return loc1.data.displayName.localeCompare(loc2.data.displayName); | ||
}); | ||
|
||
let desiredLocation: IAzureQuickPickItem<Location> = await ext.ui.showQuickPick(quickPickLocList, { | ||
'canPickMany': false, | ||
'placeHolder': 'Select a location to use' | ||
}); | ||
return desiredLocation.label; | ||
} | ||
|
||
export async function quickPickResourceGroup(canCreateNew?: boolean, subscription?: Subscription): Promise<ResourceGroup> { | ||
let resourceGroups = await AzureUtilityManager.getInstance().getResourceGroups(subscription); | ||
let quickPickResourceGroups = resourceGroups.map(res => <IAzureQuickPickItem<ResourceGroup | undefined>>{ label: res.name, data: res }); | ||
|
||
let createNewItem: IAzureQuickPickItem<ResourceGroup | undefined> = { label: '+ Create new resource group', data: undefined }; | ||
if (canCreateNew) { quickPickResourceGroups.unshift(createNewItem); } | ||
|
||
let desiredResGroup: IAzureQuickPickItem<ResourceGroup | undefined> = await ext.ui.showQuickPick(quickPickResourceGroups, { | ||
'canPickMany': false, | ||
'placeHolder': 'Choose a resource group to use' | ||
}); | ||
|
||
let resourceGroup: ResourceGroup; | ||
if (desiredResGroup === createNewItem) { | ||
if (!subscription) { | ||
subscription = await quickPickSubscription(); | ||
} | ||
const loc = await quickPickLocation(subscription); | ||
resourceGroup = await createNewResourceGroup(loc, subscription); | ||
} else { | ||
resourceGroup = desiredResGroup.data; | ||
} | ||
return resourceGroup; | ||
} | ||
|
||
/** Requests confirmation for an action and returns true only in the case that the user types in yes | ||
* @param yesOrNoPrompt Should be a yes or no question | ||
*/ | ||
export async function confirmUserIntent(yesOrNoPrompt: string): Promise<boolean> { | ||
let opt: vscode.InputBoxOptions = { | ||
ignoreFocusOut: true, | ||
placeHolder: 'Yes', | ||
value: 'No', | ||
prompt: yesOrNoPrompt + ' Enter yes to continue' | ||
}; | ||
let answer = await ext.ui.showInputBox(opt); | ||
answer = answer.toLowerCase(); | ||
if (answer === 'yes') { | ||
return answer === 'yes'; | ||
} else { | ||
throw new UserCancelledError(); | ||
} | ||
} | ||
|
||
/*Creates a new resource group within the current subscription */ | ||
async function createNewResourceGroup(loc: string, subscription?: Subscription): Promise<ResourceGroup> { | ||
const resourceGroupClient = AzureUtilityManager.getInstance().getResourceManagementClient(subscription); | ||
|
||
let opt: vscode.InputBoxOptions = { | ||
validateInput: async (value: string) => { return await checkForValidResourcegroupName(value, resourceGroupClient) }, | ||
ignoreFocusOut: false, | ||
prompt: 'New resource group name?' | ||
}; | ||
|
||
let resourceGroupName: string = await ext.ui.showInputBox(opt); | ||
|
||
let newResourceGroup: ResourceGroup = { | ||
name: resourceGroupName, | ||
location: loc, | ||
}; | ||
|
||
return await resourceGroupClient.resourceGroups.createOrUpdate(resourceGroupName, newResourceGroup); | ||
} | ||
|
||
async function checkForValidResourcegroupName(resourceGroupName: string, resourceGroupClient: ResourceManagementClient): Promise<string> { | ||
let check = isValidAzureName(resourceGroupName); | ||
if (!check.isValid) { return check.message; } | ||
let resourceGroupStatus: boolean = await resourceGroupClient.resourceGroups.checkExistence(resourceGroupName); | ||
|
||
if (resourceGroupStatus) { | ||
return 'This resource group is already in use'; | ||
} | ||
return undefined; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.