Add start/stop/restart commands

This commit is contained in:
Eric Jizba 2017-09-22 11:44:35 -07:00
Родитель 281042513a
Коммит 7b17feb67f
5 изменённых файлов: 128 добавлений и 17 удалений

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

@ -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.");
}

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

@ -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();
}
}

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

@ -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) {