From eea75edf188a72b6bc988e720acf5b7057141a47 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 30 Jan 2019 15:53:40 -0800 Subject: [PATCH 1/5] Sketch alpine debugging support. --- debugging/coreclr/debuggerClient.ts | 31 +++++++++++++++++++++------ debugging/coreclr/dockerClient.ts | 22 +++++++++++++++++++ debugging/coreclr/dockerManager.ts | 6 ++++-- debugging/coreclr/registerDebugger.ts | 1 + debugging/coreclr/vsdbgClient.ts | 12 ++++++++--- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/debugging/coreclr/debuggerClient.ts b/debugging/coreclr/debuggerClient.ts index 9f5a2e0964..263281d50a 100644 --- a/debugging/coreclr/debuggerClient.ts +++ b/debugging/coreclr/debuggerClient.ts @@ -4,22 +4,39 @@ import { PlatformOS } from '../../utils/platform'; import { VsDbgClient } from './vsdbgClient'; +import { DockerClient } from './dockerClient'; export interface DebuggerClient { - getDebugger(os: PlatformOS): Promise; + getDebugger(os: PlatformOS, containerId: string): Promise; + getDebuggerFolder(): Promise; } export class DefaultDebuggerClient { private static debuggerVersion: string = 'vs2017u5'; - private static debuggerLinuxRuntime: string = 'debian.8-x64'; + private static debuggerLinuxAlpineRuntime: string = 'linux-musl-x64'; + private static debuggerLinuxDefaultRuntime: string = 'linux-x64'; private static debuggerWindowsRuntime: string = 'win7-x64'; - constructor(private readonly vsdbgClient: VsDbgClient) { + constructor( + private readonly dockerClient: DockerClient, + private readonly vsdbgClient: VsDbgClient) { } - public async getDebugger(os: PlatformOS): Promise { - return await this.vsdbgClient.getVsDbgVersion( - DefaultDebuggerClient.debuggerVersion, - os === 'Windows' ? DefaultDebuggerClient.debuggerWindowsRuntime : DefaultDebuggerClient.debuggerLinuxRuntime); + public async getDebugger(os: PlatformOS, containerId: string): Promise { + if (os === 'Windows') { + return await this.vsdbgClient.getVsDbgVersion(DefaultDebuggerClient.debuggerVersion, DefaultDebuggerClient.debuggerWindowsRuntime); + } else { + const result = await this.dockerClient.exec(containerId, '/bin/sh -c \'. /etc/os-release && echo $ID\'', { interactive: true }); + + return await this.vsdbgClient.getVsDbgVersion( + DefaultDebuggerClient.debuggerVersion, + result.trim() === 'alpine' + ? DefaultDebuggerClient.debuggerLinuxAlpineRuntime + : DefaultDebuggerClient.debuggerLinuxDefaultRuntime); + } + } + + public getDebuggerFolder(): Promise { + return this.vsdbgClient.getVsDbgFolder(); } } diff --git a/debugging/coreclr/dockerClient.ts b/debugging/coreclr/dockerClient.ts index 9ade5647c3..39a3d5fcd9 100644 --- a/debugging/coreclr/dockerClient.ts +++ b/debugging/coreclr/dockerClient.ts @@ -61,6 +61,11 @@ export type DockerVersionOptions = { format?: string; } +export type DockerExecOptions = { + interactive?: boolean; + tty?: boolean; +} + export interface DockerClient { buildImage(options: DockerBuildImageOptions, progress?: (content: string) => void): Promise; getVersion(options?: DockerVersionOptions): Promise; @@ -70,6 +75,7 @@ export interface DockerClient { removeContainer(containerNameOrId: string, options?: DockerContainerRemoveOptions): Promise; runContainer(imageTagOrId: string, options?: DockerRunContainerOptions): Promise; trimId(id: string): string; + exec(containerNameOrId: string, command: string, options?:DockerExecOptions): Promise; } export class CliDockerClient implements DockerClient { @@ -241,6 +247,22 @@ export class CliDockerClient implements DockerClient { return id.substring(0, 12); } + + public async exec(containerNameOrId: string, args: string, options?:DockerExecOptions): Promise { + options = options || {}; + + const command = CommandLineBuilder + .create('docker', 'exec') + .withFlagArg('-i', options.interactive) + .withFlagArg('-t', options.tty) + .withQuotedArg(containerNameOrId) + .withArg(args) + .build(); + + const result = await this.processProvider.exec(command, {}); + + return result.stdout; + } } export default CliDockerClient; diff --git a/debugging/coreclr/dockerManager.ts b/debugging/coreclr/dockerManager.ts index 232e72b0d0..29cfebcc07 100644 --- a/debugging/coreclr/dockerManager.ts +++ b/debugging/coreclr/dockerManager.ts @@ -187,7 +187,7 @@ export class DefaultDockerManager implements DockerManager { const containerName = options.containerName; - const debuggerFolder = await this.debuggerClient.getDebugger(options.os); + const debuggerFolder = await this.debuggerClient.getDebuggerFolder(); const command = options.os === 'Windows' ? '-t localhost' @@ -236,6 +236,8 @@ export class DefaultDockerManager implements DockerManager { await this.addToDebugContainers(containerId); + const debuggerPath = await this.debuggerClient.getDebugger(options.run.os, containerId); + const browserUrl = await this.getContainerWebEndpoint(containerId); const additionalProbingPaths = options.run.os === 'Windows' @@ -255,7 +257,7 @@ export class DefaultDockerManager implements DockerManager { return { browserUrl, - debuggerPath: options.run.os === 'Windows' ? 'C:\\remote_debugger\\vsdbg' : '/remote_debugger/vsdbg', + debuggerPath: this.osProvider.pathJoin(options.run.os, options.run.os === 'Windows' ? 'C:\\remote_debugger' : '/remote_debugger', debuggerPath, 'vsdbg'), // tslint:disable-next-line:no-invalid-template-strings pipeArgs: ['exec', '-i', containerId, '${debuggerCommand}'], // tslint:disable-next-line:no-invalid-template-strings diff --git a/debugging/coreclr/registerDebugger.ts b/debugging/coreclr/registerDebugger.ts index 698ac0997f..7001e88ba2 100644 --- a/debugging/coreclr/registerDebugger.ts +++ b/debugging/coreclr/registerDebugger.ts @@ -38,6 +38,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) new DefaultDockerManager( new DefaultAppStorageProvider(fileSystemProvider), new DefaultDebuggerClient( + dockerClient, new RemoteVsDbgClient( dockerOutputManager, fileSystemProvider, diff --git a/debugging/coreclr/vsdbgClient.ts b/debugging/coreclr/vsdbgClient.ts index 0e2131f0af..2d07d5f920 100644 --- a/debugging/coreclr/vsdbgClient.ts +++ b/debugging/coreclr/vsdbgClient.ts @@ -12,6 +12,7 @@ import { OutputManager } from './outputManager'; import { ProcessProvider } from './processProvider'; export interface VsDbgClient { + getVsDbgFolder(): Promise; getVsDbgVersion(version: string, runtime: string): Promise; } @@ -57,13 +58,18 @@ export class RemoteVsDbgClient implements VsDbgClient { }; } + public getVsDbgFolder(): Promise { + return Promise.resolve(this.vsdbgPath); + } + public async getVsDbgVersion(version: string, runtime: string): Promise { - const vsdbgVersionPath = path.join(this.vsdbgPath, runtime, version); + const vsdbgRelativeVersionPath = path.join(runtime, version); + const vsdbgVersionPath = path.join(this.vsdbgPath, vsdbgRelativeVersionPath); const vsdbgVersionExists = await this.fileSystemProvider.dirExists(vsdbgVersionPath); if (vsdbgVersionExists && await this.isUpToDate(this.lastDebuggerAcquisitionKey(version, runtime))) { // The debugger is up to date... - return vsdbgVersionPath; + return vsdbgRelativeVersionPath; } return await this.dockerOutputManager.performOperation( @@ -80,7 +86,7 @@ export class RemoteVsDbgClient implements VsDbgClient { await this.updateDate(this.lastDebuggerAcquisitionKey(version, runtime), new Date()); - return vsdbgVersionPath; + return vsdbgRelativeVersionPath; }, 'Debugger acquired.', 'Unable to acquire the .NET Core debugger.'); From ff9140d30c1363f5720bc3d7cec2529f139cc5ad Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 30 Jan 2019 16:30:48 -0800 Subject: [PATCH 2/5] Conditional test for alpine. --- debugging/coreclr/debuggerClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debugging/coreclr/debuggerClient.ts b/debugging/coreclr/debuggerClient.ts index 263281d50a..1be15ad293 100644 --- a/debugging/coreclr/debuggerClient.ts +++ b/debugging/coreclr/debuggerClient.ts @@ -26,7 +26,7 @@ export class DefaultDebuggerClient { if (os === 'Windows') { return await this.vsdbgClient.getVsDbgVersion(DefaultDebuggerClient.debuggerVersion, DefaultDebuggerClient.debuggerWindowsRuntime); } else { - const result = await this.dockerClient.exec(containerId, '/bin/sh -c \'. /etc/os-release && echo $ID\'', { interactive: true }); + const result = await this.dockerClient.exec(containerId, '/bin/sh -c \'ID=default; if [ -e /etc/os-release ]; then . /etc/os-release; fi; echo $ID\'', { interactive: true }); return await this.vsdbgClient.getVsDbgVersion( DefaultDebuggerClient.debuggerVersion, From a611b5994aa70c4f6d6cb0079e45b69925ae3cdb Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 30 Jan 2019 16:40:30 -0800 Subject: [PATCH 3/5] Refactor and add comments. --- debugging/coreclr/debuggerClient.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/debugging/coreclr/debuggerClient.ts b/debugging/coreclr/debuggerClient.ts index 1be15ad293..f3cb6d4136 100644 --- a/debugging/coreclr/debuggerClient.ts +++ b/debugging/coreclr/debuggerClient.ts @@ -17,6 +17,10 @@ export class DefaultDebuggerClient { private static debuggerLinuxDefaultRuntime: string = 'linux-x64'; private static debuggerWindowsRuntime: string = 'win7-x64'; + // This script determines the "type" of Linux release (e.g. 'alpine', 'debian', etc.). + // NOTE: The result may contain line endings. + private static debuggerLinuxReleaseIdScript: string = '/bin/sh -c \'ID=default; if [ -e /etc/os-release ]; then . /etc/os-release; fi; echo $ID\''; + constructor( private readonly dockerClient: DockerClient, private readonly vsdbgClient: VsDbgClient) { @@ -26,7 +30,7 @@ export class DefaultDebuggerClient { if (os === 'Windows') { return await this.vsdbgClient.getVsDbgVersion(DefaultDebuggerClient.debuggerVersion, DefaultDebuggerClient.debuggerWindowsRuntime); } else { - const result = await this.dockerClient.exec(containerId, '/bin/sh -c \'ID=default; if [ -e /etc/os-release ]; then . /etc/os-release; fi; echo $ID\'', { interactive: true }); + const result = await this.dockerClient.exec(containerId, DefaultDebuggerClient.debuggerLinuxReleaseIdScript, { interactive: true }); return await this.vsdbgClient.getVsDbgVersion( DefaultDebuggerClient.debuggerVersion, From a757d0b0c0c09750f6068794e8de1b16ad89bc21 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 30 Jan 2019 16:49:46 -0800 Subject: [PATCH 4/5] Update README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cb0f91fe3..ec0b6e3300 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ The exact folder to use for certificatePaths on Linux will depend on the distrib ## Debugging .NET Core (Preview) -> Note that Windows containers are **not** currently supported, only Linux containers. +> Note that Windows containers are **not** currently supported, only Linux containers. However, both standard and Alpine .NET Core runtime base images are supported. ### Prerequisites From fe8faf69d18ea8d9e0a648592451606d4a570415 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Wed, 30 Jan 2019 16:57:24 -0800 Subject: [PATCH 5/5] Resolve linter warnings. --- debugging/coreclr/debuggerClient.ts | 6 +++--- debugging/coreclr/dockerClient.ts | 4 ++-- debugging/coreclr/vsdbgClient.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/debugging/coreclr/debuggerClient.ts b/debugging/coreclr/debuggerClient.ts index f3cb6d4136..b73a6c70d6 100644 --- a/debugging/coreclr/debuggerClient.ts +++ b/debugging/coreclr/debuggerClient.ts @@ -3,8 +3,8 @@ *--------------------------------------------------------*/ import { PlatformOS } from '../../utils/platform'; -import { VsDbgClient } from './vsdbgClient'; import { DockerClient } from './dockerClient'; +import { VsDbgClient } from './vsdbgClient'; export interface DebuggerClient { getDebugger(os: PlatformOS, containerId: string): Promise; @@ -40,7 +40,7 @@ export class DefaultDebuggerClient { } } - public getDebuggerFolder(): Promise { - return this.vsdbgClient.getVsDbgFolder(); + public async getDebuggerFolder(): Promise { + return await this.vsdbgClient.getVsDbgFolder(); } } diff --git a/debugging/coreclr/dockerClient.ts b/debugging/coreclr/dockerClient.ts index 39a3d5fcd9..d90c6f34a6 100644 --- a/debugging/coreclr/dockerClient.ts +++ b/debugging/coreclr/dockerClient.ts @@ -75,7 +75,7 @@ export interface DockerClient { removeContainer(containerNameOrId: string, options?: DockerContainerRemoveOptions): Promise; runContainer(imageTagOrId: string, options?: DockerRunContainerOptions): Promise; trimId(id: string): string; - exec(containerNameOrId: string, command: string, options?:DockerExecOptions): Promise; + exec(containerNameOrId: string, command: string, options?: DockerExecOptions): Promise; } export class CliDockerClient implements DockerClient { @@ -248,7 +248,7 @@ export class CliDockerClient implements DockerClient { return id.substring(0, 12); } - public async exec(containerNameOrId: string, args: string, options?:DockerExecOptions): Promise { + public async exec(containerNameOrId: string, args: string, options?: DockerExecOptions): Promise { options = options || {}; const command = CommandLineBuilder diff --git a/debugging/coreclr/vsdbgClient.ts b/debugging/coreclr/vsdbgClient.ts index 2d07d5f920..0ef5830dbd 100644 --- a/debugging/coreclr/vsdbgClient.ts +++ b/debugging/coreclr/vsdbgClient.ts @@ -58,8 +58,8 @@ export class RemoteVsDbgClient implements VsDbgClient { }; } - public getVsDbgFolder(): Promise { - return Promise.resolve(this.vsdbgPath); + public async getVsDbgFolder(): Promise { + return this.vsdbgPath; } public async getVsDbgVersion(version: string, runtime: string): Promise {