Don't output EXPOSE if empty port specified (#490)
* Don't output EXPOSE if empty port specified * Tests * PR fix * Lint and fix
This commit is contained in:
Родитель
82abf3c848
Коммит
7227ce4c95
|
@ -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";
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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')) {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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}";
|
||||
|
@ -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
|
||||
|
@ -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}"`);
|
||||
|
|
|
@ -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
|
||||
|
@ -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}
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
@ -23,7 +26,7 @@ FROM python:alpine
|
|||
#FROM continuumio/miniconda3
|
||||
|
||||
LABEL Name=${serviceNameAndRelativePath} Version=${version}
|
||||
EXPOSE ${port}
|
||||
${exposeStatements}
|
||||
|
||||
WORKDIR /app
|
||||
ADD . /app
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
@ -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}`]
|
||||
);
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче