Merge unified into main (#320)
This commit is contained in:
Родитель
8b5c710682
Коммит
fa9a5cd000
|
@ -19,6 +19,27 @@
|
|||
"NODE_DEBUG": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch Extension(s)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/../vscode-azureresourcegroups",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/../vscode-azureappservice",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/../vscode-azurefunctions",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/../vscode-azurestaticwebapps"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"env": {
|
||||
"DEBUGTELEMETRY": "v",
|
||||
"NODE_DEBUG": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch Extension (webpack)",
|
||||
"type": "extensionHost",
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "vscode-azurevirtualmachines",
|
||||
"version": "0.5.1-alpha.1",
|
||||
"version": "0.5.1-alpha.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vscode-azurevirtualmachines",
|
||||
"version": "0.5.1-alpha.1",
|
||||
"version": "0.5.1-alpha.4",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"dependencies": {
|
||||
"@azure/arm-compute": "^17.1.0",
|
||||
|
@ -14,8 +14,8 @@
|
|||
"@azure/arm-network-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@azure/arm-resources": "^5.0.0",
|
||||
"@azure/arm-resources-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@microsoft/vscode-azext-azureutils": "^0.1.3",
|
||||
"@microsoft/vscode-azext-utils": "^0.1.0",
|
||||
"@microsoft/vscode-azext-azureutils": "^0.2.0",
|
||||
"@microsoft/vscode-azext-utils": "^0.2.4",
|
||||
"fs-extra": "^8.1.0",
|
||||
"open": "^8.0.4",
|
||||
"semver": "^5.7.0",
|
||||
|
@ -610,9 +610,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@microsoft/vscode-azext-azureutils": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.1.3.tgz",
|
||||
"integrity": "sha512-Ht5yPiiUDkt4g8ML0Rn5koL8vcOYusD5rX3BS5dO1AIbGDrRIaqZ+i7ietYoZfl8NCj1UszPd67XVUHFGzHYMA==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.2.0.tgz",
|
||||
"integrity": "sha512-QnWxoOcZGT/dEYk7hHSZEzBNN+3fMOLPb/RaabE42/j/EIAExvU09RW7VPjpkQlrNuN72ZajFuWNT3zN1PPWcg==",
|
||||
"dependencies": {
|
||||
"@azure/arm-resources": "^5.0.0",
|
||||
"@azure/arm-resources-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
|
@ -620,7 +620,7 @@
|
|||
"@azure/arm-storage": "^17.0.0",
|
||||
"@azure/arm-storage-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@azure/ms-rest-js": "^2.2.1",
|
||||
"@microsoft/vscode-azext-utils": "^0.1.0",
|
||||
"@microsoft/vscode-azext-utils": "^0.2.0",
|
||||
"semver": "^5.7.1",
|
||||
"uuid": "^8.3.2",
|
||||
"vscode-nls": "^4.1.1"
|
||||
|
@ -899,9 +899,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@microsoft/vscode-azext-utils": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.1.0.tgz",
|
||||
"integrity": "sha512-HJGh8eXP5pJ+d9Fp+3BLsZ29NK20/86sWQ/OAeKwV2W+J8JiiDJmZvNapC/G3MrMpb8tcD4KthAl0mJDo0o6gw==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.2.4.tgz",
|
||||
"integrity": "sha512-srD+x3JJWZ5tZDM4A39NsuSayO18zsk86AE8oYqt05VTyvs8gnPit3IiQyXiBK/kBZlrk3vqhVpeTkcf22HoUQ==",
|
||||
"dependencies": {
|
||||
"@vscode/extension-telemetry": "^0.4.7",
|
||||
"dayjs": "^1.9.3",
|
||||
|
@ -12605,9 +12605,9 @@
|
|||
}
|
||||
},
|
||||
"@microsoft/vscode-azext-azureutils": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.1.3.tgz",
|
||||
"integrity": "sha512-Ht5yPiiUDkt4g8ML0Rn5koL8vcOYusD5rX3BS5dO1AIbGDrRIaqZ+i7ietYoZfl8NCj1UszPd67XVUHFGzHYMA==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.2.0.tgz",
|
||||
"integrity": "sha512-QnWxoOcZGT/dEYk7hHSZEzBNN+3fMOLPb/RaabE42/j/EIAExvU09RW7VPjpkQlrNuN72ZajFuWNT3zN1PPWcg==",
|
||||
"requires": {
|
||||
"@azure/arm-resources": "^5.0.0",
|
||||
"@azure/arm-resources-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
|
@ -12615,7 +12615,7 @@
|
|||
"@azure/arm-storage": "^17.0.0",
|
||||
"@azure/arm-storage-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@azure/ms-rest-js": "^2.2.1",
|
||||
"@microsoft/vscode-azext-utils": "^0.1.0",
|
||||
"@microsoft/vscode-azext-utils": "^0.2.0",
|
||||
"semver": "^5.7.1",
|
||||
"uuid": "^8.3.2",
|
||||
"vscode-nls": "^4.1.1"
|
||||
|
@ -12863,9 +12863,9 @@
|
|||
}
|
||||
},
|
||||
"@microsoft/vscode-azext-utils": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.1.0.tgz",
|
||||
"integrity": "sha512-HJGh8eXP5pJ+d9Fp+3BLsZ29NK20/86sWQ/OAeKwV2W+J8JiiDJmZvNapC/G3MrMpb8tcD4KthAl0mJDo0o6gw==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.2.4.tgz",
|
||||
"integrity": "sha512-srD+x3JJWZ5tZDM4A39NsuSayO18zsk86AE8oYqt05VTyvs8gnPit3IiQyXiBK/kBZlrk3vqhVpeTkcf22HoUQ==",
|
||||
"requires": {
|
||||
"@vscode/extension-telemetry": "^0.4.7",
|
||||
"dayjs": "^1.9.3",
|
||||
|
|
129
package.json
129
package.json
|
@ -2,7 +2,7 @@
|
|||
"name": "vscode-azurevirtualmachines",
|
||||
"displayName": "Azure Virtual Machines",
|
||||
"description": "%extension.description%",
|
||||
"version": "0.5.1-alpha.1",
|
||||
"version": "0.5.1-alpha.4",
|
||||
"publisher": "ms-azuretools",
|
||||
"icon": "resources/azure-vm.png",
|
||||
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
|
||||
|
@ -36,23 +36,35 @@
|
|||
],
|
||||
"preview": true,
|
||||
"activationEvents": [
|
||||
"azureVirtualMachines.selectSubscriptions",
|
||||
"onCommand:azureVirtualMachines.addSshKey",
|
||||
"onCommand:azureVirtualMachines.createVirtualMachine",
|
||||
"onCommand:azureVirtualMachines.createVirtualMachineAdvanced",
|
||||
"onCommand:azureVirtualMachines.deleteVirtualMachine",
|
||||
"onCommand:azureVirtualMachines.loadMore",
|
||||
"onCommand:azureVirtualMachines.openInPortal",
|
||||
"onCommand:azureVirtualMachines.refresh",
|
||||
"onCommand:azureVirtualMachines.reportIssue",
|
||||
"onCommand:azureVirtualMachines.restartVirtualMachine",
|
||||
"onCommand:azureVirtualMachines.startVirtualMachine",
|
||||
"onCommand:azureVirtualMachines.stopVirtualMachine",
|
||||
"onCommand:azureVirtualMachines.viewProperties",
|
||||
"onView:azVmTree"
|
||||
"onCommand:azureVirtualMachines.viewProperties"
|
||||
],
|
||||
"main": "./main.js",
|
||||
"contributes": {
|
||||
"x-azResources": {
|
||||
"activation": {
|
||||
"onFetch": [
|
||||
"microsoft.compute/virtualmachines"
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "azureVirtualMachines.createVirtualMachine",
|
||||
"title": "%azureVirtualMachines.createVirtualMachine%",
|
||||
"type": "microsoft.compute/virtualmachines",
|
||||
"detail": "%azureVirtualMachines.createVirtualMachineDetail%"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "azureVirtualMachines.addSshKey",
|
||||
|
@ -80,27 +92,11 @@
|
|||
"title": "%azureVirtualMachines.deleteVirtualMachine%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.loadMore",
|
||||
"title": "%azureVirtualMachines.loadMore%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.openInPortal",
|
||||
"title": "%azureVirtualMachines.openInPortal%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.openInRemoteSsh",
|
||||
"title": "%azureVirtualMachines.openInRemoteSsh%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.refresh",
|
||||
"title": "%azureVirtualMachines.refresh%",
|
||||
"category": "Azure Virtual Machines",
|
||||
"icon": "$(refresh)"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.reportIssue",
|
||||
"title": "%azureVirtualMachines.reportIssue%",
|
||||
|
@ -111,11 +107,6 @@
|
|||
"title": "%azureVirtualMachines.restartVirtualMachine%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.selectSubscriptions",
|
||||
"title": "Select Subscriptions...",
|
||||
"icon": "$(filter)"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.startVirtualMachine",
|
||||
"title": "%azureVirtualMachines.startVirtualMachine%",
|
||||
|
@ -125,121 +116,57 @@
|
|||
"command": "azureVirtualMachines.stopVirtualMachine",
|
||||
"title": "%azureVirtualMachines.stopVirtualMachine%",
|
||||
"category": "Azure Virtual Machines"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.viewProperties",
|
||||
"title": "%azureVirtualMachines.viewProperties%",
|
||||
"category": "Azure Virtual Machines"
|
||||
}
|
||||
],
|
||||
"views": {
|
||||
"azure": [
|
||||
{
|
||||
"id": "azVmTree",
|
||||
"name": "Virtual Machines"
|
||||
}
|
||||
]
|
||||
},
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "azureVirtualMachines.createVirtualMachine",
|
||||
"when": "view == azVmTree",
|
||||
"group": "navigation@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.refresh",
|
||||
"when": "view == azVmTree",
|
||||
"group": "navigation@2"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "azureVirtualMachines.selectSubscriptions",
|
||||
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
|
||||
"group": "inline"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.createVirtualMachine",
|
||||
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /azureResourceTypeGroup.*microsoft.compute/virtualmachines/",
|
||||
"group": "1@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.createVirtualMachineAdvanced",
|
||||
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /azureResourceTypeGroup.*microsoft.compute/virtualmachines/",
|
||||
"group": "1@2"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.openInPortal",
|
||||
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
|
||||
"group": "2@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.refresh",
|
||||
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
|
||||
"group": "3@2"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.copyIpAddress",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /VirtualMachine$/",
|
||||
"group": "1@2"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.viewProperties",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"group": "3@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.openInPortal",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"group": "3@2"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.startVirtualMachine",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /VirtualMachine$/",
|
||||
"group": "2@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.restartVirtualMachine",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /VirtualMachine$/",
|
||||
"group": "2@2"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.stopVirtualMachine",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /VirtualMachine$/",
|
||||
"group": "2@3"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.deleteVirtualMachine",
|
||||
"when": "view == azVmTree && viewItem =~ /VirtualMachine$/",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /VirtualMachine$/",
|
||||
"group": "2@4"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.openInRemoteSsh",
|
||||
"when": "view == azVmTree && viewItem == linuxVirtualMachine",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /linuxVirtualMachine/",
|
||||
"group": "1@1"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.addSshKey",
|
||||
"when": "view == azVmTree && viewItem == linuxVirtualMachine",
|
||||
"when": "view == azureResourceGroups && viewItem =~ /linuxVirtualMachine/",
|
||||
"group": "1@3"
|
||||
}
|
||||
],
|
||||
"explorer/context": [],
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "azureVirtualMachines.loadMore",
|
||||
"when": "never"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.refresh",
|
||||
"when": "never"
|
||||
},
|
||||
{
|
||||
"command": "azureVirtualMachines.selectSubscriptions",
|
||||
"when": "never"
|
||||
}
|
||||
],
|
||||
"editor/context": []
|
||||
},
|
||||
"configuration": [
|
||||
|
@ -306,8 +233,8 @@
|
|||
"@azure/arm-network-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@azure/arm-resources": "^5.0.0",
|
||||
"@azure/arm-resources-profile-2020-09-01-hybrid": "^1.0.0",
|
||||
"@microsoft/vscode-azext-azureutils": "^0.1.3",
|
||||
"@microsoft/vscode-azext-utils": "^0.1.0",
|
||||
"@microsoft/vscode-azext-azureutils": "^0.2.0",
|
||||
"@microsoft/vscode-azext-utils": "^0.2.4",
|
||||
"fs-extra": "^8.1.0",
|
||||
"open": "^8.0.4",
|
||||
"semver": "^5.7.0",
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
"azureVirtualMachines.copyIpAddress": "Copy IP Address",
|
||||
"azureVirtualMachines.createVirtualMachine": "Create Virtual Machine...",
|
||||
"azureVirtualMachines.createVirtualMachineAdvanced": "Create Virtual Machine... (Advanced)",
|
||||
"azureVirtualMachines.createVirtualMachineDetail": "For any workload or a remote dev box.",
|
||||
"azureVirtualMachines.deleteVirtualMachine": "Delete...",
|
||||
"azureVirtualMachines.enableOutputTimestamps": "Prepends each line displayed in the output channel with a timestamp.",
|
||||
"azureVirtualMachines.loadMore": "Load More",
|
||||
"azureVirtualMachines.openInPortal": "Open in Portal",
|
||||
"azureVirtualMachines.openInRemoteSsh": "Connect to Host via Remote SSH",
|
||||
"azureVirtualMachines.promptForPassphrase": "Prompts for a passphrase when creating a Linux Azure Virtual Machine.",
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ComputeManagementClient, VirtualMachine } from "@azure/arm-compute";
|
||||
import { callWithTelemetryAndErrorHandling, IActionContext, ISubscriptionContext, nonNullProp } from "@microsoft/vscode-azext-utils";
|
||||
import { AppResource, AppResourceResolver } from "@microsoft/vscode-azext-utils/hostapi";
|
||||
import { ResolvedVirtualMachine, VirtualMachineTreeItem } from './tree/VirtualMachineTreeItem';
|
||||
import { createComputeClient } from "./utils/azureClients";
|
||||
import { getResourceGroupFromId } from "./utils/azureUtils";
|
||||
|
||||
export class VirtualMachineResolver implements AppResourceResolver {
|
||||
|
||||
// possibly pass down the full tree item, but for now try to get away with just the AppResource
|
||||
public async resolveResource(subContext: ISubscriptionContext, resource: AppResource): Promise<ResolvedVirtualMachine | null> {
|
||||
return await callWithTelemetryAndErrorHandling('resolveResource', async (context: IActionContext) => {
|
||||
try {
|
||||
const client: ComputeManagementClient = await createComputeClient([context, subContext]);
|
||||
const vm: VirtualMachine = await client.virtualMachines.get(getResourceGroupFromId(nonNullProp(resource, 'id')), nonNullProp(resource, 'name'))
|
||||
const instanceView = await client.virtualMachines.instanceView(getResourceGroupFromId(nonNullProp(vm, 'id')), nonNullProp(vm, 'name'));
|
||||
|
||||
return new VirtualMachineTreeItem(subContext, { ...resource, ...vm }, instanceView);
|
||||
} catch (e) {
|
||||
console.error({ ...context, ...subContext });
|
||||
throw e;
|
||||
}
|
||||
}) ?? null;
|
||||
}
|
||||
|
||||
public matchesResource(resource: AppResource): boolean {
|
||||
return resource.type.toLowerCase() === 'microsoft.compute/virtualmachines';
|
||||
}
|
||||
}
|
|
@ -7,21 +7,28 @@ import { ComputeManagementClient, VirtualMachine, VirtualMachineExtension } from
|
|||
import { IActionContext, parseError } from "@microsoft/vscode-azext-utils";
|
||||
import * as fse from "fs-extra";
|
||||
import { ProgressLocation, Uri, window } from "vscode";
|
||||
import { sshFsPath } from "../constants";
|
||||
import { sshFsPath, vmFilter } from "../constants";
|
||||
import { ext } from "../extensionVariables";
|
||||
import { localize } from "../localize";
|
||||
import { VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../utils/azureClients";
|
||||
import { nonNullValueAndProp } from "../utils/nonNull";
|
||||
import { configureSshConfig } from "../utils/sshUtils";
|
||||
|
||||
export async function addSshKey(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function addSshKey(context: IActionContext, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.linuxContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.linuxContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
const vm: VirtualMachine = node.virtualMachine;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node.subscription]);
|
||||
const vm: VirtualMachine = node.data;
|
||||
|
||||
const sshPublicKey: Uri = (await context.ui.showOpenDialog({
|
||||
defaultUri: Uri.file(sshFsPath),
|
||||
|
|
|
@ -11,9 +11,9 @@ export async function revealTreeItem(resourceId: string): Promise<void> {
|
|||
// https://github.com/microsoft/vscode-azurevirtualmachines/issues/70
|
||||
resourceId = resourceId.toLowerCase();
|
||||
|
||||
const node: AzExtTreeItem | undefined = await ext.tree.findTreeItem(resourceId, { ...context, loadAll: true });
|
||||
const node: AzExtTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(resourceId, { ...context, loadAll: true });
|
||||
if (node) {
|
||||
await ext.treeView.reveal(node, { select: true, focus: true, expand: true });
|
||||
await ext.rgApi.appResourceTreeView.reveal(node, { select: true, focus: true, expand: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,13 +5,21 @@
|
|||
|
||||
import { IActionContext } from '@microsoft/vscode-azext-utils';
|
||||
import * as vscode from 'vscode';
|
||||
import { vmFilter } from '../constants';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { localize } from '../localize';
|
||||
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
|
||||
export async function copyIpAddress(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function copyIpAddress(context: IActionContext, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.allOSContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
await vscode.env.clipboard.writeText(await node.getIpAddress(context));
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
import { ImageReference, OperatingSystemTypes, VirtualMachine, VirtualMachineSizeTypes } from '@azure/arm-compute';
|
||||
import { NetworkInterface, NetworkSecurityGroup, PublicIPAddress, Subnet, VirtualNetwork } from '@azure/arm-network';
|
||||
import { IResourceGroupWizardContext } from '@microsoft/vscode-azext-azureutils';
|
||||
import { ExecuteActivityContext } from '@microsoft/vscode-azext-utils';
|
||||
|
||||
export interface IVirtualMachineWizardContext extends IResourceGroupWizardContext {
|
||||
export interface IVirtualMachineWizardContext extends IResourceGroupWizardContext, ExecuteActivityContext {
|
||||
advancedCreation?: boolean;
|
||||
/**
|
||||
* The newly created Virtual Machine
|
||||
* This will be defined after `VirtualMachineCreateStep.execute` occurs.
|
||||
|
|
|
@ -82,6 +82,13 @@ export class VirtualMachineCreateStep extends AzureWizardExecuteStep<IVirtualMac
|
|||
ext.outputChannel.appendLog(creatingVmDetails);
|
||||
progress.report({ message: creatingVm });
|
||||
context.virtualMachine = await computeClient.virtualMachines.beginCreateOrUpdateAndWait(rgName, vmName, virtualMachineProps);
|
||||
|
||||
context.activityResult = {
|
||||
id: nonNullProp(context.virtualMachine, 'id'),
|
||||
name: nonNullProp(context.virtualMachine, 'name'),
|
||||
type: nonNullProp(context.virtualMachine, 'type'),
|
||||
};
|
||||
|
||||
ext.outputChannel.appendLog(createdVm);
|
||||
|
||||
// Note: intentionally not waiting for the result of this before returning
|
||||
|
|
|
@ -3,19 +3,95 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IActionContext, ICreateChildImplContext } from "@microsoft/vscode-azext-utils";
|
||||
import { VirtualMachine, VirtualMachineSizeTypes } from "@azure/arm-compute";
|
||||
import { LocationListStep, ResourceGroupCreateStep, SubscriptionTreeItemBase, VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
|
||||
import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, ICreateChildImplContext, nonNullProp } from "@microsoft/vscode-azext-utils";
|
||||
import { ext } from "../../extensionVariables";
|
||||
import { SubscriptionTreeItem } from '../../tree/SubscriptionTreeItem';
|
||||
import { localize } from "../../localize";
|
||||
import { VirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { createActivityContext } from "../../utils/activityUtils";
|
||||
import { configureSshConfig } from "../../utils/sshUtils";
|
||||
import { getAvailableVMLocations } from "./getAvailableVMLocations";
|
||||
import { ImageListStep } from "./ImageListStep";
|
||||
import { IVirtualMachineWizardContext } from "./IVirtualMachineWizardContext";
|
||||
import { NetworkInterfaceCreateStep } from "./NetworkInterfaceCreateStep";
|
||||
import { NetworkSecurityGroupCreateStep } from "./NetworkSecurityGroupCreateStep";
|
||||
import { OSListStep } from "./OSListStep";
|
||||
import { PassphrasePromptStep } from "./PassphrasePromptStep";
|
||||
import { PublicIpCreateStep } from "./PublicIpCreateStep";
|
||||
import { SubnetCreateStep } from "./SubnetCreateStep";
|
||||
import { UsernamePromptStep } from "./UsernamePromptStep";
|
||||
import { VirtualMachineCreateStep } from "./VirtualMachineCreateStep";
|
||||
import { VirtualMachineNameStep } from "./VirtualMachineNameStep";
|
||||
import { VirtualNetworkCreateStep } from "./VirtualNetworkCreateStep";
|
||||
|
||||
export async function createVirtualMachine(context: IActionContext & Partial<ICreateChildImplContext>, node?: SubscriptionTreeItem | undefined): Promise<VirtualMachineTreeItem> {
|
||||
export async function createVirtualMachine(context: IActionContext & Partial<ICreateChildImplContext>, node?: SubscriptionTreeItemBase | undefined): Promise<VirtualMachineTreeItem> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<SubscriptionTreeItem>(SubscriptionTreeItem.contextValue, context);
|
||||
node = await ext.rgApi.appResourceTree.showTreeItemPicker<SubscriptionTreeItemBase>(SubscriptionTreeItemBase.contextValue, context);
|
||||
}
|
||||
|
||||
return await node.createChild(context);
|
||||
const size: VirtualMachineSizeTypes = node.subscription.isCustomCloud ? 'Standard_DS1_v2' : 'Standard_D2s_v3';
|
||||
const wizardContext: IVirtualMachineWizardContext = Object.assign(context, node.subscription, {
|
||||
addressPrefix: '10.1.0.0/24',
|
||||
size,
|
||||
includeExtendedLocations: true,
|
||||
...(await createActivityContext())
|
||||
});
|
||||
|
||||
const computeProvider: string = 'Microsoft.Compute';
|
||||
LocationListStep.setLocationSubset(wizardContext, getAvailableVMLocations(wizardContext), computeProvider);
|
||||
|
||||
// By default, only prompt for VM and Location. A new RG is made for every VM
|
||||
const promptSteps: AzureWizardPromptStep<IVirtualMachineWizardContext>[] = [];
|
||||
const executeSteps: AzureWizardExecuteStep<IVirtualMachineWizardContext>[] = [];
|
||||
|
||||
promptSteps.push(new VirtualMachineNameStep());
|
||||
promptSteps.push(new OSListStep());
|
||||
const imageListStep = new ImageListStep();
|
||||
promptSteps.push(imageListStep);
|
||||
|
||||
promptSteps.push(new UsernamePromptStep());
|
||||
promptSteps.push(new PassphrasePromptStep());
|
||||
LocationListStep.addStep(wizardContext, promptSteps);
|
||||
|
||||
executeSteps.push(new ResourceGroupCreateStep());
|
||||
executeSteps.push(new PublicIpCreateStep());
|
||||
executeSteps.push(new VirtualNetworkCreateStep());
|
||||
executeSteps.push(new SubnetCreateStep());
|
||||
executeSteps.push(new NetworkSecurityGroupCreateStep());
|
||||
executeSteps.push(new NetworkInterfaceCreateStep());
|
||||
executeSteps.push(new VirtualMachineCreateStep());
|
||||
executeSteps.push(new VerifyProvidersStep([computeProvider, 'Microsoft.Network']));
|
||||
|
||||
const title: string = 'Create new virtual machine';
|
||||
|
||||
if (!context.advancedCreation) {
|
||||
// for basic create, default to image Ubuntu 18.04 LTS
|
||||
wizardContext.os = 'Linux';
|
||||
wizardContext.imageTask = imageListStep.getDefaultImageReference(wizardContext);
|
||||
wizardContext.adminUsername = 'azureuser';
|
||||
}
|
||||
|
||||
const wizard: AzureWizard<IVirtualMachineWizardContext> = new AzureWizard(wizardContext, { promptSteps, executeSteps, title });
|
||||
|
||||
await wizard.prompt();
|
||||
|
||||
wizardContext.newResourceGroupName = await wizardContext.relatedNameTask;
|
||||
|
||||
wizardContext.activityTitle = localize('createVirtualMachine', 'Create virtual machine "{0}"', nonNullProp(wizardContext, 'newVirtualMachineName'));
|
||||
|
||||
await wizard.execute();
|
||||
|
||||
const virtualMachine: VirtualMachine = nonNullProp(wizardContext, 'virtualMachine');
|
||||
|
||||
const newVm: VirtualMachineTreeItem = new VirtualMachineTreeItem(node.subscription, virtualMachine, undefined /* assume all newly created VMs are running */);
|
||||
if (newVm.contextValuesToAdd.includes(VirtualMachineTreeItem.linuxContextValue)) {
|
||||
await configureSshConfig(context, newVm, `~/.ssh/${wizardContext.sshKeyName}`);
|
||||
}
|
||||
|
||||
return newVm;
|
||||
}
|
||||
|
||||
export async function createVirtualMachineAdvanced(context: IActionContext, node?: SubscriptionTreeItem | undefined): Promise<VirtualMachineTreeItem> {
|
||||
export async function createVirtualMachineAdvanced(context: IActionContext, node?: SubscriptionTreeItemBase | undefined): Promise<VirtualMachineTreeItem> {
|
||||
return await createVirtualMachine({ ...context, advancedCreation: true }, node);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,16 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
|
||||
import { vmFilter } from '../constants';
|
||||
import { ext } from '../extensionVariables';
|
||||
|
||||
export async function deleteNode(context: IActionContext, expectedContextValue: string | RegExp, node?: AzExtTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker(expectedContextValue, { ...context, suppressCreatePick: true });
|
||||
node = await ext.rgApi.pickAppResource({ ...context, suppressCreatePick: true }, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: expectedContextValue
|
||||
});
|
||||
}
|
||||
|
||||
await node.deleteTreeItem(context);
|
||||
await node?.deleteTreeItem(context);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep, DialogResponses, nonNullProp } from "@microsoft/vscode-azext-utils";
|
||||
import { virtualMachineLabel } from "../../constants";
|
||||
import { localize } from "../../localize";
|
||||
import { IDeleteChildImplContext } from "./deleteConstants";
|
||||
|
||||
export class ConfirmDeleteStep extends AzureWizardPromptStep<IDeleteChildImplContext> {
|
||||
public async prompt(context: IDeleteChildImplContext): Promise<void> {
|
||||
const resourcesToDelete = nonNullProp(context, 'resourcesToDelete');
|
||||
|
||||
const multiDelete: boolean = resourcesToDelete.length > 1;
|
||||
const resourceList: string = resourcesToDelete.map(r => `"${r.resourceName}"`).join(', ');
|
||||
context.resourceList = resourceList;
|
||||
|
||||
const confirmMessage: string = multiDelete ? localize('multiDeleteConfirmation', 'Are you sure you want to delete the following resources: {0}?', resourceList) :
|
||||
localize('deleteConfirmation', 'Are you sure you want to delete {0} "{1}"?', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
await context.ui.showWarningMessage(confirmMessage, { modal: true }, DialogResponses.deleteResponse);
|
||||
|
||||
const deleteVm = resourcesToDelete.some(r => r.resourceType === virtualMachineLabel);
|
||||
context.telemetry.properties.numOfResources = resourcesToDelete.length.toString();
|
||||
context.telemetry.properties.deleteVm = String(deleteVm);
|
||||
}
|
||||
|
||||
|
||||
public shouldPrompt(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzExtErrorButton, AzureWizardExecuteStep, nonNullProp } from "@microsoft/vscode-azext-utils";
|
||||
import { Progress } from "vscode";
|
||||
import { viewOutput, virtualMachineLabel } from "../../constants";
|
||||
import { ext } from "../../extensionVariables";
|
||||
import { localize } from "../../localize";
|
||||
import { deleteAllResources } from "./deleteAllResources";
|
||||
import { IDeleteChildImplContext, ResourceToDelete } from "./deleteConstants";
|
||||
|
||||
export class DeleteVirtualMachineStep extends AzureWizardExecuteStep<IDeleteChildImplContext> {
|
||||
public priority: number = 100;
|
||||
|
||||
public async execute(context: IDeleteChildImplContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined; }>): Promise<void> {
|
||||
|
||||
const node = nonNullProp(context, 'node');
|
||||
const resourcesToDelete = nonNullProp(context, 'resourcesToDelete');
|
||||
const multiDelete: boolean = resourcesToDelete.length > 1;
|
||||
|
||||
const message: string = multiDelete ? localize('deleteVirtualMachine', 'Delete {0}...', context.resourceList) :
|
||||
localize('Deleting', 'Delete {0} "{1}"...', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
progress.report({ message });
|
||||
|
||||
if (multiDelete) {
|
||||
ext.outputChannel.appendLog(message);
|
||||
}
|
||||
|
||||
const failedResources: ResourceToDelete[] = await deleteAllResources(context, node.subscription, node.resourceGroup, resourcesToDelete);
|
||||
const failedResourceList: string = failedResources.map(r => `"${r.resourceName}"`).join(', ');
|
||||
|
||||
const messageDeleteWithErrors: string = localize('messageDeleteWithErrors', 'Failed to delete the following resources: {0}.', failedResourceList);
|
||||
|
||||
const deleteSucceeded: string = multiDelete ? localize('DeleteSucceeded', 'Successfully deleted {0}.', context.resourceList) :
|
||||
localize('DeleteSucceeded', 'Successfully deleted {0} "{1}".', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
// single resources are already displayed in the output channel
|
||||
if (multiDelete) {
|
||||
ext.outputChannel.appendLog(failedResources.length > 0 ? messageDeleteWithErrors : deleteSucceeded);
|
||||
}
|
||||
|
||||
if (failedResources.length > 0) {
|
||||
context.telemetry.properties.failedResources = failedResources.length.toString();
|
||||
// if the vm failed to delete or was not being deleted, we want to throw an error to make sure that the node is not removed from the tree
|
||||
if (failedResources.some(r => r.resourceType === virtualMachineLabel) || !context.deleteVm) {
|
||||
// tslint:disable-next-line: no-floating-promises
|
||||
const viewOutputAzureButton: AzExtErrorButton = { title: viewOutput.title, callback: async (): Promise<void> => ext.outputChannel.show() };
|
||||
context.errorHandling.buttons = [viewOutputAzureButton];
|
||||
throw new Error(messageDeleteWithErrors);
|
||||
}
|
||||
|
||||
void context.ui.showWarningMessage(`${messageDeleteWithErrors} Check the [output channel](command:${ext.prefix}.showOutputChannel) for more information.`);
|
||||
}
|
||||
}
|
||||
|
||||
public shouldExecute(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ComputeManagementClient } from "@azure/arm-compute";
|
||||
import { AzureWizardPromptStep, IActionContext, IAzureQuickPickItem, nonNullProp, UserCancelledError } from "@microsoft/vscode-azext-utils";
|
||||
import { virtualMachineLabel } from "../../constants";
|
||||
import { localize } from "../../localize";
|
||||
import { ResolvedVirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../../utils/azureClients";
|
||||
import { IDeleteChildImplContext, ResourceToDelete } from "./deleteConstants";
|
||||
import { getResourcesAssociatedToVm } from "./getResourcesAssociatedToVm";
|
||||
|
||||
export class SelectResourcesToDeleteStep extends AzureWizardPromptStep<IDeleteChildImplContext> {
|
||||
public async prompt(context: IDeleteChildImplContext): Promise<void> {
|
||||
const resourcesToDelete: IAzureQuickPickItem<ResourceToDelete>[] = await context.ui.showQuickPick(getQuickPicks(context, nonNullProp(context, 'node')), { placeHolder: localize('selectResources', 'Select resources to delete'), canPickMany: true });
|
||||
if (resourcesToDelete.length === 0) {
|
||||
// if nothing is checked, it should be considered a cancel
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
|
||||
context.resourcesToDelete = resourcesToDelete.map((r) => r.data);
|
||||
}
|
||||
|
||||
public shouldPrompt(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function getQuickPicks(context: IActionContext, node: ResolvedVirtualMachineTreeItem): Promise<IAzureQuickPickItem<ResourceToDelete>[]> {
|
||||
const resources: ResourceToDelete[] = await getResourcesAssociatedToVm(context, node);
|
||||
|
||||
// add the vm to the resources to delete since it is not an associated resource
|
||||
resources.unshift({
|
||||
resourceName: node.name, resourceType: virtualMachineLabel, picked: true,
|
||||
deleteMethod: async (): Promise<void> => {
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node?.subscription]);
|
||||
await computeClient.virtualMachines.beginDeleteAndWait(node.resourceGroup, node.name);
|
||||
}
|
||||
});
|
||||
|
||||
return resources.map(resource => {
|
||||
return { label: resource.resourceName, description: resource.resourceType, data: resource, picked: resource.picked };
|
||||
});
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { ExecuteActivityContext, IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { ResolvedVirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
|
||||
export type ResourceToDelete = {
|
||||
resourceName: string;
|
||||
|
@ -12,20 +13,21 @@ export type ResourceToDelete = {
|
|||
deleteMethod(): Promise<void>; // an async wrapper for the deleteMethod to be called
|
||||
};
|
||||
|
||||
export interface IDeleteChildImplContext extends IActionContext {
|
||||
|
||||
export interface IDeleteChildImplContext extends IActionContext, ExecuteActivityContext {
|
||||
/**
|
||||
* Resources to be deleted
|
||||
*/
|
||||
resourcesToDelete: ResourceToDelete[];
|
||||
resourcesToDelete?: ResourceToDelete[];
|
||||
|
||||
/**
|
||||
* String of resources that are being deleted used for output
|
||||
*/
|
||||
resourceList: string;
|
||||
resourceList?: string;
|
||||
|
||||
/**
|
||||
* Flag to determine if the virtual machine is in the resourcesToDelete
|
||||
*/
|
||||
deleteVm?: boolean;
|
||||
|
||||
node?: ResolvedVirtualMachineTreeItem;
|
||||
}
|
||||
|
|
|
@ -3,64 +3,21 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ComputeManagementClient } from "@azure/arm-compute";
|
||||
import { DialogResponses, IActionContext, IAzureQuickPickItem, UserCancelledError } from "@microsoft/vscode-azext-utils";
|
||||
import { virtualMachineLabel } from "../../constants";
|
||||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { vmFilter } from "../../constants";
|
||||
import { ext } from "../../extensionVariables";
|
||||
import { localize } from "../../localize";
|
||||
import { VirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../../utils/azureClients";
|
||||
import { IDeleteChildImplContext, ResourceToDelete } from "./deleteConstants";
|
||||
import { getResourcesAssociatedToVm } from "./getResourcesAssociatedToVm";
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { IDeleteChildImplContext } from "./deleteConstants";
|
||||
|
||||
export async function deleteVirtualMachine(context: IActionContext & Partial<IDeleteChildImplContext>, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function deleteVirtualMachine(context: IActionContext & Partial<IDeleteChildImplContext>, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, { ...context, suppressCreatePick: true });
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>({ ...context, suppressCreatePick: true }, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.allOSContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
context.telemetry.properties.cancelStep = 'prompt';
|
||||
const resourcesToDelete: IAzureQuickPickItem<ResourceToDelete>[] = await context.ui.showQuickPick(getQuickPicks(context, node), { placeHolder: localize('selectResources', 'Select resources to delete'), canPickMany: true });
|
||||
if (resourcesToDelete.length === 0) {
|
||||
// if nothing is checked, it should be considered a cancel
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
|
||||
context.telemetry.properties.cancelStep = undefined;
|
||||
|
||||
const multiDelete: boolean = resourcesToDelete.length > 1;
|
||||
const resourceList: string = resourcesToDelete.map(r => `"${r.data.resourceName}"`).join(', ');
|
||||
const confirmMessage: string = multiDelete ? localize('multiDeleteConfirmation', 'Are you sure you want to delete the following resources: {0}?', resourceList) :
|
||||
localize('deleteConfirmation', 'Are you sure you want to delete {0} "{1}"?', resourcesToDelete[0].data.resourceType, resourcesToDelete[0].data.resourceName);
|
||||
|
||||
context.telemetry.properties.cancelStep = 'confirmation';
|
||||
await context.ui.showWarningMessage(confirmMessage, { modal: true }, DialogResponses.deleteResponse);
|
||||
context.deleteVm = resourcesToDelete.some(v => v.data.resourceType === virtualMachineLabel);
|
||||
|
||||
context.telemetry.properties.cancelStep = undefined;
|
||||
|
||||
context.telemetry.properties.numOfResources = resourcesToDelete.length.toString();
|
||||
context.telemetry.properties.deleteVm = String(context.deleteVm);
|
||||
|
||||
context.resourcesToDelete = resourcesToDelete.map(r => r.data);
|
||||
context.resourceList = resourceList;
|
||||
// context.telemetry.properties.numOfResources = resourcesToDelete.length.toString();
|
||||
// context.telemetry.properties.deleteVm = String(context.deleteVm);
|
||||
|
||||
await node.deleteTreeItem(context);
|
||||
|
||||
}
|
||||
|
||||
async function getQuickPicks(context: IActionContext, node: VirtualMachineTreeItem): Promise<IAzureQuickPickItem<ResourceToDelete>[]> {
|
||||
const resources: ResourceToDelete[] = await getResourcesAssociatedToVm(context, node);
|
||||
|
||||
// add the vm to the resources to delete since it is not an associated resource
|
||||
resources.unshift({
|
||||
resourceName: node.name, resourceType: virtualMachineLabel, picked: true,
|
||||
deleteMethod: async (): Promise<void> => {
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
await computeClient.virtualMachines.beginDeleteAndWait(node.resourceGroup, node.name);
|
||||
}
|
||||
});
|
||||
|
||||
return resources.map(resource => {
|
||||
return { label: resource.resourceName, description: resource.resourceType, data: resource, picked: resource.picked };
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ import { NetworkInterface, NetworkManagementClient, Subnet } from "@azure/arm-ne
|
|||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { networkInterfaceLabel, virtualNetworkLabel } from "../../constants";
|
||||
import { localize } from "../../localize";
|
||||
import { VirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { ResolvedVirtualMachineTreeItem } from "../../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient, createNetworkClient } from "../../utils/azureClients";
|
||||
import { getNameFromId, getResourceGroupFromId } from "../../utils/azureUtils";
|
||||
import { ResourceToDelete } from "./deleteConstants";
|
||||
|
||||
export async function getResourcesAssociatedToVm(context: IActionContext, node: VirtualMachineTreeItem): Promise<ResourceToDelete[]> {
|
||||
export async function getResourcesAssociatedToVm(context: IActionContext, node: ResolvedVirtualMachineTreeItem): Promise<ResourceToDelete[]> {
|
||||
|
||||
const associatedResources: ResourceToDelete[] = [];
|
||||
|
||||
|
@ -27,7 +27,7 @@ export async function getResourcesAssociatedToVm(context: IActionContext, node:
|
|||
|
||||
}
|
||||
|
||||
const networkClient: NetworkManagementClient = await createNetworkClient([context, node]);
|
||||
const networkClient: NetworkManagementClient = await createNetworkClient([context, node?.subscription]);
|
||||
for (const networkRef of networkReferences) {
|
||||
// if we fail to get a resource, we keep trying to get all associated resources we can rather than erroring out
|
||||
try {
|
||||
|
@ -86,7 +86,7 @@ export async function getResourcesAssociatedToVm(context: IActionContext, node:
|
|||
if (node.data.storageProfile?.osDisk?.managedDisk?.id) {
|
||||
const diskName: string = getNameFromId(node.data.storageProfile.osDisk.managedDisk.id);
|
||||
const diskRg: string = getResourceGroupFromId(node.data.storageProfile.osDisk.managedDisk.id);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node?.subscription]);
|
||||
associatedResources.push({
|
||||
resourceName: diskName, resourceType: localize('disk', 'disk'),
|
||||
deleteMethod: async (): Promise<void> => { await computeClient.disks.beginDeleteAndWait(diskRg, diskName); }
|
||||
|
|
|
@ -1,17 +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 { openInPortal as uiOpenInPortal } from '@microsoft/vscode-azext-azureutils';
|
||||
import { AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
|
||||
export async function openInPortal(context: IActionContext, node?: AzExtTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<AzExtTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
}
|
||||
|
||||
await uiOpenInPortal(node, node.fullId)
|
||||
}
|
|
@ -8,16 +8,19 @@ import * as fse from 'fs-extra';
|
|||
import { join } from 'path';
|
||||
import * as SSHConfig from 'ssh-config';
|
||||
import { commands } from 'vscode';
|
||||
import { sshFsPath } from '../constants';
|
||||
import { sshFsPath, vmFilter } from '../constants';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { localize } from '../localize';
|
||||
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
import { addSshKey } from './addSshKey';
|
||||
import { verifyRemoteSshExtension } from './verifyRemoteSshExtension';
|
||||
|
||||
export async function openInRemoteSsh(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function openInRemoteSsh(context: IActionContext & { canPickMany: false }, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.linuxContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.linuxContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
await verifyRemoteSshExtension(context);
|
||||
|
|
|
@ -5,21 +5,25 @@
|
|||
|
||||
import { ComputeManagementClient } from "@azure/arm-compute";
|
||||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { vmFilter } from "../constants";
|
||||
import { ext } from "../extensionVariables";
|
||||
import { localize } from "../localize";
|
||||
import { VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../utils/azureClients";
|
||||
import { nonNullValue } from "../utils/nonNull";
|
||||
|
||||
export async function restartVirtualMachine(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function restartVirtualMachine(context: IActionContext, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.allOSContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node?.subscription]);
|
||||
|
||||
await node.runWithTemporaryDescription(context, localize('restarting', 'Restarting...'), async () => {
|
||||
const vmti: VirtualMachineTreeItem = nonNullValue(node);
|
||||
const vmti: ResolvedVirtualMachineTreeItem = nonNullValue(node);
|
||||
ext.outputChannel.appendLog(localize('restartingVm', `Restarting "${vmti.name}"...`));
|
||||
await computeClient.virtualMachines.beginRestartAndWait(vmti.resourceGroup, vmti.name);
|
||||
ext.outputChannel.appendLog(localize('restartedVm', `"${vmti.name}" has been restarted.`));
|
||||
|
|
|
@ -5,21 +5,25 @@
|
|||
|
||||
import { ComputeManagementClient } from "@azure/arm-compute";
|
||||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { vmFilter } from "../constants";
|
||||
import { ext } from "../extensionVariables";
|
||||
import { localize } from "../localize";
|
||||
import { VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../utils/azureClients";
|
||||
import { nonNullValue } from "../utils/nonNull";
|
||||
|
||||
export async function startVirtualMachine(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function startVirtualMachine(context: IActionContext, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.allOSContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node?.subscription]);
|
||||
|
||||
await node.runWithTemporaryDescription(context, localize('starting', 'Starting...'), async () => {
|
||||
const vmti: VirtualMachineTreeItem = nonNullValue(node);
|
||||
const vmti: ResolvedVirtualMachineTreeItem = nonNullValue(node);
|
||||
ext.outputChannel.appendLog(localize('startingVm', `Starting "${vmti.name}"...`));
|
||||
await computeClient.virtualMachines.beginStartAndWait(vmti.resourceGroup, vmti.name);
|
||||
ext.outputChannel.appendLog(localize('startedVm', `"${vmti.name}" has been started.`));
|
||||
|
|
|
@ -5,21 +5,25 @@
|
|||
|
||||
import { ComputeManagementClient } from "@azure/arm-compute";
|
||||
import { IActionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { vmFilter } from "../constants";
|
||||
import { ext } from "../extensionVariables";
|
||||
import { localize } from "../localize";
|
||||
import { VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from "../tree/VirtualMachineTreeItem";
|
||||
import { createComputeClient } from "../utils/azureClients";
|
||||
import { nonNullValue } from "../utils/nonNull";
|
||||
|
||||
export async function stopVirtualMachine(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
export async function stopVirtualMachine(context: IActionContext, node?: ResolvedVirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
node = await ext.rgApi.pickAppResource<ResolvedVirtualMachineTreeItem>(context, {
|
||||
filter: vmFilter,
|
||||
expectedChildContextValue: new RegExp(VirtualMachineTreeItem.allOSContextValue)
|
||||
});
|
||||
}
|
||||
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node]);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, node?.subscription]);
|
||||
|
||||
await node.runWithTemporaryDescription(context, localize('deallocating', 'Deallocating...'), async () => {
|
||||
const vmti: VirtualMachineTreeItem = nonNullValue(node);
|
||||
const vmti: ResolvedVirtualMachineTreeItem = nonNullValue(node);
|
||||
ext.outputChannel.appendLog(localize('deallocatingVm', `Deallocating "${vmti.name}"...`));
|
||||
await computeClient.virtualMachines.beginDeallocateAndWait(vmti.resourceGroup, vmti.name);
|
||||
ext.outputChannel.appendLog(localize('deallocatedVm', `"${vmti.name}" has been deallocated.`));
|
||||
|
|
|
@ -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, openReadOnlyJson } from '@microsoft/vscode-azext-utils';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
|
||||
export async function viewProperties(context: IActionContext, node?: VirtualMachineTreeItem): Promise<void> {
|
||||
if (!node) {
|
||||
node = await ext.tree.showTreeItemPicker<VirtualMachineTreeItem>(VirtualMachineTreeItem.allOSContextValue, context);
|
||||
}
|
||||
|
||||
await openReadOnlyJson(node, node.data);
|
||||
}
|
|
@ -16,3 +16,7 @@ export const virtualMachineLabel: string = localize('virtualMachine', 'virtual m
|
|||
export const virtualNetworkLabel: string = localize('virtualNetwork', 'virtual network');
|
||||
|
||||
export const sshFsPath: string = join(os.homedir(), '.ssh');
|
||||
|
||||
export const vmFilter = {
|
||||
type: 'Microsoft.Compute/virtualMachines'
|
||||
}
|
||||
|
|
|
@ -6,23 +6,23 @@
|
|||
'use strict';
|
||||
|
||||
import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-azureutils';
|
||||
import { AzExtTreeDataProvider, AzExtTreeItem, callWithTelemetryAndErrorHandling, createApiProvider, createAzExtOutputChannel, IActionContext, registerCommand, registerErrorHandler, registerReportIssueCommand, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
|
||||
import { callWithTelemetryAndErrorHandling, createApiProvider, createAzExtOutputChannel, IActionContext, registerCommand, registerErrorHandler, registerReportIssueCommand, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
|
||||
import { AzureExtensionApi, AzureExtensionApiProvider } from '@microsoft/vscode-azext-utils/api';
|
||||
import { AzureHostExtensionApi } from '@microsoft/vscode-azext-utils/hostapi';
|
||||
import * as vscode from 'vscode';
|
||||
import { addSshKey } from './commands/addSshKey';
|
||||
import { revealTreeItem } from './commands/api/revealTreeItem';
|
||||
import { copyIpAddress } from './commands/copyIpAddress';
|
||||
import { createVirtualMachine, createVirtualMachineAdvanced } from './commands/createVirtualMachine/createVirtualMachine';
|
||||
import { deleteVirtualMachine } from './commands/deleteVirtualMachine/deleteVirtualMachine';
|
||||
import { openInPortal } from './commands/openInPortal';
|
||||
import { openInRemoteSsh } from './commands/openInRemoteSsh';
|
||||
import { restartVirtualMachine } from './commands/restartVirtualMachine';
|
||||
import { startVirtualMachine } from './commands/startVirtualMachine';
|
||||
import { stopVirtualMachine } from './commands/stopVirtualMachine';
|
||||
import { viewProperties } from './commands/viewProperties';
|
||||
import { remoteSshExtensionId } from './constants';
|
||||
import { ext } from './extensionVariables';
|
||||
import { AzureAccountTreeItem } from './tree/AzureAccountTreeItem';
|
||||
import { getApiExport } from './getExtensionApi';
|
||||
import { VirtualMachineResolver } from './VirtualMachineTreeItemResolver';
|
||||
|
||||
export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number; loadEndTime: number }, ignoreBundle?: boolean): Promise<AzureExtensionApiProvider> {
|
||||
ext.context = context;
|
||||
|
@ -37,16 +37,6 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
|
|||
activateContext.telemetry.properties.isActivationEvent = 'true';
|
||||
activateContext.telemetry.measurements.mainFileLoad = (perfStats.loadEndTime - perfStats.loadStartTime) / 1000;
|
||||
|
||||
ext.azureAccountTreeItem = new AzureAccountTreeItem();
|
||||
context.subscriptions.push(ext.azureAccountTreeItem);
|
||||
ext.tree = new AzExtTreeDataProvider(ext.azureAccountTreeItem, 'azureVirtualMachines.loadMore');
|
||||
ext.treeView = vscode.window.createTreeView('azVmTree', { treeDataProvider: ext.tree, showCollapseAll: true });
|
||||
context.subscriptions.push(ext.treeView);
|
||||
|
||||
registerCommand('azureVirtualMachines.selectSubscriptions', () => vscode.commands.executeCommand('azure-account.selectSubscriptions'));
|
||||
registerCommand('azureVirtualMachines.refresh', async (actionContext: IActionContext, node?: AzExtTreeItem) => await ext.tree.refresh(actionContext, node));
|
||||
registerCommand('azureVirtualMachines.loadMore', async (actionContext: IActionContext, node: AzExtTreeItem) => await ext.tree.loadMore(node, actionContext));
|
||||
registerCommand('azureVirtualMachines.openInPortal', openInPortal);
|
||||
registerCommand('azureVirtualMachines.createVirtualMachine', createVirtualMachine);
|
||||
registerCommand('azureVirtualMachines.createVirtualMachineAdvanced', createVirtualMachineAdvanced);
|
||||
registerCommand('azureVirtualMachines.startVirtualMachine', startVirtualMachine);
|
||||
|
@ -55,7 +45,6 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
|
|||
registerCommand('azureVirtualMachines.addSshKey', addSshKey);
|
||||
registerCommand('azureVirtualMachines.deleteVirtualMachine', deleteVirtualMachine);
|
||||
registerCommand('azureVirtualMachines.copyIpAddress', copyIpAddress);
|
||||
registerCommand('azureVirtualMachines.viewProperties', viewProperties);
|
||||
registerCommand('azureVirtualMachines.openInRemoteSsh', openInRemoteSsh);
|
||||
registerCommand('azureVirtualMachines.showOutputChannel', () => { ext.outputChannel.show(); });
|
||||
registerCommand('azureVirtualMachines.showRemoteSshExtension', () => { void vscode.commands.executeCommand('extension.open', remoteSshExtensionId); });
|
||||
|
@ -63,6 +52,15 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
|
|||
// Suppress "Report an Issue" button for all errors in favor of the command
|
||||
registerErrorHandler(c => c.errorHandling.suppressReportIssue = true);
|
||||
registerReportIssueCommand('azureVirtualMachines.reportIssue');
|
||||
|
||||
const rgApiProvider = await getApiExport<AzureExtensionApiProvider>('ms-azuretools.vscode-azureresourcegroups');
|
||||
if (rgApiProvider) {
|
||||
const api = rgApiProvider.getApi<AzureHostExtensionApi>('0.0.1');
|
||||
ext.rgApi = api;
|
||||
api.registerApplicationResourceResolver('Microsoft.Compute/virtualMachines', new VirtualMachineResolver());
|
||||
} else {
|
||||
throw new Error('Could not find the Azure Resource Groups extension');
|
||||
}
|
||||
});
|
||||
|
||||
return createApiProvider([<AzureExtensionApi>{
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzExtTreeDataProvider, AzExtTreeItem, IAzExtOutputChannel } from "@microsoft/vscode-azext-utils";
|
||||
import { ExtensionContext, TreeView } from "vscode";
|
||||
import { AzureAccountTreeItem } from "./tree/AzureAccountTreeItem";
|
||||
import { IAzExtOutputChannel } from "@microsoft/vscode-azext-utils";
|
||||
import { AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi";
|
||||
import { ExtensionContext } from "vscode";
|
||||
|
||||
/**
|
||||
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
|
||||
|
@ -14,9 +14,8 @@ export namespace ext {
|
|||
export let outputChannel: IAzExtOutputChannel;
|
||||
export let context: ExtensionContext;
|
||||
|
||||
export let tree: AzExtTreeDataProvider;
|
||||
export let treeView: TreeView<AzExtTreeItem>;
|
||||
export let azureAccountTreeItem: AzureAccountTreeItem;
|
||||
export let ignoreBundle: boolean | undefined;
|
||||
export const prefix: string = 'azureVirtualMachines';
|
||||
|
||||
export let rgApi: AzureHostExtensionApi;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Extension, extensions } from "vscode";
|
||||
|
||||
export async function getApiExport<T>(extensionId: string): Promise<T | undefined> {
|
||||
const extension: Extension<T> | undefined = extensions.getExtension(extensionId);
|
||||
if (extension) {
|
||||
if (!extension.isActive) {
|
||||
await extension.activate();
|
||||
}
|
||||
|
||||
return extension.exports;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
|
@ -3,28 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ComputeManagementClient, VirtualMachine, VirtualMachineInstanceView, VirtualMachineSizeTypes } from '@azure/arm-compute';
|
||||
import { LocationListStep, ResourceGroupCreateStep, SubscriptionTreeItemBase, uiUtils, VerifyProvidersStep } from '@microsoft/vscode-azext-azureutils';
|
||||
import { AzExtTreeItem, AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, ICreateChildImplContext } from '@microsoft/vscode-azext-utils';
|
||||
import { getAvailableVMLocations } from '../commands/createVirtualMachine/getAvailableVMLocations';
|
||||
import { ImageListStep } from '../commands/createVirtualMachine/ImageListStep';
|
||||
import { IVirtualMachineWizardContext } from '../commands/createVirtualMachine/IVirtualMachineWizardContext';
|
||||
import { NetworkInterfaceCreateStep } from '../commands/createVirtualMachine/NetworkInterfaceCreateStep';
|
||||
import { NetworkSecurityGroupCreateStep } from '../commands/createVirtualMachine/NetworkSecurityGroupCreateStep';
|
||||
import { OSListStep } from '../commands/createVirtualMachine/OSListStep';
|
||||
import { PassphrasePromptStep } from '../commands/createVirtualMachine/PassphrasePromptStep';
|
||||
import { PublicIpCreateStep } from '../commands/createVirtualMachine/PublicIpCreateStep';
|
||||
import { SubnetCreateStep } from '../commands/createVirtualMachine/SubnetCreateStep';
|
||||
import { UsernamePromptStep } from '../commands/createVirtualMachine/UsernamePromptStep';
|
||||
import { VirtualMachineCreateStep } from '../commands/createVirtualMachine/VirtualMachineCreateStep';
|
||||
import { VirtualMachineNameStep } from '../commands/createVirtualMachine/VirtualMachineNameStep';
|
||||
import { VirtualNetworkCreateStep } from '../commands/createVirtualMachine/VirtualNetworkCreateStep';
|
||||
import { SubscriptionTreeItemBase } from '@microsoft/vscode-azext-azureutils';
|
||||
import { AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
|
||||
import { localize } from '../localize';
|
||||
import { createComputeClient } from '../utils/azureClients';
|
||||
import { getResourceGroupFromId } from '../utils/azureUtils';
|
||||
import { nonNullProp } from '../utils/nonNull';
|
||||
import { configureSshConfig } from '../utils/sshUtils';
|
||||
import { VirtualMachineTreeItem } from './VirtualMachineTreeItem';
|
||||
|
||||
export class SubscriptionTreeItem extends SubscriptionTreeItemBase {
|
||||
public readonly childTypeLabel: string = localize('VirtualMachine', 'Virtual Machine');
|
||||
|
@ -36,84 +17,11 @@ export class SubscriptionTreeItem extends SubscriptionTreeItemBase {
|
|||
return this._nextLink !== undefined;
|
||||
}
|
||||
|
||||
public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise<AzExtTreeItem[]> {
|
||||
public async loadMoreChildrenImpl(clearCache: boolean, _context: IActionContext): Promise<AzExtTreeItem[]> {
|
||||
if (clearCache) {
|
||||
this._nextLink = undefined;
|
||||
}
|
||||
|
||||
const client: ComputeManagementClient = await createComputeClient([context, this]);
|
||||
const virtualMachines: VirtualMachine[] = await uiUtils.listAllIterator(client.virtualMachines.listAll());
|
||||
|
||||
return await this.createTreeItemsWithErrorHandling(
|
||||
virtualMachines,
|
||||
'invalidVirtualMachine',
|
||||
async (vm: VirtualMachine) => {
|
||||
const instanceView: VirtualMachineInstanceView = await client.virtualMachines.instanceView(getResourceGroupFromId(nonNullProp(vm, 'id')), nonNullProp(vm, 'name'));
|
||||
return new VirtualMachineTreeItem(this, vm, instanceView);
|
||||
},
|
||||
(vm: VirtualMachine) => {
|
||||
return vm.name;
|
||||
}
|
||||
);
|
||||
}
|
||||
public async createChildImpl(context: ICreateChildImplContext): Promise<AzExtTreeItem> {
|
||||
const size: VirtualMachineSizeTypes = this.subscription.isCustomCloud ? 'Standard_DS1_v2' : 'Standard_D2s_v3';
|
||||
const wizardContext: IVirtualMachineWizardContext = Object.assign(context, this.subscription, {
|
||||
addressPrefix: '10.1.0.0/24',
|
||||
size,
|
||||
includeExtendedLocations: true
|
||||
});
|
||||
|
||||
const computeProvider: string = 'Microsoft.Compute';
|
||||
LocationListStep.setLocationSubset(wizardContext, getAvailableVMLocations(wizardContext), computeProvider);
|
||||
|
||||
// By default, only prompt for VM and Location. A new RG is made for every VM
|
||||
const promptSteps: AzureWizardPromptStep<IVirtualMachineWizardContext>[] = [];
|
||||
const executeSteps: AzureWizardExecuteStep<IVirtualMachineWizardContext>[] = [];
|
||||
|
||||
promptSteps.push(new VirtualMachineNameStep());
|
||||
promptSteps.push(new OSListStep());
|
||||
const imageListStep = new ImageListStep();
|
||||
promptSteps.push(imageListStep);
|
||||
|
||||
promptSteps.push(new UsernamePromptStep());
|
||||
promptSteps.push(new PassphrasePromptStep());
|
||||
LocationListStep.addStep(wizardContext, promptSteps);
|
||||
|
||||
executeSteps.push(new ResourceGroupCreateStep());
|
||||
executeSteps.push(new PublicIpCreateStep());
|
||||
executeSteps.push(new VirtualNetworkCreateStep());
|
||||
executeSteps.push(new SubnetCreateStep());
|
||||
executeSteps.push(new NetworkSecurityGroupCreateStep());
|
||||
executeSteps.push(new NetworkInterfaceCreateStep());
|
||||
executeSteps.push(new VirtualMachineCreateStep());
|
||||
executeSteps.push(new VerifyProvidersStep([computeProvider, 'Microsoft.Network']));
|
||||
|
||||
const title: string = 'Create new virtual machine';
|
||||
|
||||
if (!context.advancedCreation) {
|
||||
// for basic create, default to image Ubuntu 18.04 LTS
|
||||
wizardContext.os = 'Linux';
|
||||
wizardContext.imageTask = imageListStep.getDefaultImageReference(wizardContext);
|
||||
wizardContext.adminUsername = 'azureuser';
|
||||
}
|
||||
|
||||
const wizard: AzureWizard<IVirtualMachineWizardContext> = new AzureWizard(wizardContext, { promptSteps, executeSteps, title });
|
||||
|
||||
await wizard.prompt();
|
||||
|
||||
context.showCreatingTreeItem(nonNullProp(wizardContext, 'newVirtualMachineName'));
|
||||
wizardContext.newResourceGroupName = await wizardContext.relatedNameTask;
|
||||
|
||||
await wizard.execute();
|
||||
|
||||
const virtualMachine: VirtualMachine = nonNullProp(wizardContext, 'virtualMachine');
|
||||
|
||||
const newVm: VirtualMachineTreeItem = new VirtualMachineTreeItem(this, virtualMachine, undefined /* assume all newly created VMs are running */);
|
||||
if (newVm.contextValue === VirtualMachineTreeItem.linuxContextValue) {
|
||||
await configureSshConfig(context, newVm, `~/.ssh/${wizardContext.sshKeyName}`);
|
||||
}
|
||||
|
||||
return newVm;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,19 +5,32 @@
|
|||
|
||||
import { ComputeManagementClient, InstanceViewStatus, NetworkInterfaceReference, VirtualMachine, VirtualMachineInstanceView } from '@azure/arm-compute';
|
||||
import { NetworkInterface, NetworkManagementClient, PublicIPAddress } from '@azure/arm-network';
|
||||
import { AzExtErrorButton, AzExtParentTreeItem, AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
|
||||
import { AzExtTreeItem, AzureWizard, IActionContext, ISubscriptionContext } from '@microsoft/vscode-azext-utils';
|
||||
import { ResolvedAppResourceBase, ResolvedAppResourceTreeItem } from '@microsoft/vscode-azext-utils/hostapi';
|
||||
import * as vscode from 'vscode';
|
||||
import { deleteAllResources } from '../commands/deleteVirtualMachine/deleteAllResources';
|
||||
import { ConfirmDeleteStep } from '../commands/deleteVirtualMachine/ConfirmDeleteStep';
|
||||
import { IDeleteChildImplContext, ResourceToDelete } from '../commands/deleteVirtualMachine/deleteConstants';
|
||||
import { viewOutput, virtualMachineLabel } from '../constants';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { DeleteVirtualMachineStep } from '../commands/deleteVirtualMachine/DeleteVirtualMachineStep';
|
||||
import { SelectResourcesToDeleteStep } from '../commands/deleteVirtualMachine/SelectResourcesToDeleteStep';
|
||||
import { localize } from '../localize';
|
||||
import { createActivityContext } from '../utils/activityUtils';
|
||||
import { createComputeClient, createNetworkClient } from '../utils/azureClients';
|
||||
import { getNameFromId, getResourceGroupFromId } from '../utils/azureUtils';
|
||||
import { nonNullProp, nonNullValueAndProp } from '../utils/nonNull';
|
||||
import { treeUtils } from '../utils/treeUtils';
|
||||
|
||||
export class VirtualMachineTreeItem extends AzExtTreeItem {
|
||||
export interface ResolvedVirtualMachine extends ResolvedAppResourceBase {
|
||||
data: VirtualMachine;
|
||||
resourceGroup: string;
|
||||
getIpAddress(context: IActionContext): Promise<string>;
|
||||
getUser(): string;
|
||||
label: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type ResolvedVirtualMachineTreeItem = ResolvedAppResourceTreeItem<ResolvedVirtualMachine> & AzExtTreeItem;
|
||||
|
||||
export class VirtualMachineTreeItem implements ResolvedVirtualMachine {
|
||||
public get label(): string {
|
||||
return `${this.name}`;
|
||||
}
|
||||
|
@ -26,6 +39,8 @@ export class VirtualMachineTreeItem extends AzExtTreeItem {
|
|||
return treeUtils.getThemedIconPath('Virtual-Machine');
|
||||
}
|
||||
|
||||
public readonly collapsibleState = vscode.TreeItemCollapsibleState.None;
|
||||
|
||||
public get id(): string {
|
||||
// https://github.com/microsoft/vscode-azurevirtualmachines/issues/70
|
||||
return nonNullProp(this.virtualMachine, 'id').toLowerCase();
|
||||
|
@ -52,15 +67,15 @@ export class VirtualMachineTreeItem extends AzExtTreeItem {
|
|||
public static windowsContextValue: string = 'windowsVirtualMachine';
|
||||
public static allOSContextValue: RegExp = /VirtualMachine$/;
|
||||
|
||||
public contextValue: string;
|
||||
public contextValuesToAdd: string[] = [];
|
||||
public virtualMachine: VirtualMachine;
|
||||
|
||||
private _state?: string;
|
||||
|
||||
public constructor(parent: AzExtParentTreeItem, vm: VirtualMachine, instanceView?: VirtualMachineInstanceView) {
|
||||
super(parent);
|
||||
public constructor(private readonly _subscription: ISubscriptionContext, vm: VirtualMachine, instanceView?: VirtualMachineInstanceView) {
|
||||
this.virtualMachine = vm;
|
||||
this._state = instanceView ? this.getStateFromInstanceView(instanceView) : undefined;
|
||||
this.contextValue = vm.osProfile?.linuxConfiguration ? VirtualMachineTreeItem.linuxContextValue : VirtualMachineTreeItem.windowsContextValue;
|
||||
this.contextValuesToAdd = vm.osProfile?.linuxConfiguration ? [VirtualMachineTreeItem.linuxContextValue] : [VirtualMachineTreeItem.windowsContextValue];
|
||||
}
|
||||
|
||||
public getUser(): string {
|
||||
|
@ -68,7 +83,7 @@ export class VirtualMachineTreeItem extends AzExtTreeItem {
|
|||
}
|
||||
|
||||
public async getIpAddress(context: IActionContext): Promise<string> {
|
||||
const networkClient: NetworkManagementClient = await createNetworkClient([context, this]);
|
||||
const networkClient: NetworkManagementClient = await createNetworkClient([context, this._subscription]);
|
||||
const rgName: string = getResourceGroupFromId(this.id);
|
||||
|
||||
const networkInterfaces: NetworkInterfaceReference[] = nonNullValueAndProp(this.virtualMachine.networkProfile, 'networkInterfaces');
|
||||
|
@ -87,43 +102,27 @@ export class VirtualMachineTreeItem extends AzExtTreeItem {
|
|||
return nonNullProp(ip, 'ipAddress');
|
||||
}
|
||||
|
||||
public async deleteTreeItemImpl(context: IDeleteChildImplContext): Promise<void> {
|
||||
const multiDelete: boolean = context.resourcesToDelete.length > 1;
|
||||
const resourcesToDelete: ResourceToDelete[] = context.resourcesToDelete;
|
||||
public async deleteTreeItemImpl(context: IActionContext): Promise<void> {
|
||||
|
||||
const deleting: string = multiDelete ? localize('Deleting', 'Deleting {0}...', context.resourceList) :
|
||||
localize('Deleting', 'Deleting {0} "{1}"...', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: `${deleting} Check the [output channel](command:${ext.prefix}.showOutputChannel) for status.` }, async (): Promise<void> => {
|
||||
if (multiDelete) { ext.outputChannel.appendLog(deleting); }
|
||||
|
||||
const failedResources: ResourceToDelete[] = await deleteAllResources(context, this.subscription, this.resourceGroup, resourcesToDelete);
|
||||
const failedResourceList: string = failedResources.map(r => `"${r.resourceName}"`).join(', ');
|
||||
|
||||
const messageDeleteWithErrors: string = localize(
|
||||
'messageDeleteWithErrors',
|
||||
'Failed to delete the following resources: {0}.', failedResourceList);
|
||||
|
||||
const deleteSucceeded: string = multiDelete ? localize('DeleteSucceeded', 'Successfully deleted {0}.', context.resourceList) :
|
||||
localize('DeleteSucceeded', 'Successfully deleted {0} "{1}".', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
// single resources are already displayed in the output channel
|
||||
if (multiDelete) { ext.outputChannel.appendLog(failedResources.length > 0 ? messageDeleteWithErrors : deleteSucceeded); }
|
||||
if (failedResources.length > 0) {
|
||||
context.telemetry.properties.failedResources = failedResources.length.toString();
|
||||
// if the vm failed to delete or was not being deleted, we want to throw an error to make sure that the node is not removed from the tree
|
||||
if (failedResources.some(r => r.resourceType === virtualMachineLabel) || !context.deleteVm) {
|
||||
// tslint:disable-next-line: no-floating-promises
|
||||
const viewOutputAzureButton: AzExtErrorButton = { title: viewOutput.title, callback: async (): Promise<void> => ext.outputChannel.show() };
|
||||
context.errorHandling.buttons = [viewOutputAzureButton];
|
||||
throw new Error(messageDeleteWithErrors);
|
||||
}
|
||||
|
||||
void context.ui.showWarningMessage(`${messageDeleteWithErrors} Check the [output channel](command:${ext.prefix}.showOutputChannel) for more information.`);
|
||||
} else {
|
||||
void vscode.window.showInformationMessage(deleteSucceeded);
|
||||
}
|
||||
const wizardContext: IDeleteChildImplContext = Object.assign(context, {
|
||||
node: this as ResolvedVirtualMachineTreeItem,
|
||||
...(await createActivityContext()),
|
||||
});
|
||||
|
||||
const wizard = new AzureWizard<IDeleteChildImplContext>(wizardContext, {
|
||||
promptSteps: [new SelectResourcesToDeleteStep(), new ConfirmDeleteStep()],
|
||||
executeSteps: [new DeleteVirtualMachineStep()],
|
||||
});
|
||||
|
||||
await wizard.prompt();
|
||||
|
||||
const resourcesToDelete: ResourceToDelete[] = nonNullProp(wizardContext, 'resourcesToDelete');
|
||||
const multiDelete: boolean = resourcesToDelete.length > 1;
|
||||
|
||||
wizardContext.activityTitle = multiDelete ? localize('delete', 'Delete {0}...', wizardContext.resourceList) :
|
||||
localize('delete', 'Delete {0} "{1}"...', resourcesToDelete[0].resourceType, resourcesToDelete[0].resourceName);
|
||||
|
||||
await wizard.execute();
|
||||
}
|
||||
|
||||
public async refreshImpl(context: IActionContext): Promise<void> {
|
||||
|
@ -132,11 +131,10 @@ export class VirtualMachineTreeItem extends AzExtTreeItem {
|
|||
} catch {
|
||||
this._state = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async getState(context: IActionContext): Promise<string | undefined> {
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, this]);
|
||||
const computeClient: ComputeManagementClient = await createComputeClient([context, this._subscription]);
|
||||
return this.getStateFromInstanceView(await computeClient.virtualMachines.instanceView(this.resourceGroup, this.name));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExecuteActivityContext } from "@microsoft/vscode-azext-utils";
|
||||
import { ext } from "../extensionVariables";
|
||||
import { getWorkspaceSetting } from "../vsCodeConfig/settings";
|
||||
|
||||
export async function createActivityContext(): Promise<ExecuteActivityContext> {
|
||||
return {
|
||||
registerActivity: async (activity) => ext.rgApi.registerActivity(activity),
|
||||
suppressNotification: await getWorkspaceSetting('suppressActivityNotifications', undefined, 'azureResourceGroups'),
|
||||
};
|
||||
}
|
|
@ -14,7 +14,7 @@ import { IVirtualMachineWizardContext } from '../commands/createVirtualMachine/I
|
|||
import { sshFsPath } from '../constants';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { localize } from '../localize';
|
||||
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
import { ResolvedVirtualMachineTreeItem, VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
|
||||
import { createComputeClient } from './azureClients';
|
||||
import { cpUtils } from "./cpUtils";
|
||||
import { nonNullValueAndProp } from './nonNull';
|
||||
|
@ -76,7 +76,7 @@ export async function createSshKey(context: IVirtualMachineWizardContext, vmName
|
|||
passphrase);
|
||||
}
|
||||
|
||||
export async function configureSshConfig(context: IActionContext, vmti: VirtualMachineTreeItem, sshKeyPath: string): Promise<void> {
|
||||
export async function configureSshConfig(context: IActionContext, vmti: ResolvedVirtualMachineTreeItem | VirtualMachineTreeItem, sshKeyPath: string): Promise<void> {
|
||||
const sshConfigPath: string = join(sshFsPath, 'config');
|
||||
await fse.ensureFile(sshConfigPath);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ComputeManagementClient } from '@azure/arm-compute';
|
|||
import { ResourceManagementClient } from '@azure/arm-resources';
|
||||
import { createTestActionContext, TestAzureAccount } from '@microsoft/vscode-azext-dev';
|
||||
import * as vscode from 'vscode';
|
||||
import { AzExtTreeDataProvider, AzureAccountTreeItem, createAzureClient, createComputeClient, ext, ISubscriptionContext } from '../../extension.bundle';
|
||||
import { createAzureClient, createComputeClient, ISubscriptionContext } from '../../extension.bundle';
|
||||
import { longRunningTestsEnabled } from '../global.test';
|
||||
|
||||
export let testAccount: TestAzureAccount;
|
||||
|
@ -19,8 +19,6 @@ suiteSetup(async function (this: Mocha.Context): Promise<void> {
|
|||
this.timeout(20 * 60 * 1000);
|
||||
testAccount = new TestAzureAccount(vscode);
|
||||
await testAccount.signIn();
|
||||
ext.azureAccountTreeItem = new AzureAccountTreeItem(testAccount);
|
||||
ext.tree = new AzExtTreeDataProvider(ext.azureAccountTreeItem, 'azureVirtualMachines.loadMore');
|
||||
|
||||
computeClient = await createComputeClient([await createTestActionContext(), <ISubscriptionContext>testAccount.getSubscriptionContext()]);
|
||||
}
|
||||
|
@ -30,7 +28,7 @@ suiteTeardown(async function (this: Mocha.Context): Promise<void> {
|
|||
if (longRunningTestsEnabled) {
|
||||
this.timeout(10 * 60 * 1000);
|
||||
await deleteResourceGroups();
|
||||
ext.azureAccountTreeItem.dispose();
|
||||
// ext.azureAccountTreeItem.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче