From c1076f221f3bbd20d8c3234932026c036ec29a60 Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Tue, 1 Jun 2021 10:05:18 +0300 Subject: [PATCH] Fix operator image name and tag parsing Signed-off-by: Mykola Morhun --- package.json | 2 +- src/tasks/installers/operator.ts | 16 ++++++------ src/util.ts | 38 +++++++++++++++++++++++++++++ test/other/util.test.ts | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 test/other/util.test.ts diff --git a/package.json b/package.json index fc9e25297..d5bcf0395 100644 --- a/package.json +++ b/package.json @@ -185,7 +185,7 @@ "modulePathIgnorePatterns": [ "/dist" ], - "testRegex": "/test/(api|tasks)/.*.test.ts", + "testRegex": "/test/(api|tasks|other)/.*.test.ts", "testEnvironment": "node", "moduleFileExtensions": [ "ts", diff --git a/src/tasks/installers/operator.ts b/src/tasks/installers/operator.ts index c45110b84..4e49745a8 100644 --- a/src/tasks/installers/operator.ts +++ b/src/tasks/installers/operator.ts @@ -18,7 +18,7 @@ import { ChectlContext } from '../../api/context' import { KubeHelper } from '../../api/kube' import { VersionHelper } from '../../api/version' import { CHE_CLUSTER_CRD, CHE_OPERATOR_SELECTOR, OPERATOR_DEPLOYMENT_NAME, OPERATOR_TEMPLATE_DIR } from '../../constants' -import { safeLoadFromYamlFile } from '../../util' +import { safeLoadFromYamlFile, getImageNameAndTag } from '../../util' import { KubeTasks } from '../kube' import { createEclipseCheCluster, createNamespaceTask, patchingEclipseCheCluster } from './common-tasks' @@ -319,10 +319,9 @@ export class OperatorTasks { title: 'Detecting existing version...', task: async (ctx: any, task: any) => { ctx.deployedCheOperatorImage = this.retrieveContainerImage(ctx.deployedCheOperatorYaml) - const deployedCheOperatorImageAndTag = ctx.deployedCheOperatorImage.split(':', 2) - ctx.deployedCheOperatorImageName = deployedCheOperatorImageAndTag[0] - ctx.deployedCheOperatorImageTag = deployedCheOperatorImageAndTag.length === 2 ? deployedCheOperatorImageAndTag[1] : 'latest' - ctx.deployedCheOperatorImage = ctx.deployedCheOperatorImageName + ':' + ctx.deployedCheOperatorImageTag + const [deployedImage, deployedTag] = getImageNameAndTag(ctx.deployedCheOperatorImage) + ctx.deployedCheOperatorImageName = deployedImage + ctx.deployedCheOperatorImageTag = deployedTag if (flags['che-operator-image']) { ctx.newCheOperatorImage = flags['che-operator-image'] @@ -331,10 +330,9 @@ export class OperatorTasks { const newCheOperatorYaml = safeLoadFromYamlFile(path.join(flags.templates, OPERATOR_TEMPLATE_DIR, 'operator.yaml')) as V1Deployment ctx.newCheOperatorImage = this.retrieveContainerImage(newCheOperatorYaml) } - const newCheOperatorImageAndTag = ctx.newCheOperatorImage.split(':', 2) - ctx.newCheOperatorImageName = newCheOperatorImageAndTag[0] - ctx.newCheOperatorImageTag = newCheOperatorImageAndTag.length === 2 ? newCheOperatorImageAndTag[1] : 'latest' - ctx.newCheOperatorImage = ctx.newCheOperatorImageName + ':' + ctx.newCheOperatorImageTag + const [newImage, newTag] = getImageNameAndTag(ctx.newCheOperatorImage) + ctx.newCheOperatorImageName = newImage + ctx.newCheOperatorImageTag = newTag task.title = `${task.title} ${ctx.deployedCheOperatorImageTag} -> ${ctx.newCheOperatorImageTag}` } diff --git a/src/util.ts b/src/util.ts index c8edeb6a7..c4ad7f011 100644 --- a/src/util.ts +++ b/src/util.ts @@ -83,6 +83,44 @@ export function base64Decode(arg: string): string { return Buffer.from(arg, 'base64').toString('ascii') } +/** + * Separates docker image repository and tag. + * @param image string with image and tag separated by a colon + * @returns image name (including registry and account) and image tag correspondingly + */ +export function getImageNameAndTag(image: string): [string, string] { + let deployedCheOperatorImageName: string + let deployedCheOperatorImageTag: string + + if (image.includes('@')) { + // Image is referenced via a digest + const index = image.indexOf('@') + deployedCheOperatorImageName = image.substring(0, index) + deployedCheOperatorImageTag = image.substring(index + 1) + } else { + // Image is referenced via a tag + const lastColonIndex = image.lastIndexOf(':') + if (lastColonIndex === -1) { + // Image name without a tag + deployedCheOperatorImageName = image + deployedCheOperatorImageTag = 'latest' + } else { + let beforeLastColon = image.substring(0, lastColonIndex) + let afterLastColon = image.substring(lastColonIndex + 1) + if (afterLastColon.includes('/')) { + // The colon is for registry port and not for a tag + deployedCheOperatorImageName = image + deployedCheOperatorImageTag = 'latest' + } else { + // The colon separates image name from the tag + deployedCheOperatorImageName = beforeLastColon + deployedCheOperatorImageTag = afterLastColon + } + } + } + return [deployedCheOperatorImageName, deployedCheOperatorImageTag] +} + /** * Returns the tag of the image. */ diff --git a/test/other/util.test.ts b/test/other/util.test.ts new file mode 100644 index 000000000..4d13461e6 --- /dev/null +++ b/test/other/util.test.ts @@ -0,0 +1,42 @@ +/********************************************************************* + * Copyright (c) 2021 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +import { expect, fancy } from 'fancy-test' + +import { getImageNameAndTag } from '../../src/util' + +describe('Util tests', () => { + describe('Test getImageNameAndTag', () => { + // test data format: full image reference, image repository, tag + const data = [ + ['registry.io/account/image:tag', 'registry.io/account/image', 'tag'], + ['registry.io/account/image', 'registry.io/account/image', 'latest'], + ['account/image:4387', 'account/image', '4387'], + ['docker-registry.default.svc:5000/namespace/operator-image:tag2.6', 'docker-registry.default.svc:5000/namespace/operator-image', 'tag2.6'], + ['registry.io:5000/account/image', 'registry.io:5000/account/image', 'latest'], + ['the-image@sha256:12b235c10daa7e4358fe26c4cff725dcf218e0100d680a9722c8ac76170c32ed', 'the-image', 'sha256:12b235c10daa7e4358fe26c4cff725dcf218e0100d680a9722c8ac76170c32ed'], + ['registry.io/account/image@sha256:82b23dc10daf7e43a8fe26c4cffc25acf268e0110168009722f8ac76170c8ce2', 'registry.io/account/image', 'sha256:82b23dc10daf7e43a8fe26c4cffc25acf268e0110168009722f8ac76170c8ce2'], + ['registry.io:1234/image@sha256:12b235c10daa7e4358fe26c4cff725dcf218e0100d680a9722c8ac76170c32ed', 'registry.io:1234/image', 'sha256:12b235c10daa7e4358fe26c4cff725dcf218e0100d680a9722c8ac76170c32ed'], + ] + fancy.it('Should parse image repository and tag', () => { + for (const testCaseData of data) { + const image = testCaseData[0] + const expectedImageRepo = testCaseData[1] + const expectedImageTag = testCaseData[2] + + const [imageRepo, imageTag] = getImageNameAndTag(image) + + expect(imageRepo).to.equal(expectedImageRepo) + expect(imageTag).to.equal(expectedImageTag) + } + }) + }) + +})