Skip to content
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 28 additions & 7 deletions debugging/coreclr/debuggerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,44 @@
*--------------------------------------------------------*/

import { PlatformOS } from '../../utils/platform';
import { DockerClient } from './dockerClient';
import { VsDbgClient } from './vsdbgClient';

export interface DebuggerClient {
getDebugger(os: PlatformOS): Promise<string>;
getDebugger(os: PlatformOS, containerId: string): Promise<string>;
getDebuggerFolder(): Promise<string>;
}

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) {
// 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) {
}

public async getDebugger(os: PlatformOS, containerId: string): Promise<string> {
if (os === 'Windows') {
return await this.vsdbgClient.getVsDbgVersion(DefaultDebuggerClient.debuggerVersion, DefaultDebuggerClient.debuggerWindowsRuntime);
} else {
const result = await this.dockerClient.exec(containerId, DefaultDebuggerClient.debuggerLinuxReleaseIdScript, { interactive: true });

return await this.vsdbgClient.getVsDbgVersion(
DefaultDebuggerClient.debuggerVersion,
result.trim() === 'alpine'
? DefaultDebuggerClient.debuggerLinuxAlpineRuntime
: DefaultDebuggerClient.debuggerLinuxDefaultRuntime);
}
}

public async getDebugger(os: PlatformOS): Promise<string> {
return await this.vsdbgClient.getVsDbgVersion(
DefaultDebuggerClient.debuggerVersion,
os === 'Windows' ? DefaultDebuggerClient.debuggerWindowsRuntime : DefaultDebuggerClient.debuggerLinuxRuntime);
public async getDebuggerFolder(): Promise<string> {
return await this.vsdbgClient.getVsDbgFolder();
}
}
22 changes: 22 additions & 0 deletions debugging/coreclr/dockerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>;
getVersion(options?: DockerVersionOptions): Promise<string>;
Expand All @@ -70,6 +75,7 @@ export interface DockerClient {
removeContainer(containerNameOrId: string, options?: DockerContainerRemoveOptions): Promise<void>;
runContainer(imageTagOrId: string, options?: DockerRunContainerOptions): Promise<string>;
trimId(id: string): string;
exec(containerNameOrId: string, command: string, options?: DockerExecOptions): Promise<string>;
}

export class CliDockerClient implements DockerClient {
Expand Down Expand Up @@ -241,6 +247,22 @@ export class CliDockerClient implements DockerClient {

return id.substring(0, 12);
}

public async exec(containerNameOrId: string, args: string, options?: DockerExecOptions): Promise<string> {
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;
6 changes: 4 additions & 2 deletions debugging/coreclr/dockerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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'
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions debugging/coreclr/registerDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext)
new DefaultDockerManager(
new DefaultAppStorageProvider(fileSystemProvider),
new DefaultDebuggerClient(
dockerClient,
new RemoteVsDbgClient(
dockerOutputManager,
fileSystemProvider,
Expand Down
12 changes: 9 additions & 3 deletions debugging/coreclr/vsdbgClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { OutputManager } from './outputManager';
import { ProcessProvider } from './processProvider';

export interface VsDbgClient {
getVsDbgFolder(): Promise<string>;
getVsDbgVersion(version: string, runtime: string): Promise<string>;
}

Expand Down Expand Up @@ -57,13 +58,18 @@ export class RemoteVsDbgClient implements VsDbgClient {
};
}

public async getVsDbgFolder(): Promise<string> {
return this.vsdbgPath;
}

public async getVsDbgVersion(version: string, runtime: string): Promise<string> {
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(
Expand All @@ -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.');
Expand Down