From 381273281d9470c88465a192ebda5e62151a6577 Mon Sep 17 00:00:00 2001 From: Jitendra Kumar Date: Wed, 24 Jan 2018 17:39:51 +0530 Subject: [PATCH] Users/jikuma/imagename (#6278) * Docker imagename fix * docker image name and telemetry changes * kubernetes recompile with new docker-common * changed ui * rename task input --- .../docker-common/containerimageutils.ts | 7 +------ .../acrauthenticationtokenprovider.ts | 2 +- .../authenticationtokenprovider.ts | 13 +++++++++++++ .../genericauthenticationtokenprovider.ts | 2 +- .../resources.resjson/en-US/resources.resjson | 2 ++ Tasks/Docker/Tests/L0.ts | 19 +++++++++++++++++++ Tasks/Docker/Tests/TestSetup.ts | 6 ++++++ Tasks/Docker/Tests/TestShared.ts | 3 ++- Tasks/Docker/containerbuild.ts | 2 +- Tasks/Docker/containerpush.ts | 2 +- Tasks/Docker/containerrun.ts | 4 ++-- Tasks/Docker/task.json | 11 ++++++++++- Tasks/Docker/task.loc.json | 11 ++++++++++- Tasks/Docker/utils.ts | 13 ++++++++++++- Tasks/DockerCompose/Tests/L0Linux.ts | 1 + Tasks/DockerCompose/Tests/L0Windows.ts | 1 + Tasks/DockerCompose/task.json | 2 +- Tasks/Kubernetes/task.json | 2 +- Tasks/Kubernetes/task.loc.json | 2 +- 19 files changed, 86 insertions(+), 19 deletions(-) diff --git a/Tasks/Common/docker-common/containerimageutils.ts b/Tasks/Common/docker-common/containerimageutils.ts index 7eb6f8e22864..a2e54b38b376 100644 --- a/Tasks/Common/docker-common/containerimageutils.ts +++ b/Tasks/Common/docker-common/containerimageutils.ts @@ -20,13 +20,8 @@ export function imageNameWithoutTag(imageName: string): string { return generateValidImageName(endIndex < 0 ? imageName : imageName.substr(0, endIndex)); } -export function getImageName(): string { - var imageName = tl.getInput("imageName", true); - return generateValidImageName(imageName); -} - export function generateValidImageName(imageName: string): string { imageName = imageName.toLowerCase(); imageName = imageName.replace(/ /g,""); return imageName; -} +} \ No newline at end of file diff --git a/Tasks/Common/docker-common/registryauthenticationprovider/acrauthenticationtokenprovider.ts b/Tasks/Common/docker-common/registryauthenticationprovider/acrauthenticationtokenprovider.ts index bc869fae6415..b1657ef06c57 100644 --- a/Tasks/Common/docker-common/registryauthenticationprovider/acrauthenticationtokenprovider.ts +++ b/Tasks/Common/docker-common/registryauthenticationprovider/acrauthenticationtokenprovider.ts @@ -29,7 +29,7 @@ export default class ACRAuthenticationTokenProvider extends AuthenticationTokenP public getAuthenticationToken(): RegistryAuthenticationToken { if(this.registryURL && this.endpointName) { - return new RegistryAuthenticationToken(tl.getEndpointAuthorizationParameter(this.endpointName, 'serviceprincipalid', true), tl.getEndpointAuthorizationParameter(this.endpointName, 'serviceprincipalkey', true), this.registryURL, "ServicePrincipal@AzureRM", "azure/vsts"); + return new RegistryAuthenticationToken(tl.getEndpointAuthorizationParameter(this.endpointName, 'serviceprincipalid', true), tl.getEndpointAuthorizationParameter(this.endpointName, 'serviceprincipalkey', true), this.registryURL, "ServicePrincipal@AzureRM", this.getXMetaSourceClient()); } return null; diff --git a/Tasks/Common/docker-common/registryauthenticationprovider/authenticationtokenprovider.ts b/Tasks/Common/docker-common/registryauthenticationprovider/authenticationtokenprovider.ts index 3eed74b66c14..d04448706106 100644 --- a/Tasks/Common/docker-common/registryauthenticationprovider/authenticationtokenprovider.ts +++ b/Tasks/Common/docker-common/registryauthenticationprovider/authenticationtokenprovider.ts @@ -1,10 +1,23 @@ "use strict"; +import * as tl from "vsts-task-lib/task"; +import * as url from "url"; + import RegistryAuthenticationToken from "./registryauthenticationtoken" export abstract class AuthenticationTokenProvider { // get registry login creds public abstract getAuthenticationToken(): RegistryAuthenticationToken + + protected getXMetaSourceClient(): string { + var collectionUri: string = tl.getVariable('System.TeamFoundationCollectionUri'); + var collectionUrlObject = url.parse(collectionUri); + if(collectionUrlObject.hostname.toUpperCase().endsWith(".VISUALSTUDIO.COM")) { + return "VSTS"; + } + + return "TFS"; + } } export default AuthenticationTokenProvider; \ No newline at end of file diff --git a/Tasks/Common/docker-common/registryauthenticationprovider/genericauthenticationtokenprovider.ts b/Tasks/Common/docker-common/registryauthenticationprovider/genericauthenticationtokenprovider.ts index 4c405c983a13..cc9414b78faa 100644 --- a/Tasks/Common/docker-common/registryauthenticationprovider/genericauthenticationtokenprovider.ts +++ b/Tasks/Common/docker-common/registryauthenticationprovider/genericauthenticationtokenprovider.ts @@ -19,7 +19,7 @@ export default class GenericAuthenticationTokenProvider extends AuthenticationTo public getAuthenticationToken(): RegistryAuthenticationToken { if(this.registryAuth) { - return new RegistryAuthenticationToken(this.registryAuth["username"], this.registryAuth["password"], this.registryAuth["registry"], this.registryAuth["email"], "generic/vsts"); + return new RegistryAuthenticationToken(this.registryAuth["username"], this.registryAuth["password"], this.registryAuth["registry"], this.registryAuth["email"], this.getXMetaSourceClient()); } return null; diff --git a/Tasks/Docker/Strings/resources.resjson/en-US/resources.resjson b/Tasks/Docker/Strings/resources.resjson/en-US/resources.resjson index 5b9a67c6012a..a2515e0924d2 100644 --- a/Tasks/Docker/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/Docker/Strings/resources.resjson/en-US/resources.resjson @@ -60,6 +60,8 @@ "loc.input.help.customCommand": "Docker command to execute, with arguments. For example, 'rmi -f image-name' to force remove an image.", "loc.input.label.dockerHostEndpoint": "Docker Host Connection", "loc.input.help.dockerHostEndpoint": "Select a Docker host connection. Defaults to the agent's host.", + "loc.input.label.enforceDockerNamingConvention": "Force image name to follow Docker naming convention", + "loc.input.help.enforceDockerNamingConvention": "If enabled docker image name will be modified to follow Docker naming convention. Converts upper case character to lower case and removes spaces in image name.", "loc.input.label.cwd": "Working Directory", "loc.input.help.cwd": "Working directory for the Docker command.", "loc.messages.ContainerPatternFound": "Pattern found in docker filepath parameter", diff --git a/Tasks/Docker/Tests/L0.ts b/Tasks/Docker/Tests/L0.ts index aad28b4f200a..e59aa56f74fa 100644 --- a/Tasks/Docker/Tests/L0.ts +++ b/Tasks/Docker/Tests/L0.ts @@ -18,6 +18,7 @@ describe('Docker Suite', function() { delete process.env[shared.TestEnvVars.includeLatestTag]; delete process.env[shared.TestEnvVars.imageName]; delete process.env[shared.TestEnvVars.additionalImageTags]; + delete process.env[shared.TestEnvVars.enforceDockerNamingConvention]; }); after(function () { }); @@ -41,6 +42,7 @@ describe('Docker Suite', function() { let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); process.env[shared.TestEnvVars.action] = shared.ActionTypes.buildImage; process.env[shared.TestEnvVars.imageName] = 'test/Te st:2'; + process.env[shared.TestEnvVars.enforceDockerNamingConvention] = 'true'; tr.run(); assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); @@ -51,12 +53,29 @@ describe('Docker Suite', function() { done(); }); + it('Runs fails for docker build for invalid image name and modify image name false', (done:MochaDone) => { + let tp = path.join(__dirname, 'TestSetup.js'); + let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); + process.env[shared.TestEnvVars.action] = shared.ActionTypes.buildImage; + process.env[shared.TestEnvVars.imageName] = 'test/Te st:2'; + process.env[shared.TestEnvVars.enforceDockerNamingConvention] = 'false'; + tr.run(); + + assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); + assert(tr.stderr.length == 1 || tr.errorIssues.length, 'should have written to stderror'); + assert(tr.failed, 'task should have failed'); + assert(tr.stdout.indexOf(`test/Te st:2 not valid imagename`) != -1, "docker build should fail"); + console.log(tr.stderr); + done(); + }); + it('Runs successfully for docker build for invalid image name and additional image tag', (done:MochaDone) => { let tp = path.join(__dirname, 'TestSetup.js'); let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp); process.env[shared.TestEnvVars.action] = shared.ActionTypes.buildImage; process.env[shared.TestEnvVars.imageName] = 'test/Test:2'; process.env[shared.TestEnvVars.additionalImageTags] = '6'; + process.env[shared.TestEnvVars.enforceDockerNamingConvention] = 'true'; tr.run(); assert(tr.invokedToolCount == 1, 'should have invoked tool one times. actual: ' + tr.invokedToolCount); diff --git a/Tasks/Docker/Tests/TestSetup.ts b/Tasks/Docker/Tests/TestSetup.ts index ccb97bb2ce7a..c841ee06bf77 100644 --- a/Tasks/Docker/Tests/TestSetup.ts +++ b/Tasks/Docker/Tests/TestSetup.ts @@ -22,11 +22,13 @@ tr.setInput('qualifyImageName', process.env[shared.TestEnvVars.qualifyImageName] tr.setInput('azureSubscriptionEndpoint', 'AzureRMSpn'); tr.setInput('azureContainerRegistry', '{"loginServer":"ajgtestacr1.azurecr.io", "id" : "/subscriptions/c00d16c7-6c1f-4c03-9be1-6934a4c49682/resourcegroups/ajgtestacr1rg/providers/Microsoft.ContainerRegistry/registries/ajgtestacr1"}') tr.setInput('additionalImageTags', process.env[shared.TestEnvVars.additionalImageTags] || ''); +tr.setInput('enforceDockerNamingConvention', process.env[shared.TestEnvVars.enforceDockerNamingConvention]); console.log("Inputs have been set"); process.env["RELEASE_RELEASENAME"] = "Release-1"; process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = DefaultWorkingDirectory; +process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/"; process.env["ENDPOINT_AUTH_dockerhubendpoint"] = "{\"parameters\":{\"username\":\"test\", \"password\":\"regpassword\", \"email\":\"test@microsoft.com\",\"registry\":\"https://index.docker.io/v1/\"},\"scheme\":\"UsernamePassword\"}"; process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; @@ -72,6 +74,10 @@ a.exec[`docker build -f ${DockerFilePath} -t test/test:2`] = { "code": 0, "stdout": "successfully build test/test:2 image" }; +a.exec[`docker build -f ${DockerFilePath} -t test/Te st:2`] = { + "code": 1, + "stdout": "test/Te st:2 not valid imagename" +}; a.exec[`docker build -f ${DockerFilePath} -t test/test:2 -t test/test`] = { "code": 0, "stdout": "successfully build test/test image with latest tag" diff --git a/Tasks/Docker/Tests/TestShared.ts b/Tasks/Docker/Tests/TestShared.ts index 333c18abf732..65570a70a664 100644 --- a/Tasks/Docker/Tests/TestShared.ts +++ b/Tasks/Docker/Tests/TestShared.ts @@ -7,7 +7,8 @@ export let TestEnvVars = { qualifyImageName: "__qualifyImageName__", includeLatestTag: "__includeLatestTag__", imageName: "__imageName__", - additionalImageTags: "__additionalImageTags__" + additionalImageTags: "__additionalImageTags__", + enforceDockerNamingConvention: "__enforceDockerNamingConvention__" }; export let OperatingSystems = { diff --git a/Tasks/Docker/containerbuild.ts b/Tasks/Docker/containerbuild.ts index d7fb738b35f3..fe3cb581ca19 100644 --- a/Tasks/Docker/containerbuild.ts +++ b/Tasks/Docker/containerbuild.ts @@ -45,7 +45,7 @@ export function run(connection: ContainerConnection): any { command.arg(["--build-arg", buildArgument]); }); - var imageName = imageUtils.getImageName(); + var imageName = utils.getImageName(); var qualifyImageName = tl.getBoolInput("qualifyImageName"); if (qualifyImageName) { imageName = connection.qualifyImageName(imageName); diff --git a/Tasks/Docker/containerpush.ts b/Tasks/Docker/containerpush.ts index bef5bb68640b..fe22c00baded 100644 --- a/Tasks/Docker/containerpush.ts +++ b/Tasks/Docker/containerpush.ts @@ -45,7 +45,7 @@ export function run(connection: ContainerConnection): any { if (useMultiImageMode) { imageNames = utils.getImageNames(); } else { - imageNames = [imageUtils.getImageName()]; + imageNames = [utils.getImageName()]; } let imageMappings = utils.getImageMappings(connection, imageNames); diff --git a/Tasks/Docker/containerrun.ts b/Tasks/Docker/containerrun.ts index 322aa6526c41..98a38c27f4a9 100644 --- a/Tasks/Docker/containerrun.ts +++ b/Tasks/Docker/containerrun.ts @@ -65,8 +65,8 @@ export function run(connection: ContainerConnection): any { if (workDir) { command.arg(["-w", workDir]); } - - var imageName = imageUtils.getImageName(); + + var imageName = utils.getImageName(); var qualifyImageName = tl.getBoolInput("qualifyImageName"); if (qualifyImageName) { imageName = connection.qualifyImageName(imageName); diff --git a/Tasks/Docker/task.json b/Tasks/Docker/task.json index e6a44df36136..45528f88e527 100644 --- a/Tasks/Docker/task.json +++ b/Tasks/Docker/task.json @@ -13,7 +13,7 @@ "version": { "Major": 0, "Minor": 3, - "Patch": 5 + "Patch": 6 }, "demands": [], "preview": "false", @@ -277,6 +277,15 @@ "helpMarkDown": "Select a Docker host connection. Defaults to the agent's host.", "groupName": "advanced" }, + { + "name": "enforceDockerNamingConvention", + "type": "boolean", + "label": "Force image name to follow Docker naming convention", + "required": false, + "defaultValue": "true", + "helpMarkDown": "If enabled docker image name will be modified to follow Docker naming convention. Converts upper case character to lower case and removes spaces in image name.", + "groupName": "advanced" + }, { "name": "cwd", "aliases": ["workingDirectory"], diff --git a/Tasks/Docker/task.loc.json b/Tasks/Docker/task.loc.json index 964630fb38d1..7b2fb3bcfb76 100644 --- a/Tasks/Docker/task.loc.json +++ b/Tasks/Docker/task.loc.json @@ -13,7 +13,7 @@ "version": { "Major": 0, "Minor": 3, - "Patch": 5 + "Patch": 6 }, "demands": [], "preview": "false", @@ -281,6 +281,15 @@ "helpMarkDown": "ms-resource:loc.input.help.dockerHostEndpoint", "groupName": "advanced" }, + { + "name": "enforceDockerNamingConvention", + "type": "boolean", + "label": "ms-resource:loc.input.label.enforceDockerNamingConvention", + "required": false, + "defaultValue": "true", + "helpMarkDown": "ms-resource:loc.input.help.enforceDockerNamingConvention", + "groupName": "advanced" + }, { "name": "cwd", "aliases": [ diff --git a/Tasks/Docker/utils.ts b/Tasks/Docker/utils.ts index 9dd11531891e..49376e5a8d09 100644 --- a/Tasks/Docker/utils.ts +++ b/Tasks/Docker/utils.ts @@ -7,12 +7,23 @@ import * as imageUtils from "docker-common/containerimageutils"; export function getImageNames(): string[] { let imageNamesFilePath = tl.getPathInput("imageNamesPath", /* required */ true, /* check exists */ true); + var enforceDockerNamingConvention = tl.getBoolInput("enforceDockerNamingConvention"); let imageNames = fs.readFileSync(imageNamesFilePath, "utf-8").trim().replace("\r\n", "\n").split("\n"); if (!imageNames.length) { throw new Error(tl.loc("NoImagesInImageNamesFile", imageNamesFilePath)); } - return imageNames.map(n => imageUtils.generateValidImageName(n)); + return imageNames.map(n => (enforceDockerNamingConvention === true)? imageUtils.generateValidImageName(n): n); +} + +export function getImageName(): string { + var enforceDockerNamingConvention = tl.getBoolInput("enforceDockerNamingConvention"); + var imageName = tl.getInput("imageName", true); + if(enforceDockerNamingConvention === true) { + return imageUtils.generateValidImageName(imageName); + } + + return imageName; } export function getImageMappings(connection: ContainerConnection, imageNames: string[]): ImageMapping[] { diff --git a/Tasks/DockerCompose/Tests/L0Linux.ts b/Tasks/DockerCompose/Tests/L0Linux.ts index 6451fdc4f951..6af44c78534d 100644 --- a/Tasks/DockerCompose/Tests/L0Linux.ts +++ b/Tasks/DockerCompose/Tests/L0Linux.ts @@ -22,6 +22,7 @@ console.log("Inputs have been set"); process.env["RELEASE_RELEASENAME"] = "Release-1"; process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = DefaultWorkingDirectory; +process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/"; process.env["ENDPOINT_AUTH_dockerhubendpoint"] = "{\"parameters\":{\"username\":\"test\", \"password\":\"regpassword\", \"email\":\"test@microsoft.com\",\"registry\":\"https://index.docker.io/v1/\"},\"scheme\":\"UsernamePassword\"}"; process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; diff --git a/Tasks/DockerCompose/Tests/L0Windows.ts b/Tasks/DockerCompose/Tests/L0Windows.ts index a6963daf8441..a997889c70dd 100644 --- a/Tasks/DockerCompose/Tests/L0Windows.ts +++ b/Tasks/DockerCompose/Tests/L0Windows.ts @@ -22,6 +22,7 @@ console.log("Inputs have been set"); process.env["RELEASE_RELEASENAME"] = "Release-1"; process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = DefaultWorkingDirectory; +process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/"; process.env["ENDPOINT_AUTH_dockerhubendpoint"] = "{\"parameters\":{\"username\":\"test\", \"password\":\"regpassword\", \"email\":\"test@microsoft.com\",\"registry\":\"https://index.docker.io/v1/\"},\"scheme\":\"UsernamePassword\"}"; process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; diff --git a/Tasks/DockerCompose/task.json b/Tasks/DockerCompose/task.json index 02b9db86ee8c..53640ed715a8 100644 --- a/Tasks/DockerCompose/task.json +++ b/Tasks/DockerCompose/task.json @@ -13,7 +13,7 @@ "version": { "Major": 0, "Minor": 4, - "Patch": 11 + "Patch": 12 }, "demands": [], "preview": "false", diff --git a/Tasks/Kubernetes/task.json b/Tasks/Kubernetes/task.json index c0aaab9faf43..e0a456251aae 100644 --- a/Tasks/Kubernetes/task.json +++ b/Tasks/Kubernetes/task.json @@ -13,7 +13,7 @@ "version": { "Major": 0, "Minor": 1, - "Patch": 10 + "Patch": 11 }, "demands": [], "preview": "false", diff --git a/Tasks/Kubernetes/task.loc.json b/Tasks/Kubernetes/task.loc.json index e3364f69bdef..5bd6e49e12cc 100644 --- a/Tasks/Kubernetes/task.loc.json +++ b/Tasks/Kubernetes/task.loc.json @@ -13,7 +13,7 @@ "version": { "Major": 0, "Minor": 1, - "Patch": 10 + "Patch": 11 }, "demands": [], "preview": "false",