Skip to content

Commit

Permalink
Refactor remote debugging commands into common library so we can supp…
Browse files Browse the repository at this point in the history
…ort functions
  • Loading branch information
mrcrane committed Jun 13, 2019
1 parent 55e413e commit f4366b0
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 11 deletions.
75 changes: 64 additions & 11 deletions appservice/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions appservice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.22.0",
"mocha-multi-reporters": "^1.1.7",
"portfinder": "^1.0.20",
"tslint": "^5.16.0",
"tslint-microsoft-contrib": "5.0.1",
"typescript": "^3.4.5",
Expand Down
3 changes: 3 additions & 0 deletions appservice/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export * from './createAppService/SiteHostingPlanStep';
export * from './createAppService/SiteNameStep';
export * from './createAppService/SiteOSStep';
export * from './createAppService/SiteRuntimeStep';
export * from './remoteDebug/remoteDebugCommon';
export * from './remoteDebug/startRemoteDebug';
export * from './remoteDebug/stopRemoteDebug';
export * from './createSlot';
export * from './deploy/deploy';
export * from './deploy/runPreDeployTask';
Expand Down
45 changes: 45 additions & 0 deletions appservice/src/remoteDebug/remoteDebugCommon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SiteConfigResource } from 'azure-arm-website/lib/models';
import * as vscode from 'vscode';
import { callWithTelemetryAndErrorHandling, DialogResponses, IActionContext } from 'vscode-azureextensionui';
import { ext } from '../extensionVariables';
import { SiteClient } from '../SiteClient';

export const remoteDebugLink: string = 'https://aka.ms/appsvc-remotedebug';

export function reportMessage(message: string, progress: vscode.Progress<{}>): void {
ext.outputChannel.appendLine(message);
progress.report({ message: message });
}

export async function setRemoteDebug(isRemoteDebuggingToBeEnabled: boolean, confirmMessage: string, noopMessage: string | undefined, siteClient: SiteClient, siteConfig: SiteConfigResource, progress?: vscode.Progress<{}>, learnMoreLink?: string): Promise<void> {
if (isRemoteDebuggingToBeEnabled !== siteConfig.remoteDebuggingEnabled) {
const confirmButton: vscode.MessageItem = isRemoteDebuggingToBeEnabled ? { title: 'Enable' } : { title: 'Disable' };

// don't have to check input as this handles cancels and learnMore responses
await ext.ui.showWarningMessage(confirmMessage, { modal: true, learnMoreLink }, confirmButton, DialogResponses.cancel);
siteConfig.remoteDebuggingEnabled = isRemoteDebuggingToBeEnabled;
if (progress) {
reportMessage('Updating site configuration to set remote debugging...', progress);
}

await callWithTelemetryAndErrorHandling('appService.remoteDebugUpdateConfiguration', async (context: IActionContext) => {
context.errorHandling.suppressDisplay = true;
context.errorHandling.rethrow = true;
await siteClient.updateConfiguration(siteConfig);
});

if (progress) {
reportMessage('Updating site configuration done.', progress);
}
} else {
// Update not needed
if (noopMessage) {
vscode.window.showWarningMessage(noopMessage);
}
}
}
112 changes: 112 additions & 0 deletions appservice/src/remoteDebug/startRemoteDebug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SiteConfigResource, User } from 'azure-arm-website/lib/models';
import * as portfinder from 'portfinder';
import * as vscode from 'vscode';
import { callWithTelemetryAndErrorHandling, IActionContext } from 'vscode-azureextensionui';
import { ext } from '../extensionVariables';
import { SiteClient } from '../SiteClient';
import { TunnelProxy } from '../TunnelProxy';
import { remoteDebugLink, reportMessage, setRemoteDebug } from './remoteDebugCommon';

let isRemoteDebugging: boolean = false;

export async function startRemoteDebug(siteClient: SiteClient, siteConfig: SiteConfigResource): Promise<void> {
if (isRemoteDebugging) {
throw new Error('Azure Remote Debugging is currently starting or already started.');
}

isRemoteDebugging = true;
try {
await startRemoteDebugInternal(siteClient, siteConfig);
} catch (error) {
isRemoteDebugging = false;
throw error;
}
}

async function startRemoteDebugInternal(siteClient: SiteClient, siteConfig: SiteConfigResource): Promise<void> {
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (progress: vscode.Progress<{}>): Promise<void> => {
const debugConfig: vscode.DebugConfiguration = await getDebugConfiguration();
// tslint:disable-next-line:no-unsafe-any
const localHostPortNumber: number = debugConfig.port;

reportMessage('Checking app settings...', progress);

const confirmEnableMessage: string = 'The app configuration will be updated to enable remote debugging and restarted. Would you like to continue?';
await setRemoteDebug(true, confirmEnableMessage, undefined, siteClient, siteConfig, progress, remoteDebugLink);

reportMessage('Starting tunnel proxy...', progress);

const publishCredential: User = await siteClient.getWebAppPublishCredential();
const tunnelProxy: TunnelProxy = new TunnelProxy(localHostPortNumber, siteClient, publishCredential);
await callWithTelemetryAndErrorHandling('appService.remoteDebugStartProxy', async (startContext: IActionContext) => {
startContext.errorHandling.suppressDisplay = true;
startContext.errorHandling.rethrow = true;
await tunnelProxy.startProxy();
});

reportMessage('Attaching debugger...', progress);

await callWithTelemetryAndErrorHandling('appService.remoteDebugAttach', async (attachContext: IActionContext) => {
attachContext.errorHandling.suppressDisplay = true;
attachContext.errorHandling.rethrow = true;
await vscode.debug.startDebugging(undefined, debugConfig);
});

reportMessage('Attached!', progress);

const terminateDebugListener: vscode.Disposable = vscode.debug.onDidTerminateDebugSession(async (event: vscode.DebugSession) => {
if (event.name === debugConfig.name) {
isRemoteDebugging = false;

if (tunnelProxy !== undefined) {
tunnelProxy.dispose();
}
terminateDebugListener.dispose();

const confirmDisableMessage: string = 'Leaving the app in debugging mode may cause performance issues. Would you like to disable debugging for this app? The app will be restarted.';
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (innerProgress: vscode.Progress<{}>): Promise<void> => {
await setRemoteDebug(false, confirmDisableMessage, undefined, siteClient, siteConfig, innerProgress, remoteDebugLink);
});
}
});
});
}

async function getDebugConfiguration(): Promise<vscode.DebugConfiguration> {
const sessionId: string = Date.now().toString();
const portNumber: number = await portfinder.getPortPromise();

// So far only node is supported
const config: vscode.DebugConfiguration = {
// return {
name: sessionId,
type: 'node',
protocol: 'inspector',
request: 'attach',
address: 'localhost',
port: portNumber
};

// Try to map workspace folder source files to the remote instance
if (vscode.workspace.workspaceFolders) {
if (vscode.workspace.workspaceFolders.length === 1) {
config.localRoot = vscode.workspace.workspaceFolders[0].uri.fsPath;
config.remoteRoot = '/home/site/wwwroot';
} else {
// In this case we don't know which folder to use. Show a warning and proceed.
// In the future we should allow users to choose a workspace folder to map sources from.
// tslint:disable-next-line:no-floating-promises
ext.ui.showWarningMessage('Unable to bind breakpoints from workspace when multiple folders are open. Use "loaded scripts" instead.');
}
} else {
// vscode will throw an error if you try to start debugging without any workspace folder open
throw new Error('Please open a workspace folder before attaching a debugger.');
}

return config;
}
18 changes: 18 additions & 0 deletions appservice/src/remoteDebug/stopRemoteDebug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { SiteConfigResource } from 'azure-arm-website/lib/models';
import * as vscode from 'vscode';
import { SiteClient } from '../SiteClient';
import { remoteDebugLink, setRemoteDebug } from './remoteDebugCommon';

export async function stopRemoteDebug(siteClient: SiteClient, siteConfig: SiteConfigResource): Promise<void> {
const confirmMessage: string = 'The app configuration will be updated to disable remote debugging and restarted. Would you like to continue?';
const noopMessage: string = 'The app is not configured for debugging.';

await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (progress: vscode.Progress<{}>): Promise<void> => {
await setRemoteDebug(false, confirmMessage, noopMessage, siteClient, siteConfig, progress, remoteDebugLink);
});
}

0 comments on commit f4366b0

Please sign in to comment.