From 44e98bf2e024412af97638b61fe1666cd947712d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Contreras?= Date: Mon, 29 Apr 2024 18:07:07 -0500 Subject: [PATCH 1/5] NEXUS-42440 Add base image reference as a label --- Jenkinsfile-Internal-Release | 83 +++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile-Internal-Release b/Jenkinsfile-Internal-Release index 583936ea..6f48efad 100644 --- a/Jenkinsfile-Internal-Release +++ b/Jenkinsfile-Internal-Release @@ -9,6 +9,7 @@ import com.sonatype.jenkins.pipeline.OsTools String OPENJDK8 = 'OpenJDK 8' String OPENJDK11 = 'OpenJDK 11' List javaVersions = [OPENJDK8, OPENJDK11] +String RED_HAT_REGISTRY = "registry.access.redhat.com" properties([ parameters([ @@ -54,7 +55,13 @@ node('ubuntu-zion') { if (params.java_version == OPENJDK11) { dockerfilePath = 'Dockerfile.java11' } - def hash = OsTools.runSafe(this, "docker build --quiet --no-cache --tag ${imageName} . -f ${dockerfilePath}") + def baseImage = extractBaseImage(dockerfilePath) + OsTools.runSafe(this, "docker pull ${baseImage}") + def baseImageRef = getRedHatCatalogReference(baseImage, RED_HAT_REGISTRY) + if (baseImageRef == null) { + baseImageRef = baseImage + } + def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageRef}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") imageId = hash.split(':')[1] } if (params.scan_for_policy_violations) { @@ -156,3 +163,77 @@ def getSha(url) { ).trim() return sha } + +def extractBaseImage (dockerFileLocation) { + def dockerFile = readFile(file: dockerFileLocation) + def baseImageRegex = "FROM\\s+([^\\s]+)" + def usedImages = dockerFile =~ baseImageRegex + + return usedImages[0][1] +} + +def getRedHatCatalogReference(baseImage, registryName) { + def repoName = extractRedHatRepoName(baseImage, registryName) + def dockerImageId = getDockerImageId(baseImage) + if(repoName == null || dockerImageId == null) { + return null + } + + def imageId = getRedHatImageId(dockerImageId) + def repoId = getRedHatRepoId(repoName, registryName) + if(imageId == null || repoId == null) { + return "Base Image: ${baseImage} - Docker Image ID: ${dockerImageId}" + } + + def imageArch = sh ( + script: "docker image inspect ${baseImage} | jq -r '.[0].Architecture' ", + returnStdout: true + ).trim() + imageArch == "null" ? null : imageArch + + if (imageArch != null) { + return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?architecture=${imageArch}&image=${imageId}" + } else { + return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?image=${imageId}" + } +} + +def extractRedHatRepoName(baseImage, registryName) { + if(!baseImage.contains(registryName)) { + return null + } + def repositoryRegex = "${registryName}\\/(.*)" + def repository = (baseImage =~ repositoryRegex)[0][1] + return repository +} + +def getDockerImageId(baseImage) { + def dockerImageId = sh ( + script: "docker image inspect ${baseImage} | jq -r '.[0].Id' ", + returnStdout: true + ).trim() + + return dockerImageId == "null" ? null : dockerImageId +} + +def getRedHatImageId (dockerImageId) { + def imageSearchUrl = + "https://catalog.redhat.com/api/containers/v1/images?filter=docker_image_id==\"${dockerImageId}\"" + def imageId = sh ( + script: "curl -s -L ${imageSearchUrl} | jq -r '.data[0]._id' ", + returnStdout: true + ).trim() + + return imageId == "null" ? null : imageId +} + +def getRedHatRepoId (repoName, registryName) { + def repoSearchUrl = + "https://catalog.redhat.com/api/containers/v1/repositories/registry/${registryName}/repository/${repoName}" + def repoId = sh ( + script: "curl -s -L ${repoSearchUrl} | jq -r '._id' ", + returnStdout: true + ).trim() + + return repoId == "null" ? null : repoId +} \ No newline at end of file From 92fb3c5818b5e54c9c6d18e2def915f6f7700ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Contreras?= Date: Tue, 30 Apr 2024 15:09:41 -0500 Subject: [PATCH 2/5] NEXUS-42440 Add logic as a folder-level script --- Jenkinsfile-Internal-Release | 77 +------------- scripts/BaseImageReference.groovy | 164 ++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 73 deletions(-) create mode 100644 scripts/BaseImageReference.groovy diff --git a/Jenkinsfile-Internal-Release b/Jenkinsfile-Internal-Release index 6f48efad..e066a0e9 100644 --- a/Jenkinsfile-Internal-Release +++ b/Jenkinsfile-Internal-Release @@ -9,7 +9,6 @@ import com.sonatype.jenkins.pipeline.OsTools String OPENJDK8 = 'OpenJDK 8' String OPENJDK11 = 'OpenJDK 11' List javaVersions = [OPENJDK8, OPENJDK11] -String RED_HAT_REGISTRY = "registry.access.redhat.com" properties([ parameters([ @@ -56,12 +55,10 @@ node('ubuntu-zion') { dockerfilePath = 'Dockerfile.java11' } def baseImage = extractBaseImage(dockerfilePath) - OsTools.runSafe(this, "docker pull ${baseImage}") - def baseImageRef = getRedHatCatalogReference(baseImage, RED_HAT_REGISTRY) - if (baseImageRef == null) { - baseImageRef = baseImage - } - def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageRef}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") + def baseImageRefFactory = load 'scripts/BaseImageReference.groovy' + def baseImageReference = baseImageRefFactory.build(this, baseImage as String) + def baseImageReferenceStr = baseImageReference.getReference() + def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageReferenceStr}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") imageId = hash.split(':')[1] } if (params.scan_for_policy_violations) { @@ -170,70 +167,4 @@ def extractBaseImage (dockerFileLocation) { def usedImages = dockerFile =~ baseImageRegex return usedImages[0][1] -} - -def getRedHatCatalogReference(baseImage, registryName) { - def repoName = extractRedHatRepoName(baseImage, registryName) - def dockerImageId = getDockerImageId(baseImage) - if(repoName == null || dockerImageId == null) { - return null - } - - def imageId = getRedHatImageId(dockerImageId) - def repoId = getRedHatRepoId(repoName, registryName) - if(imageId == null || repoId == null) { - return "Base Image: ${baseImage} - Docker Image ID: ${dockerImageId}" - } - - def imageArch = sh ( - script: "docker image inspect ${baseImage} | jq -r '.[0].Architecture' ", - returnStdout: true - ).trim() - imageArch == "null" ? null : imageArch - - if (imageArch != null) { - return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?architecture=${imageArch}&image=${imageId}" - } else { - return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?image=${imageId}" - } -} - -def extractRedHatRepoName(baseImage, registryName) { - if(!baseImage.contains(registryName)) { - return null - } - def repositoryRegex = "${registryName}\\/(.*)" - def repository = (baseImage =~ repositoryRegex)[0][1] - return repository -} - -def getDockerImageId(baseImage) { - def dockerImageId = sh ( - script: "docker image inspect ${baseImage} | jq -r '.[0].Id' ", - returnStdout: true - ).trim() - - return dockerImageId == "null" ? null : dockerImageId -} - -def getRedHatImageId (dockerImageId) { - def imageSearchUrl = - "https://catalog.redhat.com/api/containers/v1/images?filter=docker_image_id==\"${dockerImageId}\"" - def imageId = sh ( - script: "curl -s -L ${imageSearchUrl} | jq -r '.data[0]._id' ", - returnStdout: true - ).trim() - - return imageId == "null" ? null : imageId -} - -def getRedHatRepoId (repoName, registryName) { - def repoSearchUrl = - "https://catalog.redhat.com/api/containers/v1/repositories/registry/${registryName}/repository/${repoName}" - def repoId = sh ( - script: "curl -s -L ${repoSearchUrl} | jq -r '._id' ", - returnStdout: true - ).trim() - - return repoId == "null" ? null : repoId } \ No newline at end of file diff --git a/scripts/BaseImageReference.groovy b/scripts/BaseImageReference.groovy new file mode 100644 index 00000000..f842ea27 --- /dev/null +++ b/scripts/BaseImageReference.groovy @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2016-present Sonatype, Inc. All rights reserved. + * Includes the third-party code listed at http://links.sonatype.com/products/nexus/attributions. + * "Sonatype" is a trademark of Sonatype, Inc. + */ +interface BaseImageReference +{ + String getReference() + + String getReference(String registryName) +} + +class DefaultBaseImageReference + implements BaseImageReference +{ + private String baseImage + private DockerImageHelper dockerImageHelper + + DefaultBaseImageReference(String baseImage, DockerImageHelper dockerImageHelper) { + this.baseImage = baseImage + this.dockerImageHelper = dockerImageHelper + } + + + String getReference(String registryName = null) { + def imageDigest = dockerImageHelper.getImageFirstRepoDigest(baseImage) + if (imageDigest == null) { + return baseImage + } + return imageDigest + } +} + +class RedHatBaseImageReference + implements BaseImageReference +{ + final static RED_HAT_REGISTRY = "registry.access.redhat.com" + + private String baseImage + private DockerImageHelper dockerImageHelper + private steps + + RedHatBaseImageReference(String baseImage, DockerImageHelper dockerImageHelper, steps) { + this.baseImage = baseImage + this.dockerImageHelper = dockerImageHelper + this.steps = steps + } + + String getReference(String registryName = RED_HAT_REGISTRY) { + def repoName = extractRedHatRepoName(baseImage, registryName) + def dockerImageId = dockerImageHelper.getImageId(baseImage) + if (repoName == null || dockerImageId == null) { + return null + } + + def imageId = getRedHatImageId(dockerImageId) + def repoId = getRedHatRepoId(repoName, registryName) + if (imageId == null || repoId == null) { + def imageDigest = dockerImageHelper.getImageFirstRepoDigest(baseImage) + return imageDigest + } + + def imageArch = dockerImageHelper.getImageArchitecture(baseImage) + if (imageArch != null) { + return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?architecture=${imageArch}&image=${imageId}" + } + else { + return "https://catalog.redhat.com/software/containers/${repoName}/${repoId}?image=${imageId}" + } + } + + private static extractRedHatRepoName(baseImage, registryName) { + if (!baseImage.contains(registryName)) { + return null + } + def repositoryRegex = "${registryName}\\/(.*)" + def repository = (baseImage =~ repositoryRegex)[0][1] + return repository + } + + private getRedHatImageId(dockerImageId) { + def imageSearchUrl = + "https://catalog.redhat.com/api/containers/v1/images?filter=docker_image_id==\"${dockerImageId}\"" + def imageId = steps.sh( + script: "curl -s -L ${imageSearchUrl} | jq -r '.data[0]._id' ", + returnStdout: true + ).trim() + + return imageId == "null" ? null : imageId + } + + private getRedHatRepoId(repoName, registryName) { + def repoSearchUrl = + "https://catalog.redhat.com/api/containers/v1/repositories/registry/${registryName}/repository/${repoName}" + def repoId = steps.sh( + script: "curl -s -L ${repoSearchUrl} | jq -r '._id' ", + returnStdout: true + ).trim() + + return repoId == "null" ? null : repoId + } +} + +class DockerImageHelper +{ + private steps + + DockerImageHelper(steps) { + this.steps = steps + } + def getImageId(baseImage) { + pullImage(baseImage) + def dockerImageId = steps.sh( + script: "docker image inspect ${baseImage} | jq -r '.[0].Id' ", + returnStdout: true + ).trim() + return dockerImageId == "null" ? null : dockerImageId + } + + def getImageArchitecture(baseImage) { + pullImage(baseImage) + def imageArch = steps.sh( + script: "docker image inspect ${baseImage} | jq -r '.[0].Architecture' ", + returnStdout: true + ).trim() + return imageArch == "null" ? null : imageArch + } + + def getImageFirstRepoDigest(baseImage) { + pullImage(baseImage) + def imageDigest = steps.sh( + script: "docker image inspect ${baseImage} | jq -r '.[0].RepoDigests[0]'", + returnStdout: true + ).trim() + return imageDigest == "null" ? null : imageDigest + } + + private def pullImage(baseImage) { + if (!isPulled(baseImage)) { + steps.sh("docker pull ${baseImage}") + } + } + + private def isPulled(baseImage) { + def status = steps.sh( + script: "docker image inspect ${baseImage} 1> /dev/null", + returnStatus: true + ) + return status == 0 + } +} + +static BaseImageReference build(steps, String baseImage) { + def dockerHelper = new DockerImageHelper(steps) + + if (baseImage.contains(RedHatBaseImageReference.RED_HAT_REGISTRY)) { + return new RedHatBaseImageReference(baseImage, dockerHelper, steps) + } + else { + return new DefaultBaseImageReference(baseImage, dockerHelper) + } +} + +return this \ No newline at end of file From aab8152e854b485c9f238f78c750e83d1e59da60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Contreras?= Date: Tue, 30 Apr 2024 22:09:44 -0500 Subject: [PATCH 3/5] NEXUS-42440 Change production release Jenkinsfile --- Jenkinsfile-Release | 14 +++++++++++++- scripts/BaseImageReference.groovy | 6 +++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile-Release b/Jenkinsfile-Release index 20e06042..3512c635 100644 --- a/Jenkinsfile-Release +++ b/Jenkinsfile-Release @@ -100,7 +100,11 @@ node('ubuntu-zion') { if (params.java_version == OPENJDK11) { dockerfilePath = 'Dockerfile.java11' } - def hash = OsTools.runSafe(this, "docker build --quiet --no-cache --tag ${imageName} . -f ${dockerfilePath}") + def baseImage = extractBaseImage(dockerfilePath) + def baseImageRefFactory = load 'scripts/BaseImageReference.groovy' + def baseImageReference = baseImageRefFactory.build(this, baseImage as String) + def baseImageReferenceStr = baseImageReference.getReference() + def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageReferenceStr}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") imageId = hash.split(':')[1] if (currentBuild.result == 'FAILURE') { @@ -295,3 +299,11 @@ def updateRepositoryCookbookVersion(dockerFileLocation) { writeFile(file: dockerFileLocation, text: dockerFile) } + +def extractBaseImage (dockerFileLocation) { + def dockerFile = readFile(file: dockerFileLocation) + def baseImageRegex = "FROM\\s+([^\\s]+)" + def usedImages = dockerFile =~ baseImageRegex + + return usedImages[0][1] +} diff --git a/scripts/BaseImageReference.groovy b/scripts/BaseImageReference.groovy index f842ea27..21dc61b1 100644 --- a/scripts/BaseImageReference.groovy +++ b/scripts/BaseImageReference.groovy @@ -3,6 +3,7 @@ * Includes the third-party code listed at http://links.sonatype.com/products/nexus/attributions. * "Sonatype" is a trademark of Sonatype, Inc. */ + interface BaseImageReference { String getReference() @@ -14,6 +15,7 @@ class DefaultBaseImageReference implements BaseImageReference { private String baseImage + private DockerImageHelper dockerImageHelper DefaultBaseImageReference(String baseImage, DockerImageHelper dockerImageHelper) { @@ -21,7 +23,6 @@ class DefaultBaseImageReference this.dockerImageHelper = dockerImageHelper } - String getReference(String registryName = null) { def imageDigest = dockerImageHelper.getImageFirstRepoDigest(baseImage) if (imageDigest == null) { @@ -37,7 +38,9 @@ class RedHatBaseImageReference final static RED_HAT_REGISTRY = "registry.access.redhat.com" private String baseImage + private DockerImageHelper dockerImageHelper + private steps RedHatBaseImageReference(String baseImage, DockerImageHelper dockerImageHelper, steps) { @@ -108,6 +111,7 @@ class DockerImageHelper DockerImageHelper(steps) { this.steps = steps } + def getImageId(baseImage) { pullImage(baseImage) def dockerImageId = steps.sh( From d8ea92ca6c127725999a46aa25c9bfa51f832bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Contreras?= Date: Mon, 6 May 2024 15:56:03 -0500 Subject: [PATCH 4/5] Adding an end line --- scripts/BaseImageReference.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/BaseImageReference.groovy b/scripts/BaseImageReference.groovy index 21dc61b1..41ffd6e9 100644 --- a/scripts/BaseImageReference.groovy +++ b/scripts/BaseImageReference.groovy @@ -165,4 +165,4 @@ static BaseImageReference build(steps, String baseImage) { } } -return this \ No newline at end of file +return this From 6a5bef23ebaf3581c89bd2bfae81de5ccf2d243b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Contreras?= Date: Mon, 6 May 2024 15:58:02 -0500 Subject: [PATCH 5/5] Adding an end line --- Jenkinsfile-Internal-Release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile-Internal-Release b/Jenkinsfile-Internal-Release index e066a0e9..57f5a0ec 100644 --- a/Jenkinsfile-Internal-Release +++ b/Jenkinsfile-Internal-Release @@ -167,4 +167,4 @@ def extractBaseImage (dockerFileLocation) { def usedImages = dockerFile =~ baseImageRegex return usedImages[0][1] -} \ No newline at end of file +}