Skip to content

Commit

Permalink
Don't output EXPOSE if empty port specified (#490)
Browse files Browse the repository at this point in the history
* Don't output EXPOSE if empty port specified

* Tests

* PR fix

* Lint and fix
  • Loading branch information
StephenWeatherford committed Sep 22, 2018
1 parent 82abf3c commit 7227ce4
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 46 deletions.
14 changes: 11 additions & 3 deletions configureWorkspace/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { isNumber } from 'util';
import vscode = require('vscode');
import { IAzureQuickPickItem, IAzureUserInput } from 'vscode-azureextensionui';
import { ext } from "../extensionVariables";
Expand All @@ -22,11 +23,18 @@ export type Platform =
* Prompts for a port number
* @throws `UserCancelledError` if the user cancels.
*/
export async function promptForPort(port: number): Promise<string> {
export async function promptForPort(port: string): Promise<string> {
let opt: vscode.InputBoxOptions = {
placeHolder: `${port}`,
prompt: 'What port does your app listen on?',
value: `${port}`
prompt: 'What port does your app listen on? ENTER for none.',
value: `${port}`,
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';
}

return undefined;
}
}

return ext.ui.showInputBox(opt);
Expand Down
29 changes: 20 additions & 9 deletions configureWorkspace/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@ export type ConfigureTelemetryProperties = {
packageFileSubfolderDepth?: string; // 0 = project/etc file in root folder, 1 = in subfolder, 2 = in subfolder of subfolder, etc.
};

const generatorsByPlatform = new Map<Platform, {
export interface IPlatformGeneratorInfo {
genDockerFile: GeneratorFunction,
genDockerCompose: GeneratorFunction,
genDockerComposeDebug: GeneratorFunction
}>();
genDockerComposeDebug: GeneratorFunction,
defaultPort: string
}

export function getExposeStatements(port: string): string {
return port ? `EXPOSE ${port}` : '';
}

const generatorsByPlatform = new Map<Platform, IPlatformGeneratorInfo>();
generatorsByPlatform.set('ASP.NET Core', configureAspDotNetCore);
generatorsByPlatform.set('Go', configureGo);
generatorsByPlatform.set('Java', configureJava);
Expand All @@ -70,7 +77,14 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o
let generators = generatorsByPlatform.get(platform);
assert(generators, `Could not find dockerfile generator functions for "${platform}"`);
if (generators.genDockerFile) {
return generators.genDockerFile(serviceNameAndRelativePath, platform, os, port, { cmd, author, version, artifactName });
let contents = generators.genDockerFile(serviceNameAndRelativePath, platform, os, port, { 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");

return contents;
}
}

Expand Down Expand Up @@ -325,6 +339,7 @@ async function configureCore(actionContext: IActionContext, options: ConfigureAp

const platformType: Platform = options.platform || await quickPickPlatform();
properties.configurePlatform = platformType;
let generatorInfo = generatorsByPlatform.get(platformType);

let os: OS | undefined = options.os;
if (!os && platformType.toLowerCase().includes('.net')) {
Expand All @@ -334,11 +349,7 @@ async function configureCore(actionContext: IActionContext, options: ConfigureAp

let port: string | undefined = options.port;
if (!port) {
if (platformType.toLowerCase().includes('.net')) {
port = await promptForPort(80);
} else {
port = await promptForPort(3000);
}
port = await promptForPort(generatorInfo.defaultPort);
}

let targetFramework: string;
Expand Down
22 changes: 11 additions & 11 deletions configureWorkspace/configure_dotnetcore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ import * as semver from 'semver';
import { extractRegExGroups } from '../helpers/extractRegExGroups';
import { isWindows, isWindows10RS3OrNewer, isWindows10RS4OrNewer } from '../helpers/windowsVersion';
import { OS, Platform } from './config-utils';
import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

// This file handles both ASP.NET core and .NET Core Console

let configureDotNetCore = {
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
genDockerComposeDebug: undefined, // We don't generate compose files for .net core
defaultPort: '80'
};

export let configureAspDotNetCore = configureDotNetCore;
export let configureDotNetCoreConsole = configureDotNetCore;
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: ''
};

const AspNetCoreRuntimeImageFormat = "microsoft/aspnetcore:{0}.{1}{2}";
const AspNetCoreSdkImageFormat = "microsoft/aspnetcore-build:{0}.{1}{2}";
Expand Down Expand Up @@ -164,7 +169,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o
let assemblyNameNoExtension = serviceName;
// example: COPY Core2.0ConsoleAppWindows/Core2.0ConsoleAppWindows.csproj Core2.0ConsoleAppWindows/
let copyProjectCommands = `COPY ["${serviceNameAndRelativePath}.csproj", "${projectDirectory}/"]`
let exposeStatements = port ? `EXPOSE ${port}` : '';
let exposeStatements = getExposeStatements(port);

// Parse version from TargetFramework
// Example: netcoreapp1.0
Expand Down Expand Up @@ -226,11 +231,6 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: Platform, o
.replace(/\$assembly_name\$/g, assemblyNameNoExtension)
.replace(/\$copy_project_commands\$/g, copyProjectCommands);

// 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");

let unreplacedToken = extractRegExGroups(contents, /(\$[a-z_]+\$)/, ['']);
if (unreplacedToken[0]) {
assert.fail(`Unreplaced template token "${unreplacedToken}"`);
Expand Down
11 changes: 7 additions & 4 deletions configureWorkspace/configure_go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

export let configureGo = {
export let configureGo: IPlatformGeneratorInfo = {
genDockerFile,
genDockerCompose,
genDockerComposeDebug
genDockerComposeDebug,
defaultPort: '3000'
};

function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial<PackageInfo>): string {
let exposeStatements = getExposeStatements(port);

return `
#build stage
FROM golang:alpine AS builder
Expand All @@ -27,7 +30,7 @@ RUN apk --no-cache add ca-certificates
COPY --from=builder /go/bin/app /app
ENTRYPOINT ./app
LABEL Name=${serviceNameAndRelativePath} Version=${version}
EXPOSE ${port}
${exposeStatements}
`;
}

Expand Down
12 changes: 7 additions & 5 deletions configureWorkspace/configure_java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

export let configureJava = {
export let configureJava: IPlatformGeneratorInfo = {
genDockerFile,
genDockerCompose,
genDockerComposeDebug
genDockerComposeDebug,
defaultPort: '3000'
};

function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial<PackageInfo>): string {

let exposeStatements = getExposeStatements(port);
const artifact = artifactName ? artifactName : `${serviceNameAndRelativePath}.jar`;

return `
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAVA_OPTS
ENV JAVA_OPTS=$JAVA_OPTS
ADD ${artifact} ${serviceNameAndRelativePath}.jar
EXPOSE ${port}
${exposeStatements}
ENTRYPOINT exec java $JAVA_OPTS -jar ${serviceNameAndRelativePath}.jar
# For Spring-Boot project, use the entrypoint below to reduce Tomcat startup time.
#ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ${serviceNameAndRelativePath}.jar
Expand Down
11 changes: 7 additions & 4 deletions configureWorkspace/configure_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

export let configureNode = {
export let configureNode: IPlatformGeneratorInfo = {
genDockerFile,
genDockerCompose,
genDockerComposeDebug
genDockerComposeDebug,
defaultPort: '3000'
};

function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial<PackageInfo>): string {
let exposeStatements = getExposeStatements(port);

return `FROM node:8.9-alpine
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN npm install --production --silent && mv node_modules ../
COPY . .
EXPOSE ${port}
${exposeStatements}
CMD ${cmd}`;
}

Expand Down
11 changes: 7 additions & 4 deletions configureWorkspace/configure_python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

export let configurePython = {
export let configurePython: IPlatformGeneratorInfo = {
genDockerFile,
genDockerCompose,
genDockerComposeDebug
genDockerComposeDebug,
defaultPort: '3000'
};

function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial<PackageInfo>): string {
let exposeStatements = getExposeStatements(port);

return `# Python support can be specified down to the minor or micro version
# (e.g. 3.6 or 3.6.3).
# OS Support also exists for jessie & stretch (slim and full).
Expand All @@ -23,7 +26,7 @@ FROM python:alpine
#FROM continuumio/miniconda3
LABEL Name=${serviceNameAndRelativePath} Version=${version}
EXPOSE ${port}
${exposeStatements}
WORKDIR /app
ADD . /app
Expand Down
11 changes: 7 additions & 4 deletions configureWorkspace/configure_ruby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PackageInfo } from './configure';
import { getExposeStatements, IPlatformGeneratorInfo, PackageInfo } from './configure';

export let configureRuby = {
export let configureRuby: IPlatformGeneratorInfo = {
genDockerFile,
genDockerCompose,
genDockerComposeDebug
genDockerComposeDebug,
defaultPort: '3000'
};

function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, port: string, { cmd, author, version, artifactName }: Partial<PackageInfo>): string {
let exposeStatements = getExposeStatements(port);

return `FROM ruby:2.5-slim
LABEL Name=${serviceNameAndRelativePath} Version=${version}
EXPOSE ${port}
${exposeStatements}
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
Expand Down
59 changes: 57 additions & 2 deletions test/configure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as path from 'path';
import { Platform, OS } from "../configureWorkspace/config-utils";
import { ext } from '../extensionVariables';
import { Suite } from 'mocha';
import { configure, ConfigureTelemetryProperties, configureApi, ConfigureApiOptions } from '../configureWorkspace/configure';
import { configure, ConfigureTelemetryProperties, ConfigureApiOptions } from '../configureWorkspace/configure';
import { TestUserInput, IActionContext, TelemetryProperties } from 'vscode-azureextensionui';
import { globAsync } from '../helpers/async';
import { getTestRootFolder, constants, testInEmptyFolder } from './global.test';
Expand Down Expand Up @@ -393,7 +393,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void
packageFileType: '.csproj',
packageFileSubfolderDepth: '1'
},
[os, '' /* no port */],
[os, undefined /* no port */],
['Dockerfile', '.dockerignore', `${projectFolder}/Program.cs`, `${projectFolder}/${projectFileName}`]
);

Expand Down Expand Up @@ -599,6 +599,17 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void
});

suite(".NET Core Console 2.1", async () => {
testInEmptyFolder("Default port (none)", async () => {
await writeFile('projectFolder1', 'aspnetapp.csproj', dotNetCoreConsole_21_ProjectFileContents);
await testConfigureDocker(
'.NET Core Console',
undefined,
['Windows', undefined]
);

assertNotFileContains('Dockerfile', 'EXPOSE');
});

testInEmptyFolder("Windows", async () => {
await testDotNetCoreConsole(
'Windows',
Expand Down Expand Up @@ -783,6 +794,28 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void
// ASP.NET Core

suite("ASP.NET Core 2.2", async () => {
testInEmptyFolder("Default port (80)", async () => {
await writeFile('projectFolder1', 'aspnetapp.csproj', dotNetCoreConsole_21_ProjectFileContents);
await testConfigureDocker(
'ASP.NET Core',
undefined,
['Windows', undefined]
);

assertFileContains('Dockerfile', 'EXPOSE 80');
});

testInEmptyFolder("No port", async () => {
await writeFile('projectFolder1', 'aspnetapp.csproj', dotNetCoreConsole_21_ProjectFileContents);
await testConfigureDocker(
'ASP.NET Core',
undefined,
['Windows', '']
);

assertNotFileContains('Dockerfile', 'EXPOSE');
});

testInEmptyFolder("Windows 10 RS4", async () => {
await testAspNetCore(
'Windows',
Expand Down Expand Up @@ -922,6 +955,28 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void
// Java

suite("Java", () => {
testInEmptyFolder("No port", async () => {
await testConfigureDocker(
'Java',
undefined,
[''],
['Dockerfile', 'docker-compose.debug.yml', 'docker-compose.yml', '.dockerignore']
);

assertNotFileContains('Dockerfile', 'EXPOSE');
});

testInEmptyFolder("Default port", async () => {
await testConfigureDocker(
'Java',
undefined,
[undefined],
['Dockerfile', 'docker-compose.debug.yml', 'docker-compose.yml', '.dockerignore']
);

assertFileContains('Dockerfile', 'EXPOSE 3000');
});

testInEmptyFolder("No pom file", async () => {
await testConfigureDocker(
'Java',
Expand Down

0 comments on commit 7227ce4

Please sign in to comment.