This commit is contained in:
nturinski 2019-10-22 14:02:59 -07:00
Родитель 4db86b7414
Коммит 8587572318
77 изменённых файлов: 11568 добавлений и 4 удалений

Просмотреть файл

@ -0,0 +1,14 @@
steps:
- task: NodeTool@0
displayName: 'Use Node 8.x'
inputs:
versionSpec: 8.x
- task: Npm@1
displayName: 'npm install'
- task: Npm@1
displayName: 'Build'
inputs:
command: custom
customCommand: run build

Просмотреть файл

@ -0,0 +1,10 @@
steps:
- task: Npm@1
displayName: 'Lint'
inputs:
command: custom
customCommand: run lint
- task: ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
condition: ne(variables['System.PullRequest.IsFork'], 'True')

Просмотреть файл

@ -0,0 +1,19 @@
steps:
- task: Npm@1
displayName: 'Package'
inputs:
command: custom
customCommand: run package
- task: CopyFiles@2
displayName: 'Copy vsix to staging directory'
inputs:
Contents: '**/*.vsix'
TargetFolder: '$(build.artifactstagingdirectory)'
- task: PublishBuildArtifacts@1
displayName: 'Publish artifacts: vsix'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: vsix
condition: ne(variables['System.PullRequest.IsFork'], 'True')

Просмотреть файл

@ -0,0 +1,31 @@
steps:
- script: |
sudo cp .azure-pipelines/linux/xvfb.init /etc/init.d/xvfb
sudo chmod +x /etc/init.d/xvfb
sudo update-rc.d xvfb defaults
sudo service xvfb start
displayName: 'Start X Virtual Frame Buffer'
condition: eq(variables['Agent.OS'], 'Linux')
- task: UsePythonVersion@0
displayName: 'Use Python 3.6.x'
inputs:
versionSpec: 3.6.x
- task: Npm@1
displayName: 'Test'
inputs:
command: custom
customCommand: test
env:
SERVICE_PRINCIPAL_CLIENT_ID: $(SERVICE_PRINCIPAL_CLIENT_ID)
SERVICE_PRINCIPAL_SECRET: $(SERVICE_PRINCIPAL_SECRET)
SERVICE_PRINCIPAL_DOMAIN: $(SERVICE_PRINCIPAL_DOMAIN)
DISPLAY: :10 # Only necessary for linux tests
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '*-results.xml'
testRunTitle: '$(Agent.OS)'
condition: succeededOrFailed()

Просмотреть файл

@ -0,0 +1,56 @@
#!/bin/bash
#
# COPIED FROM https://github.com/Microsoft/vscode/blob/e29c517386fe6f3a40e2f0ff00effae4919406aa/build/tfs/linux/x64/xvfb.init
#
#
# /etc/rc.d/init.d/xvfbd
#
# chkconfig: 345 95 28
# description: Starts/Stops X Virtual Framebuffer server
# processname: Xvfb
#
### BEGIN INIT INFO
# Provides: xvfb
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start xvfb at boot time
# Description: Enable xvfb provided by daemon.
### END INIT INFO
[ "${NETWORKING}" = "no" ] && exit 0
PROG="/usr/bin/Xvfb"
PROG_OPTIONS=":10 -ac"
PROG_OUTPUT="/tmp/Xvfb.out"
case "$1" in
start)
echo "Starting : X Virtual Frame Buffer "
$PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 &
disown -ar
;;
stop)
echo "Shutting down : X Virtual Frame Buffer"
killproc $PROG
RETVAL=$?
[ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb
/var/run/Xvfb.pid
echo
;;
restart|reload)
$0 stop
$0 start
RETVAL=$?
;;
status)
status Xvfb
RETVAL=$?
;;
*)
echo $"Usage: $0 (start|stop|restart|reload|status)"
exit 1
esac
exit $RETVAL

26
.azure-pipelines/main.yml Normal file
Просмотреть файл

@ -0,0 +1,26 @@
jobs:
- job: Windows
pool:
vmImage: VS2017-Win2016
steps:
- template: common/build.yml
- template: common/lint.yml
- template: common/test.yml
- template: windows/intellinav.yml
- job: Linux
pool:
vmImage: ubuntu-16.04
steps:
- template: common/build.yml
- template: common/publish-vsix.yml # Only publish vsix from linux build since we use this to release and want to stay consistent
- template: common/lint.yml
- template: common/test.yml
- job: macOS
pool:
vmImage: macOS 10.13
steps:
- template: common/build.yml
- template: common/lint.yml
- template: common/test.yml

Просмотреть файл

@ -0,0 +1,7 @@
steps:
- task: RichCodeNavIndexer@0
inputs:
serviceConnection: 'azuretools-dev' # name of the IntelliNav service connection
languages: 'typescript' # languages in your repo, separated by commas
githubServiceConnection: 'GitHub connection' # if your repo is on GitHub, this is the name of the GitHub service connection (GitHub organization by default)
continueOnError: true

1
.github/CODEOWNERS поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
* @Microsoft/vscodeazuretools

5
.github/locker.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
{
daysAfterClose: 45,
daysSinceLastUpdate: 7,
perform: true
}

6
.github/needs_more_info.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
daysUntilClose: 21,
needsMoreInfoLabel: 'needs more info',
perform: true,
closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/azcodeissuereporting) guidelines.\n\nHappy Coding!"
}

18
.gitignore поставляемый
Просмотреть файл

@ -29,14 +29,14 @@ bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
# Typescript v1 declaration files
typings/
# Optional npm cache directory
@ -57,5 +57,15 @@ typings/
# dotenv environment variables file
.env
# next.js build output
.next
# Output
out
bin
obj
*.vsix
.vscode-test
test-results.xml
dist
stats.json
# debugging artifacts
resources/dotnetTemplates

6
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"recommendations": [
"ms-vscode.vscode-typescript-tslint-plugin",
"ms-vscode.azure-account"
]
}

96
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,96 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"preLaunchTask": "npm: compile",
"env": {
"AZCODE_VIRTUALMACHINES_IGNORE_BUNDLE": "1",
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": ""
}
},
{
"name": "Launch Extension (webpack)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"preLaunchTask": "npm: webpack",
"env": {
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": ""
}
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test",
"${workspaceFolder}/test/test.code-workspace"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: compile",
"env": {
"AZCODE_VIRTUALMACHINES_IGNORE_BUNDLE": "1",
"MOCHA_grep": "", // RegExp of tests to run (empty for all)
"MOCHA_enableTimeouts": "0", // Disable time-outs
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": "",
"ENABLE_LONG_RUNNING_TESTS": "",
"FUNC_PATH": "func"
}
},
{
"name": "Launch Tests (webpack)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/dist/test",
"${workspaceFolder}/test/test.code-workspace"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "npm: webpack",
"env": {
"MOCHA_grep": "", // RegExp of tests to run (empty for all)
"MOCHA_enableTimeouts": "0", // Disable time-outs
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": "",
"ENABLE_LONG_RUNNING_TESTS": "",
"FUNC_PATH": "func"
}
}
]
}

22
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
{
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.detectIndentation": false,
"editor.tabSize": 4,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"search.exclude": {
"out": true,
"**/node_modules": true,
".vscode-test": true,
"resources/backup*Templates": true
},
"editor.codeActionsOnSave": {
"source.fixAll.tslint": true,
},
"tslint.ignoreDefinitionFiles": true,
"files.exclude": {
"tools/JsonCli/src/**": true
},
"typescript.preferences.importModuleSpecifier": "relative"
}

23
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "compile",
"group": {
"kind": "build",
"isDefault": true
},
"isBackground": true,
"presentation": {
"reveal": "never"
},
"problemMatcher": "$tsc-watch"
},
{
"type": "npm",
"script": "lint",
"problemMatcher": "$tslint5"
}
]
}

25
.vscodeignore Normal file
Просмотреть файл

@ -0,0 +1,25 @@
.azure-pipelines/**
.github/**
.gitignore
.vscode-test/**
.vscode/**
*.tgz
**/*.gif
**/*.map
**/*.ts
build/**
dist/test/**
docs/**
gulp*
node_modules/**
out/**
src/**
stats.json
test-results.xml
test/**
testOutput/**
tools/**
tsconfig.json
tslint.json
typings/**
webpack.config*

10
CHANGELOG.md Normal file
Просмотреть файл

@ -0,0 +1,10 @@
<!-- markdownlint-disable MD024 -->
# Change Log
All notable changes to the "azurevirtualmachines" extension will be documented in this file.
## 0.1.0 - 2019-10-22
### Added
-

21
LICENSE.md Normal file
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

26
extension.bundle.ts Normal file
Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* This is the external face of extension.bundle.js, the main webpack bundle for the extension.
* Anything needing to be exposed outside of the extension sources must be exported from here, because
* everything else will be in private modules in extension.bundle.js.
*/
// Export activate/deactivate for main.js
export { activateInternal, deactivateInternal } from './src/extension';
// Exports for tests
// The tests are not packaged with the webpack bundle and therefore only have access to code exported from this file.
//
// The tests should import '../extension.bundle'. At design-time they live in tests/ and so will pick up this file (extension.bundle.ts).
// At runtime the tests live in dist/tests and will therefore pick up the main webpack bundle at dist/extension.bundle.js.
export * from './src/extensionVariables';
export * from './src/vsCodeConfig/settings';
export * from './src/tree/SubscriptionTreeItem';
export * from './src/utils/delay';
export * from './src/utils/cpUtils';
export * from './src/vsCodeConfig/extensions';
export * from 'vscode-azureextensionui';

29
gulpfile.ts Normal file
Просмотреть файл

@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// tslint:disable:no-console
// tslint:disable:no-implicit-dependencies (this allows the use of dev dependencies)
// Grandfathered in
// tslint:disable:typedef
// tslint:disable:no-unsafe-any
import * as cp from 'child_process';
import * as gulp from 'gulp';
import * as path from 'path';
import { gulp_installAzureAccount, gulp_webpack } from 'vscode-azureextensiondev';
function test() {
const env = process.env;
env.DEBUGTELEMETRY = '1';
env.MOCHA_timeout = String(20 * 1000);
env.CODE_TESTS_WORKSPACE = path.join(__dirname, 'test/test.code-workspace');
env.CODE_TESTS_PATH = path.join(__dirname, 'dist/test');
return cp.spawn('node', ['./node_modules/vscode/bin/test'], { stdio: 'inherit', env });
}
exports['webpack-dev'] = () => gulp_webpack('development');
exports['webpack-prod'] = () => gulp_webpack('production');
exports.test = gulp.series(gulp_installAzureAccount, test);

35
main.js Normal file
Просмотреть файл

@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";
// This is the extension entrypoint module, which imports extension.bundle.js, the actual extension code.
//
// This is in a separate file so we can properly measure extension.bundle.js load time.
let perfStats = {
loadStartTime: Date.now(),
loadEndTime: undefined
};
Object.defineProperty(exports, "__esModule", { value: true });
const ignoreBundle = !/^(false|0)?$/i.test(process.env.AZCODE_VIRTUALMACHINES_IGNORE_BUNDLE || '');
const extensionPath = ignoreBundle ? "./out/src/extension" : "./dist/extension.bundle";
const extension = require(extensionPath);
async function activate(ctx) {
return await extension.activateInternal(ctx, perfStats);
}
async function deactivate(ctx) {
return await extension.deactivateInternal(ctx, perfStats);
}
// Export as entrypoints for vscode
exports.activate = activate;
exports.deactivate = deactivate;
perfStats.loadEndTime = Date.now();

9412
package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

229
package.json Normal file
Просмотреть файл

@ -0,0 +1,229 @@
{
"name": "vscode-azurevirtualmachines",
"displayName": "Azure Virtual Machines",
"description": "%extension.description%",
"version": "0.1.0-alpha",
"publisher": "ms-azuretools",
"icon": "resources/azure-vm.png",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "^1.31.0"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode-azurevirtualmachines"
},
"galleryBanner": {
"color": "#0072c6",
"theme": "dark"
},
"homepage": "https://github.com/Microsoft/vscode-azurevirtualmachines/blob/master/README.md",
"license": "SEE LICENSE IN LICENSE.md",
"categories": [
"Azure"
],
"keywords": [
"Azure",
"Virtual Machines",
"Remote",
"multi-root ready"
],
"preview": true,
"activationEvents": [
"onCommand:azureVirtualMachines.refresh",
"onCommand:azureVirtualMachines.loadMore",
"onCommand:azureVirtualMachines.openInPortal",
"onView:azVmTree",
"onDebugInitialConfigurations"
],
"main": "./main.js",
"contributes": {
"commands": [
{
"command": "azureVirtualMachines.selectSubscriptions",
"title": "Select Subscriptions...",
"icon": {
"light": "resources/light/filter.svg",
"dark": "resources/dark/filter.svg"
}
},
{
"command": "azureVirtualMachines.refresh",
"title": "%azVms.refresh%",
"category": "Azure Virtual Machines",
"icon": {
"light": "resources/light/Refresh.svg",
"dark": "resources/dark/Refresh.svg"
}
},
{
"command": "azureVirtualMachines.loadMore",
"title": "%azVms.loadMore%",
"category": "Azure Virtual Machines"
},
{
"command": "azureVirtualMachines.createVirtualMachine",
"title": "Create Virtual Machine",
"category": "Azure Virtual Machines"
},
{
"command": "azureVirtualMachines.openInPortal",
"title": "Open in Portal",
"category": "Azure Virtual machines"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "azure",
"title": "Azure",
"icon": "resources/azure.svg"
}
]
},
"views": {
"azure": [
{
"id": "azVmTree",
"name": "Virtual Machines",
"when": "config.azureVirtualMachines.showExplorer == true"
}
]
},
"menus": {
"view/title": [
{
"command": "azureVirtualMachines.refresh",
"when": "view == azVmTree",
"group": "navigation@3"
}
],
"view/item/context": [
{
"command": "azureVirtualMachines.selectSubscriptions",
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
"group": "inline"
},
{
"command": "azureVirtualMachines.createVirtualMachine",
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
"group": "1@1"
},
{
"command": "azureVirtualMachines.openInPortal",
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
"group": "2@1"
},
{
"command": "azureVirtualMachines.refresh",
"when": "view == azVmTree && viewItem == azureextensionui.azureSubscription",
"group": "3@1"
},
{
"command": "azureVirtualMachines.openInPortal",
"when": "view == azVmTree && viewItem == azVmVirtualMachine",
"group": "1@1"
}
],
"explorer/context": [],
"commandPalette": [
{
"command": "azureVirtualMachines.selectSubscriptions",
"when": "never"
},
{
"command": "azureVirtualMachines.refresh",
"when": "never"
},
{
"command": "azureVirtualMachines.loadMore",
"when": "never"
}
],
"editor/context": []
},
"jsonValidation": [],
"taskDefinitions": [],
"problemPatterns": [],
"problemMatchers": [],
"keybindings": [
{
"command": "workbench.view.extension.azure",
"key": "ctrl+shift+a",
"mac": "cmd+shift+a"
}
],
"configuration": [
{
"title": "Azure Virtual Machines",
"properties": {
"azureVirtualMachines.showExplorer": {
"type": "boolean",
"default": true,
"description": "%azVms.showExplorerDescription%"
},
"azureVirtualMachines.templateFilter": {
"scope": "resource",
"type": "string",
"default": "Verified",
"enum": [
"Verified",
"Core",
"All"
],
"description": "%azVms.templateFilterDescription%"
}
}
}
]
},
"scripts": {
"vscode:prepublish": "npm run webpack-prod",
"build": "tsc -p ./",
"compile": "tsc -watch -p ./",
"package": "vsce package",
"lint": "tslint --project tsconfig.json -e src/*.d.ts -t verbose",
"postinstall": "node ./node_modules/vscode/bin/install",
"pretest": "npm run webpack-prod",
"test": "gulp test",
"webpack": "npm run build && gulp webpack-dev",
"webpack-prod": "npm run build && gulp webpack-prod",
"webpack-profile": "webpack --profile --json --mode production > webpack-stats.json && echo Use http://webpack.github.io/analyse to analyze the stats"
},
"devDependencies": {
"@types/gulp": "^4.0.6",
"@types/gulp-filter": "^3.0.33",
"@types/mocha": "^5.2.6",
"@types/node": "^8.10.45",
"@types/request": "2.0.7",
"azure-arm-resource": "^3.0.0-preview",
"copy-webpack-plugin": "^4.6.0",
"gulp": "^4.0.0",
"gulp-chmod": "^2.0.0",
"gulp-decompress": "^2.0.1",
"gulp-download": "^0.0.1",
"gulp-filter": "^5.1.0",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.18.0",
"mocha-multi-reporters": "^1.1.7",
"ts-node": "^7.0.1",
"tslint": "^5.14.0",
"tslint-microsoft-contrib": "^5.2.1",
"typescript": "^3.3.4000",
"vsce": "^1.59.0",
"vscode": "^1.1.33",
"vscode-azureextensiondev": "0.2.3",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0"
},
"dependencies": {
"azure-arm-compute": "^10.0.0",
"azure-arm-network": "^13.0.0",
"semver": "^5.7.0",
"vscode-azureextensionui": "^0.28.2",
"vscode-nls": "^4.1.0"
},
"extensionDependencies": [
"ms-vscode.azure-account"
]
}

75
package.nls.json Normal file
Просмотреть файл

@ -0,0 +1,75 @@
{
"extension.description": "An Azure Functions extension for Visual Studio Code.",
"azVms.refresh": "Refresh",
"azVms.loadMore": "Load More",
"azVms.createNewProject": "Create New Project...",
"azVms.initProjectForVSCode": "Initialize Project for Use with VS Code...",
"azVms.createFunction": "Create Function...",
"azVms.createFunctionApp": "Create Function App in Azure...",
"azVms.openInPortal": "Open in Portal",
"azVms.startFunctionApp": "Start",
"azVms.stopFunctionApp": "Stop",
"azVms.restartFunctionApp": "Restart",
"azVms.deleteFunctionApp": "Delete Function App...",
"azVms.copyFunctionUrl": "Copy Function Url",
"azVms.executeFunction": "Execute Function Now",
"azVms.deleteFunction": "Delete Function...",
"azVms.showExplorerDescription": "Show or hide the Azure Functions Explorer",
"azVms.templateFilterDescription": "Specify the templates to display when creating a new function. The supported values are 'Verified', 'Core', and 'All'. The 'Verified' category is a subset of 'Core' that has been verified to work with the latest VS Code extension.",
"azVms.projectRuntimeDescription": "The default runtime to use when performing operations in the Azure Functions extension (e.g. \"Create New Function\").",
"azVms.projectLanguageDescription": "The default language to use when performing operations in the Azure Functions extension (e.g. \"Create New Function\").",
"azVms.deploy": "Deploy to Function App...",
"azVms.configureDeploymentSource": "Configure Deployment Source...",
"azVms.debugFunctionAppOnAzure": "Attach Debugger",
"azVms.appSettings.add": "Add New Setting...",
"azVms.appSettings.download": "Download Remote Settings...",
"azVms.appSettings.upload": "Upload Local Settings...",
"azVms.appSettings.edit": "Edit Setting...",
"azVms.appSettings.rename": "Rename Setting...",
"azVms.appSettings.delete": "Delete Setting...",
"azVms.appSettings.decrypt": "Decrypt Settings",
"azVms.appSettings.encrypt": "Encrypt Settings",
"azVms.appSettings.toggleSlotSetting": "Toggle as Slot Setting",
"azVms.pickProcess": "Pick Process",
"azVms.deploySubpathDescription": "The default subpath of a workspace folder to use when deploying. If set, you will not be prompted for the folder path when deploying.",
"azVms.projectSubpathDescription": "The default subpath of a workspace folder to use for project operations. This is only necessary if you have multiple projects in one workspace. See https://aka.ms/AA4nmfy for more information.",
"azVms.showCoreToolsWarningDescription": "Show a warning if your installed version of Azure Functions Core Tools is out-of-date.",
"azVms.showMultiCoreToolsWarningDescription": "Show a warning if multiple installs of Azure Functions Core Tools are detected.",
"azVms.show64BitWarningDescription": "Show a warning to install a 64-bit version of the Azure Functions Core Tools when you create a .NET Framework project.",
"azVms.showProjectWarningDescription": "Show a warning when an Azure Functions project was detected that has not been initialized for use in VS Code.",
"azVms.showDebugConfigWarningDescription": "Show a warning when an Azure Functions project was detected that has an out-of-date debug configuration.",
"azVms.showJavaDeployConfigWarningDescription": "Show a warning when an Azure Functions Java project was detected that has an out-of-date deploy configuration.",
"azVms.showPythonVenvWarningDescription": "Show a warning when an Azure Functions Python project was detected that does not have a virtual environment.",
"azVms.showTargetFrameworkWarningDescription": "Show a warning when an Azure Functions .NET project was detected that has mismatched target frameworks.",
"azVms.showDeploySubpathWarningDescription": "Show a warning when the \"deploySubpath\" setting does not match the selected folder for deploying.",
"azVms.startStreamingLogs": "Start Streaming Logs",
"azVms.stopStreamingLogs": "Stop Streaming Logs",
"azVms.enableRemoteDebugging": "Enable remote debugging, an experimental feature that only supports Java-based Functions Apps.",
"azVms.deleteProxy": "Delete Proxy...",
"azVms.pickProcessTimeoutDescription": "The timeout (in seconds) to be used when searching for the Azure Functions host process. Since a build is required every time you F5, you may need to adjust this based on how long your build takes.",
"azVms.templateVersion": "A runtime release version (any runtime) that species which templates will be used rather than the latest templates. This version will be used for ALL runtimes. (Requires a restart of VS Code to take effect)",
"azVms.projectOpenBehaviorDescription": "The behavior to use after creating a new project. The options are \"AddToWorkspace\", \"OpenInNewWindow\", or \"OpenInCurrentWindow\".",
"azVms.uninstallFuncCoreTools": "Uninstall Azure Functions Core Tools",
"azVms.installOrUpdateFuncCoreTools": "Install or Update Azure Functions Core Tools",
"azVms.preDeployTaskDescription": "The name of the task to run before zip deployments.",
"azVms.problemMatchers.funcWatch.label": "Azure Functions problems (watch mode)",
"azVms.projectRuntime.v1Description": "Azure Functions v1 (.NET Framework)",
"azVms.projectRuntime.v2Description": "Azure Functions v2 (.NET Standard)",
"azVms.projectRuntime.betaDescription": "DEPRECATED Use \"~2\" instead.",
"azVms.projectLanguage.previewDescription": "(Preview)",
"azVms.pythonVenvDescription": "The name of the Python virtual environment used for your project. A virtual environment is required to debug and deploy Python functions.",
"azVms.createPythonVenv": "Create a virtual environment when creating a new Python project.",
"azVms.enableSlotsDescription": "Enable preview support for slots.",
"azVms.redeploy": "Redeploy",
"azVms.viewDeploymentLogs": "View Deployment Logs",
"azVms.viewCommitInGitHub": "View Commit in GitHub",
"azVms.ConnectToGitHub": "Connect to GitHub Repository...",
"azVms.disconnectRepo": "Disconnect from Repo...",
"azVms.createSlot": "Create Slot...",
"azVms.swapSlot": "Swap Slot...",
"azVms.advancedCreationDescription": "Enables advanced creation of Azure Function Apps, which will prompt for several additional values instead of using a default.",
"azVms.toggleAppSettingVisibility": "Toggle App Setting Visibility.",
"azVms.addBinding": "Add binding...",
"azVms.setAzureWebJobsStorage": "Set AzureWebJobsStorage...",
"azVms.enableProjectTree": "Enable preview support for a tree view of the local project."
}

1
resources/azure-vm.svg Normal file
Просмотреть файл

@ -0,0 +1 @@
<svg viewBox="0 0 50 50" class="fxs-portal-svg" role="presentation" focusable="false" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="FxSymbol0-064"><g><title></title><path d="M31 38H19c1.634 5.768-.386 7-10 7v3h32v-3c-9.614 0-11.636-1.229-10-7z" class="msportalfx-svg-c04" fill="#7a7a7a"></path><path d="M46.979 2H2.717C1.213 2 0 3.325 0 4.805v30.412c0 1.471 1.213 2.782 2.717 2.782h44.262c1.501 0 3.021-1.31 3.021-2.782V4.805C50 3.321 48.48 2 46.979 2" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path opacity=".2" d="M47.01 2.003l-.031-.002H2.716c-1.504 0-2.717 1.324-2.717 2.805v30.411C-.001 36.69 1.212 38 2.716 38h1.053L47.01 2.003z" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M46 6v28H4V6z" class="msportalfx-svg-c15" fill="#59b4d9"></path><path d="M9 44.979L41 45v3H9z" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path d="M25.517 4.095a.71.71 0 1 1-1.42 0 .71.71 0 0 1 1.42 0" class="msportalfx-svg-c13" fill="#b8d432"></path><path d="M25.027 18.802a.271.271 0 0 1-.13-.036l-8.56-4.858a.257.257 0 0 1-.128-.221c0-.091.05-.175.128-.22l8.508-4.826a.265.265 0 0 1 .256 0l8.563 4.859a.25.25 0 0 1 .127.22.251.251 0 0 1-.127.22l-8.505 4.825a.274.274 0 0 1-.132.037" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".7" d="M23.797 30.608a.245.245 0 0 1-.13-.034l-8.534-4.842a.25.25 0 0 1-.133-.221v-9.717c0-.092.05-.175.132-.221a.26.26 0 0 1 .261 0l8.533 4.84a.265.265 0 0 1 .126.223v9.717a.26.26 0 0 1-.126.221.274.274 0 0 1-.129.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".4" d="M26.213 30.608a.28.28 0 0 1-.134-.034.257.257 0 0 1-.125-.221v-9.656c0-.09.05-.174.125-.221l8.533-4.84a.257.257 0 0 1 .257 0 .252.252 0 0 1 .131.22v9.655a.25.25 0 0 1-.131.221l-8.53 4.842a.236.236 0 0 1-.126.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M15 8H6v9h3v-6h6zM6 23v9h9v-3H9v-6zm29 9h9v-9h-3v6h-6zm9-15V8h-9v3h6v6z" class="msportalfx-svg-c01" fill="#ffffff"></path></g></svg>

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

6
resources/azure.svg Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="28" width="28" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#0072C6" d="M11.423,44.326l23.623-4.156L22.894,25.748l6.328-17.346L50,44.33L11.423,44.326z M27.566,5.67L11.469,40.109v-0.034H0l12.717-21.975L27.566,5.67z" />
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 337 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-action-green{fill:#89d185}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 1.414L11.414 6H16v1.414L7.414 16H5v-1.234L7.371 10H4V8.764L4.382 8H2.019V6H0V2.018h2.019V0H6v2.018h1.373L8.382 0H16v1.414z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M9 7h6l-8 8H6l2.984-6H5l.5-1H6V7l.5-1H8V3l1-2h6L9 7z" id="iconBg"/><path class="icon-vs-action-green" d="M7 3.018H5V1H3.019v2.018H1V5h2.019v2H5V5h2V3.018z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 684 B

Просмотреть файл

@ -0,0 +1 @@
<svg viewBox="0 0 50 50" class="fxs-portal-svg" role="presentation" focusable="false" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="FxSymbol0-064"><g><title></title><path d="M31 38H19c1.634 5.768-.386 7-10 7v3h32v-3c-9.614 0-11.636-1.229-10-7z" class="msportalfx-svg-c04" fill="#7a7a7a"></path><path d="M46.979 2H2.717C1.213 2 0 3.325 0 4.805v30.412c0 1.471 1.213 2.782 2.717 2.782h44.262c1.501 0 3.021-1.31 3.021-2.782V4.805C50 3.321 48.48 2 46.979 2" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path opacity=".2" d="M47.01 2.003l-.031-.002H2.716c-1.504 0-2.717 1.324-2.717 2.805v30.411C-.001 36.69 1.212 38 2.716 38h1.053L47.01 2.003z" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M46 6v28H4V6z" class="msportalfx-svg-c15" fill="#59b4d9"></path><path d="M9 44.979L41 45v3H9z" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path d="M25.517 4.095a.71.71 0 1 1-1.42 0 .71.71 0 0 1 1.42 0" class="msportalfx-svg-c13" fill="#b8d432"></path><path d="M25.027 18.802a.271.271 0 0 1-.13-.036l-8.56-4.858a.257.257 0 0 1-.128-.221c0-.091.05-.175.128-.22l8.508-4.826a.265.265 0 0 1 .256 0l8.563 4.859a.25.25 0 0 1 .127.22.251.251 0 0 1-.127.22l-8.505 4.825a.274.274 0 0 1-.132.037" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".7" d="M23.797 30.608a.245.245 0 0 1-.13-.034l-8.534-4.842a.25.25 0 0 1-.133-.221v-9.717c0-.092.05-.175.132-.221a.26.26 0 0 1 .261 0l8.533 4.84a.265.265 0 0 1 .126.223v9.717a.26.26 0 0 1-.126.221.274.274 0 0 1-.129.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".4" d="M26.213 30.608a.28.28 0 0 1-.134-.034.257.257 0 0 1-.125-.221v-9.656c0-.09.05-.174.125-.221l8.533-4.84a.257.257 0 0 1 .257 0 .252.252 0 0 1 .131.22v9.655a.25.25 0 0 1-.131.221l-8.53 4.842a.236.236 0 0 1-.126.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M15 8H6v9h3v-6h6zM6 23v9h9v-3H9v-6zm29 9h9v-9h-3v6h-6zm9-15V8h-9v3h6v6z" class="msportalfx-svg-c01" fill="#ffffff"></path></g></svg>

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Просмотреть файл

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#e0e0e0" viewBox="0 0 24 24" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svg="http://www.w3.org/2000/svg">
<rect x="8" y="4" width="13" height="2"/>
<rect x="8" y="11" width="13" height="2"/>
<rect x="8" y="18" width="13" height="2"/>
<rect x="2" y="3" width="4" height="4"/>
<rect x="2" y="10" width="4" height="4"/>
<rect x="2" y="17" width="4" height="4"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 424 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-folder{fill:#c09553}.icon-vs-fg{fill:#2b282e}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0V0z" id="canvas"/><path class="icon-vs-out" d="M14.996 9.418V10H16v1.352l-1.004.96v.188c0 .827-.673 1.5-1.5 1.5h-.266l-2.092 2H9.441l.961-2H1.5C.673 14 0 13.327 0 12.5v-10C0 1.673.673 1 1.5 1h8.11l1 2h2.886c.827 0 1.5.673 1.5 1.5V7H16v1.414l-1.004 1.004z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M2 3h6.374l.5 1H2V3z" id="iconBg" style="display: none;"/><g id="iconFg"><path class="icon-vs-bg" d="M12 8l-2 4h2.5L11 15l4-4h-3l3-3z"/><path class="icon-folder" d="M13.996 7V4.5a.5.5 0 0 0-.5-.5H9.992l-1-2H1.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h6.882l3-6h2.614zM2 4V3h6.374l.5 1H2z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 894 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M13 0v3H9.149l4.617 4.652-2.124 2.115L9 7.106V16H6V7.107L3.361 9.766 1.23 7.654 5.851 3H2V0h11z" id="outline" style="display: none;"/><g id="iconBg"><path class="icon-vs-action-blue" d="M3 1h9v1H3V1zm8.646 7.352l.708-.704L7.5 2.758l-4.855 4.89.71.704L7 4.681V15h1V4.681l3.646 3.671z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 580 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2a292c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 0v15H1V0h14z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M4 2v11h9V2H4zm7 3v1H6V4h5v1z" id="iconFg" style="display: none;"/><g id="iconBg"><path class="icon-vs-bg" d="M2 1v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h12V1H2zm11 12H4V2h9v11zm-2-9v2H6V4h5z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 599 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#2D2D30"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#C5C5C5"/></svg>

После

Ширина:  |  Высота:  |  Размер: 986 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>Visible_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,9v1H11.444a3.965,3.965,0,0,1-6.888,0H0V9A8,8,0,0,1,16,9Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M15,9H14A6,6,0,0,0,2,9H1A7,7,0,0,1,15,9ZM11,8A3,3,0,1,1,8,5,3,3,0,0,1,11,8ZM10,8a2,2,0,1,0-2,2A2,2,0,0,0,10,8Z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 629 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 0v3.043l5 6V16h6V9.043l5-6V0H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M7 14h2V8.319l5-6V2H2v.319l5 6V14z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 15H6V8.681l-5-6V1h14v1.681l-5 6V15zm-3-1h2V8.319l5-6V2H2v.319l5 6V14z" id="iconBg"/></svg>

После

Ширина:  |  Высота:  |  Размер: 596 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-green{fill:#388a34}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 1.414L11.414 6H16v1.414L7.414 16H5v-1.234L7.371 10H4V8.764L4.382 8H2.019V6H0V2.018h2.019V0H6v2.018h1.373L8.382 0H16v1.414z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M9 7h6l-8 8H6l2.984-6H5l.5-1H6V7l.5-1H8V3l1-2h6L9 7z" id="iconBg"/><path class="icon-vs-action-green" d="M7 3.018H5V1H3.019v2.018H1V5h2.019v2H5V5h2V3.018z" id="colorAction"/></svg>

После

Ширина:  |  Высота:  |  Размер: 684 B

Просмотреть файл

@ -0,0 +1 @@
<svg viewBox="0 0 50 50" class="fxs-portal-svg" role="presentation" focusable="false" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="FxSymbol0-064"><g><title></title><path d="M31 38H19c1.634 5.768-.386 7-10 7v3h32v-3c-9.614 0-11.636-1.229-10-7z" class="msportalfx-svg-c04" fill="#7a7a7a"></path><path d="M46.979 2H2.717C1.213 2 0 3.325 0 4.805v30.412c0 1.471 1.213 2.782 2.717 2.782h44.262c1.501 0 3.021-1.31 3.021-2.782V4.805C50 3.321 48.48 2 46.979 2" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path opacity=".2" d="M47.01 2.003l-.031-.002H2.716c-1.504 0-2.717 1.324-2.717 2.805v30.411C-.001 36.69 1.212 38 2.716 38h1.053L47.01 2.003z" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M46 6v28H4V6z" class="msportalfx-svg-c15" fill="#59b4d9"></path><path d="M9 44.979L41 45v3H9z" class="msportalfx-svg-c03" fill="#a0a1a2"></path><path d="M25.517 4.095a.71.71 0 1 1-1.42 0 .71.71 0 0 1 1.42 0" class="msportalfx-svg-c13" fill="#b8d432"></path><path d="M25.027 18.802a.271.271 0 0 1-.13-.036l-8.56-4.858a.257.257 0 0 1-.128-.221c0-.091.05-.175.128-.22l8.508-4.826a.265.265 0 0 1 .256 0l8.563 4.859a.25.25 0 0 1 .127.22.251.251 0 0 1-.127.22l-8.505 4.825a.274.274 0 0 1-.132.037" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".7" d="M23.797 30.608a.245.245 0 0 1-.13-.034l-8.534-4.842a.25.25 0 0 1-.133-.221v-9.717c0-.092.05-.175.132-.221a.26.26 0 0 1 .261 0l8.533 4.84a.265.265 0 0 1 .126.223v9.717a.26.26 0 0 1-.126.221.274.274 0 0 1-.129.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path opacity=".4" d="M26.213 30.608a.28.28 0 0 1-.134-.034.257.257 0 0 1-.125-.221v-9.656c0-.09.05-.174.125-.221l8.533-4.84a.257.257 0 0 1 .257 0 .252.252 0 0 1 .131.22v9.655a.25.25 0 0 1-.131.221l-8.53 4.842a.236.236 0 0 1-.126.034" class="msportalfx-svg-c01" fill="#ffffff"></path><path d="M15 8H6v9h3v-6h6zM6 23v9h9v-3H9v-6zm29 9h9v-9h-3v6h-6zm9-15V8h-9v3h6v6z" class="msportalfx-svg-c01" fill="#ffffff"></path></g></svg>

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Просмотреть файл

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#464f59" viewBox="0 0 24 24" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svg="http://www.w3.org/2000/svg">
<rect x="8" y="4" width="13" height="2"/>
<rect x="8" y="11" width="13" height="2"/>
<rect x="8" y="18" width="13" height="2"/>
<rect x="2" y="3" width="4" height="4"/>
<rect x="2" y="10" width="4" height="4"/>
<rect x="2" y="17" width="4" height="4"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 424 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-folder{fill:#dcb67a}.icon-vs-fg{fill:#f0eff1}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0V0z" id="canvas"/><path class="icon-vs-out" d="M14.996 9.418V10H16v1.352l-1.004.96v.188c0 .827-.673 1.5-1.5 1.5h-.266l-2.092 2H9.441l.961-2H1.5C.673 14 0 13.327 0 12.5v-10C0 1.673.673 1 1.5 1h8.11l1 2h2.886c.827 0 1.5.673 1.5 1.5V7H16v1.414l-1.004 1.004z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M2 3h6.374l.5 1H2V3z" id="iconBg" style="display: none;"/><g id="iconFg"><path class="icon-vs-bg" d="M12 8l-2 4h2.5L11 15l4-4h-3l3-3z"/><path class="icon-folder" d="M13.996 7V4.5a.5.5 0 0 0-.5-.5H9.992l-1-2H1.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h6.882l3-6h2.614zM2 4V3h6.374l.5 1H2z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 894 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M13 0v3H9.149l4.617 4.652-2.124 2.115L9 7.106V16H6V7.107L3.361 9.766 1.23 7.654 5.851 3H2V0h11z" id="outline" style="display: none;"/><g id="iconBg"><path class="icon-vs-action-blue" d="M3 1h9v1H3V1zm8.646 7.352l.708-.704L7.5 2.758l-4.855 4.89.71.704L7 4.681V15h1V4.681l3.646 3.671z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 580 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 0v15H1V0h14z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M4 2v11h9V2H4zm7 3v1H6V4h5v1z" id="iconFg" style="display: none;"/><g id="iconBg"><path class="icon-vs-bg" d="M2 1v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h1v1H2v1h12V1H2zm11 12H4V2h9v11zm-2-9v2H6V4h5z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 599 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#F6F6F6"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#424242"/></svg>

После

Ширина:  |  Высота:  |  Размер: 986 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>Visible_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,9v1H11.444a3.965,3.965,0,0,1-6.888,0H0V9A8,8,0,0,1,16,9Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M15,9H14A6,6,0,0,0,2,9H1A7,7,0,0,1,15,9ZM11,8A3,3,0,1,1,8,5,3,3,0,0,1,11,8ZM10,8a2,2,0,1,0-2,2A2,2,0,0,0,10,8Z"/></g></svg>

После

Ширина:  |  Высота:  |  Размер: 629 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 0v3.043l5 6V16h6V9.043l5-6V0H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M7 14h2V8.319l5-6V2H2v.319l5 6V14z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 15H6V8.681l-5-6V1h14v1.681l-5 6V15zm-3-1h2V8.319l5-6V2H2v.319l5 6V14z" id="iconBg"/></svg>

После

Ширина:  |  Высота:  |  Размер: 596 B

Просмотреть файл

@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ComputeManagementModels } from 'azure-arm-compute';
import { NetworkManagementModels } from 'azure-arm-network';
import { IResourceGroupWizardContext } from 'vscode-azureextensionui';
export interface IVirtualMachineWizardContext extends IResourceGroupWizardContext {
/**
* The newly created Virtual Machine
* This will be defined after `VirtualMachineCreateStep.execute` occurs.
*/
virtualMachine?: ComputeManagementModels.VirtualMachine;
/**
* The name of the new VM.
* This will be defined after `VirtualMachineNameStep.prompt` occurs.
*/
newVirtualMachineName?: string;
/**
* The hardware profile of the new VM. Specifies the size of the virtual machine.
* This will be defined after `VirtualMachineNameStep.execute` occurs.
*/
hardwareProfile?: ComputeManagementModels.HardwareProfile;
/**
* The size of the VM. The default value is `Standard_D2s_v3`.
*/
size?: string;
/**
* The storage profile of the new VM. Specifies information about the image to use for the disk.
* This will be defined after `VirtualMachineNameStep.execute` occurs.
*/
storageProfile?: ComputeManagementModels.StorageProfile;
/**
* The image used to create the VM. The default is `Ubuntu Server 18.04 LTS`.
*/
image?: ComputeManagementModels.ImageReference;
/**
* The network profile of the new VM. This is used to link the VM to a Network Interface.
* This will be defined after `VirtualMachineNameStep.execute` occurs.
*/
networkProfile?: NetworkManagementModels.NetworkProfile;
/**
* The network interface of the new VM. This contains all the ipConfigurations such as public IP and subnet
* This will be defined after `NetworkInterfaceCreateStep.execute`
*/
networkInterface?: NetworkManagementModels.NetworkInterface;
/**
* The name to use for the new network interface.
* It is set to newVirtualMachineName123 by default, where 123 are random 0-9 digits.
*/
newNetworkInterfaceName?: string;
/**
* The network security group for the new VM. It contains the security rules that control opening ports for SSH/HTTP/HTTPS.
* This will be defined after `NetworkSecurityGroupCreateStep.execute`
*/
networkSecurityGroup?: NetworkManagementModels.NetworkSecurityGroup;
/**
* The public IP address for the new VM. This is the public IP address that the user connects to.
* This will be defined after `PublicIpCreateStep.execute`
*/
publicIpAddress?: NetworkManagementModels.PublicIPAddress;
/**
* The subnet for the new VM.
* This will be defined after `SubnetCreateStep.execute`
*/
subnet?: NetworkManagementModels.Subnet;
/**
* The virutal network for the new VM.
* This will be defined after `VirtualNetworkCreateStep.execute`
*/
virtualNetwork?: NetworkManagementModels.VirtualNetwork;
/**
* The username to connect to the VM via SSH. It defaults to `azureuser`
*/
adminUsername?: string;
/**
* The addressPrefix that is used for subnets and virtual networks. It defaults to `10.1.0.0/24`.
*/
addressPrefix?: string;
}

Просмотреть файл

@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkManagementClient, NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { localize } from '../../localize';
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class NetworkInterfaceCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 250;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const networkClient: NetworkManagementClient = createAzureClient(context, NetworkManagementClient);
const location: string = nonNullValueAndProp(context.location, 'name');
const vmName: string = nonNullProp(context, 'newVirtualMachineName');
// this is the naming convention used by the portal
context.newNetworkInterfaceName = context.newNetworkInterfaceName || this.appendThreeRandomDigits(vmName);
const publicIpAddress: NetworkManagementModels.PublicIPAddress = nonNullProp(context, 'publicIpAddress');
const subnet: NetworkManagementModels.Subnet = nonNullProp(context, 'subnet');
const networkInterfaceProps: NetworkManagementModels.NetworkInterface = {
location, ipConfigurations: [{ name: context.newNetworkInterfaceName, publicIPAddress: publicIpAddress, subnet: subnet }]
};
const creatingNi: string = localize('creatingNi', 'Creating network interface...');
progress.report({ message: creatingNi });
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
context.networkInterface = await networkClient.networkInterfaces.createOrUpdate(rgName, context.newNetworkInterfaceName, networkInterfaceProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.networkInterface;
}
private appendThreeRandomDigits(niName: string): string {
for (let i: number = 0; i < 3; i += 1) {
// as this isn't being used for security purposes, it should be sufficient to use Math.random()
// tslint:disable-next-line: insecure-random
niName += Math.round(Math.random() * 9);
}
return niName;
}
}

Просмотреть файл

@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkManagementClient, NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { localize } from '../../localize';
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class NetworkSecurityGroupCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 250;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const networkClient: NetworkManagementClient = createAzureClient(context, NetworkManagementClient);
const location: string = nonNullValueAndProp(context.location, 'name');
// when creating a VM on the portal, this is the suffix that is added to the network security group
const nsgName: string = nonNullProp(context, 'newVirtualMachineName') + '-nsg';
const networkInterface: NetworkManagementModels.NetworkInterface = nonNullProp(context, 'networkInterface');
const networkSecurityGroupProps: NetworkManagementModels.NetworkSecurityGroup = {
name: nsgName, location, securityRules: [
{ name: 'SSH', protocol: 'TCP', sourcePortRange: '*', destinationPortRange: '22', sourceAddressPrefix: '*', destinationAddressPrefix: '*', access: 'Allow', priority: 340, direction: 'Inbound' },
{ name: 'HTTPS', protocol: 'TCP', sourcePortRange: '*', destinationPortRange: '443', sourceAddressPrefix: '*', destinationAddressPrefix: '*', access: 'Allow', priority: 320, direction: 'Inbound' },
{ name: 'HTTP', protocol: 'TCP', sourcePortRange: '*', destinationPortRange: '80', sourceAddressPrefix: '*', destinationAddressPrefix: '*', access: 'Allow', priority: 300, direction: 'Inbound' }
],
networkInterfaces: [networkInterface]
};
const creatingNsg: string = localize('creatingNsg', 'Creating network security group...');
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
progress.report({ message: creatingNsg });
context.networkSecurityGroup = await networkClient.networkSecurityGroups.createOrUpdate(rgName, nsgName, networkSecurityGroupProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.networkSecurityGroup && !!context.location && !!context.newVirtualMachineName && !!context.networkInterface && !!context.resourceGroup;
}
}

Просмотреть файл

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkManagementClient, NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { localize } from '../../localize';
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class PublicIpCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 210;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const networkClient: NetworkManagementClient = createAzureClient(context, NetworkManagementClient);
const location: string = nonNullValueAndProp(context.location, 'name');
const publicIpProps: NetworkManagementModels.PublicIPAddress = { publicIPAddressVersion: 'IPv4', sku: { name: 'Standard' }, publicIPAllocationMethod: 'Static', location };
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
// when creating a VM on the portal, this is the suffix that is added to the public IP address
const ipName: string = nonNullProp(context, 'newVirtualMachineName') + '-ip';
const creatingIp: string = localize('creatingIp', 'Creating public IP addresss...');
progress.report({ message: creatingIp });
context.publicIpAddress = await networkClient.publicIPAddresses.createOrUpdate(rgName, ipName, publicIpProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.publicIpAddress;
}
}

Просмотреть файл

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkManagementClient, NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class SubnetCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 230;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const networkClient: NetworkManagementClient = createAzureClient(context, NetworkManagementClient);
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
const vnetName: string = nonNullValueAndProp(context.virtualNetwork, 'name');
// this is the name the portal uses
const subnetName: string = 'default';
const subnetProps: NetworkManagementModels.Subnet = { addressPrefix: nonNullProp(context, 'addressPrefix'), name: subnetName };
progress.report({ message: 'Creating subnet...' });
context.subnet = await networkClient.subnets.createOrUpdate(rgName, vnetName, subnetName, subnetProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.subnet;
}
}

Просмотреть файл

@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ComputeManagementClient, ComputeManagementModels } from 'azure-arm-compute';
import { NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { localize } from '../../localize';
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { getSshKey } from "../getSshKey";
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class VirtualMachineCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 260;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const computeClient: ComputeManagementClient = createAzureClient(context, ComputeManagementClient);
// tslint:disable-next-line: strict-boolean-expressions
const hardwareProfile: ComputeManagementModels.HardwareProfile = { vmSize: context.size || 'Standard_D2s_v3' };
const vmName: string = nonNullProp(context, 'newVirtualMachineName');
const storageProfile: ComputeManagementModels.StorageProfile = {
// tslint:disable-next-line: strict-boolean-expressions
imageReference: context.image || { offer: 'UbuntuServer', publisher: 'Canonical', sku: '18.04-LTS', version: 'latest' },
osDisk: { name: vmName, createOption: 'fromImage', managedDisk: { storageAccountType: 'Premium_LRS' } }
};
const networkInterface: NetworkManagementModels.NetworkInterface = nonNullProp(context, 'networkInterface');
const networkProfile: ComputeManagementModels.NetworkProfile = { networkInterfaces: [{ id: networkInterface.id }] };
// tslint:disable-next-line: strict-boolean-expressions
context.adminUsername = context.adminUsername || 'azureuser';
const linuxConfiguration: ComputeManagementModels.LinuxConfiguration = {
disablePasswordAuthentication: true, ssh: {
publicKeys: [{
keyData: await getSshKey(),
// because this is a Linux VM, use '/' as path separator rather than using path.join()
path: `/home/${context.adminUsername}/.ssh/authorized_keys`
}]
}
};
const osProfile: ComputeManagementModels.OSProfile = { computerName: vmName, adminUsername: context.adminUsername, linuxConfiguration };
const location: string = nonNullValueAndProp(context.location, 'name');
const virtualMachineProps: ComputeManagementModels.VirtualMachine = { location, hardwareProfile, storageProfile, networkProfile, osProfile };
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
const creatingVm: string = localize('creatingVm', 'Creating virtual machine...');
progress.report({ message: creatingVm });
context.virtualMachine = await computeClient.virtualMachines.createOrUpdate(rgName, vmName, virtualMachineProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.virtualMachine && !!context.newVirtualMachineName && !!context.networkInterface && !!context.location && !!context.resourceGroup;
}
}

Просмотреть файл

@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ComputeManagementClient, ComputeManagementModels } from "azure-arm-compute";
import { AzureNameStep, createAzureClient, IAzureNamingRules, resourceGroupNamingRules } from "vscode-azureextensionui";
import { ext } from "../../extensionVariables";
import { localize } from "../../localize";
import { nonNullProp, nonNullValueAndProp } from "../../utils/nonNull";
import { IVirtualMachineWizardContext } from "./IVirtualMachineWizardContext";
export const virtualMachineNamingRules: IAzureNamingRules = {
minLength: 1,
maxLength: 64,
// cannot accept charactres that are invalid for Linux OS computername which is basically any non-alphanumeric except . and -
invalidCharsRegExp: /[^a-zA-Z0-9\.\-]/
};
export class VirtualMachineNameStep extends AzureNameStep<IVirtualMachineWizardContext> {
public async prompt(wizardContext: IVirtualMachineWizardContext): Promise<void> {
const namingRules: IAzureNamingRules[] = [resourceGroupNamingRules];
namingRules.push(virtualMachineNamingRules);
const rgName: string = wizardContext.newResourceGroupName || nonNullValueAndProp(wizardContext.resourceGroup, 'name');
const suggestedName: string | undefined = await this.generateRelatedName(wizardContext, rgName, namingRules);
const prompt: string = localize('virtualMachineNamePrompt', 'Enter a globally unique name for the new virutalMachine.');
wizardContext.newVirtualMachineName = (await ext.ui.showInputBox({
value: suggestedName,
prompt,
validateInput: async (value: string | undefined): Promise<string | undefined> => await this.validateVirtualMachineName(wizardContext, value)
})).trim();
}
public shouldPrompt(wizardContext: IVirtualMachineWizardContext): boolean {
return !wizardContext.newVirtualMachineName && !wizardContext.virtualMachine;
}
protected async isRelatedNameAvailable(wizardContext: IVirtualMachineWizardContext, name: string): Promise<boolean> {
return await this.isNameAvailable(wizardContext, name);
}
private async validateVirtualMachineName(wizardContext: IVirtualMachineWizardContext, name: string | undefined): Promise<string | undefined> {
name = name ? name.trim() : '';
if (name.length < virtualMachineNamingRules.minLength || name.length > virtualMachineNamingRules.maxLength) {
return localize('invalidLength', 'The name must be between {0} and {1} characters.', virtualMachineNamingRules.minLength, virtualMachineNamingRules.maxLength);
} else if (name.match(virtualMachineNamingRules.invalidCharsRegExp) !== null) {
return localize('invalidChars', "The name can only contain alphanumeric characters and the symbols .-");
} else if (name.endsWith('.') || name.endsWith('-')) {
return localize('invalidEndingChar', "The name cannot end in a period or hyphen.");
} else if (!await this.isNameAvailable(wizardContext, name)) {
return localize('nameAlreadyExists', 'Virtual machine name "{0}" already exists in resource group "{1}".', name, nonNullValueAndProp(wizardContext.resourceGroup, 'name'));
} else {
return undefined;
}
}
private async isNameAvailable(wizardContext: IVirtualMachineWizardContext, name: string): Promise<boolean> {
// Virtual machine name must be unique in the current resource group.
if (wizardContext.resourceGroup) {
// only need to check if user used an existing resource group
const computeClient: ComputeManagementClient = createAzureClient(wizardContext, ComputeManagementClient);
const vmsInRg: ComputeManagementModels.VirtualMachineListResult = await computeClient.virtualMachines.list(nonNullProp(wizardContext.resourceGroup, 'name'));
if (vmsInRg.find((vm: ComputeManagementModels.VirtualMachine) => vm.name === name)) {
return false;
}
}
return true;
}
}

Просмотреть файл

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkManagementClient, NetworkManagementModels } from 'azure-arm-network';
import { Progress } from "vscode";
import { AzureWizardExecuteStep, createAzureClient } from "vscode-azureextensionui";
import { nonNullProp, nonNullValueAndProp } from '../../utils/nonNull';
import { IVirtualMachineWizardContext } from './IVirtualMachineWizardContext';
export class VirtualNetworkCreateStep extends AzureWizardExecuteStep<IVirtualMachineWizardContext> {
public priority: number = 220;
public async execute(context: IVirtualMachineWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const networkClient: NetworkManagementClient = createAzureClient(context, NetworkManagementClient);
const location: string = nonNullValueAndProp(context.location, 'name');
const virtualNetworkProps: NetworkManagementModels.VirtualNetwork = { location, addressSpace: { addressPrefixes: [nonNullProp(context, 'addressPrefix')] } };
const rgName: string = nonNullValueAndProp(context.resourceGroup, 'name');
const vnName: string = nonNullProp(context, 'newVirtualMachineName') + '-vnet';
progress.report({ message: 'Creating virtual network...' });
context.virtualNetwork = await networkClient.virtualNetworks.createOrUpdate(rgName, vnName, virtualNetworkProps);
}
public shouldExecute(context: IVirtualMachineWizardContext): boolean {
return !context.virtualNetwork;
}
}

Просмотреть файл

@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IActionContext } from "vscode-azureextensionui";
import { ext } from "../../extensionVariables";
import { SubscriptionTreeItem } from '../../tree/SubscriptionTreeItem';
export async function createVirtualMachine(context: IActionContext, node?: SubscriptionTreeItem | undefined): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker<SubscriptionTreeItem>(SubscriptionTreeItem.contextValue, context);
}
await node.createChild(context);
}

38
src/commands/getSshKey.ts Normal file
Просмотреть файл

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from "os";
import { Terminal, window } from "vscode";
import { IParsedError, parseError } from "vscode-azureextensionui";
import { cpUtils } from "../utils/cpUtils";
import { delay } from "../utils/delay";
export async function getSshKey(): Promise<string> {
try {
return await cpUtils.executeCommand(undefined, undefined, 'cat ~/.ssh/id_rsa.pub');
} catch (error) {
const pError: IParsedError = parseError(error);
if (pError.message.includes('No such file or directory')) {
const sshKeygenTerminal: string = 'ssh-keygen';
const sshKeygenCmd: string = 'ssh-keygen -t rsa -b 2048';
// tslint:disable-next-line: strict-boolean-expressions
const terminal: Terminal = window.terminals.find((activeTerminal: Terminal) => { return activeTerminal.name === sshKeygenTerminal; }) || window.createTerminal(sshKeygenTerminal);
terminal.sendText(sshKeygenCmd, true);
await delay(1000);
// Enter file in which to save the key
terminal.sendText(os.EOL, true);
await delay(1000);
//Enter passphrase (empty for no passphrase):
terminal.sendText(os.EOL, true);
await delay(1000);
// Enter same passphrase again:
terminal.sendText(os.EOL, true);
return await cpUtils.executeCommand(undefined, undefined, 'cat ~/.ssh/id_rsa.pub');
}
throw error;
}
}

Просмотреть файл

@ -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 { AzureTreeItem, IActionContext } from 'vscode-azureextensionui';
import { ext } from '../extensionVariables';
import { VirtualMachineTreeItem } from '../tree/VirtualMachineTreeItem';
export async function openInPortal(context: IActionContext, node?: AzureTreeItem): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker<AzureTreeItem>(VirtualMachineTreeItem.contextValue, context);
}
await node.openInPortal();
}

48
src/extension.ts Normal file
Просмотреть файл

@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { AzExtTreeDataProvider, AzureTreeItem, AzureUserInput, callWithTelemetryAndErrorHandling, createApiProvider, createAzExtOutputChannel, createTelemetryReporter, IActionContext, registerCommand, registerUIExtensionVariables } from 'vscode-azureextensionui';
// tslint:disable-next-line:no-submodule-imports
import { AzureExtensionApiProvider } from 'vscode-azureextensionui/api';
import { createVirtualMachine } from './commands/createVirtualMachine/createVirtualMachine';
import { openInPortal } from './commands/openInPortal';
import { ext } from './extensionVariables';
import { AzureAccountTreeItem } from './tree/AzureAccountTreeItem';
import { SubscriptionTreeItem } from './tree/SubscriptionTreeItem';
export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number; loadEndTime: number }): Promise<AzureExtensionApiProvider> {
ext.context = context;
ext.reporter = createTelemetryReporter(context);
ext.outputChannel = createAzExtOutputChannel('Azure Virtual Machines', ext.prefix);
context.subscriptions.push(ext.outputChannel);
ext.ui = new AzureUserInput(context.globalState);
registerUIExtensionVariables(ext);
await callWithTelemetryAndErrorHandling('azureVirtualMachines.activate', async (activateContext: IActionContext) => {
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');
context.subscriptions.push(vscode.window.createTreeView('azVmTree', { treeDataProvider: ext.tree }));
registerCommand('azureVirtualMachines.selectSubscriptions', () => vscode.commands.executeCommand('azure-account.selectSubscriptions'));
registerCommand('azureVirtualMachines.refresh', async (_actionContext: IActionContext, node?: AzureTreeItem) => await ext.tree.refresh(node));
registerCommand('azureVirtualMachines.loadMore', async (actionContext: IActionContext, node: AzureTreeItem) => await ext.tree.loadMore(node, actionContext));
registerCommand('azureVirtualMachines.openInPortal', openInPortal);
registerCommand('azureVirtualMachines.createVirtualMachine', async (actionContext: IActionContext, node?: SubscriptionTreeItem) => await createVirtualMachine(actionContext, node));
});
return createApiProvider([]);
}
// tslint:disable-next-line:no-empty
export function deactivateInternal(): void {
}

23
src/extensionVariables.ts Normal file
Просмотреть файл

@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, TreeView } from "vscode";
import { AzExtTreeDataProvider, AzExtTreeItem, IAzExtOutputChannel, IAzureUserInput, ITelemetryReporter } from "vscode-azureextensionui";
import { AzureAccountTreeItem } from "./tree/AzureAccountTreeItem";
/**
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
*/
export namespace ext {
export let outputChannel: IAzExtOutputChannel;
export let ui: IAzureUserInput;
export let reporter: ITelemetryReporter;
export let context: ExtensionContext;
export let tree: AzExtTreeDataProvider;
export let treeView: TreeView<AzExtTreeItem>;
export let azureAccountTreeItem: AzureAccountTreeItem;
export const prefix: string = 'azureVirtualMachines';
}

8
src/localize.ts Normal file
Просмотреть файл

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
export const localize: nls.LocalizeFunc = nls.loadMessageBundle();

Просмотреть файл

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureAccountTreeItemBase, ISubscriptionContext } from 'vscode-azureextensionui';
import { SubscriptionTreeItem } from './SubscriptionTreeItem';
export class AzureAccountTreeItem extends AzureAccountTreeItemBase {
public constructor(testAccount?: {}) {
super(undefined, testAccount);
}
public createSubscriptionTreeItem(root: ISubscriptionContext): SubscriptionTreeItem {
return new SubscriptionTreeItem(this, root);
}
}

Просмотреть файл

@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ComputeManagementClient, ComputeManagementModels } from 'azure-arm-compute';
import { AzExtTreeItem, AzureTreeItem, AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, createAzureClient, ICreateChildImplContext, LocationListStep, parseError, ResourceGroupListStep, SubscriptionTreeItemBase } from 'vscode-azureextensionui';
import { IVirtualMachineWizardContext } from '../commands/createVirtualMachine/IVirtualMachineWizardContext';
import { NetworkInterfaceCreateStep } from '../commands/createVirtualMachine/NetworkInterfaceCreateStep';
import { NetworkSecurityGroupCreateStep } from '../commands/createVirtualMachine/NetworkSecurityGroupCreateStep';
import { PublicIpCreateStep } from '../commands/createVirtualMachine/PublicIpCreateStep';
import { SubnetCreateStep } from '../commands/createVirtualMachine/SubnetCreateStep';
import { VirtualMachineCreateStep } from '../commands/createVirtualMachine/VirtualMachineCreateStep';
import { VirtualMachineNameStep } from '../commands/createVirtualMachine/VirtualMachineNameStep';
import { VirtualNetworkCreateStep } from '../commands/createVirtualMachine/VirtualNetworkCreateStep';
import { ext } from '../extensionVariables';
import { nonNullProp } from '../utils/nonNull';
import { VirtualMachineTreeItem } from './VirtualMachineTreeItem';
export class SubscriptionTreeItem extends SubscriptionTreeItemBase {
public readonly childTypeLabel: string = 'azVmVirtualMachine';
private _nextLink: string | undefined;
public hasMoreChildrenImpl(): boolean {
return this._nextLink !== undefined;
}
public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzExtTreeItem[]> {
if (clearCache) {
this._nextLink = undefined;
}
const client: ComputeManagementClient = createAzureClient(this.root, ComputeManagementClient);
let virtualMachines: ComputeManagementModels.VirtualMachineListResult;
try {
virtualMachines = this._nextLink === undefined ?
await client.virtualMachines.listAll() :
await client.virtualMachines.listNext(this._nextLink);
} catch (error) {
if (parseError(error).errorType.toLowerCase() === 'notfound') {
// This error type means the 'Microsoft.Web' provider has not been registered in this subscription
// In that case, we know there are no web apps, so we can return an empty array
// (The provider will be registered automatically if the user creates a new web app)
return [];
} else {
throw error;
}
}
this._nextLink = virtualMachines.nextLink;
return await this.createTreeItemsWithErrorHandling(
virtualMachines,
'invalidVirtualMachine',
(vm: ComputeManagementModels.VirtualMachine) => {
return new VirtualMachineTreeItem(this, vm);
},
(vm: ComputeManagementModels.VirtualMachine) => {
return vm.name;
}
);
}
public async createChildImpl(context: ICreateChildImplContext): Promise<AzureTreeItem> {
const wizardContext: IVirtualMachineWizardContext = Object.assign(context, this.root, {
resourceGroupDeferLocationStep: true,
addressPrefix: '10.1.0.0/24'
});
// prompt for resourceGroup, VM name, and location
const promptSteps: AzureWizardPromptStep<IVirtualMachineWizardContext>[] = [];
promptSteps.push(new ResourceGroupListStep());
promptSteps.push(new VirtualMachineNameStep());
LocationListStep.addStep(wizardContext, promptSteps);
// create a disk, publicIp, virtualNetwork, subnet, networkInterface, networkSecurityGroup (this has the security rules), and then virtuaMachine
const executeSteps: AzureWizardExecuteStep<IVirtualMachineWizardContext>[] = [];
// executeSteps.push(new DiskCreateStep());
executeSteps.push(new PublicIpCreateStep());
executeSteps.push(new VirtualNetworkCreateStep());
executeSteps.push(new SubnetCreateStep());
executeSteps.push(new NetworkInterfaceCreateStep());
executeSteps.push(new NetworkSecurityGroupCreateStep());
executeSteps.push(new VirtualMachineCreateStep());
const title: string = 'Create new virtual machine';
const wizard: AzureWizard<IVirtualMachineWizardContext> = new AzureWizard(wizardContext, { promptSteps, executeSteps, title });
await wizard.prompt();
context.showCreatingTreeItem(nonNullProp(wizardContext, 'newVirtualMachineName'));
await wizard.execute();
const virtualMachine: ComputeManagementModels.VirtualMachine = nonNullProp(wizardContext, 'virtualMachine');
// context.telemetry.properties.vm = virtualMachine.name;
const createNewVmMsg: string = `Created new virtual machine "${virtualMachine.name}".`;
ext.outputChannel.appendLine(createNewVmMsg);
return new VirtualMachineTreeItem(this, virtualMachine);
}
}

Просмотреть файл

@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ComputeManagementModels } from 'azure-arm-compute';
import NetworkManagementClient, { NetworkManagementModels } from 'azure-arm-network';
import { env } from 'vscode';
import { AzureParentTreeItem, AzureTreeItem, createAzureClient } from 'vscode-azureextensionui';
import { localize } from '../localize';
import { getNameFromId, getResourceGroupFromId } from '../utils/azureUtils';
import { nonNullProp, nonNullValueAndProp } from '../utils/nonNull';
import { treeUtils } from '../utils/treeUtils';
export class VirtualMachineTreeItem extends AzureTreeItem {
public get label(): string {
return `${getResourceGroupFromId(this.id).toLocaleLowerCase()}/${nonNullProp(this.virtualMachine, 'name')}`;
}
public get iconPath(): treeUtils.IThemedIconPath {
return treeUtils.getThemedIconPath('AzureVm');
}
public get id(): string {
return nonNullProp(this.virtualMachine, 'id');
}
public static contextValue: string = 'azVmVirtualMachine';
public readonly contextValue: string = VirtualMachineTreeItem.contextValue;
public virtualMachine: ComputeManagementModels.VirtualMachine;
public constructor(parent: AzureParentTreeItem, vm: ComputeManagementModels.VirtualMachine) {
super(parent);
this.virtualMachine = vm;
}
public async getHostname(): Promise<string> {
const networkClient: NetworkManagementClient = createAzureClient(this.root, NetworkManagementClient);
const rgName: string = getResourceGroupFromId(this.id);
const networkInterfaces: ComputeManagementModels.NetworkInterfaceReference[] = nonNullValueAndProp(this.virtualMachine.networkProfile, 'networkInterfaces');
if (networkInterfaces.length === 0) {
throw new Error();
}
const networkInterfaceName: string = getNameFromId(nonNullProp(networkInterfaces[0], 'id'));
const networkInterface: NetworkManagementModels.NetworkInterface = await networkClient.networkInterfaces.get(rgName, networkInterfaceName);
if (!networkInterface.ipConfigurations || networkInterface.ipConfigurations.length === 0) {
const noIpConfigs: string = localize('noIpConfigs', 'No IP configurations are associated with network interface "{0}"', networkInterface.name);
throw new Error(noIpConfigs);
}
const publicIPAddressName: string = getNameFromId(nonNullValueAndProp(networkInterface.ipConfigurations[0].publicIPAddress, 'id'));
const ip: NetworkManagementModels.PublicIPAddress = await networkClient.publicIPAddresses.get(rgName, publicIPAddressName);
const hostname: string = `${nonNullValueAndProp(this.virtualMachine.osProfile, 'adminUsername')}@${nonNullProp(ip, 'ipAddress')}`;
await env.clipboard.writeText(hostname);
return hostname;
}
}

28
src/utils/azureUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from '../localize';
function parseResourceId(id: string): RegExpMatchArray {
const matches: RegExpMatchArray | null = id.match(/\/subscriptions\/(.*)\/resourceGroups\/(.*)\/providers\/(.*)\/(.*)/);
if (matches === null || matches.length < 3) {
throw new Error(localize('InvalidResourceId', 'Invalid Azure Resource Id'));
}
return matches;
}
export function getResourceGroupFromId(id: string): string {
return parseResourceId(id)[2];
}
export function getSubscriptionFromId(id: string): string {
return parseResourceId(id)[1];
}
export function getNameFromId(id: string): string {
return parseResourceId(id)[4];
}

84
src/utils/cpUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import * as os from 'os';
import * as vscode from 'vscode';
import { localize } from '../localize';
export namespace cpUtils {
export async function executeCommand(outputChannel: vscode.OutputChannel | undefined, workingDirectory: string | undefined, command: string, ...args: string[]): Promise<string> {
const result: ICommandResult = await tryExecuteCommand(outputChannel, workingDirectory, command, ...args);
if (result.code !== 0) {
// We want to make sure the full error message is displayed to the user, not just the error code.
// If outputChannel is defined, then we simply call 'outputChannel.show()' and throw a generic error telling the user to check the output window
// If outputChannel is _not_ defined, then we include the command's output in the error itself and rely on AzureActionHandler to display it properly
if (outputChannel) {
outputChannel.show();
throw new Error(localize('azVms.commandErrorWithOutput', 'Failed to run "{0}" command. Check output window for more details.', command));
} else {
throw new Error(localize('azVms.commandError', 'Command "{0} {1}" failed with exit code "{2}":{3}{4}', command, result.formattedArgs, result.code, os.EOL, result.cmdOutputIncludingStderr));
}
} else {
if (outputChannel) {
outputChannel.appendLine(localize('finishedRunningCommand', 'Finished running command: "{0} {1}".', command, result.formattedArgs));
}
}
return result.cmdOutput;
}
export async function tryExecuteCommand(outputChannel: vscode.OutputChannel | undefined, workingDirectory: string | undefined, command: string, ...args: string[]): Promise<ICommandResult> {
return await new Promise((resolve: (res: ICommandResult) => void, reject: (e: Error) => void): void => {
let cmdOutput: string = '';
let cmdOutputIncludingStderr: string = '';
const formattedArgs: string = args.join(' ');
workingDirectory = workingDirectory || os.tmpdir();
const options: cp.SpawnOptions = {
cwd: workingDirectory,
shell: true
};
const childProc: cp.ChildProcess = cp.spawn(command, args, options);
if (outputChannel) {
outputChannel.appendLine(localize('runningCommand', 'Running command: "{0} {1}"...', command, formattedArgs));
}
childProc.stdout.on('data', (data: string | Buffer) => {
data = data.toString();
cmdOutput = cmdOutput.concat(data);
cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data);
if (outputChannel) {
outputChannel.append(data);
}
});
childProc.stderr.on('data', (data: string | Buffer) => {
data = data.toString();
cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data);
if (outputChannel) {
outputChannel.append(data);
}
});
childProc.on('error', reject);
childProc.on('close', (code: number) => {
resolve({
code,
cmdOutput,
cmdOutputIncludingStderr,
formattedArgs
});
});
});
}
export interface ICommandResult {
code: number;
cmdOutput: string;
cmdOutputIncludingStderr: string;
formattedArgs: string;
}
}

8
src/utils/delay.ts Normal file
Просмотреть файл

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export async function delay(ms: number): Promise<void> {
await new Promise<void>((resolve: () => void): NodeJS.Timer => setTimeout(resolve, ms));
}

Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
/**
* Returns a node module installed with VSCode, or undefined if it fails.
*/
export function getCoreNodeModule<T>(moduleName: string): T | undefined {
try {
// tslint:disable-next-line:non-literal-require no-unsafe-any
return require(`${vscode.env.appRoot}/node_modules.asar/${moduleName}`);
} catch (err) {
// ignore
}
try {
// tslint:disable-next-line:non-literal-require no-unsafe-any
return require(`${vscode.env.appRoot}/node_modules/${moduleName}`);
} catch (err) {
// ignore
}
return undefined;
}

38
src/utils/nonNull.ts Normal file
Просмотреть файл

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isNullOrUndefined } from 'util';
/**
* Retrieves a property by name from an object and checks that it's not null and not undefined. It is strongly typed
* for the property and will give a compile error if the given name is not a property of the source.
*/
export function nonNullProp<TSource, TKey extends keyof TSource>(source: TSource, name: TKey): NonNullable<TSource[TKey]> {
const value: NonNullable<TSource[TKey]> = <NonNullable<TSource[TKey]>>source[name];
return nonNullValue(value, <string>name);
}
/**
* Validates that a given value is not null and not undefined.
*/
export function nonNullValue<T>(value: T | undefined | null, propertyNameOrMessage?: string): T {
if (isNullOrUndefined(value)) {
throw new Error(
// tslint:disable-next-line:prefer-template
'Internal error: Expected value to be neither null nor undefined'
+ (propertyNameOrMessage ? `: ${propertyNameOrMessage}` : ''));
}
return value;
}
/**
* Validates that a given object is not null and not undefined.
* Then retrieves a property by name from that object and checks that it's not null and not undefined. It is strongly typed
* for the property and will give a compile error if the given name is not a property of the source.
*/
export function nonNullValueAndProp<TSource, TKey extends keyof TSource>(source: TSource | undefined, name: TKey): NonNullable<TSource[TKey]> {
return nonNullProp(nonNullValue(source, <string>name), name);
}

10
src/utils/openUrl.ts Normal file
Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export async function openUrl(url: string): Promise<void> {
await vscode.env.openExternal(vscode.Uri.parse(url));
}

29
src/utils/treeUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { ext } from '../extensionVariables';
export namespace treeUtils {
export interface IThemedIconPath {
light: string;
dark: string;
}
export function getIconPath(iconName: string): string {
return path.join(getResourcesPath(), `${iconName}.svg`);
}
export function getThemedIconPath(iconName: string): IThemedIconPath {
return {
light: path.join(getResourcesPath(), 'light', `${iconName}.svg`),
dark: path.join(getResourcesPath(), 'dark', `${iconName}.svg`)
};
}
function getResourcesPath(): string {
return ext.context.asAbsolutePath('resources');
}
}

Просмотреть файл

@ -0,0 +1 @@
This folder contains logic related to VS Code project files, like "settings.json", "tasks.json", and "launch.json".

Просмотреть файл

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IExtensionsJson {
recommendations?: string[];
}

Просмотреть файл

@ -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 { ConfigurationTarget, Uri, workspace, WorkspaceConfiguration } from "vscode";
import { ext } from "../extensionVariables";
/**
* Uses ext.prefix 'azureVirtualMachines' unless otherwise specified
*/
export async function updateGlobalSetting<T = string>(section: string, value: T, prefix: string = ext.prefix): Promise<void> {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix);
await projectConfiguration.update(section, value, ConfigurationTarget.Global);
}
/**
* Uses ext.prefix 'azureVirtualMachines' unless otherwise specified
*/
export async function updateWorkspaceSetting<T = string>(section: string, value: T, fsPath: string, prefix: string = ext.prefix): Promise<void> {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix, Uri.file(fsPath));
await projectConfiguration.update(section, value);
}
/**
* Uses ext.prefix 'azureVirtualMachines' unless otherwise specified
*/
export function getGlobalSetting<T>(key: string, prefix: string = ext.prefix): T | undefined {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix);
const result: { globalValue?: T } | undefined = projectConfiguration.inspect<T>(key);
return result && result.globalValue;
}
/**
* Uses ext.prefix 'azureVirtualMachines' unless otherwise specified
*/
export function getWorkspaceSetting<T>(key: string, fsPath?: string, prefix: string = ext.prefix): T | undefined {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix, fsPath ? Uri.file(fsPath) : undefined);
return projectConfiguration.get<T>(key);
}
/**
* Searches through all open folders and gets the current workspace setting (as long as there are no conflicts)
* Uses ext.prefix 'azureVirtualMachines' unless otherwise specified
*/
export function getWorkspaceSettingFromAnyFolder(key: string, prefix: string = ext.prefix): string | undefined {
if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) {
let result: string | undefined;
for (const folder of workspace.workspaceFolders) {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix, folder.uri);
const folderResult: string | undefined = projectConfiguration.get<string>(key);
if (!result) {
result = folderResult;
} else if (folderResult && result !== folderResult) {
return undefined;
}
}
return result;
} else {
return getGlobalSetting(key, prefix);
}
}

32
test/global.test.ts Normal file
Просмотреть файл

@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IHookCallbackContext } from 'mocha';
import * as vscode from 'vscode';
import { TestOutputChannel, TestUserInput } from 'vscode-azureextensiondev';
import { } from 'vscode-azureextensionui';
import { ext } from '../extension.bundle';
export let longRunningTestsEnabled: boolean;
export let testUserInput: TestUserInput = new TestUserInput(vscode);
// Runs before all tests
suiteSetup(async function (this: IHookCallbackContext): Promise<void> {
this.timeout(120 * 1000);
await vscode.commands.executeCommand('azureVirtualMachines.refresh'); // activate the extension before tests begin
ext.outputChannel = new TestOutputChannel();
ext.ui = testUserInput;
// tslint:disable-next-line:strict-boolean-expressions
longRunningTestsEnabled = !/^(false|0)?$/i.test(process.env.ENABLE_LONG_RUNNING_TESTS || '');
// set AzureWebJobsStorage so that it doesn't prompt during tests
process.env.AzureWebJobsStorage = 'ignore';
});
suiteTeardown(async function (this: IHookCallbackContext): Promise<void> {
this.timeout(90 * 1000);
});

66
test/index.ts Normal file
Просмотреть файл

@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
import * as path from 'path';
// tslint:disable-next-line:no-require-imports no-submodule-imports
import testRunner = require('vscode/lib/testrunner');
const options: { [key: string]: string | boolean | number | object } = {
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: true // colored output from test results
};
// You can directly control Mocha options using environment variables beginning with MOCHA_.
// For example:
// {
// "name": "Launch Tests",
// "type": "extensionHost",
// "request": "launch",
// ...
// "env": {
// "MOCHA_enableTimeouts": "0",
// "MOCHA_grep": "tests-to-run"
// }
//
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for all available options
// Defaults
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
mochaFile: path.join(__dirname, '..', '..', 'test-results.xml')
}
};
for (const envVar of Object.keys(process.env)) {
const match: RegExpMatchArray | null = envVar.match(/^mocha_(.+)/i);
if (match) {
const [, option] = match;
// tslint:disable-next-line:strict-boolean-expressions
let value: string | number = process.env[envVar] || '';
if (typeof value === 'string' && !isNaN(parseInt(value))) {
value = parseInt(value);
}
options[option] = value;
}
}
console.warn(`Mocha options: ${JSON.stringify(options, null, 2)}`);
testRunner.configure(options);
module.exports = testRunner;

23
test/tslint.json Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"extends": "../tslint.json",
"rules": {
"no-console": false,
"no-implicit-dependencies": [
true,
"dev"
],
"typedef": [
true,
"call-signature",
// "arrow-call-signature",
"parameter",
// "arrow-parameter",
"property-declaration",
"variable-declaration",
"member-variable-declaration"
// "object-destructuring",
// "array-destructuring"
],
"no-unexternalized-strings": false
}
}

29
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": ".",
"noUnusedLocals": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noUnusedParameters": true,
"baseUrl": "./",
"paths": {
"*": [
"node_modules/vscode/*",
"*"
]
}
},
"exclude": [
"node_modules",
".vscode-test",
"gulpfile.ts"
]
}

89
tslint.json Normal file
Просмотреть файл

@ -0,0 +1,89 @@
{
"extends": "tslint-microsoft-contrib",
"rules": {
"await-promise": [
true,
"Thenable"
],
"no-backbone-get-set-outside-model": false,
"radix": false,
"strict-boolean-expressions": [
true,
"allow-string",
"allow-undefined-union",
"allow-mix",
"allow-null-union"
],
"completed-docs": [
false,
"classes"
],
"import-name": false,
"max-line-length": [
false,
140
],
"missing-jsdoc": false,
"no-relative-imports": false,
"no-void-expression": [
true,
"ignore-arrow-function-shorthand"
],
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore"
],
"linebreak-style": false,
"newline-before-return": false,
"no-single-line-block-comment": false,
"quotemark": [
false,
"single"
],
"no-unexternalized-strings": [
true,
{
"signatures": [
"localize",
"nls.localize"
],
"keyIndex": 0,
"messageIndex": 1
}
],
"no-implicit-dependencies": [
true,
[
"vscode"
]
],
"switch-final-break": false,
"prefer-object-spread": false,
"no-return-await": false,
"no-parameter-reassignment": false,
"no-object-literal-type-assertion": false,
"function-name": [
true,
{
"static-method-regex": "^[a-z][\\w_]+$"
}
],
"typedef": [
true,
"call-signature",
"arrow-call-signature",
"parameter",
// "arrow-parameter",
"property-declaration",
"variable-declaration",
"member-variable-declaration"
],
"prefer-template": [
true,
"allow-single-concat"
],
"no-use-before-declare": false
}
}

47
webpack.config.js Normal file
Просмотреть файл

@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
//@ts-check
// See https://github.com/Microsoft/vscode-azuretools/wiki/webpack for guidance
const process = require('process');
const dev = require("vscode-azureextensiondev");
const CopyWebpackPlugin = require('copy-webpack-plugin');
let DEBUG_WEBPACK = !/^(false|0)?$/i.test(process.env.DEBUG_WEBPACK || '');
let config = dev.getDefaultWebpackConfig({
projectRoot: __dirname,
verbosity: DEBUG_WEBPACK ? 'debug' : 'normal',
externals:
{
// Fix "Module not found" errors in ./node_modules/websocket/lib/{BufferUtil,Validation}.js
// These files are not in node_modules and so will fail normally at runtime and instead use fallbacks.
// Make them as external so webpack doesn't try to process them, and they'll simply fail at runtime as before.
'../build/Release/validation': 'commonjs ../build/Release/validation',
'../build/default/validation': 'commonjs ../build/default/validation',
'../build/Release/bufferutil': 'commonjs ../build/Release/bufferutil',
'../build/default/bufferutil': 'commonjs ../build/default/bufferutil',
// ./getCoreNodeModule.js (path from windowsProcessTree.ts) uses a dynamic require which can't be webpacked
'./getCoreNodeModule': 'commonjs getCoreNodeModule',
},
plugins: [
// Copy files to dist folder where the runtime can find them
new CopyWebpackPlugin([
// getCoreNodeModule.js -> dist/node_modules/getCoreNodeModule.js
{ from: './out/src/utils/getCoreNodeModule.js', to: 'node_modules' }
])
]
});
if (DEBUG_WEBPACK) {
console.log('Config:', config);
}
module.exports = config;