From 7279594507d5be6b6ae28d2f06eef7a61b933b84 Mon Sep 17 00:00:00 2001 From: "Brandon Waterloo [MSFT]" <36966225+bwateratmsft@users.noreply.github.com> Date: Tue, 16 Mar 2021 13:43:38 -0400 Subject: [PATCH] Use global listener for debug start/stop (#2786) * Global debug termination listener * Do the same for port output --- .../DockerDebugConfigurationProvider.ts | 117 ++++++++---------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/src/debugging/DockerDebugConfigurationProvider.ts b/src/debugging/DockerDebugConfigurationProvider.ts index f919a02a42..b65b6aba57 100644 --- a/src/debugging/DockerDebugConfigurationProvider.ts +++ b/src/debugging/DockerDebugConfigurationProvider.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, commands, debug, DebugConfiguration, DebugConfigurationProvider, MessageItem, ProviderResult, window, workspace, WorkspaceFolder } from 'vscode'; -import { callWithTelemetryAndErrorHandling, IActionContext } from 'vscode-azureextensionui'; +import { CancellationToken, commands, debug, DebugConfiguration, DebugConfigurationProvider, DebugSession, MessageItem, ProviderResult, window, workspace, WorkspaceFolder } from 'vscode'; +import { callWithTelemetryAndErrorHandling, IActionContext, registerEvent } from 'vscode-azureextensionui'; import { DockerOrchestration } from '../constants'; import { ext } from '../extensionVariables'; import { localize } from '../localize'; @@ -26,7 +26,21 @@ export interface DockerAttachConfiguration extends NetCoreDockerDebugConfigurati export class DockerDebugConfigurationProvider implements DebugConfigurationProvider { public constructor( private readonly helpers: { [key in DockerPlatform]: DebugHelper } - ) { } + ) { + // Listen for debug termination events to shut down debug containers as needed + registerEvent('debugTermination', debug.onDidTerminateDebugSession, async (context: IActionContext, session: DebugSession) => { + context.errorHandling.suppressDisplay = true; + context.telemetry.suppressAll = true; + await this.removeDebugContainerIfNeeded(context, session.configuration); + }); + + // Listen for debug start events to emit ports being listened on as needed + registerEvent('debugStart', debug.onDidStartDebugSession, async (context: IActionContext, session: DebugSession) => { + context.errorHandling.suppressDisplay = true; + context.telemetry.suppressAll = true; + await this.outputPortsAtDebuggingIfNeeded(context, session.configuration); + }); + } public provideDebugConfigurations(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult { const add: MessageItem = { title: localize('vscode-docker.debug.configProvider.addDockerFiles', 'Add Docker Files') }; @@ -98,8 +112,7 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi if (resolvedConfiguration) { await this.validateResolvedConfiguration(resolvedConfiguration); - await this.registerRemoveContainerAfterDebugging(context.actionContext, resolvedConfiguration); - await this.registerOutputPortsAtDebugging(context.actionContext, resolvedConfiguration); + await this.removeDebugContainerIfNeeded(context.actionContext, resolvedConfiguration); } return resolvedConfiguration; @@ -113,74 +126,52 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi } } - private async registerRemoveContainerAfterDebugging(context: IActionContext, resolvedConfiguration: ResolvedDebugConfiguration): Promise { - if ((resolvedConfiguration.dockerOptions?.removeContainerAfterDebug ?? true) && - resolvedConfiguration.dockerOptions?.containerName) { + private getHelper(platform: DockerPlatform): DebugHelper { + const helper = this.helpers[platform]; + + if (!helper) { + throw new Error(localize('vscode-docker.debug.configProvider.unsupportedPlatform', 'The platform \'{0}\' is not currently supported for Docker debugging.', platform)); + } + + return helper; + } + + private async removeDebugContainerIfNeeded(context: IActionContext, configuration: ResolvedDebugConfiguration): Promise { + if ((configuration?.dockerOptions?.removeContainerAfterDebug ?? true) && // removeContainerAfterDebug must be undefined or true + configuration?.dockerOptions?.containerName && // containerName must be specified + !(configuration?.subProcessId)) { // Must not have subProcessId, i.e. not a subprocess debug session (which is how Python does hot reload sessions) try { - await ext.dockerClient.removeContainer(context, resolvedConfiguration.dockerOptions.containerName); - } catch { } - - // Now register the container for removal after the debug session ends - const disposable = debug.onDidTerminateDebugSession(async session => { - const sessionConfiguration = session.configuration; - - // Don't do anything if this isn't our debug session, or if it's a subprocess debug session (which is how Python does hot reload sessions) - if (sessionConfiguration?.dockerOptions?.containerName === resolvedConfiguration.dockerOptions.containerName && !(sessionConfiguration?.subProcessId)) { - try { - await ext.dockerClient.removeContainer(context, resolvedConfiguration.dockerOptions.containerName); - } finally { - disposable.dispose(); - } - } - }); + await ext.dockerClient.removeContainer(context, configuration.dockerOptions.containerName); + } catch { } // Best effort } } - private async registerOutputPortsAtDebugging(context: IActionContext, resolvedConfiguration: ResolvedDebugConfiguration): Promise { - if (resolvedConfiguration?.dockerOptions?.containerName) { - const disposable = debug.onDidStartDebugSession(async session => { - const sessionConfiguration = session.configuration; - - // Don't do anything if this isn't our debug session - if (sessionConfiguration?.dockerOptions?.containerName === resolvedConfiguration.dockerOptions.containerName) { - try { - const inspectInfo = await ext.dockerClient.inspectContainer(context, resolvedConfiguration.dockerOptions.containerName); - const portMappings: string[] = []; - - if (inspectInfo?.NetworkSettings?.Ports) { - for (const containerPort of Object.keys(inspectInfo.NetworkSettings.Ports)) { - const mappings = inspectInfo.NetworkSettings.Ports[containerPort]; - - if (mappings) { - for (const mapping of mappings) { - if (mapping?.HostPort) { - // TODO: if we ever do non-localhost debugging this would need to change - portMappings.push(`localhost:${mapping.HostPort} => ${containerPort}`); - } - } + private async outputPortsAtDebuggingIfNeeded(context: IActionContext, configuration: ResolvedDebugConfiguration): Promise { + if (configuration?.dockerOptions?.containerName) { + try { + const inspectInfo = await ext.dockerClient.inspectContainer(context, configuration.dockerOptions.containerName); + const portMappings: string[] = []; + + if (inspectInfo?.NetworkSettings?.Ports) { + for (const containerPort of Object.keys(inspectInfo.NetworkSettings.Ports)) { + const mappings = inspectInfo.NetworkSettings.Ports[containerPort]; + + if (mappings) { + for (const mapping of mappings) { + if (mapping?.HostPort) { + // TODO: if we ever do non-localhost debugging this would need to change + portMappings.push(`localhost:${mapping.HostPort} => ${containerPort}`); } } } - - if (portMappings.length > 0) { - ext.outputChannel.appendLine(localize('vscode-docker.debug.configProvider.portMappings', 'The application is listening on the following port(s) (Host => Container):')); - ext.outputChannel.appendLine(portMappings.join('\n')); - } - } finally { - disposable.dispose(); } } - }); - } - } - - private getHelper(platform: DockerPlatform): DebugHelper { - const helper = this.helpers[platform]; - if (!helper) { - throw new Error(localize('vscode-docker.debug.configProvider.unsupportedPlatform', 'The platform \'{0}\' is not currently supported for Docker debugging.', platform)); + if (portMappings.length > 0) { + ext.outputChannel.appendLine(localize('vscode-docker.debug.configProvider.portMappings', 'The application is listening on the following port(s) (Host => Container):')); + ext.outputChannel.appendLine(portMappings.join('\n')); + } + } catch { } // Best effort } - - return helper; } }