Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remote debugging support for Node.js on Linux App Service plans #1458

Merged
merged 3 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ Install the prerequisites for your desired language:
* Stream logs from your Azure Function Apps
* View and manage deployment slots
> **NOTE**: To enable, set `azureFunctions.enableSlots` to true.
* Debug Java function project in Azure (experimental)
* Debug Node.js function project in Azure (experimental)
> **NOTE**: To enable, set `azureFunctions.enableRemoteDebugging` to true.
* Debug Java function project in Azure (experimental)
> **NOTE**: To enable, set `azureFunctions.enableJavaRemoteDebugging` to true.

### Create New Project

Expand Down
30 changes: 25 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"onCommand:azureFunctions.deleteFunction",
"onCommand:azureFunctions.deploy",
"onCommand:azureFunctions.configureDeploymentSource",
"onCommand:azureFunctions.debugFunctionAppOnAzure",
"onCommand:azureFunctions.startRemoteDebug",
"onCommand:azureFunctions.startJavaRemoteDebug",
"onCommand:azureFunctions.appSettings.add",
"onCommand:azureFunctions.appSettings.download",
"onCommand:azureFunctions.appSettings.upload",
Expand Down Expand Up @@ -176,8 +177,13 @@
"category": "Azure Functions"
},
{
"command": "azureFunctions.debugFunctionAppOnAzure",
"title": "%azFunc.debugFunctionAppOnAzure%",
"command": "azureFunctions.startRemoteDebug",
"title": "%azFunc.startRemoteDebug%",
"category": "Azure Functions"
},
{
"command": "azureFunctions.startJavaRemoteDebug",
"title": "%azFunc.startJavaRemoteDebug%",
"category": "Azure Functions"
},
{
Expand Down Expand Up @@ -452,10 +458,15 @@
"group": "4@2"
},
{
"command": "azureFunctions.debugFunctionAppOnAzure",
"command": "azureFunctions.startRemoteDebug",
"when": "view == azFuncTree && viewItem =~ /^azFunc(Production|)Slot$/ && config.azureFunctions.enableRemoteDebugging == true",
"group": "5@1"
},
{
"command": "azureFunctions.startJavaRemoteDebug",
"when": "view == azFuncTree && viewItem =~ /^azFunc(Production|)Slot$/ && config.azureFunctions.enableJavaRemoteDebugging == true",
"group": "5@2"
},
{
"command": "azureFunctions.viewProperties",
"when": "view == azFuncTree && viewItem =~ /^azFunc(Production|)Slot$/",
Expand Down Expand Up @@ -646,9 +657,13 @@
"when": "never"
},
{
"command": "azureFunctions.debugFunctionAppOnAzure",
"command": "azureFunctions.startRemoteDebug",
"when": "config.azureFunctions.enableRemoteDebugging == true"
},
{
"command": "azureFunctions.startJavaRemoteDebug",
"when": "config.azureFunctions.enableJavaRemoteDebugging == true"
},
{
"command": "azureFunctions.createSlot",
"when": "config.azureFunctions.enableSlots == true"
Expand Down Expand Up @@ -831,6 +846,11 @@
"description": "%azFunc.enableRemoteDebugging%",
"default": false
},
"azureFunctions.enableJavaRemoteDebugging": {
"type": "boolean",
"description": "%azFunc.enableJavaRemoteDebugging%",
"default": false
},
"azureFunctions.showProjectWarning": {
"type": "boolean",
"description": "%azFunc.showProjectWarningDescription%",
Expand Down
6 changes: 4 additions & 2 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"azFunc.projectLanguageDescription": "The default language to use when performing operations in the Azure Functions extension (e.g. \"Create New Function\").",
"azFunc.deploy": "Deploy to Function App...",
"azFunc.configureDeploymentSource": "Configure Deployment Source...",
"azFunc.debugFunctionAppOnAzure": "Attach Debugger",
"azFunc.startRemoteDebug": "Start Remote Debugging",
"azFunc.startJavaRemoteDebug": "Attach Debugger",
"azFunc.appSettings.add": "Add New Setting...",
"azFunc.appSettings.download": "Download Remote Settings...",
"azFunc.appSettings.upload": "Upload Local Settings...",
Expand All @@ -46,7 +47,8 @@
"azFunc.showDeploySubpathWarningDescription": "Show a warning when the \"deploySubpath\" setting does not match the selected folder for deploying.",
"azFunc.startStreamingLogs": "Start Streaming Logs",
"azFunc.stopStreamingLogs": "Stop Streaming Logs",
"azFunc.enableRemoteDebugging": "Enable remote debugging, an experimental feature that only supports Java-based Functions Apps.",
"azFunc.enableRemoteDebugging": "Enable remote debugging for Node.js Function Apps running on Linux App Service plans. Consumption plans are not supported. (experimental)",
"azFunc.enableJavaRemoteDebugging": "Enable remote debugging for Java Functions Apps running on Windows. (experimental)",
"azFunc.deleteProxy": "Delete Proxy...",
"azFunc.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.",
"azFunc.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)",
Expand Down
19 changes: 19 additions & 0 deletions src/commands/remoteDebug/checkForRemoteDebugSupport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { WebSiteManagementModels } from 'azure-arm-website';

export function checkForRemoteDebugSupport(siteConfig: WebSiteManagementModels.SiteConfig): void {
// Read siteConfig.linuxFxVersion to determine debugging support
// If the Function App is running on Windows, it will be empty
// If the Function App is running on Linux consumption, it will be empty
// If the Function App is running on a Linux App Service plan, it will contain Docker registry information, e.g. "DOCKER|repo.azurecr.io/image:tag"
// The blessed Node.js Docker image will be something like "DOCKER|mcr.microsoft.com/azure-functions/node:2.0-node8-appservice"
if (siteConfig.linuxFxVersion && siteConfig.linuxFxVersion.startsWith('DOCKER|mcr.microsoft.com/azure-functions/node')) {
return;
}

throw new Error('Azure Remote Debugging is currently only supported for Node.js Function Apps running on Linux App Service plans. Consumption plans are not supported.');
}
29 changes: 29 additions & 0 deletions src/commands/remoteDebug/startRemoteDebug.ts
Original file line number Diff line number Diff line change
@@ -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 { WebSiteManagementModels } from 'azure-arm-website';
import * as vscode from 'vscode';
import * as appservice from 'vscode-azureappservice';
import { IActionContext } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { ProductionSlotTreeItem } from '../../tree/ProductionSlotTreeItem';
import { SlotTreeItemBase } from '../../tree/SlotTreeItemBase';
import { checkForRemoteDebugSupport } from './checkForRemoteDebugSupport';

export async function startRemoteDebug(context: IActionContext, node?: SlotTreeItemBase): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker<SlotTreeItemBase>(ProductionSlotTreeItem.contextValue, context);
}

const siteClient: appservice.SiteClient = node.root.client;
const siteConfig: WebSiteManagementModels.SiteConfig = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async progress => {
appservice.reportMessage('Fetching site configuration...', progress);
return await siteClient.getSiteConfig();
});

checkForRemoteDebugSupport(siteConfig);

await appservice.startRemoteDebug(siteClient, siteConfig);
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import * as portfinder from 'portfinder';
import * as vscode from 'vscode';
import { SiteClient } from 'vscode-azureappservice';
import { DialogResponses, IActionContext } from 'vscode-azureextensionui';
import { DebugProxy } from '../DebugProxy';
import { ext } from '../extensionVariables';
import { localize } from '../localize';
import { ProductionSlotTreeItem } from '../tree/ProductionSlotTreeItem';
import { SlotTreeItemBase } from '../tree/SlotTreeItemBase';
import { openUrl } from '../utils/openUrl';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { ProductionSlotTreeItem } from '../../tree/ProductionSlotTreeItem';
import { SlotTreeItemBase } from '../../tree/SlotTreeItemBase';
import { openUrl } from '../../utils/openUrl';
import { DebugProxy } from './DebugProxy';

const HTTP_PLATFORM_DEBUG_PORT: string = '8898';
const JAVA_OPTS: string = `-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=127.0.0.1:${HTTP_PLATFORM_DEBUG_PORT}`;

export async function remoteDebugFunctionApp(context: IActionContext, node?: SlotTreeItemBase): Promise<void> {
export async function remoteDebugJavaFunctionApp(context: IActionContext, node?: SlotTreeItemBase): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker<SlotTreeItemBase>(ProductionSlotTreeItem.contextValue, context);
}
Expand Down
6 changes: 4 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import { startStreamingLogs } from './commands/logstream/startStreamingLogs';
import { stopStreamingLogs } from './commands/logstream/stopStreamingLogs';
import { openInPortal } from './commands/openInPortal';
import { pickFuncProcess } from './commands/pickFuncProcess';
import { remoteDebugFunctionApp } from './commands/remoteDebugFunctionApp';
import { startRemoteDebug } from './commands/remoteDebug/startRemoteDebug';
import { remoteDebugJavaFunctionApp } from './commands/remoteDebugJava/remoteDebugJavaFunctionApp';
import { renameAppSetting } from './commands/renameAppSetting';
import { restartFunctionApp } from './commands/restartFunctionApp';
import { startFunctionApp } from './commands/startFunctionApp';
Expand Down Expand Up @@ -128,7 +129,8 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
registerCommand('azureFunctions.appSettings.encrypt', encryptLocalSettings);
registerCommand('azureFunctions.appSettings.delete', async (actionContext: IActionContext, node?: AzExtTreeItem) => await deleteNode(actionContext, AppSettingTreeItem.contextValue, node));
registerCommand('azureFunctions.appSettings.toggleSlotSetting', toggleSlotSetting);
registerCommand('azureFunctions.debugFunctionAppOnAzure', remoteDebugFunctionApp);
registerCommand('azureFunctions.startRemoteDebug', startRemoteDebug);
registerCommand('azureFunctions.startJavaRemoteDebug', remoteDebugJavaFunctionApp);
registerCommand('azureFunctions.deleteProxy', async (actionContext: IActionContext, node?: AzExtTreeItem) => await deleteNode(actionContext, ProxyTreeItem.contextValue, node));
registerCommand('azureFunctions.installOrUpdateFuncCoreTools', installOrUpdateFuncCoreTools);
registerCommand('azureFunctions.uninstallFuncCoreTools', uninstallFuncCoreTools);
Expand Down