Add start/stop/restart commands
This commit is contained in:
Родитель
281042513a
Коммит
7b17feb67f
40
package.json
40
package.json
|
@ -25,6 +25,12 @@
|
|||
"preview": true,
|
||||
"activationEvents": [
|
||||
"onCommand:azureFunctions.refresh",
|
||||
"onCommand:azureFunctions.initFunctionApp",
|
||||
"onCommand:azureFunctions.createFunction",
|
||||
"onCommand:azureFunctions.openInPortal",
|
||||
"onCommand:azureFunctions.startFunctionApp",
|
||||
"onCommand:azureFunctions.stopFunctionApp",
|
||||
"onCommand:azureFunctions.restartFunctionApp",
|
||||
"onView:azureFunctionsExplorer"
|
||||
],
|
||||
"main": "./out/src/extension",
|
||||
|
@ -61,6 +67,21 @@
|
|||
"command": "azureFunctions.openInPortal",
|
||||
"title": "Open In Portal",
|
||||
"category": "Azure Functions"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.startFunctionApp",
|
||||
"title": "Start",
|
||||
"category": "Azure Functions"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.stopFunctionApp",
|
||||
"title": "Stop",
|
||||
"category": "Azure Functions"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.restartFunctionApp",
|
||||
"title": "Restart",
|
||||
"category": "Azure Functions"
|
||||
}
|
||||
],
|
||||
"views": {
|
||||
|
@ -93,12 +114,27 @@
|
|||
{
|
||||
"command": "azureFunctions.refresh",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsSubscription",
|
||||
"group": "1_functionAppGeneralCommands@2"
|
||||
"group": "1_subscriptionGeneralCommands@1"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.openInPortal",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsFunctionApp",
|
||||
"group": "1_subscriptionGeneralCommands@2"
|
||||
"group": "1_functionAppGeneralCommands@1"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.startFunctionApp",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsFunctionApp",
|
||||
"group": "2_functionAppControlCommands@1"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.stopFunctionApp",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsFunctionApp",
|
||||
"group": "2_functionAppControlCommands@2"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.restartFunctionApp",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsFunctionApp",
|
||||
"group": "2_functionAppControlCommands@3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as path from 'path';
|
|||
import * as util from './util';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { INode } from './nodes';
|
||||
import { INode, FunctionAppNode } from './nodes';
|
||||
import { FunctionsCli } from './functions-cli';
|
||||
import { QuickPickItemWithData } from './util';
|
||||
|
||||
|
@ -81,6 +81,30 @@ export async function initFunctionApp(functionsCli: FunctionsCli) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function startFunctionApp(outputChannel: vscode.OutputChannel, node?: FunctionAppNode) {
|
||||
if (node) {
|
||||
outputChannel.appendLine(`Starting Function App "${node.label}"...`);
|
||||
await node.start();
|
||||
outputChannel.appendLine(`Function App "${node.label}" has been started.`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopFunctionApp(outputChannel: vscode.OutputChannel, node?: FunctionAppNode) {
|
||||
if (node) {
|
||||
outputChannel.appendLine(`Stopping Function App "${node.label}"...`);
|
||||
await node.stop();
|
||||
outputChannel.appendLine(`Function App "${node.label}" has been stopped.`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function restartFunctionApp(outputChannel: vscode.OutputChannel, node?: FunctionAppNode) {
|
||||
if (node) {
|
||||
outputChannel.appendLine(`Restarting Function App "${node.label}"...`);
|
||||
await node.restart();
|
||||
outputChannel.appendLine(`Function App "${node.label}" has been restarted.`);
|
||||
}
|
||||
}
|
||||
|
||||
function getMissingFunctionAppFiles(rootPath: string) {
|
||||
return _expectedFunctionAppFiles.filter(file => !fs.existsSync(path.join(rootPath, file)));
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import * as commands from './commands';
|
|||
|
||||
import { AzureAccount } from './azure-account.api';
|
||||
import { AzureFunctionsExplorer } from './explorer';
|
||||
import { INode } from './nodes'
|
||||
import { INode, FunctionAppNode } from './nodes'
|
||||
import { Reporter } from './telemetry';
|
||||
import { FunctionsCli } from './functions-cli'
|
||||
|
||||
|
@ -30,10 +30,16 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
context.subscriptions.push(terminal);
|
||||
const functionsCli = new FunctionsCli(terminal);
|
||||
|
||||
const outputChannel = vscode.window.createOutputChannel("Azure Functions");
|
||||
context.subscriptions.push(outputChannel);
|
||||
|
||||
initCommand(context, 'azureFunctions.refresh', (node?: INode) => explorer.refresh(node));
|
||||
initCommand(context, 'azureFunctions.openInPortal', (node?: INode) => commands.openInPortal(node));
|
||||
initCommand(context, 'azureFunctions.createFunction', (node?: INode) => commands.createFunction(functionsCli));
|
||||
initCommand(context, 'azureFunctions.initFunctionApp', (node?: INode) => commands.initFunctionApp(functionsCli));
|
||||
initAsyncCommand(context, 'azureFunctions.createFunction', (node?: INode) => commands.createFunction(functionsCli));
|
||||
initAsyncCommand(context, 'azureFunctions.initFunctionApp', (node?: INode) => commands.initFunctionApp(functionsCli));
|
||||
initAsyncCommand(context, 'azureFunctions.startFunctionApp', (node?: FunctionAppNode) => commands.startFunctionApp(outputChannel, node));
|
||||
initAsyncCommand(context, 'azureFunctions.stopFunctionApp', (node?: FunctionAppNode) => commands.stopFunctionApp(outputChannel, node));
|
||||
initAsyncCommand(context, 'azureFunctions.restartFunctionApp', (node?: FunctionAppNode) => commands.restartFunctionApp(outputChannel, node));
|
||||
} else {
|
||||
vscode.window.showErrorMessage("The Azure Account Extension is required for the Azure Functions extension.");
|
||||
}
|
||||
|
|
45
src/nodes.ts
45
src/nodes.ts
|
@ -2,8 +2,9 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as util from './util';
|
||||
import * as vscode from 'vscode';
|
||||
import * as WebSiteModels from '../node_modules/azure-arm-website/lib/models';
|
||||
|
||||
import { AzureResourceFilter } from './azure-account.api';
|
||||
|
@ -33,12 +34,14 @@ export class SubscriptionNode implements INode {
|
|||
readonly contextValue: string = 'azureFunctionsSubscription';
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
readonly tenantId: string;
|
||||
|
||||
readonly collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
|
||||
|
||||
constructor(private readonly subscriptionFilter: AzureResourceFilter) {
|
||||
this.label = subscriptionFilter.subscription.displayName!;
|
||||
this.id = subscriptionFilter.subscription.subscriptionId!;
|
||||
constructor(private readonly _subscriptionFilter: AzureResourceFilter) {
|
||||
this.label = _subscriptionFilter.subscription.displayName!;
|
||||
this.id = _subscriptionFilter.subscription.subscriptionId!;
|
||||
this.tenantId = _subscriptionFilter.session.tenantId;
|
||||
}
|
||||
|
||||
get iconPath(): any {
|
||||
|
@ -49,11 +52,14 @@ export class SubscriptionNode implements INode {
|
|||
}
|
||||
|
||||
async getChildren(): Promise<INode[]> {
|
||||
const client = new WebSiteManagementClient(this.subscriptionFilter.session.credentials, this.id);
|
||||
const webApps = await client.webApps.list();
|
||||
const webApps = await this.getWebSiteClient().webApps.list();
|
||||
return webApps.filter(s => s.kind === "functionapp")
|
||||
.sort((s1, s2) => s1.id!.localeCompare(s2.id!))
|
||||
.map(s => new FunctionAppNode(s, this.subscriptionFilter.session.tenantId));
|
||||
.map(s => new FunctionAppNode(s, this));
|
||||
}
|
||||
|
||||
getWebSiteClient() {
|
||||
return new WebSiteManagementClient(this._subscriptionFilter.session.credentials, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,10 +67,12 @@ export class FunctionAppNode implements INode {
|
|||
readonly contextValue: string = 'azureFunctionsFunctionApp';
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
readonly tenantId: string;
|
||||
|
||||
constructor(readonly functionApp: WebSiteModels.Site, readonly tenantId?: string) {
|
||||
this.label = `${functionApp.name} (${this.functionApp.resourceGroup})`;
|
||||
this.id = functionApp.id!;
|
||||
constructor(private readonly _functionApp: WebSiteModels.Site, private readonly _subscriptionNode: SubscriptionNode) {
|
||||
this.label = `${_functionApp.name} (${this._functionApp.resourceGroup})`;
|
||||
this.id = _functionApp.id!;
|
||||
this.tenantId = _subscriptionNode.tenantId;
|
||||
}
|
||||
|
||||
get iconPath(): any {
|
||||
|
@ -73,4 +81,21 @@ export class FunctionAppNode implements INode {
|
|||
dark: path.join(__filename, '..', '..', '..', 'resources', 'dark', 'AzureFunctionsApp.svg')
|
||||
};
|
||||
}
|
||||
|
||||
async start() {
|
||||
const client = this._subscriptionNode.getWebSiteClient();
|
||||
await client.webApps.start(this._functionApp.resourceGroup!, this._functionApp.name!);
|
||||
await util.waitForFunctionAppState(client, this._functionApp.resourceGroup!, this._functionApp.name!, util.FunctionAppState.Running);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
const client = this._subscriptionNode.getWebSiteClient();
|
||||
await client.webApps.stop(this._functionApp.resourceGroup!, this._functionApp.name!);
|
||||
await util.waitForFunctionAppState(client, this._functionApp.resourceGroup!, this._functionApp.name!, util.FunctionAppState.Stopped);
|
||||
}
|
||||
|
||||
async restart() {
|
||||
await this.stop();
|
||||
await this.start();
|
||||
}
|
||||
}
|
||||
|
|
22
src/util.ts
22
src/util.ts
|
@ -3,9 +3,11 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { reporter } from './telemetry';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { reporter } from './telemetry';
|
||||
import WebSiteManagementClient = require('azure-arm-website');
|
||||
|
||||
export function sendTelemetry(eventName: string, properties?: { [key: string]: string; }, measures?: { [key: string]: number; }) {
|
||||
if (reporter) {
|
||||
reporter.sendTelemetryEvent(eventName, properties, measures);
|
||||
|
@ -61,6 +63,24 @@ export async function showInputBox(placeHolder: string, prompt: string, validate
|
|||
return result;
|
||||
}
|
||||
|
||||
export enum FunctionAppState {
|
||||
Stopped = "stopped",
|
||||
Running = "running"
|
||||
};
|
||||
|
||||
export async function waitForFunctionAppState(webSiteManagementClient: WebSiteManagementClient, resourceGroup: string, name: string, state: FunctionAppState, intervalMs = 5000, timeoutMs = 60000) {
|
||||
let count = 0;
|
||||
while (count < timeoutMs) {
|
||||
count += intervalMs;
|
||||
const currentSite = await webSiteManagementClient.webApps.get(resourceGroup, name);
|
||||
if (currentSite.state && currentSite.state.toLowerCase() === state) {
|
||||
return;
|
||||
}
|
||||
await new Promise(r => setTimeout(r, intervalMs));
|
||||
}
|
||||
throw new Error(`Timeout waiting for Function App "${name}" state "${state}".`);
|
||||
}
|
||||
|
||||
export class QuickPickItemWithData<T> implements vscode.QuickPickItem {
|
||||
readonly description: string;
|
||||
constructor(readonly label: string, description?: string, readonly data?: T) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче