Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add SSL support for ASP.NET Core web apps #1150

Closed
wants to merge 43 commits into from
Closed
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9f5e5ed
Volume mapping of certs and usersecrets folders
bwateratmsft Jul 11, 2019
ba816b5
Whitespace
bwateratmsft Jul 11, 2019
54e54fc
Change name to dotNetClient
bwateratmsft Jul 11, 2019
009e3de
Add UUID dependency
bwateratmsft Jul 11, 2019
943f951
Export/trust certs and map secrets volumes
bwateratmsft Jul 11, 2019
3a862b8
Fix path issues
bwateratmsft Jul 11, 2019
e152538
Save a variable
bwateratmsft Jul 12, 2019
69707fd
Cache already-setup projects to avoid redoing them
bwateratmsft Jul 15, 2019
672b3e9
Add ASPNETCORE_ENVIRONMENT environment variable
bwateratmsft Jul 15, 2019
1f82cfb
Add support for multiple ports exposed
bwateratmsft Jul 15, 2019
dd386cb
Add ASPNETCORE_URLS to Dockerfile
bwateratmsft Jul 15, 2019
5bf59a9
Scaffold UserSecretsId into the project file
bwateratmsft Jul 15, 2019
f82a47a
Merge branch 'bmw/netcorehttps' into bmw/ports
bwateratmsft Jul 15, 2019
0bb2ae5
Fix env_statements replacement token
bwateratmsft Jul 17, 2019
70f97df
Fix uninit'd variable
bwateratmsft Jul 17, 2019
2a8b488
Rearranging some things
bwateratmsft Jul 18, 2019
43a35e6
Make configuring SSL optional
bwateratmsft Jul 18, 2019
f535402
Minor fixes
bwateratmsft Jul 18, 2019
4ddcf54
Scaffold configureSslCertificate by default
bwateratmsft Jul 18, 2019
398ca6c
Fix some failing tests
bwateratmsft Jul 18, 2019
0e4b015
Fix a few more tests
bwateratmsft Jul 18, 2019
f8b768a
Decided not to put ASPNETCORE_URLS into Dockerfile
bwateratmsft Jul 18, 2019
6b8eedd
Allow passing env variables up via LaunchResult
bwateratmsft Jul 19, 2019
e540eff
Merge branch 'master' into bmw/netcorehttps
bwateratmsft Jul 19, 2019
6478aa3
Separate launch tasks for .NET Core vs ASP.NET
bwateratmsft Jul 19, 2019
fe9bd03
Spawn a trust prompt on Mac and Windows
bwateratmsft Jul 19, 2019
0ef9b24
Prompting for trust on Windows and Mac
bwateratmsft Jul 19, 2019
72107e8
Only auto-trust for Windows
bwateratmsft Jul 22, 2019
e46c1b0
Merge branch 'master' into bmw/netcorehttps
bwateratmsft Jul 22, 2019
6d6bc1c
Must do synchronously
bwateratmsft Jul 23, 2019
7263188
Change string type to PlatformOS
bwateratmsft Jul 23, 2019
f01cb11
Empty port list bug
bwateratmsft Jul 23, 2019
702a2f8
Use ext for popups
bwateratmsft Jul 23, 2019
ff966d6
Use crypto instead of UUID
bwateratmsft Jul 25, 2019
327b32e
Change to number
bwateratmsft Jul 25, 2019
65a76b0
Minor changes from review feedback
bwateratmsft Jul 25, 2019
20708bf
Fix cyclomatic complexity
bwateratmsft Jul 25, 2019
0691c71
Look for specific error
bwateratmsft Jul 25, 2019
606f1d5
Shadowing problem
bwateratmsft Jul 25, 2019
cd06246
Revert EOL change while we discuss
bwateratmsft Jul 25, 2019
5af4fb8
Remove commented code
bwateratmsft Jul 25, 2019
fe1fbc4
Separate debug configurations
bwateratmsft Jul 25, 2019
30fe5a4
Buttons
bwateratmsft Jul 25, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 28 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -482,10 +482,28 @@
"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",
"dockerBuild": {},
"dockerRun": {
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://+:80;https://+:443"
}
},
"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",
Expand Down Expand Up @@ -659,6 +677,11 @@
}
}
}
},
"configureSslCertificate": {
"type": "boolean",
"default": true,
"description": "Whether to configure an SSL certificate for ASP.NET Core web services."
}
}
}
Expand Down Expand Up @@ -1544,6 +1567,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",
Expand Down Expand Up @@ -1584,6 +1608,7 @@
"request-promise-native": "^1.0.5",
"semver": "^6.0.0",
"tar": "^4.4.6",
"uuid": "^3.3.2",
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
"vscode-azureextensionui": "^0.25.3",
"vscode-azureappservice": "^0.42.0",
"vscode-languageclient": "^5.1.1",
Expand Down
37 changes: 29 additions & 8 deletions src/configureWorkspace/configUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
export async function promptForPorts(ports: Number[]): Promise<Number[]> {
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
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;
}

/**
Expand Down
44 changes: 24 additions & 20 deletions src/configureWorkspace/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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') : '';
ejizba marked this conversation as resolved.
Show resolved Hide resolved
}

export function getComposePorts(ports: Number[]): string {
return ports && ports.length > 0 ? ' ports:\n' + ports.map(port => ` - ${port}:${port}`).join('\n') : '';
}

const generatorsByPlatform = new Map<Platform, IPlatformGeneratorInfo>();
Expand All @@ -77,38 +81,38 @@ 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<PackageInfo>): string {
function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[] | undefined, { cmd, author, version, artifactName }: Partial<PackageInfo>): 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
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;
}
}

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<PackageInfo>): string {
function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageInfo: Partial<PackageInfo>): 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*
Expand Down Expand Up @@ -260,7 +264,7 @@ async function findCSProjOrFSProjFile(folderPath: string): Promise<string> {
}
}

type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, port: string, packageJson?: Partial<PackageInfo>) => string;
type GeneratorFunction = (serviceName: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], packageJson?: Partial<PackageInfo>) => string;

const DOCKER_FILE_TYPES: { [key: string]: GeneratorFunction } = {
'docker-compose.yml': genDockerCompose,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/configureWorkspace/configureCpp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PackageInfo>): string {
function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial<PackageInfo>): 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
Expand Down
8 changes: 4 additions & 4 deletions src/configureWorkspace/configureDotNetCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -166,7 +166,7 @@ ENTRYPOINT ["dotnet", "$assembly_name$.dll"]

//#endregion

function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, port: string, { version, artifactName }: Partial<PackageInfo>): string {
function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, os: PlatformOS | undefined, ports: Number[], { version, artifactName }: Partial<PackageInfo>): string {
// VS version of this function is in ResolveImageNames (src/Docker/Microsoft.VisualStudio.Docker.DotNetCore/DockerDotNetCoreScaffoldingProvider.cs)

if (os !== 'Windows' && os !== 'Linux') {
Expand All @@ -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
Expand Down
19 changes: 8 additions & 11 deletions src/configureWorkspace/configureGo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PackageInfo>): string {
let exposeStatements = getExposeStatements(port);
function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { cmd, author, version, artifactName }: Partial<PackageInfo>): string {
let exposeStatements = getExposeStatements(ports);

return `
#build stage
Expand All @@ -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<PackageInfo>): string {
function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: Number[], { fullCommand: cmd }: Partial<PackageInfo>): string {
return `version: '2.1'

services:
Expand All @@ -54,7 +53,5 @@ services:
build:
context: .
dockerfile: Dockerfile
ports:
- ${port}:${port}
`;
${getComposePorts(ports)}`;
}
Loading