From 9f5e5ed3f915906297bc52973da877d6c47a9859 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 09:34:09 -0400 Subject: [PATCH 01/40] Volume mapping of certs and usersecrets folders --- src/debugging/coreclr/dockerManager.ts | 27 +++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index d899906753..b20c734055 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -320,6 +320,7 @@ export class DefaultDockerManager implements DockerManager { } private static readonly ProgramFilesEnvironmentVariable: string = 'ProgramFiles'; + private static readonly AppDataEnvironmentVariable: string = 'AppData'; private getVolumes(debuggerFolder: string, options: DockerManagerRunContainerOptions): DockerContainerVolume[] { const appVolume: DockerContainerVolume = { @@ -357,11 +358,35 @@ export class DefaultDockerManager implements DockerManager { permissions: 'ro' }; + let appDataEnvironmentVariable: string | undefined; + + if (this.osProvider.os === 'Windows') { + appDataEnvironmentVariable = this.processProvider.env[DefaultDockerManager.AppDataEnvironmentVariable]; + + if (appDataEnvironmentVariable === undefined) { + throw new Error(`The environment variable '${DefaultDockerManager.AppDataEnvironmentVariable}' is not defined. This variable is used to locate the HTTPS certificate and user secrets folders.`); + } + } + + const certVolume: DockerContainerVolume = { + localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : path.join(this.osProvider.homedir, '.aspnet', 'https'), + containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', + permissions: 'ro' + }; + + const userSecretsVolume: DockerContainerVolume = { + localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), + containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', + permissions: 'ro' + }; + const volumes: DockerContainerVolume[] = [ appVolume, debuggerVolume, nugetVolume, - nugetFallbackVolume + nugetFallbackVolume, + certVolume, + userSecretsVolume, ]; return volumes; From ba816b58ab895e3d451ac8d9ea7c0c0ba336494a Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 09:36:33 -0400 Subject: [PATCH 02/40] Whitespace --- src/debugging/coreclr/dockerManager.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index b20c734055..b6c9f00a56 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -369,13 +369,15 @@ export class DefaultDockerManager implements DockerManager { } const certVolume: DockerContainerVolume = { - localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : path.join(this.osProvider.homedir, '.aspnet', 'https'), + localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : + path.join(this.osProvider.homedir, '.aspnet', 'https'), containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', permissions: 'ro' }; const userSecretsVolume: DockerContainerVolume = { - localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), + localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : + path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', permissions: 'ro' }; From 54e54fc0047830124917b8e2b45e51eec81e0899 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 12:54:42 -0400 Subject: [PATCH 03/40] Change name to dotNetClient --- src/debugging/coreclr/dockerManager.ts | 6 ++++-- src/debugging/coreclr/netCoreProjectProvider.ts | 4 ++-- src/debugging/coreclr/prereqManager.ts | 4 ++-- .../coreclr/registerDebugConfigurationProvider.ts | 7 ++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index b6c9f00a56..1e39f8667f 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -9,6 +9,7 @@ import { PlatformOS } from '../../utils/platform'; import { AppStorageProvider } from './appStorage'; import { ProcessProvider } from './ChildProcessProvider'; import { DockerBuildImageOptions, DockerClient, DockerContainerVolume, DockerRunContainerOptions } from "./CliDockerClient"; +import { DotNetClient } from './CommandLineDotNetClient'; import { DebuggerClient } from './debuggerClient'; import { FileSystemProvider } from './fsProvider'; import Lazy from './lazy'; @@ -117,6 +118,7 @@ export class DefaultDockerManager implements DockerManager { private readonly appCacheFactory: AppStorageProvider, private readonly debuggerClient: DebuggerClient, private readonly dockerClient: DockerClient, + private readonly dotNetClient: DotNetClient, private readonly dockerOutputManager: OutputManager, private readonly fileSystemProvider: FileSystemProvider, private readonly osProvider: OSProvider, @@ -193,7 +195,7 @@ export class DefaultDockerManager implements DockerManager { ? 'ping' : 'tail'; - const volumes = this.getVolumes(debuggerFolder, options); + const additionalVolumes = this.getVolumes(debuggerFolder, options); const containerId = await this.dockerOutputManager.performOperation( 'Starting container...', @@ -217,7 +219,7 @@ export class DefaultDockerManager implements DockerManager { network: options.network, networkAlias: options.networkAlias, ports: options.ports, - volumes: [...(volumes || []), ...(options.volumes || [])] + volumes: [...(additionalVolumes || []), ...(options.volumes || [])] }); }, id => `Container ${this.dockerClient.trimId(id)} started.`, diff --git a/src/debugging/coreclr/netCoreProjectProvider.ts b/src/debugging/coreclr/netCoreProjectProvider.ts index a6b9be3110..344a5bea3c 100644 --- a/src/debugging/coreclr/netCoreProjectProvider.ts +++ b/src/debugging/coreclr/netCoreProjectProvider.ts @@ -30,7 +30,7 @@ export interface NetCoreProjectProvider { export class MsBuildNetCoreProjectProvider implements NetCoreProjectProvider { constructor( private readonly fsProvider: FileSystemProvider, - private readonly msBuildClient: DotNetClient, + private readonly dotNetClient: DotNetClient, private readonly tempFileProvider: TempFileProvider) { } @@ -39,7 +39,7 @@ export class MsBuildNetCoreProjectProvider implements NetCoreProjectProvider { const targetOutputFilename = this.tempFileProvider.getTempFilename(); await this.fsProvider.writeFile(getTargetPathProjectFile, getTargetPathProjectFileContent); try { - await this.msBuildClient.execTarget( + await this.dotNetClient.execTarget( getTargetPathProjectFile, { target: 'GetTargetPath', diff --git a/src/debugging/coreclr/prereqManager.ts b/src/debugging/coreclr/prereqManager.ts index 99bfb8ba6a..5a8ca817cc 100644 --- a/src/debugging/coreclr/prereqManager.ts +++ b/src/debugging/coreclr/prereqManager.ts @@ -74,12 +74,12 @@ export class DotNetExtensionInstalledPrerequisite implements Prerequisite { export class DotNetSdkInstalledPrerequisite implements Prerequisite { constructor( - private readonly msbuildClient: DotNetClient, + private readonly dotNetClient: DotNetClient, private readonly showErrorMessage: ShowErrorMessageFunction) { } public async checkPrerequisite(): Promise { - const result = await this.msbuildClient.getVersion(); + const result = await this.dotNetClient.getVersion(); if (result) { return true; diff --git a/src/debugging/coreclr/registerDebugConfigurationProvider.ts b/src/debugging/coreclr/registerDebugConfigurationProvider.ts index 0f6cf0f849..92b8b37394 100644 --- a/src/debugging/coreclr/registerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/registerDebugConfigurationProvider.ts @@ -26,7 +26,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) const processProvider = new ChildProcessProvider(); const dockerClient = new CliDockerClient(processProvider); - const msBuildClient = new CommandLineDotNetClient(processProvider); + const dotNetClient = new CommandLineDotNetClient(processProvider); const osProvider = new LocalOSProvider(); const dockerOutputManager = new DefaultOutputManager(ext.outputChannel); @@ -44,6 +44,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) osProvider, processProvider)), dockerClient, + dotNetClient, dockerOutputManager, fileSystemProvider, osProvider, @@ -67,7 +68,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) osProvider, new MsBuildNetCoreProjectProvider( fileSystemProvider, - msBuildClient, + dotNetClient, new OSTempFileProvider( osProvider, processProvider)), @@ -80,7 +81,7 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) vscode.extensions.getExtension, vscode.window.showErrorMessage), new DotNetSdkInstalledPrerequisite( - msBuildClient, + dotNetClient, vscode.window.showErrorMessage), new MacNuGetFallbackFolderSharedPrerequisite( fileSystemProvider, From 009e3de303e81e528661c68fa8f340049ece93e1 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 12:55:16 -0400 Subject: [PATCH 04/40] Add UUID dependency --- package-lock.json | 9 +++++++++ package.json | 2 ++ src/debugging/coreclr/dockerManager.ts | 1 + 3 files changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index df277179c7..658d2635a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,6 +164,15 @@ } } }, + "@types/uuid": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", + "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/webpack": { "version": "4.4.24", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.24.tgz", diff --git a/package.json b/package.json index dbc63e4f23..3085a0de59 100644 --- a/package.json +++ b/package.json @@ -1544,6 +1544,7 @@ "@types/request-promise-native": "^1.0.15", "@types/semver": "^5.5.0", "@types/string-replace-webpack-plugin": "^0.1.0", + "@types/uuid": "^3.3.2", "@types/xml2js": "^0.4.3", "adm-zip": "^0.4.11", "copy-webpack-plugin": "^4.5.4", @@ -1584,6 +1585,7 @@ "request-promise-native": "^1.0.5", "semver": "^6.0.0", "tar": "^4.4.6", + "uuid": "^3.3.2", "vscode-azureextensionui": "^0.25.3", "vscode-azureappservice": "^0.42.0", "vscode-languageclient": "^5.1.1", diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 1e39f8667f..e947fd781a 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -4,6 +4,7 @@ import deepEqual = require('deep-equal'); import * as path from 'path'; +import { v4 as uuidv4 } from 'uuid'; import { Memento } from 'vscode'; import { PlatformOS } from '../../utils/platform'; import { AppStorageProvider } from './appStorage'; From 943f9517242f13306c6990f84689c66e5a52fe7c Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 12:55:58 -0400 Subject: [PATCH 05/40] Export/trust certs and map secrets volumes --- .../coreclr/CommandLineDotNetClient.ts | 12 +++ .../dockerDebugConfigurationProvider.ts | 4 +- src/debugging/coreclr/dockerManager.ts | 74 ++++++++++++++----- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 46d531a03d..43b9bb3d56 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -12,6 +12,7 @@ export type MSBuildExecOptions = { export interface DotNetClient { execTarget(projectFile: string, options?: MSBuildExecOptions): Promise; getVersion(): Promise; + trustAndExportCertificate(projectFile: string, exportPath: string, password: string): Promise; } export class CommandLineDotNetClient implements DotNetClient { @@ -48,6 +49,17 @@ export class CommandLineDotNetClient implements DotNetClient { return undefined; } } + + public async trustAndExportCertificate(projectFile: string, exportPath: string, password: string): Promise { + const exportCommand = `dotnet dev-certs https --trust -ep "${exportPath}" -p "${password}"`; + await this.processProvider.exec(exportCommand, {}); + + const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; + await this.processProvider.exec(userSecretsPasswordCommand, {}); + + const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${exportPath}"`; + await this.processProvider.exec(userSecretsPathCommand, {}); + } } export default CommandLineDotNetClient; diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index 72583103e8..c38582bdb5 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -115,7 +115,9 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi const launchOptions = { appFolder: resolvedAppFolder, - appOutput, + appOutput: appOutput, + appProject: resolvedAppProject, + appName: appName, build: buildOptions, run: runOptions }; diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index e947fd781a..8647ff134f 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -40,6 +40,8 @@ export type LaunchRunOptions = Omit; runContainer(imageTagOrId: string, options: DockerManagerRunContainerOptions): Promise; @@ -232,6 +243,10 @@ export class DefaultDockerManager implements DockerManager { public async prepareForLaunch(options: LaunchOptions): Promise { const imageId = await this.buildImage({ appFolder: options.appFolder, ...options.build }); + const certificateExportPfxPath = path.join(this.getSecretsPaths(options.run.os).certificateExportPath, `${options.appName}.pfx`) + + await this.dotNetClient.trustAndExportCertificate(options.appProject, certificateExportPfxPath, uuidv4()); + const containerId = await this.runContainer(imageId, { appFolder: options.appFolder, ...options.run }); await this.addToDebugContainers(containerId); @@ -310,13 +325,19 @@ export class DefaultDockerManager implements DockerManager { } private async getContainerWebEndpoint(containerNameOrId: string): Promise { - const webPorts = await this.dockerClient.inspectObject(containerNameOrId, { format: '{{(index (index .NetworkSettings.Ports \\\"80/tcp\\\") 0).HostPort}}' }); - - if (webPorts) { - const webPort = webPorts.split('\n')[0]; - - // tslint:disable-next-line:no-http-string - return `http://localhost:${webPort}`; + let portMappingsString = await this.dockerClient.inspectObject(containerNameOrId, { format: '{{json .NetworkSettings.Ports}}' }); + let portMappings = JSON.parse(portMappingsString); + + if (portMappings) { + let httpsPort = portMappings["443/tcp"] && portMappings["443/tcp"][0] && portMappings["443/tcp"][0].HostPort || null; + let httpPort = portMappings["80/tcp"] && portMappings["80/tcp"][0] && portMappings["80/tcp"][0].HostPort || null; + + if (httpsPort) { + return `https://localhost:${httpsPort}`; + } else if (httpPort) { + // tslint:disable-next-line:no-http-string + return `http://localhost:${httpPort}`; + } } return undefined; @@ -361,26 +382,16 @@ export class DefaultDockerManager implements DockerManager { permissions: 'ro' }; - let appDataEnvironmentVariable: string | undefined; - - if (this.osProvider.os === 'Windows') { - appDataEnvironmentVariable = this.processProvider.env[DefaultDockerManager.AppDataEnvironmentVariable]; - - if (appDataEnvironmentVariable === undefined) { - throw new Error(`The environment variable '${DefaultDockerManager.AppDataEnvironmentVariable}' is not defined. This variable is used to locate the HTTPS certificate and user secrets folders.`); - } - } + const { certificateExportPath, userSecretsPath } = this.getSecretsPaths(options.os); const certVolume: DockerContainerVolume = { - localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : - path.join(this.osProvider.homedir, '.aspnet', 'https'), + localPath: certificateExportPath, containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', permissions: 'ro' }; const userSecretsVolume: DockerContainerVolume = { - localPath: options.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : - path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), + localPath: userSecretsPath, containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', permissions: 'ro' }; @@ -396,4 +407,27 @@ export class DefaultDockerManager implements DockerManager { return volumes; } + + private getSecretsPaths(os: string): { certificateExportPath: string, userSecretsPath: string } { + let appDataEnvironmentVariable: string | undefined; + + if (this.osProvider.os === 'Windows') { + appDataEnvironmentVariable = this.processProvider.env[DefaultDockerManager.AppDataEnvironmentVariable]; + + if (appDataEnvironmentVariable === undefined) { + throw new Error(`The environment variable '${DefaultDockerManager.AppDataEnvironmentVariable}' is not defined. This variable is used to locate the HTTPS certificate and user secrets folders.`); + } + } + + const folders = { + certificateExportPath: os === 'Windows' ? + path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : + path.join(this.osProvider.homedir, '.aspnet', 'https'), + userSecretsPath: os === 'Windows' ? + path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : + path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), + } + + return folders; + } } From 3a862b864cd3daf512eace29250d91865ba3b89d Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 11 Jul 2019 15:40:33 -0400 Subject: [PATCH 06/40] Fix path issues --- .../coreclr/CommandLineDotNetClient.ts | 14 +++++--- src/debugging/coreclr/LocalOSProvider.ts | 5 +++ .../dockerDebugConfigurationProvider.ts | 1 - src/debugging/coreclr/dockerManager.ts | 33 ++++++++++++------- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 43b9bb3d56..e4692621f3 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -12,7 +12,7 @@ export type MSBuildExecOptions = { export interface DotNetClient { execTarget(projectFile: string, options?: MSBuildExecOptions): Promise; getVersion(): Promise; - trustAndExportCertificate(projectFile: string, exportPath: string, password: string): Promise; + trustAndExportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string, password: string): Promise; } export class CommandLineDotNetClient implements DotNetClient { @@ -50,15 +50,19 @@ export class CommandLineDotNetClient implements DotNetClient { } } - public async trustAndExportCertificate(projectFile: string, exportPath: string, password: string): Promise { - const exportCommand = `dotnet dev-certs https --trust -ep "${exportPath}" -p "${password}"`; + public async trustAndExportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string, password: string): Promise { + // TODO : skip this for projects where it has already been done + // TODO : trust doesn't work for Linux users; need to direct them to manually fixing + + const exportCommand = `dotnet dev-certs https --trust -ep "${hostExportPath}" -p "${password}"`; await this.processProvider.exec(exportCommand, {}); const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; await this.processProvider.exec(userSecretsPasswordCommand, {}); - const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${exportPath}"`; - await this.processProvider.exec(userSecretsPathCommand, {}); + // This is not honored due to https://github.com/aspnet/AspNetCore.Docs/issues/6199#issuecomment-418194220 + //const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${containerExportPath}"`; + //await this.processProvider.exec(userSecretsPathCommand, {}); } } diff --git a/src/debugging/coreclr/LocalOSProvider.ts b/src/debugging/coreclr/LocalOSProvider.ts index 9b97f15539..b33438ffd5 100644 --- a/src/debugging/coreclr/LocalOSProvider.ts +++ b/src/debugging/coreclr/LocalOSProvider.ts @@ -13,6 +13,7 @@ export interface OSProvider { tmpdir: string; pathJoin(os: PlatformOS, ...paths: string[]): string; pathNormalize(os: PlatformOS, rawPath: string): string; + pathParse(os: PlatformOS, rawPath: string): path.ParsedPath; } export class LocalOSProvider implements OSProvider { @@ -41,6 +42,10 @@ export class LocalOSProvider implements OSProvider { pathOS === 'Windows' ? /\//g : /\\/g, pathOS === 'Windows' ? '\\' : '/'); } + + public pathParse(pathOS: PlatformOS, rawPath: string): path.ParsedPath { + return pathOS === 'Windows' ? path.win32.parse(rawPath) : path.posix.parse(rawPath); + } } export default LocalOSProvider; diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index c38582bdb5..02ab555ebc 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -117,7 +117,6 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi appFolder: resolvedAppFolder, appOutput: appOutput, appProject: resolvedAppProject, - appName: appName, build: buildOptions, run: runOptions }; diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 8647ff134f..f284814a40 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -41,7 +41,6 @@ export type LaunchOptions = { appFolder: string; appOutput: string; appProject: string; - appName: string; build: LaunchBuildOptions; run: LaunchRunOptions; }; @@ -243,9 +242,11 @@ export class DefaultDockerManager implements DockerManager { public async prepareForLaunch(options: LaunchOptions): Promise { const imageId = await this.buildImage({ appFolder: options.appFolder, ...options.build }); - const certificateExportPfxPath = path.join(this.getSecretsPaths(options.run.os).certificateExportPath, `${options.appName}.pfx`) + const appOutputName = this.osProvider.pathParse(options.run.os, options.appOutput).name; + const hostCertificateExportPfxPath = path.join(this.getHostSecretsPaths().hostCertificateExportPath, `${appOutputName}.pfx`); + const containerCertificateExportPfxPath = this.osProvider.pathJoin(options.run.os, this.getContainerSecretsPaths(options.run.os).containerCertificateExportPath, `${appOutputName}.pfx`) - await this.dotNetClient.trustAndExportCertificate(options.appProject, certificateExportPfxPath, uuidv4()); + await this.dotNetClient.trustAndExportCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath, uuidv4()); const containerId = await this.runContainer(imageId, { appFolder: options.appFolder, ...options.run }); @@ -382,17 +383,18 @@ export class DefaultDockerManager implements DockerManager { permissions: 'ro' }; - const { certificateExportPath, userSecretsPath } = this.getSecretsPaths(options.os); + const { hostCertificateExportPath, hostUserSecretsPath } = this.getHostSecretsPaths(); + const { containerCertificateExportPath, containerUserSecretsPath } = this.getContainerSecretsPaths(options.os); const certVolume: DockerContainerVolume = { - localPath: certificateExportPath, - containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', + localPath: hostCertificateExportPath, + containerPath: containerCertificateExportPath, permissions: 'ro' }; const userSecretsVolume: DockerContainerVolume = { - localPath: userSecretsPath, - containerPath: options.os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', + localPath: hostUserSecretsPath, + containerPath: containerUserSecretsPath, permissions: 'ro' }; @@ -408,7 +410,7 @@ export class DefaultDockerManager implements DockerManager { return volumes; } - private getSecretsPaths(os: string): { certificateExportPath: string, userSecretsPath: string } { + private getHostSecretsPaths(): { hostCertificateExportPath: string, hostUserSecretsPath: string } { let appDataEnvironmentVariable: string | undefined; if (this.osProvider.os === 'Windows') { @@ -420,14 +422,23 @@ export class DefaultDockerManager implements DockerManager { } const folders = { - certificateExportPath: os === 'Windows' ? + hostCertificateExportPath: this.osProvider.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : path.join(this.osProvider.homedir, '.aspnet', 'https'), - userSecretsPath: os === 'Windows' ? + hostUserSecretsPath: this.osProvider.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), } return folders; } + + private getContainerSecretsPaths(os: string): { containerCertificateExportPath: string, containerUserSecretsPath: string } { + const folders = { + containerCertificateExportPath: os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', + containerUserSecretsPath: os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', + } + + return folders; + } } From e15253887b1e942bff692750525e7f6e43b6e5bc Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Fri, 12 Jul 2019 11:34:21 -0400 Subject: [PATCH 07/40] Save a variable --- src/debugging/coreclr/dockerManager.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index f284814a40..7f71c6f0a7 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -421,24 +421,24 @@ export class DefaultDockerManager implements DockerManager { } } - const folders = { + return { hostCertificateExportPath: this.osProvider.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'ASP.NET', 'Https') : path.join(this.osProvider.homedir, '.aspnet', 'https'), hostUserSecretsPath: this.osProvider.os === 'Windows' ? path.join(appDataEnvironmentVariable, 'Microsoft', 'UserSecrets') : path.join(this.osProvider.homedir, '.microsoft', 'usersecrets'), - } - - return folders; + }; } private getContainerSecretsPaths(os: string): { containerCertificateExportPath: string, containerUserSecretsPath: string } { - const folders = { - containerCertificateExportPath: os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : '/root/.aspnet/https', - containerUserSecretsPath: os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : '/root/.microsoft/usersecrets', - } - - return folders; + return { + containerCertificateExportPath: os === 'Windows' ? + 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : + '/root/.aspnet/https', + containerUserSecretsPath: os === 'Windows' ? + 'C:\\Users\\ContainerUser\\AppData\\Roaming\\Microsoft\\UserSecrets' : + '/root/.microsoft/usersecrets', + }; } } From 69707fd76ac5a9e55c40bcd1bcb01b91869c4139 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 15 Jul 2019 09:15:48 -0400 Subject: [PATCH 08/40] Cache already-setup projects to avoid redoing them --- src/debugging/coreclr/CommandLineDotNetClient.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index e4692621f3..eb479880ba 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -16,6 +16,8 @@ export interface DotNetClient { } export class CommandLineDotNetClient implements DotNetClient { + private static KnownConfiguredProjects: Set; + constructor(private readonly processProvider: ProcessProvider) { } @@ -51,16 +53,21 @@ export class CommandLineDotNetClient implements DotNetClient { } public async trustAndExportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string, password: string): Promise { - // TODO : skip this for projects where it has already been done - // TODO : trust doesn't work for Linux users; need to direct them to manually fixing + if (CommandLineDotNetClient.KnownConfiguredProjects.has(projectFile)) { + return; + } + // TODO : trust doesn't work for Linux users; need to direct them to manually trust the cert const exportCommand = `dotnet dev-certs https --trust -ep "${hostExportPath}" -p "${password}"`; await this.processProvider.exec(exportCommand, {}); const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; await this.processProvider.exec(userSecretsPasswordCommand, {}); + CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); + // This is not honored due to https://github.com/aspnet/AspNetCore.Docs/issues/6199#issuecomment-418194220 + // Consequently, the certificate name must be equal to .pfx, i.e. MyWebApp.dll => MyWebApp.pfx //const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${containerExportPath}"`; //await this.processProvider.exec(userSecretsPathCommand, {}); } From 672b3e9717306da0fc3bc88a52e69529ae355469 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 15 Jul 2019 09:17:03 -0400 Subject: [PATCH 09/40] Add ASPNETCORE_ENVIRONMENT environment variable --- package.json | 6 +++++- src/debugging/coreclr/dockerDebugConfigurationProvider.ts | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3085a0de59..67f402102f 100644 --- a/package.json +++ b/package.json @@ -490,7 +490,11 @@ "request": "launch", "preLaunchTask": "build", "dockerBuild": {}, - "dockerRun": {} + "dockerRun": { + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } } ], diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index 02ab555ebc..b430eafe26 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -78,6 +78,7 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi dockerBuild: { }, dockerRun: { + env: { "ASPNETCORE_ENVIRONMENT": "Development" } } } ]; From 1f82cfb65764ee8c2f466acd5a0c3e1dc10a24ce Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 15 Jul 2019 13:52:31 -0400 Subject: [PATCH 10/40] Add support for multiple ports exposed --- src/configureWorkspace/configUtils.ts | 37 +++++++++++++---- src/configureWorkspace/configure.ts | 40 ++++++++++--------- src/configureWorkspace/configureCpp.ts | 4 +- src/configureWorkspace/configureDotNetCore.ts | 8 ++-- src/configureWorkspace/configureGo.ts | 19 ++++----- src/configureWorkspace/configureJava.ts | 20 ++++------ src/configureWorkspace/configureNode.ts | 19 ++++----- src/configureWorkspace/configureOther.ts | 18 ++++----- src/configureWorkspace/configurePython.ts | 20 ++++------ src/configureWorkspace/configureRuby.ts | 19 ++++----- test/configure.test.ts | 10 ++--- 11 files changed, 109 insertions(+), 105 deletions(-) diff --git a/src/configureWorkspace/configUtils.ts b/src/configureWorkspace/configUtils.ts index 6fa977af3a..5f2d1fbf9f 100644 --- a/src/configureWorkspace/configUtils.ts +++ b/src/configureWorkspace/configUtils.ts @@ -9,24 +9,45 @@ import { ext } from "../extensionVariables"; import { Platform, PlatformOS } from '../utils/platform'; /** - * Prompts for a port number + * Prompts for port numbers * @throws `UserCancelledError` if the user cancels. */ -export async function promptForPort(port: string): Promise { +export async function promptForPorts(ports: Number[]): Promise { let opt: vscode.InputBoxOptions = { - placeHolder: `${port}`, - prompt: 'What port does your app listen on? ENTER for none.', - value: `${port}`, + placeHolder: `${ports.join(', ')}`, + prompt: 'What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.', + value: `${ports.join(', ')}`, validateInput: (value: string): string | undefined => { - if (value && (!Number.isInteger(Number(value)) || Number(value) <= 0)) { - return 'Port must be a positive integer or else empty for no exposed port'; + let result = splitPorts(value); + if (!result) { + return 'Ports must be a comma-separated list of positive integers (1 to 65535), or empty for no exposed port.'; } return undefined; } } - return ext.ui.showInputBox(opt); + return splitPorts(await ext.ui.showInputBox(opt)); +} + +function splitPorts(value: string): Number[] | undefined { + value = value ? value : ''; + let matches = value.match(/\d+/g); + + if (!matches && value !== '') { + return undefined; + } else if (!matches) { + return []; // Empty list + } + + let ports = matches.map(Number); + + // If anything is non-integral or less than 1 or greater than 65535, it's not valid + if (ports.some(p => !Number.isInteger(p) || p < 1 || p > 65535)) { + return undefined; + } + + return ports; } /** diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index e123a38334..4f006fde6c 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -23,7 +23,7 @@ import { configureNode } from './configureNode'; import { configureOther } from './configureOther'; import { configurePython } from './configurePython'; import { configureRuby } from './configureRuby'; -import { promptForPort, quickPickOS, quickPickPlatform } from './configUtils'; +import { promptForPorts, quickPickOS, quickPickPlatform } from './configUtils'; export interface PackageInfo { npmStart: boolean; //has npm start @@ -59,11 +59,15 @@ export interface IPlatformGeneratorInfo { genDockerFile: GeneratorFunction, genDockerCompose: GeneratorFunction, genDockerComposeDebug: GeneratorFunction, - defaultPort: string | undefined // '' = defaults to empty but still asks user if they want a port, undefined = don't ask at all + defaultPorts: Number[] | undefined // [] = defaults to empty but still asks user if they want a port, undefined = don't ask at all } -export function getExposeStatements(port: string): string { - return port ? `EXPOSE ${port}` : ''; +export function getExposeStatements(ports: Number[]): string { + return ports ? ports.map(port => `EXPOSE ${port}`).join('\n') : ''; +} + +export function getComposePorts(ports: Number[]): string { + return ports ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; } const generatorsByPlatform = new Map(); @@ -77,11 +81,11 @@ generatorsByPlatform.set('Python', configurePython); generatorsByPlatform.set('Ruby', configureRuby); generatorsByPlatform.set('Other', configureOther); -function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, port: string | undefined, { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[] | undefined, { cmd, author, version, artifactName }: Partial): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find dockerfile generator functions for "${platform}"`); if (generators.genDockerFile) { - let contents = generators.genDockerFile(serviceNameAndRelativePath, platform, os, port, { cmd, author, version, artifactName }); + let contents = generators.genDockerFile(serviceNameAndRelativePath, platform, os, ports, { cmd, author, version, artifactName }); // Remove multiple empty lines with single empty lines, as might be produced // if $expose_statements$ or another template variable is an empty string @@ -92,23 +96,23 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o } } -function genDockerCompose(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[]): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find docker compose file generator function for "${platform}"`); if (generators.genDockerCompose) { - return generators.genDockerCompose(serviceNameAndRelativePath, platform, os, port); + return generators.genDockerCompose(serviceNameAndRelativePath, platform, os, ports); } } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, port: string, packageInfo: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageInfo: Partial): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find docker debug compose file generator function for "${platform}"`); if (generators.genDockerComposeDebug) { - return generators.genDockerComposeDebug(serviceNameAndRelativePath, platform, os, port, packageInfo); + return generators.genDockerComposeDebug(serviceNameAndRelativePath, platform, os, ports, packageInfo); } } -function genDockerIgnoreFile(service: string, platformType: string, os: string, port: string): string { +function genDockerIgnoreFile(service: string, platformType: string, os: string, ports: Number[]): string { return `node_modules npm-debug.log Dockerfile* @@ -260,7 +264,7 @@ async function findCSProjOrFSProjFile(folderPath: string): Promise { } } -type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, port: string, packageJson?: Partial) => string; +type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageJson?: Partial) => string; const DOCKER_FILE_TYPES: { [key: string]: GeneratorFunction } = { 'docker-compose.yml': genDockerCompose, @@ -298,9 +302,9 @@ export interface ConfigureApiOptions { platform?: Platform; /** - * Port to expose + * Ports to expose */ - port?: string; + ports?: Number[]; /** * The OS for the images. Currently only needed for .NET platforms. @@ -358,9 +362,9 @@ async function configureCore(context: IActionContext, options: ConfigureApiOptio } properties.configureOs = os; - let port: string | undefined = options.port; - if (!port && generatorInfo.defaultPort !== undefined) { - port = await promptForPort(generatorInfo.defaultPort); + let ports: Number[] | undefined = options.ports; + if (!ports && generatorInfo.defaultPorts !== undefined) { + ports = await promptForPorts(generatorInfo.defaultPorts); } let targetFramework: string; @@ -432,7 +436,7 @@ async function configureCore(context: IActionContext, options: ConfigureApiOptio if (writeFile) { // Paths in the docker files should be relative to the Dockerfile (which is in the output folder) - let fileContents = generatorFunction(serviceNameAndPathRelativeToOutput, platformType, os, port, packageInfo); + let fileContents = generatorFunction(serviceNameAndPathRelativeToOutput, platformType, os, ports, packageInfo); if (fileContents) { fse.writeFileSync(filePath, fileContents, { encoding: 'utf8' }); filesWritten.push(filePath); diff --git a/src/configureWorkspace/configureCpp.ts b/src/configureWorkspace/configureCpp.ts index ef43b92e5b..75c87f801e 100644 --- a/src/configureWorkspace/configureCpp.ts +++ b/src/configureWorkspace/configureCpp.ts @@ -9,10 +9,10 @@ export let configureCpp: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose: undefined, // We don't generate compose files for Cpp genDockerComposeDebug: undefined, // We don't generate compose files for Cpp - defaultPort: undefined // We don't open a port for Cpp + defaultPorts: undefined // We don't open a port for Cpp }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { return `# GCC support can be specified at major, minor, or micro version # (e.g. 8, 8.2 or 8.2.0). # See https://hub.docker.com/r/library/gcc/ for all supported GCC diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index 85c6f1adef..cc763aa0d4 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -17,14 +17,14 @@ export const configureAspDotNetCore: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose: undefined, // We don't generate compose files for .net core genDockerComposeDebug: undefined, // We don't generate compose files for .net core - defaultPort: '80' + defaultPorts: [80, 443] }; export const configureDotNetCoreConsole: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose: undefined, // We don't generate compose files for .net core genDockerComposeDebug: undefined, // We don't generate compose files for .net core - defaultPort: undefined + defaultPorts: undefined }; // .NET Core 1.0 - 2.0 images are published to Docker Hub Registry. @@ -166,7 +166,7 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] //#endregion -function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, port: string, { version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], { version, artifactName }: Partial): string { // VS version of this function is in ResolveImageNames (src/Docker/Microsoft.VisualStudio.Docker.DotNetCore/DockerDotNetCoreScaffoldingProvider.cs) if (os !== 'Windows' && os !== 'Linux') { @@ -181,7 +181,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o let assemblyNameNoExtension = serviceName; // example: COPY Core2.0ConsoleAppWindows/Core2.0ConsoleAppWindows.csproj Core2.0ConsoleAppWindows/ let copyProjectCommands = `COPY ["${artifactName}", "${projectDirectory}/"]` - let exposeStatements = getExposeStatements(port); + let exposeStatements = getExposeStatements(ports); // Parse version from TargetFramework // Example: netcoreapp1.0 diff --git a/src/configureWorkspace/configureGo.ts b/src/configureWorkspace/configureGo.ts index 9a890efd4e..096c4f51b5 100644 --- a/src/configureWorkspace/configureGo.ts +++ b/src/configureWorkspace/configureGo.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; +import { getComposePorts, getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; export let configureGo: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { - let exposeStatements = getExposeStatements(port); +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { + let exposeStatements = getExposeStatements(ports); return ` #build stage @@ -34,18 +34,17 @@ ${exposeStatements} `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: ${serviceNameAndRelativePath}: image: ${serviceNameAndRelativePath} build: . - ports: - - ${port}:${port}`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: @@ -54,7 +53,5 @@ services: build: context: . dockerfile: Dockerfile - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } diff --git a/src/configureWorkspace/configureJava.ts b/src/configureWorkspace/configureJava.ts index 6c1ba6615c..3e90b756b6 100644 --- a/src/configureWorkspace/configureJava.ts +++ b/src/configureWorkspace/configureJava.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; +import { getComposePorts, getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; export let configureJava: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { - let exposeStatements = getExposeStatements(port); +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { + let exposeStatements = getExposeStatements(ports); const artifact = artifactName ? artifactName : `${serviceNameAndRelativePath}.jar`; return ` @@ -29,18 +29,17 @@ ENTRYPOINT exec java $JAVA_OPTS -jar ${serviceNameAndRelativePath}.jar `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: ${serviceNameAndRelativePath}: image: ${serviceNameAndRelativePath} build: . - ports: - - ${port}:${port}`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: @@ -51,8 +50,5 @@ services: dockerfile: Dockerfile environment: JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005,quiet=y - ports: - - ${port}:${port} - - 5005:5005 - `; +${getComposePorts(ports.concat(5005))}`; } diff --git a/src/configureWorkspace/configureNode.ts b/src/configureWorkspace/configureNode.ts index 6c07e705b5..a1c9d1b44d 100644 --- a/src/configureWorkspace/configureNode.ts +++ b/src/configureWorkspace/configureNode.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; +import { getComposePorts, getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; export let configureNode: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { - let exposeStatements = getExposeStatements(port); +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { + let exposeStatements = getExposeStatements(ports); return `FROM node:10.13-alpine ENV NODE_ENV production @@ -25,7 +25,7 @@ ${exposeStatements} CMD ${cmd}`; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: @@ -34,11 +34,10 @@ services: build: . environment: NODE_ENV: production - ports: - - ${port}:${port}`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { const cmdArray: string[] = cmd.split(' '); if (cmdArray[0].toLowerCase() === 'node') { @@ -56,8 +55,6 @@ services: build: . environment: NODE_ENV: development - ports: - - ${port}:${port} - - 9229:9229 +${getComposePorts(ports.concat(9299))} ${cmd}`; } diff --git a/src/configureWorkspace/configureOther.ts b/src/configureWorkspace/configureOther.ts index 460c9db7a2..52407febe8 100644 --- a/src/configureWorkspace/configureOther.ts +++ b/src/configureWorkspace/configureOther.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PackageInfo } from './configure'; +import { getComposePorts, PackageInfo } from './configure'; export let configureOther = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { return `FROM docker/whalesay:latest LABEL Name=${serviceNameAndRelativePath} Version=${version} RUN apt-get -y update && apt-get install -y fortunes @@ -20,19 +20,17 @@ CMD /usr/games/fortune -a | cowsay `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: ${serviceNameAndRelativePath}: image: ${serviceNameAndRelativePath} build: . - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: @@ -41,7 +39,5 @@ services: build: context: . dockerfile: Dockerfile - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } diff --git a/src/configureWorkspace/configurePython.ts b/src/configureWorkspace/configurePython.ts index aa8d11b59e..5ccac2b939 100644 --- a/src/configureWorkspace/configurePython.ts +++ b/src/configureWorkspace/configurePython.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; +import { getComposePorts, getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; export let configurePython: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { - let exposeStatements = getExposeStatements(port); +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { + let exposeStatements = getExposeStatements(ports); return `# Python support can be specified down to the minor or micro version # (e.g. 3.6 or 3.6.3). @@ -46,19 +46,17 @@ CMD ["python3", "-m", "${serviceNameAndRelativePath}"] `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: ${serviceNameAndRelativePath}: image: ${serviceNameAndRelativePath} build: . - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: @@ -67,7 +65,5 @@ services: build: context: . dockerfile: Dockerfile - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } diff --git a/src/configureWorkspace/configureRuby.ts b/src/configureWorkspace/configureRuby.ts index f1d55d4a8e..254d7c9465 100644 --- a/src/configureWorkspace/configureRuby.ts +++ b/src/configureWorkspace/configureRuby.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; +import { getComposePorts, getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure'; export let configureRuby: IPlatformGeneratorInfo = { genDockerFile, genDockerCompose, genDockerComposeDebug, - defaultPort: '3000' + defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial): string { - let exposeStatements = getExposeStatements(port); +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { + let exposeStatements = getExposeStatements(ports); return `FROM ruby:2.5-slim @@ -33,18 +33,17 @@ CMD ["ruby", "${serviceNameAndRelativePath}.rb"] `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { return `version: '2.1' services: ${serviceNameAndRelativePath}: image: ${serviceNameAndRelativePath} build: . - ports: - - ${port}:${port}`; +${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: @@ -53,7 +52,5 @@ services: build: context: . dockerfile: Dockerfile - ports: - - ${port}:${port} -`; +${getComposePorts(ports)}`; } diff --git a/test/configure.test.ts b/test/configure.test.ts index 7f38646b11..4dcfb4d429 100644 --- a/test/configure.test.ts +++ b/test/configure.test.ts @@ -1263,7 +1263,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void rootPath: testRootFolder, outputFolder: testRootFolder, platform: 'Ruby', - port: '234' + ports: [234] } ); }); @@ -1307,7 +1307,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void { rootPath: testRootFolder, outputFolder: testRootFolder, - port: "444" + ports: [444] }, ["Ruby"], ['Dockerfile', 'docker-compose.debug.yml', 'docker-compose.yml', '.dockerignore'] @@ -1333,7 +1333,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void rootPath: path.join(testRootFolder, 'serviceFolder'), outputFolder: path.join(testRootFolder, 'serviceFolder'), os: "Linux", - port: "1234" + ports: [1234] }, ['.NET Core Console'], ['serviceFolder/Dockerfile', 'serviceFolder/.dockerignore', 'serviceFolder/somefile1.cs', 'serviceFolder/aspnetapp.csproj'] @@ -1352,7 +1352,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void rootPath: path.join(testRootFolder, 'serviceFolder'), outputFolder: path.join(testRootFolder, 'serviceFolder'), os: "Windows", - port: "1234" + ports: [1234] }, ['.NET Core Console'], ['serviceFolder/Dockerfile', 'serviceFolder/.dockerignore', 'serviceFolder/subfolder1/somefile1.cs', 'serviceFolder/subfolder1/aspnetapp.csproj'] @@ -1370,7 +1370,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void rootPath: path.join(testRootFolder, 'serviceFolder'), outputFolder: path.join(testRootFolder, 'serviceFolder', 'subfolder1'), os: "Windows", - port: "1234" + ports: [1234, 5678] }, ['ASP.NET Core'], ['serviceFolder/subfolder1/Dockerfile', 'serviceFolder/subfolder1/.dockerignore', 'serviceFolder/subfolder1/somefile1.cs', 'serviceFolder/subfolder1/aspnetapp.csproj'] ); From dd386cb0021abec94e243778dcdd782b0be4504b Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 15 Jul 2019 14:10:46 -0400 Subject: [PATCH 11/40] Add ASPNETCORE_URLS to Dockerfile --- src/configureWorkspace/configureDotNetCore.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index cc763aa0d4..9a6600d950 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -73,6 +73,7 @@ const aspNetCoreWindowsTemplate = `#Depending on the operating system of the hos FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ +$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -95,6 +96,7 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] const aspNetCoreLinuxTemplate = `FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ +$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -182,6 +184,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o // example: COPY Core2.0ConsoleAppWindows/Core2.0ConsoleAppWindows.csproj Core2.0ConsoleAppWindows/ let copyProjectCommands = `COPY ["${artifactName}", "${projectDirectory}/"]` let exposeStatements = getExposeStatements(ports); + let envStatements = getEnvStatements(ports); // Parse version from TargetFramework // Example: netcoreapp1.0 @@ -247,6 +250,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o let contents = template.replace('$base_image_name$', baseImageName) .replace(/\$expose_statements\$/g, exposeStatements) + .replace(/\$env_statements$/g, envStatements) .replace(/\$sdk_image_name\$/g, sdkImageName) .replace(/\$container_project_directory\$/g, projectDirectory) .replace(/\$project_file_name\$/g, projectFileName) @@ -260,3 +264,23 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o return contents; } + +function getEnvStatements(ports: Number[]): string { + let urls: string[] = []; + + ports.forEach(port => { + // If 443 is exposed, listen on https://+:443; for everything else listen on http://+:${port} + if (port === 443) { + urls.push(`https://+:443`); + } else { + // tslint:disable-next-line: no-http-string + urls.push(`http://+:${port}`); + } + }); + + if (urls.length > 0) { + return `ENV ASPNETCORE_URLS=${urls.join(';')}`; + } + + return ''; +} From 5bf59a970075145af33407ec2fb55eb25fd3c870 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 15 Jul 2019 14:29:17 -0400 Subject: [PATCH 12/40] Scaffold UserSecretsId into the project file --- .../coreclr/CommandLineDotNetClient.ts | 28 +++++++++++++++++-- .../registerDebugConfigurationProvider.ts | 2 +- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index eb479880ba..339e4701dd 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -2,7 +2,11 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import * as semver from 'semver'; +import { v4 as uuidv4 } from 'uuid'; import { ProcessProvider } from "./ChildProcessProvider"; +import { FileSystemProvider } from "./fsProvider"; +import { LocalOSProvider } from "./LocalOSProvider"; export type MSBuildExecOptions = { target?: string; @@ -18,7 +22,7 @@ export interface DotNetClient { export class CommandLineDotNetClient implements DotNetClient { private static KnownConfiguredProjects: Set; - constructor(private readonly processProvider: ProcessProvider) { + constructor(private readonly processProvider: ProcessProvider, private readonly fsProvider: FileSystemProvider, private readonly osProvider: LocalOSProvider) { } public async execTarget(projectFile: string, options?: MSBuildExecOptions): Promise { @@ -57,8 +61,10 @@ export class CommandLineDotNetClient implements DotNetClient { return; } - // TODO : trust doesn't work for Linux users; need to direct them to manually trust the cert - const exportCommand = `dotnet dev-certs https --trust -ep "${hostExportPath}" -p "${password}"`; + await this.addUserSecretsIfNecessary(projectFile); + + // Trust doesn't work for Linux users; need to direct them to manually trust the cert + const exportCommand = `dotnet dev-certs https ${this.osProvider.os === 'Linux' ? '' : '--trust'} -ep "${hostExportPath}" -p "${password}"`; await this.processProvider.exec(exportCommand, {}); const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; @@ -71,6 +77,22 @@ export class CommandLineDotNetClient implements DotNetClient { //const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${containerExportPath}"`; //await this.processProvider.exec(userSecretsPathCommand, {}); } + + private async addUserSecretsIfNecessary(projectFile: string): Promise { + const contents = await this.fsProvider.readFile(projectFile); + + if (contents.indexOf('UserSecretsId') >= 0) { + return; + } + + const dotNetVer = await this.getVersion(); + if (semver.lt(dotNetVer, '3.0')) { + throw new Error(`The 'UserSecretsId' MSBuild property is not set in the project file '${projectFile}'. Set it to a unique value, for example a UUID.`); + } + + const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${uuidv4()}`; + await this.processProvider.exec(userSecretsInitCommand, {}); + } } export default CommandLineDotNetClient; diff --git a/src/debugging/coreclr/registerDebugConfigurationProvider.ts b/src/debugging/coreclr/registerDebugConfigurationProvider.ts index 92b8b37394..58b850f6e4 100644 --- a/src/debugging/coreclr/registerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/registerDebugConfigurationProvider.ts @@ -26,8 +26,8 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) const processProvider = new ChildProcessProvider(); const dockerClient = new CliDockerClient(processProvider); - const dotNetClient = new CommandLineDotNetClient(processProvider); const osProvider = new LocalOSProvider(); + const dotNetClient = new CommandLineDotNetClient(processProvider, fileSystemProvider, osProvider); const dockerOutputManager = new DefaultOutputManager(ext.outputChannel); From 0bb2ae537b6575d14cd26a695e9db5f61d1cc175 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Wed, 17 Jul 2019 12:51:14 -0400 Subject: [PATCH 13/40] Fix env_statements replacement token --- src/configureWorkspace/configureDotNetCore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index 9a6600d950..89a8e056f2 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -250,7 +250,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o let contents = template.replace('$base_image_name$', baseImageName) .replace(/\$expose_statements\$/g, exposeStatements) - .replace(/\$env_statements$/g, envStatements) + .replace(/\$env_statements\$/g, envStatements) .replace(/\$sdk_image_name\$/g, sdkImageName) .replace(/\$container_project_directory\$/g, projectDirectory) .replace(/\$project_file_name\$/g, projectFileName) @@ -279,7 +279,7 @@ function getEnvStatements(ports: Number[]): string { }); if (urls.length > 0) { - return `ENV ASPNETCORE_URLS=${urls.join(';')}`; + return `ENV ASPNETCORE_URLS="${urls.join(';')}"`; } return ''; From 70f97dfe6076324082e746213891578fc3a1ca5f Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Wed, 17 Jul 2019 12:51:25 -0400 Subject: [PATCH 14/40] Fix uninit'd variable --- src/debugging/coreclr/CommandLineDotNetClient.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 339e4701dd..ad84d6c31c 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -20,7 +20,7 @@ export interface DotNetClient { } export class CommandLineDotNetClient implements DotNetClient { - private static KnownConfiguredProjects: Set; + private static KnownConfiguredProjects: Set = new Set(); constructor(private readonly processProvider: ProcessProvider, private readonly fsProvider: FileSystemProvider, private readonly osProvider: LocalOSProvider) { } @@ -64,11 +64,11 @@ export class CommandLineDotNetClient implements DotNetClient { await this.addUserSecretsIfNecessary(projectFile); // Trust doesn't work for Linux users; need to direct them to manually trust the cert - const exportCommand = `dotnet dev-certs https ${this.osProvider.os === 'Linux' ? '' : '--trust'} -ep "${hostExportPath}" -p "${password}"`; + const exportCommand = `dotnet dev-certs https ${this.osProvider.os === 'Linux' ? '' : '--trust'} -ep "${hostExportPath}" -p "foobar"`; await this.processProvider.exec(exportCommand, {}); - const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; - await this.processProvider.exec(userSecretsPasswordCommand, {}); + //const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; + //await this.processProvider.exec(userSecretsPasswordCommand, {}); CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); From 2a8b48849f73460efa30e200b4d874972381a77b Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 10:35:25 -0400 Subject: [PATCH 15/40] Rearranging some things --- src/configureWorkspace/configureDotNetCore.ts | 17 ++++++------ .../coreclr/CommandLineDotNetClient.ts | 26 +++++++++---------- src/debugging/coreclr/dockerManager.ts | 3 +-- .../registerDebugConfigurationProvider.ts | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index 89a8e056f2..a9b8310887 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -126,6 +126,7 @@ const dotNetCoreConsoleWindowsTemplate = `#Depending on the operating system of FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ +$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -148,6 +149,7 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] const dotNetCoreConsoleLinuxTemplate = `FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ +$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -184,7 +186,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o // example: COPY Core2.0ConsoleAppWindows/Core2.0ConsoleAppWindows.csproj Core2.0ConsoleAppWindows/ let copyProjectCommands = `COPY ["${artifactName}", "${projectDirectory}/"]` let exposeStatements = getExposeStatements(ports); - let envStatements = getEnvStatements(ports); + let envStatements = getEnvStatements(ports, platform); // Parse version from TargetFramework // Example: netcoreapp1.0 @@ -265,17 +267,16 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o return contents; } -function getEnvStatements(ports: Number[]): string { +function getEnvStatements(ports: Number[], platform: Platform): string { + if (platform !== 'ASP.NET Core') { + return ''; + } + let urls: string[] = []; ports.forEach(port => { // If 443 is exposed, listen on https://+:443; for everything else listen on http://+:${port} - if (port === 443) { - urls.push(`https://+:443`); - } else { - // tslint:disable-next-line: no-http-string - urls.push(`http://+:${port}`); - } + urls.push(`${port === 443 ? 'https' : 'http'}://+:${port}`); }); if (urls.length > 0) { diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index ad84d6c31c..aaab025f2e 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -6,7 +6,6 @@ import * as semver from 'semver'; import { v4 as uuidv4 } from 'uuid'; import { ProcessProvider } from "./ChildProcessProvider"; import { FileSystemProvider } from "./fsProvider"; -import { LocalOSProvider } from "./LocalOSProvider"; export type MSBuildExecOptions = { target?: string; @@ -16,13 +15,15 @@ export type MSBuildExecOptions = { export interface DotNetClient { execTarget(projectFile: string, options?: MSBuildExecOptions): Promise; getVersion(): Promise; - trustAndExportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string, password: string): Promise; + exportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise; } export class CommandLineDotNetClient implements DotNetClient { private static KnownConfiguredProjects: Set = new Set(); - constructor(private readonly processProvider: ProcessProvider, private readonly fsProvider: FileSystemProvider, private readonly osProvider: LocalOSProvider) { + constructor( + private readonly processProvider: ProcessProvider, + private readonly fsProvider: FileSystemProvider) { } public async execTarget(projectFile: string, options?: MSBuildExecOptions): Promise { @@ -56,19 +57,20 @@ export class CommandLineDotNetClient implements DotNetClient { } } - public async trustAndExportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string, password: string): Promise { + public async exportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { if (CommandLineDotNetClient.KnownConfiguredProjects.has(projectFile)) { return; } await this.addUserSecretsIfNecessary(projectFile); - // Trust doesn't work for Linux users; need to direct them to manually trust the cert - const exportCommand = `dotnet dev-certs https ${this.osProvider.os === 'Linux' ? '' : '--trust'} -ep "${hostExportPath}" -p "foobar"`; + const password = uuidv4(); + + const exportCommand = `dotnet dev-certs https -ep "${hostExportPath}" -p "${password}"`; await this.processProvider.exec(exportCommand, {}); - //const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; - //await this.processProvider.exec(userSecretsPasswordCommand, {}); + const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; + await this.processProvider.exec(userSecretsPasswordCommand, {}); CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); @@ -86,12 +88,10 @@ export class CommandLineDotNetClient implements DotNetClient { } const dotNetVer = await this.getVersion(); - if (semver.lt(dotNetVer, '3.0')) { - throw new Error(`The 'UserSecretsId' MSBuild property is not set in the project file '${projectFile}'. Set it to a unique value, for example a UUID.`); + if (semver.gte(dotNetVer, '3.0')) { + const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${uuidv4()}`; + await this.processProvider.exec(userSecretsInitCommand, {}); } - - const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${uuidv4()}`; - await this.processProvider.exec(userSecretsInitCommand, {}); } } diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 7f71c6f0a7..d251e64366 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -4,7 +4,6 @@ import deepEqual = require('deep-equal'); import * as path from 'path'; -import { v4 as uuidv4 } from 'uuid'; import { Memento } from 'vscode'; import { PlatformOS } from '../../utils/platform'; import { AppStorageProvider } from './appStorage'; @@ -246,7 +245,7 @@ export class DefaultDockerManager implements DockerManager { const hostCertificateExportPfxPath = path.join(this.getHostSecretsPaths().hostCertificateExportPath, `${appOutputName}.pfx`); const containerCertificateExportPfxPath = this.osProvider.pathJoin(options.run.os, this.getContainerSecretsPaths(options.run.os).containerCertificateExportPath, `${appOutputName}.pfx`) - await this.dotNetClient.trustAndExportCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath, uuidv4()); + await this.dotNetClient.exportCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath); const containerId = await this.runContainer(imageId, { appFolder: options.appFolder, ...options.run }); diff --git a/src/debugging/coreclr/registerDebugConfigurationProvider.ts b/src/debugging/coreclr/registerDebugConfigurationProvider.ts index 58b850f6e4..e7ca5486cf 100644 --- a/src/debugging/coreclr/registerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/registerDebugConfigurationProvider.ts @@ -26,8 +26,8 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) const processProvider = new ChildProcessProvider(); const dockerClient = new CliDockerClient(processProvider); + const dotNetClient = new CommandLineDotNetClient(processProvider, fileSystemProvider); const osProvider = new LocalOSProvider(); - const dotNetClient = new CommandLineDotNetClient(processProvider, fileSystemProvider, osProvider); const dockerOutputManager = new DefaultOutputManager(ext.outputChannel); From 43a35e6c7f54f67137fd7b5327835c6d54a6ef60 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 11:36:20 -0400 Subject: [PATCH 16/40] Make configuring SSL optional --- package.json | 5 ++ .../coreclr/CommandLineDotNetClient.ts | 4 +- .../dockerDebugConfigurationProvider.ts | 7 ++- src/debugging/coreclr/dockerManager.ts | 51 ++++++++++--------- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 67f402102f..f3d49c44c9 100644 --- a/package.json +++ b/package.json @@ -663,6 +663,11 @@ } } } + }, + "configureSslCertificate": { + "type": "boolean", + "default": true, + "description": "Whether to configure an SSL certificate for ASP.NET Core web services." } } } diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index aaab025f2e..13cf3f85a0 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -15,7 +15,7 @@ export type MSBuildExecOptions = { export interface DotNetClient { execTarget(projectFile: string, options?: MSBuildExecOptions): Promise; getVersion(): Promise; - exportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise; + exportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise; } export class CommandLineDotNetClient implements DotNetClient { @@ -57,7 +57,7 @@ export class CommandLineDotNetClient implements DotNetClient { } } - public async exportCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { + public async exportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { if (CommandLineDotNetClient.KnownConfiguredProjects.has(projectFile)) { return; } diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index b430eafe26..e7bf296c60 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -54,6 +54,7 @@ interface DockerDebugConfiguration extends DebugConfiguration { appProject?: string; dockerBuild?: DockerDebugBuildOptions; dockerRun?: DockerDebugRunOptions; + configureSslCertificate?: boolean; } export class DockerDebugConfigurationProvider implements DebugConfigurationProvider { @@ -165,6 +166,7 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi }; } + // tslint:disable-next-line:cyclomatic-complexity private static inferRunOptions(folder: WorkspaceFolder, debugConfiguration: DockerDebugConfiguration, appName: string, os: PlatformOS): LaunchRunOptions { const containerName = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.containerName ? debugConfiguration.dockerRun.containerName @@ -184,6 +186,8 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi const volumes = DockerDebugConfigurationProvider.inferVolumes(folder, debugConfiguration); const extraHosts = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.extraHosts; + const configureSslCertificate = debugConfiguration && debugConfiguration.configureSslCertificate; + return { containerName, env, @@ -194,7 +198,8 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi networkAlias, os, ports, - volumes + volumes, + configureSslCertificate }; } diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index d251e64366..618080fa2e 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -29,6 +29,7 @@ export type DockerManagerRunContainerOptions & { appFolder: string; os: PlatformOS; + configureSslCertificate: boolean; }; type Omit = Pick>; @@ -241,11 +242,12 @@ export class DefaultDockerManager implements DockerManager { public async prepareForLaunch(options: LaunchOptions): Promise { const imageId = await this.buildImage({ appFolder: options.appFolder, ...options.build }); - const appOutputName = this.osProvider.pathParse(options.run.os, options.appOutput).name; - const hostCertificateExportPfxPath = path.join(this.getHostSecretsPaths().hostCertificateExportPath, `${appOutputName}.pfx`); - const containerCertificateExportPfxPath = this.osProvider.pathJoin(options.run.os, this.getContainerSecretsPaths(options.run.os).containerCertificateExportPath, `${appOutputName}.pfx`) - - await this.dotNetClient.exportCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath); + if (options.run.configureSslCertificate) { + const appOutputName = this.osProvider.pathParse(options.run.os, options.appOutput).name; + const hostCertificateExportPfxPath = path.join(this.getHostSecretsPaths().hostCertificateExportPath, `${appOutputName}.pfx`); + const containerCertificateExportPfxPath = this.osProvider.pathJoin(options.run.os, this.getContainerSecretsPaths(options.run.os).containerCertificateExportPath, `${appOutputName}.pfx`); + await this.dotNetClient.exportSslCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath); + } const containerId = await this.runContainer(imageId, { appFolder: options.appFolder, ...options.run }); @@ -382,30 +384,33 @@ export class DefaultDockerManager implements DockerManager { permissions: 'ro' }; - const { hostCertificateExportPath, hostUserSecretsPath } = this.getHostSecretsPaths(); - const { containerCertificateExportPath, containerUserSecretsPath } = this.getContainerSecretsPaths(options.os); - - const certVolume: DockerContainerVolume = { - localPath: hostCertificateExportPath, - containerPath: containerCertificateExportPath, - permissions: 'ro' - }; - - const userSecretsVolume: DockerContainerVolume = { - localPath: hostUserSecretsPath, - containerPath: containerUserSecretsPath, - permissions: 'ro' - }; - - const volumes: DockerContainerVolume[] = [ + let volumes: DockerContainerVolume[] = [ appVolume, debuggerVolume, nugetVolume, nugetFallbackVolume, - certVolume, - userSecretsVolume, ]; + if (options.configureSslCertificate) { + const { hostCertificateExportPath, hostUserSecretsPath } = this.getHostSecretsPaths(); + const { containerCertificateExportPath, containerUserSecretsPath } = this.getContainerSecretsPaths(options.os); + + const certVolume: DockerContainerVolume = { + localPath: hostCertificateExportPath, + containerPath: containerCertificateExportPath, + permissions: 'ro' + }; + + const userSecretsVolume: DockerContainerVolume = { + localPath: hostUserSecretsPath, + containerPath: containerUserSecretsPath, + permissions: 'ro' + }; + + volumes.push(certVolume); + volumes.push(userSecretsVolume); + } + return volumes; } From f5354028d319c41bd02f63a242d59145ea447a70 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 11:49:31 -0400 Subject: [PATCH 17/40] Minor fixes --- src/configureWorkspace/configUtils.ts | 2 +- src/debugging/coreclr/CommandLineDotNetClient.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configureWorkspace/configUtils.ts b/src/configureWorkspace/configUtils.ts index 5f2d1fbf9f..b3b2972c06 100644 --- a/src/configureWorkspace/configUtils.ts +++ b/src/configureWorkspace/configUtils.ts @@ -15,7 +15,7 @@ import { Platform, PlatformOS } from '../utils/platform'; export async function promptForPorts(ports: Number[]): Promise { let opt: vscode.InputBoxOptions = { placeHolder: `${ports.join(', ')}`, - prompt: 'What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.', + prompt: 'What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.', value: `${ports.join(', ')}`, validateInput: (value: string): string | undefined => { let result = splitPorts(value); diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 13cf3f85a0..1823dd9720 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -88,7 +88,7 @@ export class CommandLineDotNetClient implements DotNetClient { } const dotNetVer = await this.getVersion(); - if (semver.gte(dotNetVer, '3.0')) { + if (semver.gte(dotNetVer, '3.0.0')) { const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${uuidv4()}`; await this.processProvider.exec(userSecretsInitCommand, {}); } From 4ddcf54bc4742c03a0eda2cfa7fbb2fd55616885 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 11:49:45 -0400 Subject: [PATCH 18/40] Scaffold configureSslCertificate by default --- package.json | 3 ++- src/debugging/coreclr/dockerDebugConfigurationProvider.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f3d49c44c9..4926d71827 100644 --- a/package.json +++ b/package.json @@ -494,7 +494,8 @@ "env": { "ASPNETCORE_ENVIRONMENT": "Development" } - } + }, + "configureSslCertificate": true } } ], diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index e7bf296c60..d47217f557 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -80,7 +80,8 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi }, dockerRun: { env: { "ASPNETCORE_ENVIRONMENT": "Development" } - } + }, + configureSslCertificate: true } ]; } From 398ca6c37943a23697e5bf0af788ff47ab8c6090 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 12:16:17 -0400 Subject: [PATCH 19/40] Fix some failing tests --- src/configureWorkspace/configureNode.ts | 2 +- test/configure.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/configureWorkspace/configureNode.ts b/src/configureWorkspace/configureNode.ts index a1c9d1b44d..aca1e76756 100644 --- a/src/configureWorkspace/configureNode.ts +++ b/src/configureWorkspace/configureNode.ts @@ -55,6 +55,6 @@ services: build: . environment: NODE_ENV: development -${getComposePorts(ports.concat(9299))} +${getComposePorts(ports.concat(9229))} ${cmd}`; } diff --git a/test/configure.test.ts b/test/configure.test.ts index 4dcfb4d429..e50851f866 100644 --- a/test/configure.test.ts +++ b/test/configure.test.ts @@ -840,6 +840,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-nanoserver-1809 AS base WORKDIR /app EXPOSE 1234 + ENV ASPNETCORE_URLS="http://+:1234" FROM mcr.microsoft.com/dotnet/core/sdk:2.2-nanoserver-1809 AS build WORKDIR /src @@ -871,6 +872,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 1234 + ENV ASPNETCORE_URLS="http://+:1234" FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src From 0e4b01591350d8fe27a2ebf4ac16ae8b8ba643ca Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 12:31:34 -0400 Subject: [PATCH 20/40] Fix a few more tests --- src/configureWorkspace/configure.ts | 4 ++-- test/configure.test.ts | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index 4f006fde6c..817e5d38cc 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -89,8 +89,8 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o // Remove multiple empty lines with single empty lines, as might be produced // if $expose_statements$ or another template variable is an empty string - contents = contents.replace(/(\r\n){3}/g, "\r\n\r\n") - .replace(/(\n){3}/g, "\n\n"); + contents = contents.replace(/(\r\n){3,4}/g, "\r\n\r\n") + .replace(/(\n){3,4}/g, "\n\n"); return contents; } diff --git a/test/configure.test.ts b/test/configure.test.ts index e50851f866..c0ff4a7739 100644 --- a/test/configure.test.ts +++ b/test/configure.test.ts @@ -1238,8 +1238,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void image: testoutput build: . ports: - - 3000:3000 - `)); + - 3000:3000`)); assert.strictEqual(debugComposeContents, removeIndentation(` version: '2.1' @@ -1250,8 +1249,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void context: . dockerfile: Dockerfile ports: - - 3000:3000 - `)); + - 3000:3000`)); }); }); From f8b768a64184ecf49ffedb5e451fa83f75501816 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 18 Jul 2019 13:14:53 -0400 Subject: [PATCH 21/40] Decided not to put ASPNETCORE_URLS into Dockerfile --- package.json | 3 ++- src/configureWorkspace/configureDotNetCore.ts | 25 ------------------- .../dockerDebugConfigurationProvider.ts | 6 ++++- test/configure.test.ts | 2 -- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 4926d71827..edf272553e 100644 --- a/package.json +++ b/package.json @@ -492,7 +492,8 @@ "dockerBuild": {}, "dockerRun": { "env": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://+:80;https://+:443" } }, "configureSslCertificate": true diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index a9b8310887..cc763aa0d4 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -73,7 +73,6 @@ const aspNetCoreWindowsTemplate = `#Depending on the operating system of the hos FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ -$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -96,7 +95,6 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] const aspNetCoreLinuxTemplate = `FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ -$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -126,7 +124,6 @@ const dotNetCoreConsoleWindowsTemplate = `#Depending on the operating system of FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ -$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -149,7 +146,6 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] const dotNetCoreConsoleLinuxTemplate = `FROM $base_image_name$ AS base WORKDIR /app $expose_statements$ -$env_statements$ FROM $sdk_image_name$ AS build WORKDIR /src @@ -186,7 +182,6 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o // example: COPY Core2.0ConsoleAppWindows/Core2.0ConsoleAppWindows.csproj Core2.0ConsoleAppWindows/ let copyProjectCommands = `COPY ["${artifactName}", "${projectDirectory}/"]` let exposeStatements = getExposeStatements(ports); - let envStatements = getEnvStatements(ports, platform); // Parse version from TargetFramework // Example: netcoreapp1.0 @@ -252,7 +247,6 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o let contents = template.replace('$base_image_name$', baseImageName) .replace(/\$expose_statements\$/g, exposeStatements) - .replace(/\$env_statements\$/g, envStatements) .replace(/\$sdk_image_name\$/g, sdkImageName) .replace(/\$container_project_directory\$/g, projectDirectory) .replace(/\$project_file_name\$/g, projectFileName) @@ -266,22 +260,3 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o return contents; } - -function getEnvStatements(ports: Number[], platform: Platform): string { - if (platform !== 'ASP.NET Core') { - return ''; - } - - let urls: string[] = []; - - ports.forEach(port => { - // If 443 is exposed, listen on https://+:443; for everything else listen on http://+:${port} - urls.push(`${port === 443 ? 'https' : 'http'}://+:${port}`); - }); - - if (urls.length > 0) { - return `ENV ASPNETCORE_URLS="${urls.join(';')}"`; - } - - return ''; -} diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index d47217f557..f2a7b9553e 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -79,7 +79,11 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi dockerBuild: { }, dockerRun: { - env: { "ASPNETCORE_ENVIRONMENT": "Development" } + env: { + "ASPNETCORE_ENVIRONMENT": "Development", + //tslint:disable-next-line:no-http-string + "ASPNETCORE_URLS": "http://+:80;https://+:443" + } }, configureSslCertificate: true } diff --git a/test/configure.test.ts b/test/configure.test.ts index c0ff4a7739..c7e85b660a 100644 --- a/test/configure.test.ts +++ b/test/configure.test.ts @@ -840,7 +840,6 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-nanoserver-1809 AS base WORKDIR /app EXPOSE 1234 - ENV ASPNETCORE_URLS="http://+:1234" FROM mcr.microsoft.com/dotnet/core/sdk:2.2-nanoserver-1809 AS build WORKDIR /src @@ -872,7 +871,6 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base WORKDIR /app EXPOSE 1234 - ENV ASPNETCORE_URLS="http://+:1234" FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src From 6b8eedd74bff017bc89f1083d0cf670bec9733d2 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Fri, 19 Jul 2019 09:39:36 -0400 Subject: [PATCH 22/40] Allow passing env variables up via LaunchResult --- src/debugging/coreclr/dockerDebugConfigurationProvider.ts | 1 + src/debugging/coreclr/dockerManager.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index f2a7b9553e..0c7c4ec323 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -346,6 +346,7 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi program: result.program, args: result.programArgs.join(' '), cwd: result.programCwd, + env: result.programEnv, launchBrowser, pipeTransport: { pipeCwd: result.pipeCwd, diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 618080fa2e..936d4d7916 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -54,6 +54,7 @@ export type LaunchResult = { program: string; programArgs: string[]; programCwd: string; + programEnv: { [key: string]: string }; }; type LastImageBuildMetadata = { @@ -282,7 +283,8 @@ export class DefaultDockerManager implements DockerManager { pipeProgram: 'docker', program: 'dotnet', programArgs: [additionalProbingPathsArgs, containerAppOutput], - programCwd: options.run.os === 'Windows' ? 'C:\\app' : '/app' + programCwd: options.run.os === 'Windows' ? 'C:\\app' : '/app', + programEnv: {} }; } From 6478aa358a7b42acdf3a0aa15ea64fefb5d7750a Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Fri, 19 Jul 2019 09:59:13 -0400 Subject: [PATCH 23/40] Separate launch tasks for .NET Core vs ASP.NET --- package.json | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c42dcf52bd..3757709869 100644 --- a/package.json +++ b/package.json @@ -482,10 +482,10 @@ "label": "Docker: Launch .NET Core (Preview)", "configurationSnippets": [ { - "label": "Docker: Launch .NET Core (Preview)", - "description": "Docker: Launch .NET Core (Preview)", + "label": "Docker: Launch ASP.NET Core (Preview)", + "description": "Docker: Launch ASP.NET Core (Preview)", "body": { - "name": "Docker: Launch .NET Core (Preview)", + "name": "Docker: Launch ASP.NET Core (Preview)", "type": "docker-coreclr", "request": "launch", "preLaunchTask": "build", @@ -498,6 +498,18 @@ }, "configureSslCertificate": true } + }, + { + "label": "Docker: Launch .NET Core Console (Preview)", + "description": "Docker: Launch .NET Core Console (Preview)", + "body": { + "name": "Docker: Launch .NET Core Console (Preview)", + "type": "docker-coreclr", + "request": "launch", + "preLaunchTask": "build", + "dockerBuild": {}, + "dockerRun": {} + } } ], "configurationAttributes": { From fe9bd037905c5615f54671467aa70d4d7d1239c3 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Fri, 19 Jul 2019 11:13:44 -0400 Subject: [PATCH 24/40] Spawn a trust prompt on Mac and Windows --- .../coreclr/CommandLineDotNetClient.ts | 32 +++++++++++++++++-- .../registerDebugConfigurationProvider.ts | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 1823dd9720..0929e94eb7 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -2,10 +2,13 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import * as cp from 'child_process'; import * as semver from 'semver'; import { v4 as uuidv4 } from 'uuid'; +import { window } from 'vscode'; import { ProcessProvider } from "./ChildProcessProvider"; import { FileSystemProvider } from "./fsProvider"; +import { OSProvider } from "./LocalOSProvider"; export type MSBuildExecOptions = { target?: string; @@ -23,7 +26,8 @@ export class CommandLineDotNetClient implements DotNetClient { constructor( private readonly processProvider: ProcessProvider, - private readonly fsProvider: FileSystemProvider) { + private readonly fsProvider: FileSystemProvider, + private readonly osProvider: OSProvider) { } public async execTarget(projectFile: string, options?: MSBuildExecOptions): Promise { @@ -64,20 +68,42 @@ export class CommandLineDotNetClient implements DotNetClient { await this.addUserSecretsIfNecessary(projectFile); + if (this.osProvider.os === 'Windows' || this.osProvider.isMac) { + try { + // Check if the certificate is already trusted + const checkCommand = `dotnet dev-certs https --check`; + await this.processProvider.exec(checkCommand, {}); + } catch { + // It is not trusted--ask if they want to do so + window.showWarningMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", + { modal: false }, + ...['Yes', 'No']).then(selection => { + if (selection === 'Yes') { + const trustCommand = `dotnet dev-certs https --trust`; + cp.spawn(trustCommand, { shell: true, windowsHide: false, detached: true }); + } + }); + } + } + const password = uuidv4(); + // Export the certificate const exportCommand = `dotnet dev-certs https -ep "${hostExportPath}" -p "${password}"`; await this.processProvider.exec(exportCommand, {}); + // Set the password to dotnet user-secrets const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; await this.processProvider.exec(userSecretsPasswordCommand, {}); - CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); - // This is not honored due to https://github.com/aspnet/AspNetCore.Docs/issues/6199#issuecomment-418194220 // Consequently, the certificate name must be equal to .pfx, i.e. MyWebApp.dll => MyWebApp.pfx //const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${containerExportPath}"`; //await this.processProvider.exec(userSecretsPathCommand, {}); + + // Cache the project so we don't do this all over again every F5 + CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); } private async addUserSecretsIfNecessary(projectFile: string): Promise { diff --git a/src/debugging/coreclr/registerDebugConfigurationProvider.ts b/src/debugging/coreclr/registerDebugConfigurationProvider.ts index e7ca5486cf..58b850f6e4 100644 --- a/src/debugging/coreclr/registerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/registerDebugConfigurationProvider.ts @@ -26,8 +26,8 @@ export function registerDebugConfigurationProvider(ctx: vscode.ExtensionContext) const processProvider = new ChildProcessProvider(); const dockerClient = new CliDockerClient(processProvider); - const dotNetClient = new CommandLineDotNetClient(processProvider, fileSystemProvider); const osProvider = new LocalOSProvider(); + const dotNetClient = new CommandLineDotNetClient(processProvider, fileSystemProvider, osProvider); const dockerOutputManager = new DefaultOutputManager(ext.outputChannel); From 0ef9b24f06b108c8077d2fe5ec499dbb48f4a81e Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Fri, 19 Jul 2019 15:36:22 -0400 Subject: [PATCH 25/40] Prompting for trust on Windows and Mac --- .../coreclr/CommandLineDotNetClient.ts | 49 ++++++++++++------- src/debugging/coreclr/dockerManager.ts | 2 +- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 0929e94eb7..94d81d9776 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -2,7 +2,6 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import * as cp from 'child_process'; import * as semver from 'semver'; import { v4 as uuidv4 } from 'uuid'; import { window } from 'vscode'; @@ -18,7 +17,7 @@ export type MSBuildExecOptions = { export interface DotNetClient { execTarget(projectFile: string, options?: MSBuildExecOptions): Promise; getVersion(): Promise; - exportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise; + trustAndExportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise; } export class CommandLineDotNetClient implements DotNetClient { @@ -61,7 +60,7 @@ export class CommandLineDotNetClient implements DotNetClient { } } - public async exportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { + public async trustAndExportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { if (CommandLineDotNetClient.KnownConfiguredProjects.has(projectFile)) { return; } @@ -69,22 +68,7 @@ export class CommandLineDotNetClient implements DotNetClient { await this.addUserSecretsIfNecessary(projectFile); if (this.osProvider.os === 'Windows' || this.osProvider.isMac) { - try { - // Check if the certificate is already trusted - const checkCommand = `dotnet dev-certs https --check`; - await this.processProvider.exec(checkCommand, {}); - } catch { - // It is not trusted--ask if they want to do so - window.showWarningMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", - { modal: false }, - ...['Yes', 'No']).then(selection => { - if (selection === 'Yes') { - const trustCommand = `dotnet dev-certs https --trust`; - cp.spawn(trustCommand, { shell: true, windowsHide: false, detached: true }); - } - }); - } + await this.promptAndTrustCertificateIfNecessary(); } const password = uuidv4(); @@ -119,6 +103,33 @@ export class CommandLineDotNetClient implements DotNetClient { await this.processProvider.exec(userSecretsInitCommand, {}); } } + + private async promptAndTrustCertificateIfNecessary(): Promise { + try { + const checkCommand = `dotnet dev-certs https --check --trust`; + await this.processProvider.exec(checkCommand, {}); + } catch { + const selection = await window.showInformationMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", + { modal: true }, + ...['Yes', 'No']); + + if (selection === 'Yes') { + const trustCommand = `dotnet dev-certs https --trust ; exit 0`; // Exiting afterward means we can listen for this terminal instance to be closed + const terminal = window.createTerminal('Trust Certificate'); + terminal.sendText(trustCommand); + terminal.show(); + + return new Promise((resolve, reject) => { + window.onDidCloseTerminal((t) => { + if (t === terminal) { + resolve(); + } + }); + }); + } + } + } } export default CommandLineDotNetClient; diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index 936d4d7916..d0d2767ea3 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -247,7 +247,7 @@ export class DefaultDockerManager implements DockerManager { const appOutputName = this.osProvider.pathParse(options.run.os, options.appOutput).name; const hostCertificateExportPfxPath = path.join(this.getHostSecretsPaths().hostCertificateExportPath, `${appOutputName}.pfx`); const containerCertificateExportPfxPath = this.osProvider.pathJoin(options.run.os, this.getContainerSecretsPaths(options.run.os).containerCertificateExportPath, `${appOutputName}.pfx`); - await this.dotNetClient.exportSslCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath); + await this.dotNetClient.trustAndExportSslCertificate(options.appProject, hostCertificateExportPfxPath, containerCertificateExportPfxPath); } const containerId = await this.runContainer(imageId, { appFolder: options.appFolder, ...options.run }); From 72107e819cdf0514ce160878be42c62bcb480971 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Mon, 22 Jul 2019 14:24:45 -0400 Subject: [PATCH 26/40] Only auto-trust for Windows --- .../coreclr/CommandLineDotNetClient.ts | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 94d81d9776..c1b762a6df 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -66,10 +66,8 @@ export class CommandLineDotNetClient implements DotNetClient { } await this.addUserSecretsIfNecessary(projectFile); - - if (this.osProvider.os === 'Windows' || this.osProvider.isMac) { - await this.promptAndTrustCertificateIfNecessary(); - } + // tslint:disable-next-line: no-floating-promises + this.promptAndTrustCertificateIfNecessary(); const password = uuidv4(); @@ -105,28 +103,29 @@ export class CommandLineDotNetClient implements DotNetClient { } private async promptAndTrustCertificateIfNecessary(): Promise { + if (this.osProvider.os !== 'Windows' && !this.osProvider.isMac) { + // No centralized notion of trust on Linux + return; + } + try { const checkCommand = `dotnet dev-certs https --check --trust`; await this.processProvider.exec(checkCommand, {}); } catch { - const selection = await window.showInformationMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", - { modal: true }, - ...['Yes', 'No']); - - if (selection === 'Yes') { - const trustCommand = `dotnet dev-certs https --trust ; exit 0`; // Exiting afterward means we can listen for this terminal instance to be closed - const terminal = window.createTerminal('Trust Certificate'); - terminal.sendText(trustCommand); - terminal.show(); - - return new Promise((resolve, reject) => { - window.onDidCloseTerminal((t) => { - if (t === terminal) { - resolve(); - } - }); - }); + if (this.osProvider.os === 'Windows') { + const selection = await window.showWarningMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", + { modal: false }, + ...['Yes', 'No']); + + if (selection === 'Yes') { + const trustCommand = `dotnet dev-certs https --trust`; + await this.processProvider.exec(trustCommand, {}); + } + } else if (this.osProvider.isMac) { + await window.showWarningMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", + { modal: false }); } } } From 6d6bc1c5648fffd7233c9513b11f251a0c1c41cd Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Tue, 23 Jul 2019 08:37:31 -0400 Subject: [PATCH 27/40] Must do synchronously --- src/debugging/coreclr/CommandLineDotNetClient.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index c1b762a6df..bdef4f36c1 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -66,8 +66,7 @@ export class CommandLineDotNetClient implements DotNetClient { } await this.addUserSecretsIfNecessary(projectFile); - // tslint:disable-next-line: no-floating-promises - this.promptAndTrustCertificateIfNecessary(); + await this.promptAndTrustCertificateIfNecessary(); const password = uuidv4(); @@ -115,7 +114,7 @@ export class CommandLineDotNetClient implements DotNetClient { if (this.osProvider.os === 'Windows') { const selection = await window.showWarningMessage( "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", - { modal: false }, + { modal: true }, ...['Yes', 'No']); if (selection === 'Yes') { @@ -125,7 +124,7 @@ export class CommandLineDotNetClient implements DotNetClient { } else if (this.osProvider.isMac) { await window.showWarningMessage( "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", - { modal: false }); + { modal: true }); } } } From 7263188180e5c7d44b5e720c1c58bbd571810ff9 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Tue, 23 Jul 2019 09:24:07 -0400 Subject: [PATCH 28/40] Change string type to PlatformOS --- src/debugging/coreclr/dockerManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debugging/coreclr/dockerManager.ts b/src/debugging/coreclr/dockerManager.ts index d0d2767ea3..a67054c925 100644 --- a/src/debugging/coreclr/dockerManager.ts +++ b/src/debugging/coreclr/dockerManager.ts @@ -437,7 +437,7 @@ export class DefaultDockerManager implements DockerManager { }; } - private getContainerSecretsPaths(os: string): { containerCertificateExportPath: string, containerUserSecretsPath: string } { + private getContainerSecretsPaths(os: PlatformOS): { containerCertificateExportPath: string, containerUserSecretsPath: string } { return { containerCertificateExportPath: os === 'Windows' ? 'C:\\Users\\ContainerUser\\AppData\\Roaming\\ASP.NET\\Https' : From f01cb117cd1d528c51ac66650faa02d0726719aa Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Tue, 23 Jul 2019 11:32:46 -0400 Subject: [PATCH 29/40] Empty port list bug --- src/configureWorkspace/configure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index 817e5d38cc..c4475312a6 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -67,7 +67,7 @@ export function getExposeStatements(ports: Number[]): string { } export function getComposePorts(ports: Number[]): string { - return ports ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; + return ports && ports.length > 0 ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; } const generatorsByPlatform = new Map(); From 702a2f8e91517d70769d2ed83115b0c32dc0498e Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Tue, 23 Jul 2019 12:10:51 -0400 Subject: [PATCH 30/40] Use ext for popups --- src/debugging/coreclr/CommandLineDotNetClient.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index bdef4f36c1..048d121fdb 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -4,7 +4,8 @@ import * as semver from 'semver'; import { v4 as uuidv4 } from 'uuid'; -import { window } from 'vscode'; +import { DialogResponses } from 'vscode-azureextensionui'; +import { ext } from '../../extensionVariables'; import { ProcessProvider } from "./ChildProcessProvider"; import { FileSystemProvider } from "./fsProvider"; import { OSProvider } from "./LocalOSProvider"; @@ -112,17 +113,17 @@ export class CommandLineDotNetClient implements DotNetClient { await this.processProvider.exec(checkCommand, {}); } catch { if (this.osProvider.os === 'Windows') { - const selection = await window.showWarningMessage( + const selection = await ext.ui.showWarningMessage( "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", { modal: true }, - ...['Yes', 'No']); + DialogResponses.yes, DialogResponses.no); - if (selection === 'Yes') { + if (selection === DialogResponses.yes) { const trustCommand = `dotnet dev-certs https --trust`; await this.processProvider.exec(trustCommand, {}); } } else if (this.osProvider.isMac) { - await window.showWarningMessage( + await ext.ui.showWarningMessage( "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", { modal: true }); } From ff966d6ecafab358c17050599f82370ead7eb279 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 09:49:54 -0400 Subject: [PATCH 31/40] Use crypto instead of UUID --- package-lock.json | 9 --------- package.json | 2 -- src/debugging/coreclr/CommandLineDotNetClient.ts | 11 ++++++++--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 537f9c1205..c5fc404ed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,15 +164,6 @@ } } }, - "@types/uuid": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", - "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/webpack": { "version": "4.4.24", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.24.tgz", diff --git a/package.json b/package.json index 3757709869..d2c71430d4 100644 --- a/package.json +++ b/package.json @@ -1567,7 +1567,6 @@ "@types/request-promise-native": "^1.0.15", "@types/semver": "^5.5.0", "@types/string-replace-webpack-plugin": "^0.1.0", - "@types/uuid": "^3.3.2", "@types/xml2js": "^0.4.3", "adm-zip": "^0.4.11", "copy-webpack-plugin": "^4.5.4", @@ -1608,7 +1607,6 @@ "request-promise-native": "^1.0.5", "semver": "^6.0.0", "tar": "^4.4.6", - "uuid": "^3.3.2", "vscode-azureextensionui": "^0.25.3", "vscode-azureappservice": "^0.42.0", "vscode-languageclient": "^5.1.1", diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 048d121fdb..a034c414e5 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -2,8 +2,8 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import * as crypto from 'crypto'; import * as semver from 'semver'; -import { v4 as uuidv4 } from 'uuid'; import { DialogResponses } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { ProcessProvider } from "./ChildProcessProvider"; @@ -69,7 +69,7 @@ export class CommandLineDotNetClient implements DotNetClient { await this.addUserSecretsIfNecessary(projectFile); await this.promptAndTrustCertificateIfNecessary(); - const password = uuidv4(); + const password = this.getRandomHexString(32); // Export the certificate const exportCommand = `dotnet dev-certs https -ep "${hostExportPath}" -p "${password}"`; @@ -97,7 +97,7 @@ export class CommandLineDotNetClient implements DotNetClient { const dotNetVer = await this.getVersion(); if (semver.gte(dotNetVer, '3.0.0')) { - const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${uuidv4()}`; + const userSecretsInitCommand = `dotnet user-secrets init --project "${projectFile}" --id ${this.getRandomHexString(32)}`; await this.processProvider.exec(userSecretsInitCommand, {}); } } @@ -129,6 +129,11 @@ export class CommandLineDotNetClient implements DotNetClient { } } } + + private getRandomHexString(length: number): string { + const buffer: Buffer = crypto.randomBytes(Math.ceil(length / 2)); + return buffer.toString('hex').slice(0, length); + } } export default CommandLineDotNetClient; From 327b32e948b0c738576f35b4c33e65e5e6d04ede Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 09:50:05 -0400 Subject: [PATCH 32/40] Change to number --- src/configureWorkspace/configUtils.ts | 4 ++-- src/configureWorkspace/configure.ts | 20 +++++++++---------- src/configureWorkspace/configureDotNetCore.ts | 2 +- src/configureWorkspace/configureGo.ts | 6 +++--- src/configureWorkspace/configureJava.ts | 6 +++--- src/configureWorkspace/configureNode.ts | 6 +++--- src/configureWorkspace/configureOther.ts | 6 +++--- src/configureWorkspace/configurePython.ts | 6 +++--- src/configureWorkspace/configureRuby.ts | 6 +++--- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/configureWorkspace/configUtils.ts b/src/configureWorkspace/configUtils.ts index b3b2972c06..6647a5eea1 100644 --- a/src/configureWorkspace/configUtils.ts +++ b/src/configureWorkspace/configUtils.ts @@ -12,7 +12,7 @@ import { Platform, PlatformOS } from '../utils/platform'; * Prompts for port numbers * @throws `UserCancelledError` if the user cancels. */ -export async function promptForPorts(ports: Number[]): Promise { +export async function promptForPorts(ports: number[]): Promise { let opt: vscode.InputBoxOptions = { placeHolder: `${ports.join(', ')}`, prompt: 'What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.', @@ -30,7 +30,7 @@ export async function promptForPorts(ports: Number[]): Promise { return splitPorts(await ext.ui.showInputBox(opt)); } -function splitPorts(value: string): Number[] | undefined { +function splitPorts(value: string): number[] | undefined { value = value ? value : ''; let matches = value.match(/\d+/g); diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index c4475312a6..89bf2e6b46 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -59,14 +59,14 @@ export interface IPlatformGeneratorInfo { genDockerFile: GeneratorFunction, genDockerCompose: GeneratorFunction, genDockerComposeDebug: GeneratorFunction, - defaultPorts: Number[] | undefined // [] = defaults to empty but still asks user if they want a port, undefined = don't ask at all + defaultPorts: number[] | undefined // [] = defaults to empty but still asks user if they want a port, undefined = don't ask at all } -export function getExposeStatements(ports: Number[]): string { +export function getExposeStatements(ports: number[]): string { return ports ? ports.map(port => `EXPOSE ${port}`).join('\n') : ''; } -export function getComposePorts(ports: Number[]): string { +export function getComposePorts(ports: number[]): string { return ports && ports.length > 0 ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; } @@ -81,7 +81,7 @@ generatorsByPlatform.set('Python', configurePython); generatorsByPlatform.set('Ruby', configureRuby); generatorsByPlatform.set('Other', configureOther); -function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[] | undefined, { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: number[] | undefined, { cmd, author, version, artifactName }: Partial): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find dockerfile generator functions for "${platform}"`); if (generators.genDockerFile) { @@ -96,7 +96,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o } } -function genDockerCompose(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: number[]): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find docker compose file generator function for "${platform}"`); if (generators.genDockerCompose) { @@ -104,7 +104,7 @@ function genDockerCompose(serviceNameAndRelativePath: string, platform: Platform } } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageInfo: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: number[], packageInfo: Partial): string { let generators = generatorsByPlatform.get(platform); assert(generators, `Could not find docker debug compose file generator function for "${platform}"`); if (generators.genDockerComposeDebug) { @@ -112,7 +112,7 @@ function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Pla } } -function genDockerIgnoreFile(service: string, platformType: string, os: string, ports: Number[]): string { +function genDockerIgnoreFile(service: string, platformType: string, os: string, ports: number[]): string { return `node_modules npm-debug.log Dockerfile* @@ -264,7 +264,7 @@ async function findCSProjOrFSProjFile(folderPath: string): Promise { } } -type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageJson?: Partial) => string; +type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, ports: number[], packageJson?: Partial) => string; const DOCKER_FILE_TYPES: { [key: string]: GeneratorFunction } = { 'docker-compose.yml': genDockerCompose, @@ -304,7 +304,7 @@ export interface ConfigureApiOptions { /** * Ports to expose */ - ports?: Number[]; + ports?: number[]; /** * The OS for the images. Currently only needed for .NET platforms. @@ -362,7 +362,7 @@ async function configureCore(context: IActionContext, options: ConfigureApiOptio } properties.configureOs = os; - let ports: Number[] | undefined = options.ports; + let ports: number[] | undefined = options.ports; if (!ports && generatorInfo.defaultPorts !== undefined) { ports = await promptForPorts(generatorInfo.defaultPorts); } diff --git a/src/configureWorkspace/configureDotNetCore.ts b/src/configureWorkspace/configureDotNetCore.ts index cc763aa0d4..df6505dcec 100644 --- a/src/configureWorkspace/configureDotNetCore.ts +++ b/src/configureWorkspace/configureDotNetCore.ts @@ -166,7 +166,7 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"] //#endregion -function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], { version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: number[], { version, artifactName }: Partial): string { // VS version of this function is in ResolveImageNames (src/Docker/Microsoft.VisualStudio.Docker.DotNetCore/DockerDotNetCoreScaffoldingProvider.cs) if (os !== 'Windows' && os !== 'Linux') { diff --git a/src/configureWorkspace/configureGo.ts b/src/configureWorkspace/configureGo.ts index 096c4f51b5..8ae445bea3 100644 --- a/src/configureWorkspace/configureGo.ts +++ b/src/configureWorkspace/configureGo.ts @@ -12,7 +12,7 @@ export let configureGo: IPlatformGeneratorInfo = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { let exposeStatements = getExposeStatements(ports); return ` @@ -34,7 +34,7 @@ ${exposeStatements} `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -44,7 +44,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: diff --git a/src/configureWorkspace/configureJava.ts b/src/configureWorkspace/configureJava.ts index 3e90b756b6..52640ec142 100644 --- a/src/configureWorkspace/configureJava.ts +++ b/src/configureWorkspace/configureJava.ts @@ -12,7 +12,7 @@ export let configureJava: IPlatformGeneratorInfo = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { let exposeStatements = getExposeStatements(ports); const artifact = artifactName ? artifactName : `${serviceNameAndRelativePath}.jar`; @@ -29,7 +29,7 @@ ENTRYPOINT exec java $JAVA_OPTS -jar ${serviceNameAndRelativePath}.jar `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -39,7 +39,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: diff --git a/src/configureWorkspace/configureNode.ts b/src/configureWorkspace/configureNode.ts index aca1e76756..72c0c2b4c0 100644 --- a/src/configureWorkspace/configureNode.ts +++ b/src/configureWorkspace/configureNode.ts @@ -12,7 +12,7 @@ export let configureNode: IPlatformGeneratorInfo = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { let exposeStatements = getExposeStatements(ports); return `FROM node:10.13-alpine @@ -25,7 +25,7 @@ ${exposeStatements} CMD ${cmd}`; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -37,7 +37,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { const cmdArray: string[] = cmd.split(' '); if (cmdArray[0].toLowerCase() === 'node') { diff --git a/src/configureWorkspace/configureOther.ts b/src/configureWorkspace/configureOther.ts index 52407febe8..4be918a4e1 100644 --- a/src/configureWorkspace/configureOther.ts +++ b/src/configureWorkspace/configureOther.ts @@ -12,7 +12,7 @@ export let configureOther = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { return `FROM docker/whalesay:latest LABEL Name=${serviceNameAndRelativePath} Version=${version} RUN apt-get -y update && apt-get install -y fortunes @@ -20,7 +20,7 @@ CMD /usr/games/fortune -a | cowsay `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -30,7 +30,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: diff --git a/src/configureWorkspace/configurePython.ts b/src/configureWorkspace/configurePython.ts index 5ccac2b939..982b04bd04 100644 --- a/src/configureWorkspace/configurePython.ts +++ b/src/configureWorkspace/configurePython.ts @@ -12,7 +12,7 @@ export let configurePython: IPlatformGeneratorInfo = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { let exposeStatements = getExposeStatements(ports); return `# Python support can be specified down to the minor or micro version @@ -46,7 +46,7 @@ CMD ["python3", "-m", "${serviceNameAndRelativePath}"] `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -56,7 +56,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: diff --git a/src/configureWorkspace/configureRuby.ts b/src/configureWorkspace/configureRuby.ts index 254d7c9465..844ac516fe 100644 --- a/src/configureWorkspace/configureRuby.ts +++ b/src/configureWorkspace/configureRuby.ts @@ -12,7 +12,7 @@ export let configureRuby: IPlatformGeneratorInfo = { defaultPorts: [3000] }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { let exposeStatements = getExposeStatements(ports); return `FROM ruby:2.5-slim @@ -33,7 +33,7 @@ CMD ["ruby", "${serviceNameAndRelativePath}.rb"] `; } -function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[]): string { +function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { return `version: '2.1' services: @@ -43,7 +43,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { return `version: '2.1' services: From 65a76b0fd9e6e81dd086a3aa889621c5aa37db26 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 10:00:51 -0400 Subject: [PATCH 33/40] Minor changes from review feedback --- src/configureWorkspace/configure.ts | 5 +++-- src/debugging/coreclr/CommandLineDotNetClient.ts | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index 89bf2e6b46..1a717b995a 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import * as fse from 'fs-extra'; import * as gradleParser from "gradle-to-js/lib/parser"; +import * as os from 'os'; import * as path from "path"; import * as vscode from "vscode"; import { IActionContext, TelemetryProperties } from 'vscode-azureextensionui'; @@ -63,11 +64,11 @@ export interface IPlatformGeneratorInfo { } export function getExposeStatements(ports: number[]): string { - return ports ? ports.map(port => `EXPOSE ${port}`).join('\n') : ''; + return ports ? ports.map(port => `EXPOSE ${port}`).join(os.EOL) : ''; } export function getComposePorts(ports: number[]): string { - return ports && ports.length > 0 ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; + return ports && ports.length > 0 ? ` ports:${os.EOL}` + ports.map(port => ` - ${port}:${port}`).join(os.EOL) : ''; } const generatorsByPlatform = new Map(); diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index a034c414e5..f831eac123 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -22,7 +22,7 @@ export interface DotNetClient { } export class CommandLineDotNetClient implements DotNetClient { - private static KnownConfiguredProjects: Set = new Set(); + private static _KnownConfiguredProjects: Set = new Set(); constructor( private readonly processProvider: ProcessProvider, @@ -62,7 +62,7 @@ export class CommandLineDotNetClient implements DotNetClient { } public async trustAndExportSslCertificate(projectFile: string, hostExportPath: string, containerExportPath: string): Promise { - if (CommandLineDotNetClient.KnownConfiguredProjects.has(projectFile)) { + if (CommandLineDotNetClient._KnownConfiguredProjects.has(projectFile)) { return; } @@ -85,13 +85,13 @@ export class CommandLineDotNetClient implements DotNetClient { //await this.processProvider.exec(userSecretsPathCommand, {}); // Cache the project so we don't do this all over again every F5 - CommandLineDotNetClient.KnownConfiguredProjects.add(projectFile); + CommandLineDotNetClient._KnownConfiguredProjects.add(projectFile); } private async addUserSecretsIfNecessary(projectFile: string): Promise { const contents = await this.fsProvider.readFile(projectFile); - if (contents.indexOf('UserSecretsId') >= 0) { + if (/UserSecretsId/i.test(contents)) { return; } From 20708bfa21ea56fa2ce32b409d0cf41b97ec43b2 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 10:51:07 -0400 Subject: [PATCH 34/40] Fix cyclomatic complexity --- .../dockerDebugConfigurationProvider.ts | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index 0c7c4ec323..9177f90e2f 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -171,40 +171,25 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi }; } - // tslint:disable-next-line:cyclomatic-complexity private static inferRunOptions(folder: WorkspaceFolder, debugConfiguration: DockerDebugConfiguration, appName: string, os: PlatformOS): LaunchRunOptions { - const containerName = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.containerName - ? debugConfiguration.dockerRun.containerName - : `${appName}-dev`; // CONSIDER: Use unique ID instead? + debugConfiguration.dockerRun = debugConfiguration.dockerRun || {}; - const env = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.env; - const envFiles = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.envFiles + const envFiles = debugConfiguration.dockerRun.envFiles ? debugConfiguration.dockerRun.envFiles.map(file => DockerDebugConfigurationProvider.resolveFolderPath(file, folder)) : undefined; - const labels = (debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.labels) - || DockerDebugConfigurationProvider.defaultLabels; - - const network = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.network; - const networkAlias = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.networkAlias; - const ports = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.ports; - const volumes = DockerDebugConfigurationProvider.inferVolumes(folder, debugConfiguration); - const extraHosts = debugConfiguration && debugConfiguration.dockerRun && debugConfiguration.dockerRun.extraHosts; - - const configureSslCertificate = debugConfiguration && debugConfiguration.configureSslCertificate; - return { - containerName, - env, + containerName: debugConfiguration.dockerRun.containerName || `${appName}-dev`, + env: debugConfiguration.dockerRun.env, envFiles, - extraHosts, - labels, - network, - networkAlias, + extraHosts: debugConfiguration.dockerRun.extraHosts, + labels: debugConfiguration.dockerRun.labels || DockerDebugConfigurationProvider.defaultLabels, + network: debugConfiguration.dockerRun.network, + networkAlias: debugConfiguration.dockerRun.networkAlias, os, - ports, - volumes, - configureSslCertificate + ports: debugConfiguration.dockerRun.ports, + volumes: DockerDebugConfigurationProvider.inferVolumes(folder, debugConfiguration), + configureSslCertificate: debugConfiguration.configureSslCertificate }; } From 0691c71ad6c1b6de0c12b7620683fa4b10569e77 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 10:51:24 -0400 Subject: [PATCH 35/40] Look for specific error --- .../coreclr/CommandLineDotNetClient.ts | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index f831eac123..70a7120ef4 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -4,7 +4,7 @@ import * as crypto from 'crypto'; import * as semver from 'semver'; -import { DialogResponses } from 'vscode-azureextensionui'; +import { DialogResponses, parseError } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { ProcessProvider } from "./ChildProcessProvider"; import { FileSystemProvider } from "./fsProvider"; @@ -111,22 +111,26 @@ export class CommandLineDotNetClient implements DotNetClient { try { const checkCommand = `dotnet dev-certs https --check --trust`; await this.processProvider.exec(checkCommand, {}); - } catch { - if (this.osProvider.os === 'Windows') { - const selection = await ext.ui.showWarningMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", - { modal: true }, - DialogResponses.yes, DialogResponses.no); - - if (selection === DialogResponses.yes) { - const trustCommand = `dotnet dev-certs https --trust`; - await this.processProvider.exec(trustCommand, {}); + } catch (err) { + const error = parseError(err); + + if (error.errorType === "6" || error.errorType === "7") { // 6 = certificate not found, 7 = certificate not trusted + if (this.osProvider.os === 'Windows') { + const selection = await ext.ui.showWarningMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", + { modal: true }, + DialogResponses.yes, DialogResponses.no); + + if (selection === DialogResponses.yes) { + const trustCommand = `dotnet dev-certs https --trust`; + await this.processProvider.exec(trustCommand, {}); + } + } else if (this.osProvider.isMac) { + await ext.ui.showWarningMessage( + "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", + { modal: true }); } - } else if (this.osProvider.isMac) { - await ext.ui.showWarningMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", - { modal: true }); - } + } else { throw err; } } } From 606f1d55e462e37ba1c2661da6d957c25ed41ea0 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 10:51:33 -0400 Subject: [PATCH 36/40] Shadowing problem --- src/configureWorkspace/configure.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index 1a717b995a..fc582ca93e 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as fse from 'fs-extra'; import * as gradleParser from "gradle-to-js/lib/parser"; -import * as os from 'os'; +import { EOL as osEOL } from 'os'; import * as path from "path"; import * as vscode from "vscode"; import { IActionContext, TelemetryProperties } from 'vscode-azureextensionui'; @@ -64,11 +64,11 @@ export interface IPlatformGeneratorInfo { } export function getExposeStatements(ports: number[]): string { - return ports ? ports.map(port => `EXPOSE ${port}`).join(os.EOL) : ''; + return ports ? ports.map(port => `EXPOSE ${port}`).join(osEOL) : ''; } export function getComposePorts(ports: number[]): string { - return ports && ports.length > 0 ? ` ports:${os.EOL}` + ports.map(port => ` - ${port}:${port}`).join(os.EOL) : ''; + return ports && ports.length > 0 ? ` ports:${osEOL}` + ports.map(port => ` - ${port}:${port}`).join(osEOL) : ''; } const generatorsByPlatform = new Map(); From cd06246a0aff092ae6e2e755923b5b3ff466ea33 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 11:01:07 -0400 Subject: [PATCH 37/40] Revert EOL change while we discuss --- src/configureWorkspace/configure.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index fc582ca93e..89bf2e6b46 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import * as fse from 'fs-extra'; import * as gradleParser from "gradle-to-js/lib/parser"; -import { EOL as osEOL } from 'os'; import * as path from "path"; import * as vscode from "vscode"; import { IActionContext, TelemetryProperties } from 'vscode-azureextensionui'; @@ -64,11 +63,11 @@ export interface IPlatformGeneratorInfo { } export function getExposeStatements(ports: number[]): string { - return ports ? ports.map(port => `EXPOSE ${port}`).join(osEOL) : ''; + return ports ? ports.map(port => `EXPOSE ${port}`).join('\n') : ''; } export function getComposePorts(ports: number[]): string { - return ports && ports.length > 0 ? ` ports:${osEOL}` + ports.map(port => ` - ${port}:${port}`).join(osEOL) : ''; + return ports && ports.length > 0 ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : ''; } const generatorsByPlatform = new Map(); From 5af4fb8838a05d17bcf29efd4c154e24b131b87d Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 11:03:07 -0400 Subject: [PATCH 38/40] Remove commented code --- src/debugging/coreclr/CommandLineDotNetClient.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 70a7120ef4..3b748bd28b 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -79,11 +79,6 @@ export class CommandLineDotNetClient implements DotNetClient { const userSecretsPasswordCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Password "${password}"`; await this.processProvider.exec(userSecretsPasswordCommand, {}); - // This is not honored due to https://github.com/aspnet/AspNetCore.Docs/issues/6199#issuecomment-418194220 - // Consequently, the certificate name must be equal to .pfx, i.e. MyWebApp.dll => MyWebApp.pfx - //const userSecretsPathCommand = `dotnet user-secrets --project "${projectFile}" set Kestrel:Certificates:Development:Path "${containerExportPath}"`; - //await this.processProvider.exec(userSecretsPathCommand, {}); - // Cache the project so we don't do this all over again every F5 CommandLineDotNetClient._KnownConfiguredProjects.add(projectFile); } From fe1fbc4c570a9ac71fc255defcbe13fa262d3a6d Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 11:24:27 -0400 Subject: [PATCH 39/40] Separate debug configurations --- .../coreclr/dockerDebugConfigurationProvider.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts index 9177f90e2f..1e02addced 100644 --- a/src/debugging/coreclr/dockerDebugConfigurationProvider.ts +++ b/src/debugging/coreclr/dockerDebugConfigurationProvider.ts @@ -72,12 +72,11 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi public provideDebugConfigurations(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult { return [ { - name: 'Docker: Launch .NET Core (Preview)', + name: 'Docker: Launch ASP.NET Core (Preview)', type: 'docker-coreclr', request: 'launch', preLaunchTask: 'build', - dockerBuild: { - }, + dockerBuild: {}, dockerRun: { env: { "ASPNETCORE_ENVIRONMENT": "Development", @@ -86,6 +85,14 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi } }, configureSslCertificate: true + }, + { + name: 'Docker: Launch .NET Core Console (Preview)', + type: 'docker-coreclr', + request: 'launch', + preLaunchTask: 'build', + dockerBuild: {}, + dockerRun: {} } ]; } From 30fe5a489fbcfbdcc0282d46d0b6ad5c32ecfc60 Mon Sep 17 00:00:00 2001 From: Brandon Waterloo <36966225+bwateratmsft@users.noreply.github.com> Date: Thu, 25 Jul 2019 14:52:19 -0400 Subject: [PATCH 40/40] Buttons --- .../coreclr/CommandLineDotNetClient.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/debugging/coreclr/CommandLineDotNetClient.ts b/src/debugging/coreclr/CommandLineDotNetClient.ts index 3b748bd28b..2be4503dac 100644 --- a/src/debugging/coreclr/CommandLineDotNetClient.ts +++ b/src/debugging/coreclr/CommandLineDotNetClient.ts @@ -4,6 +4,7 @@ import * as crypto from 'crypto'; import * as semver from 'semver'; +import { MessageItem } from 'vscode'; import { DialogResponses, parseError } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { ProcessProvider } from "./ChildProcessProvider"; @@ -109,21 +110,26 @@ export class CommandLineDotNetClient implements DotNetClient { } catch (err) { const error = parseError(err); - if (error.errorType === "6" || error.errorType === "7") { // 6 = certificate not found, 7 = certificate not trusted + if (error.errorType === '6' || error.errorType === '7') { // 6 = certificate not found, 7 = certificate not trusted if (this.osProvider.os === 'Windows') { + const trust: MessageItem = { title: 'Trust' }; + const selection = await ext.ui.showWarningMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.", - { modal: true }, - DialogResponses.yes, DialogResponses.no); + 'The ASP.NET Core HTTPS development certificate is not trusted. Would you like to trust the certificate? A prompt may be shown.', + { modal: true, learnMoreLink: 'https://aka.ms/vscode-docker-dev-certs' }, + trust, DialogResponses.skipForNow); - if (selection === DialogResponses.yes) { + if (selection === trust) { const trustCommand = `dotnet dev-certs https --trust`; await this.processProvider.exec(trustCommand, {}); } } else if (this.osProvider.isMac) { + const debugAnyway: MessageItem = { title: 'Debug Anyway' }; + await ext.ui.showWarningMessage( - "The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.", - { modal: true }); + 'The ASP.NET Core HTTPS development certificate is not trusted. Run `dotnet dev-certs https --trust` to trust the certificate.', + { modal: true, learnMoreLink: 'https://aka.ms/vscode-docker-dev-certs' }, + debugAnyway); } } else { throw err; } }