From d1d9e5ed70534dbc9066439151b4afae53107e8d Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 25 Oct 2024 13:26:53 +0200 Subject: [PATCH] add optional cli parameter `--preserve-digests` to skopeo copy command. (#1171) --- CHANGELOG.md | 1 + .../partials/odsComponentStageCopyImage.adoc | 7 +++++++ src/org/ods/component/CopyImageOptions.groovy | 7 +++++++ src/org/ods/component/CopyImageStage.groovy | 14 +++++++++++--- .../vars/OdsComponentStageCopyImageSpec.groovy | 18 ++++++++++-------- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8ff86261..53379e445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Changed * Enhance SSDS Document Generation Performance using New Atlassian APIs ([#1084](https://github.com/opendevstack/ods-jenkins-shared-library/issues/1084)) * Deprecation of vuln-type and scanners config in Trivy ([#1150](https://github.com/opendevstack/ods-jenkins-shared-library/issues/1150)) +* Add preserve-digests cli option to skopeo copy command in CopyImageStage ([#1166](https://github.com/opendevstack/ods-jenkins-shared-library/issues/1166)) ### Fixed * Fix Tailor deployment drifts for D, Q envs ([#1055](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1055)) diff --git a/docs/modules/jenkins-shared-library/partials/odsComponentStageCopyImage.adoc b/docs/modules/jenkins-shared-library/partials/odsComponentStageCopyImage.adoc index f1b1dc834..df884b7d0 100644 --- a/docs/modules/jenkins-shared-library/partials/odsComponentStageCopyImage.adoc +++ b/docs/modules/jenkins-shared-library/partials/odsComponentStageCopyImage.adoc @@ -27,6 +27,13 @@ _List_ Next to exact matches, it also supports prefixes (e.g. `feature/`) and all branches (`*`). +| *preserveDigests* + +_Boolean_ +|preserveDigests allows to sync the source and destination image digests + + The default is false, set to true to preserve digests + + | *sourceCredential* + _String_ |sourceCredential is the token to use, if any, to access the source registry diff --git a/src/org/ods/component/CopyImageOptions.groovy b/src/org/ods/component/CopyImageOptions.groovy index 3000f54a2..6d26f828b 100644 --- a/src/org/ods/component/CopyImageOptions.groovy +++ b/src/org/ods/component/CopyImageOptions.groovy @@ -29,6 +29,13 @@ class CopyImageOptions extends Options { */ Boolean verifyTLS + /** + * preserveDigests allows to sync the source and destination image digests + * + * The default is false, set to true to preserve digests + */ + Boolean preserveDigests + @SuppressWarnings('UnusedPrivateField') private String registry @SuppressWarnings('UnusedPrivateField') diff --git a/src/org/ods/component/CopyImageStage.groovy b/src/org/ods/component/CopyImageStage.groovy index f220647d1..7480e1d07 100644 --- a/src/org/ods/component/CopyImageStage.groovy +++ b/src/org/ods/component/CopyImageStage.groovy @@ -27,6 +27,10 @@ class CopyImageStage extends Stage { config.verifyTLS = true } + if (config.preserveDigests == null) { + config.preserveDigests = false + } + this.options = new CopyImageOptions(config) this.openShift = openShift this.logger = logger @@ -52,7 +56,10 @@ class CopyImageStage extends Stage { def sourcetoken = options.sourceCredential ? "--src-creds ${options.sourceCredential}" : '' - int status = copyImage(sourcetoken, targetInternalRegistryToken, STR_DOCKER_PROTOCOL) + def copyparams = "" + if (this.options.preserveDigests) { copyparams += "--all --preserve-digests" } + + int status = copyImage(sourcetoken, targetInternalRegistryToken, STR_DOCKER_PROTOCOL, copyparams) if (status != 0) { script.error("Could not copy `${this.options.sourceImageUrlIncludingRegistry}', status ${status}") } @@ -83,10 +90,11 @@ class CopyImageStage extends Stage { } } - private int copyImage(sourcetoken, targetInternalRegistryToken, String dockerProtocol) { + private int copyImage(sourcetoken, targetInternalRegistryToken, String dockerProtocol, String copyparams) { int status = steps.sh( script: """ - skopeo copy --src-tls-verify=${this.options.verifyTLS} ${sourcetoken} \ + skopeo copy ${copyparams} \ + --src-tls-verify=${this.options.verifyTLS} ${sourcetoken} \ ${this.options.registry}/${this.options.repo}/${this.options.image} \ --dest-creds openshift:${targetInternalRegistryToken} \ ${dockerProtocol}${context.clusterRegistryAddress}/${context.cdProject}/${this.options.image} \ diff --git a/test/groovy/vars/OdsComponentStageCopyImageSpec.groovy b/test/groovy/vars/OdsComponentStageCopyImageSpec.groovy index b6f803e9d..c6acb0519 100644 --- a/test/groovy/vars/OdsComponentStageCopyImageSpec.groovy +++ b/test/groovy/vars/OdsComponentStageCopyImageSpec.groovy @@ -19,6 +19,7 @@ class OdsComponentStageCopyImageSpec extends PipelineSpockTestBase { def cfg = [ sourceImageUrlIncludingRegistry: sourceImageUrlIncludingRegistry, verifyTLS : verifyTLS, + preserveDigests : preserveDigests, tagIntoTargetEnv : tagIntoTargetEnv, ] def ctxCfg = [ @@ -51,7 +52,8 @@ class OdsComponentStageCopyImageSpec extends PipelineSpockTestBase { assertCallStackContains("Resolved source Image data: ${sourceImageUrlIncludingRegistry}") assertCallStackContains("importing into: docker://internal-registry/project-cd/${imageName}:${imageTag}") // FIXME: this should probably verify that the steps.sh is called with the correct string rather than checking the full callstack - assertCallStackContains("skopeo copy --src-tls-verify=${expectedVerifyTLS} docker://${sourceImageUrlIncludingRegistry} --dest-creds openshift:secret-token docker://internal-registry/project-cd/image:1f3d1 --dest-tls-verify=${expectedVerifyTLS}") + assertCallStackContains("skopeo copy ${expectedCopyParams}") + assertCallStackContains("--src-tls-verify=${expectedVerifyTLS} docker://${sourceImageUrlIncludingRegistry} --dest-creds openshift:secret-token docker://internal-registry/project-cd/image:1f3d1 --dest-tls-verify=${expectedVerifyTLS}") if (tagIntoTargetEnv) { 1 * openShiftService.importImageTagFromProject('project-dev', imageName, 'project-cd', imageTag, imageTag) 1 * openShiftService.findOrCreateImageStream('project-dev', imageName) @@ -60,13 +62,13 @@ class OdsComponentStageCopyImageSpec extends PipelineSpockTestBase { assertJobStatusSuccess() where: - registry || repo || imageName || imageTag || verifyTLS || expectedVerifyTLS || tagIntoTargetEnv - 'example.com' || 'repo' || 'image' || '1f3d1' || true || true || true - 'example.com' || 'repo' || 'image' || '1f3d1' || true || true || false - 'example.com' || 'repo' || 'image' || '1f3d1' || false || false || true - 'example.com' || 'repo' || 'image' || '1f3d1' || false || false || false - 'example.com' || 'repo' || 'image' || '1f3d1' || null || true || true - 'example.com' || 'repo' || 'image' || '1f3d1' || null || true || false + registry || repo || imageName || imageTag || preserveDigests || expectedCopyParams || verifyTLS || expectedVerifyTLS || tagIntoTargetEnv + 'example.com' || 'repo' || 'image' || '1f3d1' || true || '--all --preserve-digests ' || true || true || true + 'example.com' || 'repo' || 'image' || '1f3d1' || true || '--all --preserve-digests ' || true || true || false + 'example.com' || 'repo' || 'image' || '1f3d1' || false || '' || false || false || true + 'example.com' || 'repo' || 'image' || '1f3d1' || false || '' || false || false || false + 'example.com' || 'repo' || 'image' || '1f3d1' || null || '' || null || true || true + 'example.com' || 'repo' || 'image' || '1f3d1' || null || '' || null || true || false } }