From dfa47e98b1e8f2897d428bb3999688851d596244 Mon Sep 17 00:00:00 2001 From: Matt Johnson Date: Mon, 18 Nov 2024 11:33:57 -0500 Subject: [PATCH 1/3] Uses assemblies in docker images and enables arm64+amd64 platforms --- Dockerfile | 77 +++++++++++++++++++++++++-------------- Dockerfile.slim | 78 ++++++++++++++++++++++++++-------------- Jenkinsfile | 11 ++++++ Jenkinsfile.release | 36 ++++++++----------- Jenkinsfile.slim | 11 ++++++ Jenkinsfile.slim.release | 42 +++++++++------------- build_and_push_images.sh | 65 +++++++++++++++++++++++++++++++++ 7 files changed, 221 insertions(+), 99 deletions(-) create mode 100755 build_and_push_images.sh diff --git a/Dockerfile b/Dockerfile index 8a6027a..b3d61db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,12 +15,46 @@ # # hadolint ignore=DL3026 -FROM registry.access.redhat.com/ubi9/openjdk-17:1.20-2 - +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 AS builder +ARG TEMP="/tmp/work" # Build parameters ARG IQ_SERVER_VERSION=1.184.0-01 -ARG IQ_SERVER_SHA256=92698a3b49378bd387b1bcff1ac64e1340c1f241a3eb918b5794a562185a68bd -ARG TEMP="/tmp/work" +ARG IQ_SERVER_SHA256_AARCH=8587685d51fa65a1c65a5341579bad8d5f21b2eb074b105d50c83703d37747a3 +ARG IQ_SERVER_SHA256_X86_64=7f48a5637e4071cb5e6ebbb692d2bc693f8b6e8919c5577eee12e7043f8244ca +ARG SONATYPE_WORK="/sonatype-work" + +# hadolint ignore=DL3041,DL3040 +RUN mkdir -p ${TEMP} && \ + microdnf update -y && \ + microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync git which crypto-policies crypto-policies-scripts + +# Copy config.yml and set sonatypeWork to the correct value +COPY config.yml ${TEMP} + +# hadolint ignore=DL4006,SC3060 +RUN cat ${TEMP}/config.yml | sed -r "s/\s*sonatypeWork\s*:\s*\"?[-0-9a-zA-Z_/\\]+\"?/sonatypeWork: ${SONATYPE_WORK//\//\\/}/" > ${TEMP}/config-edited.yml + +# Download the server bundle, verify its checksum, and extract the server jar to the install directory +WORKDIR ${TEMP} +# hadolint ignore=SC3010 +RUN if [[ "$(uname -m)" == "x86_64" ]]; then \ + echo "${IQ_SERVER_SHA256_X86_64} nexus-iq-server.tar.gz" > nexus-iq-server.tar.gz.sha256; \ + curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-linux-x86_64.tgz --output nexus-iq-server.tar.gz; \ + elif [[ "$(uname -m)" == "aarch64" ]]; then \ + echo "${IQ_SERVER_SHA256_AARCH} nexus-iq-server.tar.gz" > nexus-iq-server.tar.gz.sha256; \ + curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-linux-aarch_64.tgz --output nexus-iq-server.tar.gz; \ + else \ + echo "Unsupported architecture: $ARCH" && exit 1; \ + fi + +RUN sha256sum -c nexus-iq-server.tar.gz.sha256 \ + && tar -xvf nexus-iq-server.tar.gz \ + && mv nexus-iq-server-${IQ_SERVER_VERSION}-linux-* nexus-iq-server + +# hadolint ignore=DL3026 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 + +ARG IQ_SERVER_VERSION=1.184.0-01 ARG IQ_HOME="/opt/sonatype/nexus-iq-server" ARG SONATYPE_WORK="/sonatype-work" ARG CONFIG_HOME="/etc/nexus-iq-server" @@ -54,45 +88,36 @@ USER root # For testing # hadolint ignore=DL3041 RUN microdnf update -y \ -&& microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync git which \ +&& microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync git which crypto-policies crypto-policies-scripts \ && microdnf clean all # Create folders & set permissions -RUN mkdir -p ${TEMP} \ -&& mkdir -p ${IQ_HOME} \ +RUN mkdir -p ${IQ_HOME} \ && mkdir -p ${SONATYPE_WORK} \ && mkdir -p ${CONFIG_HOME} \ && mkdir -p ${LOGS_HOME} \ -&& chmod 0755 ${TEMP} \ && chmod 0755 "/opt/sonatype" ${IQ_HOME} \ && chmod 0755 ${CONFIG_HOME} \ && chmod 0755 ${LOGS_HOME} -# Copy config.yml and set sonatypeWork to the correct value -COPY config.yml ${TEMP} -# hadolint ignore=DL4006,SC3060 -RUN cat ${TEMP}/config.yml | sed -r "s/\s*sonatypeWork\s*:\s*\"?[-0-9a-zA-Z_/\\]+\"?/sonatypeWork: ${SONATYPE_WORK//\//\\/}/" > ${CONFIG_HOME}/config.yml \ -&& chmod 0644 ${CONFIG_HOME}/config.yml +# Copy config.yml +COPY --from=builder /tmp/work/config-edited.yml ${CONFIG_HOME}/config.yml +RUN chmod 0644 ${CONFIG_HOME}/config.yml + +# Copy server assemblies +COPY --from=builder /tmp/work/nexus-iq-server ${IQ_HOME} # Create start script RUN echo "trap 'kill -TERM \`cut -f1 -d@ ${SONATYPE_WORK}/lock\`; timeout ${TIMEOUT} tail --pid=\`cut -f1 -d@ ${SONATYPE_WORK}/lock\` -f /dev/null' SIGTERM" > ${IQ_HOME}/start.sh \ -&& echo "/usr/bin/java \${JAVA_OPTS} -jar nexus-iq-server-${IQ_SERVER_VERSION}.jar server ${CONFIG_HOME}/config.yml 2> ${LOGS_HOME}/stderr.log & " >> ${IQ_HOME}/start.sh \ +&& echo "/opt/sonatype/nexus-iq-server/bin/nexus-iq-server server ${CONFIG_HOME}/config.yml 2> ${LOGS_HOME}/stderr.log & " >> ${IQ_HOME}/start.sh \ && echo "wait" >> ${IQ_HOME}/start.sh \ && chmod 0755 ${IQ_HOME}/start.sh -# Download the server bundle, verify its checksum, and extract the server jar to the install directory -WORKDIR ${TEMP} -RUN curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz --output nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz \ -&& echo "${IQ_SERVER_SHA256} nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz" > nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz.sha256 \ -&& sha256sum -c nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz.sha256 \ -&& tar -xvf nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz \ -&& mv nexus-iq-server-${IQ_SERVER_VERSION}.jar ${IQ_HOME} -WORKDIR ${IQ_HOME} -RUN rm -rf ${TEMP} \ +WORKDIR ${IQ_HOME} + # Add group and user -&& groupadd -g ${GID} nexus \ +RUN groupadd -g ${GID} nexus \ && adduser -u ${UID} -d ${IQ_HOME} -c "Nexus IQ user" -g nexus -s /bin/false nexus \ -\ # Change owner to nexus user && chown -R nexus:nexus ${IQ_HOME} \ && chown -R nexus:nexus ${SONATYPE_WORK} \ @@ -116,7 +141,7 @@ HEALTHCHECK CMD curl --fail --silent --show-error http://localhost:8071/healthch # Change to nexus user USER nexus -ENV JAVA_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/sun.security.rsa=ALL-UNNAMED --add-opens=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xerces.internal.jaxp.datatype=ALL-UNNAMED -Djava.util.prefs.userRoot=${SONATYPE_WORK}/javaprefs" +ENV JAVA_OPTS=" -Djava.util.prefs.userRoot=${SONATYPE_WORK}/javaprefs " ENV SONATYPE_INTERNAL_HOST_SYSTEM=Docker WORKDIR ${IQ_HOME} diff --git a/Dockerfile.slim b/Dockerfile.slim index 3b1592b..ab20885 100644 --- a/Dockerfile.slim +++ b/Dockerfile.slim @@ -11,14 +11,49 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# # hadolint ignore=DL3026 -FROM registry.access.redhat.com/ubi9/openjdk-17:1.20-2 - +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 AS builder +ARG TEMP="/tmp/work" # Build parameters ARG IQ_SERVER_VERSION=1.184.0-01 -ARG IQ_SERVER_SHA256=92698a3b49378bd387b1bcff1ac64e1340c1f241a3eb918b5794a562185a68bd -ARG TEMP="/tmp/work" +ARG IQ_SERVER_SHA256_AARCH=8587685d51fa65a1c65a5341579bad8d5f21b2eb074b105d50c83703d37747a3 +ARG IQ_SERVER_SHA256_X86_64=7f48a5637e4071cb5e6ebbb692d2bc693f8b6e8919c5577eee12e7043f8244ca +ARG SONATYPE_WORK="/sonatype-work" + +# hadolint ignore=DL3041,DL3040 +RUN mkdir -p ${TEMP} && \ + microdnf update -y && \ + microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync git which crypto-policies crypto-policies-scripts + +# Copy config.yml and set sonatypeWork to the correct value +COPY config.yml ${TEMP} + +# hadolint ignore=DL4006,SC3060 +RUN cat ${TEMP}/config.yml | sed -r "s/\s*sonatypeWork\s*:\s*\"?[-0-9a-zA-Z_/\\]+\"?/sonatypeWork: ${SONATYPE_WORK//\//\\/}/" > ${TEMP}/config-edited.yml + +# Download the server bundle, verify its checksum, and extract the server jar to the install directory +WORKDIR ${TEMP} +# hadolint ignore=SC3010 +RUN if [[ "$(uname -m)" == "x86_64" ]]; then \ + echo "${IQ_SERVER_SHA256_X86_64} nexus-iq-server.tar.gz" > nexus-iq-server.tar.gz.sha256; \ + curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-linux-x86_64.tgz --output nexus-iq-server.tar.gz; \ + elif [[ "$(uname -m)" == "aarch64" ]]; then \ + echo "${IQ_SERVER_SHA256_AARCH} nexus-iq-server.tar.gz" > nexus-iq-server.tar.gz.sha256; \ + curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-linux-aarch_64.tgz --output nexus-iq-server.tar.gz; \ + else \ + echo "Unsupported architecture: $ARCH" && exit 1; \ + fi + +RUN sha256sum -c nexus-iq-server.tar.gz.sha256 \ + && tar -xvf nexus-iq-server.tar.gz \ + && mv nexus-iq-server-${IQ_SERVER_VERSION}-linux-* nexus-iq-server + +# hadolint ignore=DL3026 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 + +ARG IQ_SERVER_VERSION=1.184.0-01 ARG IQ_HOME="/opt/sonatype/nexus-iq-server" ARG SONATYPE_WORK="/sonatype-work" ARG CONFIG_HOME="/etc/nexus-iq-server" @@ -52,45 +87,36 @@ USER root # For testing # hadolint ignore=DL3041 RUN microdnf update -y \ -&& microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync which\ +&& microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs install -y procps gzip unzip tar shadow-utils findutils util-linux less rsync which crypto-policies crypto-policies-scripts \ && microdnf clean all # Create folders & set permissions -RUN mkdir -p ${TEMP} \ -&& mkdir -p ${IQ_HOME} \ +RUN mkdir -p ${IQ_HOME} \ && mkdir -p ${SONATYPE_WORK} \ && mkdir -p ${CONFIG_HOME} \ && mkdir -p ${LOGS_HOME} \ -&& chmod 0755 ${TEMP} \ && chmod 0755 "/opt/sonatype" ${IQ_HOME} \ && chmod 0755 ${CONFIG_HOME} \ && chmod 0755 ${LOGS_HOME} -# Copy config.yml and set sonatypeWork to the correct value -COPY config.yml ${TEMP} -# hadolint ignore=DL4006,SC3060 -RUN cat ${TEMP}/config.yml | sed -r "s/\s*sonatypeWork\s*:\s*\"?[-0-9a-zA-Z_/\\]+\"?/sonatypeWork: ${SONATYPE_WORK//\//\\/}/" > ${CONFIG_HOME}/config.yml \ -&& chmod 0644 ${CONFIG_HOME}/config.yml +# Copy config.yml +COPY --from=builder /tmp/work/config-edited.yml ${CONFIG_HOME}/config.yml +RUN chmod 0644 ${CONFIG_HOME}/config.yml + +# Copy server assemblies +COPY --from=builder /tmp/work/nexus-iq-server ${IQ_HOME} # Create start script RUN echo "trap 'kill -TERM \`cut -f1 -d@ ${SONATYPE_WORK}/lock\`; timeout ${TIMEOUT} tail --pid=\`cut -f1 -d@ ${SONATYPE_WORK}/lock\` -f /dev/null' SIGTERM" > ${IQ_HOME}/start.sh \ -&& echo "/usr/bin/java \${JAVA_OPTS} -jar nexus-iq-server-${IQ_SERVER_VERSION}.jar server ${CONFIG_HOME}/config.yml 2> ${LOGS_HOME}/stderr.log & " >> ${IQ_HOME}/start.sh \ +&& echo "/opt/sonatype/nexus-iq-server/bin/nexus-iq-server server ${CONFIG_HOME}/config.yml 2> ${LOGS_HOME}/stderr.log & " >> ${IQ_HOME}/start.sh \ && echo "wait" >> ${IQ_HOME}/start.sh \ && chmod 0755 ${IQ_HOME}/start.sh -# Download the server bundle, verify its checksum, and extract the server jar to the install directory -WORKDIR ${TEMP} -RUN curl -L https://download.sonatype.com/clm/server/nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz --output nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz \ -&& echo "${IQ_SERVER_SHA256} nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz" > nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz.sha256 \ -&& sha256sum -c nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz.sha256 \ -&& tar -xvf nexus-iq-server-${IQ_SERVER_VERSION}-bundle.tar.gz \ -&& mv nexus-iq-server-${IQ_SERVER_VERSION}.jar ${IQ_HOME} -WORKDIR ${IQ_HOME} -RUN rm -rf ${TEMP} \ +WORKDIR ${IQ_HOME} + # Add group and user -&& groupadd -g ${GID} nexus \ +RUN groupadd -g ${GID} nexus \ && adduser -u ${UID} -d ${IQ_HOME} -c "Nexus IQ user" -g nexus -s /bin/false nexus \ -\ # Change owner to nexus user && chown -R nexus:nexus ${IQ_HOME} \ && chown -R nexus:nexus ${SONATYPE_WORK} \ @@ -114,7 +140,7 @@ HEALTHCHECK CMD curl --fail --silent --show-error http://localhost:8071/healthch # Change to nexus user USER nexus -ENV JAVA_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/sun.security.rsa=ALL-UNNAMED --add-opens=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xerces.internal.jaxp.datatype=ALL-UNNAMED -Djava.util.prefs.userRoot=${SONATYPE_WORK}/javaprefs" +ENV JAVA_OPTS=" -Djava.util.prefs.userRoot=${SONATYPE_WORK}/javaprefs " ENV SONATYPE_INTERNAL_HOST_SYSTEM=Docker WORKDIR ${IQ_HOME} diff --git a/Jenkinsfile b/Jenkinsfile index b8e7199..d1eb3f9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,10 +29,12 @@ void configureBranchJob() { } String deployBranch = 'main' +String imageName = 'sonatype/nexus-iq-server' configureBranchJob() dockerizedBuildPipeline( deployBranch: deployBranch, + deployCondition: { return true }, // always run the deploy stage prepare: { githubStatusUpdate('pending') }, @@ -43,6 +45,14 @@ dockerizedBuildPipeline( def expectations = load 'expectations.groovy' validateExpectations(expectations.containerExpectations()) }, + deploy: { + // Hijacking deploy step to run the docker buildx build to make sure it is working + withSonatypeDockerRegistry() { + sh "docker buildx create --driver-opt=\"image=${sonatypeDockerRegistryId()}/moby/buildkit\" --use" + sh "docker buildx build --platform linux/amd64,linux/arm64 " + + "--tag ${sonatypeDockerRegistryId()}/${imageName}:${env.BUILD_NUMBER} ." + } + }, vulnerabilityScan: { def theStage = env.BRANCH_NAME == deployBranch ? 'build' : 'develop' nexusPolicyEvaluation( @@ -61,3 +71,4 @@ dockerizedBuildPipeline( } } ) + diff --git a/Jenkinsfile.release b/Jenkinsfile.release index 21bdd4c..887b283 100644 --- a/Jenkinsfile.release +++ b/Jenkinsfile.release @@ -29,15 +29,17 @@ properties([ String imageName = 'sonatype/nexus-iq-server' String version = '' -String checksum = '' +String checksumX86_64 = '' +String checksumAarch = '' dockerizedBuildPipeline( deployBranch: 'main', prepare: { githubStatusUpdate('pending') version = getVersionFromBuildName(env.releaseBuild_NAME) - checksum = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-bundle.tar.gz.sha256").trim() - updateIQServerVersionAndChecksum(version, checksum) + checksumX86_64 = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-linux-x86_64.tgz.sha256").trim() + checksumAarch = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-linux-aarch_64.tgz.sha256").trim() + updateIQServerVersionAndChecksum(version, checksumX86_64, checksumAarch) commitAndPushChanges(version) }, setVersion: { @@ -46,12 +48,6 @@ dockerizedBuildPipeline( lint: { hadolint(['./Dockerfile']) }, - postPrepareImage: { - dir('build') { - runSafely "docker save ${imageName} | gzip > docker-nexus-iq-server-${env.VERSION}.tar.gz" - } - }, - archiveArtifacts: 'build/*.tar.gz', buildAndTest: { currentBuild.displayName = "#${currentBuild.id} ${imageName}-${env.VERSION}" def expectations = load 'expectations.groovy' @@ -81,15 +77,17 @@ dockerizedBuildPipeline( } ) -void updateIQServerVersionAndChecksum(String version, String checksum) { +void updateIQServerVersionAndChecksum(String version, String checksumX86_64, String checksumAarch) { def dockerFile = readFile(file: 'Dockerfile') def metaShortVersionRegex = /(release=")(\d\.\d{1,3}\.\d)(" \\)/ def versionRegex = /(ARG IQ_SERVER_VERSION=)(\d\.\d{1,3}\.\d\-\d{2})/ - def shaRegex = /(ARG IQ_SERVER_SHA256=)([A-Fa-f0-9]{64})/ + def shaRegexX64_64 = /(ARG IQ_SERVER_SHA256_AARCH=)([A-Fa-f0-9]{64})/ + def shaRegexAarch = /(ARG IQ_SERVER_SHA256_X86_64=)([A-Fa-f0-9]{64})/ dockerFile = dockerFile.replaceAll(metaShortVersionRegex, "\$1${version.substring(0, version.indexOf('-'))}\$3") dockerFile = dockerFile.replaceAll(versionRegex, "\$1${version}") - dockerFile = dockerFile.replaceAll(shaRegex, "\$1${checksum}") + dockerFile = dockerFile.replaceAll(shaRegexX64_64, "\$1${checksumX86_64}") + dockerFile = dockerFile.replaceAll(shaRegexAarch, "\$1${checksumAarch}") writeFile(file: 'Dockerfile', text: dockerFile) } @@ -123,17 +121,11 @@ void pushImage(String imageName) { docker trust key load $NEXUS_IQ_SERVER_REPOSITORY_KEY docker trust key load $SONATYPE_KEY""" - // add signer - for this you need signers public key and repository keys password withCredentials([string(credentialsId: 'nexus-iq-server_dct_reg_pw', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { - runSafely "docker trust signer add sonatype ${imageName} --key $SONATYPE_PUB" - } - - runSafely "docker tag ${env.DOCKER_IMAGE_ID} ${imageName}:${env.VERSION}" - runSafely "docker tag ${env.DOCKER_IMAGE_ID} ${imageName}:latest" - - withCredentials([string(credentialsId: 'sonatype-password', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { - runSafely "docker image push ${imageName}:${env.VERSION}" - runSafely "docker image push ${imageName}:latest" + sh """ + docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit + ./build_and_push_images.sh ${env.VERSION} latest + """ } String response = runSafely("""curl -X POST https://hub.docker.com/v2/users/login/ \ diff --git a/Jenkinsfile.slim b/Jenkinsfile.slim index d22bf0d..088f5d9 100644 --- a/Jenkinsfile.slim +++ b/Jenkinsfile.slim @@ -29,10 +29,12 @@ void configureBranchJob() { } String deployBranch = 'main' +String imageName = 'sonatype/nexus-iq-server' configureBranchJob() dockerizedBuildPipeline( deployBranch: deployBranch, + deployCondition: { return true }, // always run the deploy stage prepare: { githubStatusUpdate('pending') }, @@ -44,6 +46,15 @@ dockerizedBuildPipeline( def expectations = load 'expectations.groovy' validateExpectations(expectations.containerExpectations()) }, + deploy: { + // Hijacking deploy step to run the docker buildx build to make sure it is working + withSonatypeDockerRegistry() { + sh "docker buildx create --driver-opt=\"image=${sonatypeDockerRegistryId()}/moby/buildkit\" --use" + sh "docker buildx build --platform linux/amd64,linux/arm64 " + + " -f Dockerfile.slim " + + "--tag ${sonatypeDockerRegistryId()}/${imageName}:${env.BUILD_NUMBER} ." + } + }, vulnerabilityScan: { def theStage = env.BRANCH_NAME == deployBranch ? 'build' : 'develop' nexusPolicyEvaluation( diff --git a/Jenkinsfile.slim.release b/Jenkinsfile.slim.release index 879c2c6..40fed78 100644 --- a/Jenkinsfile.slim.release +++ b/Jenkinsfile.slim.release @@ -29,15 +29,17 @@ properties([ String imageName = 'sonatype/nexus-iq-server' String version = '' -String checksum = '' +String checksumX86_64 = '' +String checksumAarch = '' dockerizedBuildPipeline( deployBranch: 'main', prepare: { githubStatusUpdate('pending') version = getVersionFromBuildName(env.releaseBuild_NAME) - checksum = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-bundle.tar.gz.sha256").trim() - updateIQServerVersionAndChecksum(version, checksum) + checksumX86_64 = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-linux-x86_64.tgz.sha256").trim() + checksumAarch = readBuildArtifact('insight/insight-brain/release', env.releaseBuild_NUMBER, "artifacts/nexus-iq-server-${version}-linux-aarch_64.tgz.sha256").trim() + updateIQServerVersionAndChecksum(version, checksumX86_64, checksumAarch) commitAndPushChanges(version) }, pathToDockerfile: './Dockerfile.slim', @@ -47,12 +49,6 @@ dockerizedBuildPipeline( lint: { hadolint(['./Dockerfile.slim']) }, - postPrepareImage: { - dir('build') { - runSafely "docker save ${imageName}-slim | gzip > docker-nexus-iq-server-slim-${env.VERSION}.tar.gz" - } - }, - archiveArtifacts: 'build/*.tar.gz', buildAndTest: { currentBuild.displayName = "#${currentBuild.id} ${imageName}-slim-${env.VERSION}" def expectations = load 'expectations.groovy' @@ -75,15 +71,17 @@ dockerizedBuildPipeline( } ) -void updateIQServerVersionAndChecksum(String version, String checksum) { - def dockerFile = readFile(file: 'Dockerfile.slim') +void updateIQServerVersionAndChecksum(String version, String checksumX86_64, String checksumAarch) { + def dockerFile = readFile(file: 'Dockerfile') def metaShortVersionRegex = /(release=")(\d\.\d{1,3}\.\d)(" \\)/ def versionRegex = /(ARG IQ_SERVER_VERSION=)(\d\.\d{1,3}\.\d\-\d{2})/ - def shaRegex = /(ARG IQ_SERVER_SHA256=)([A-Fa-f0-9]{64})/ + def shaRegexX64_64 = /(ARG IQ_SERVER_SHA256_AARCH=)([A-Fa-f0-9]{64})/ + def shaRegexAarch = /(ARG IQ_SERVER_SHA256_X86_64=)([A-Fa-f0-9]{64})/ dockerFile = dockerFile.replaceAll(metaShortVersionRegex, "\$1${version.substring(0, version.indexOf('-'))}\$3") dockerFile = dockerFile.replaceAll(versionRegex, "\$1${version}") - dockerFile = dockerFile.replaceAll(shaRegex, "\$1${checksum}") + dockerFile = dockerFile.replaceAll(shaRegexX64_64, "\$1${checksumX86_64}") + dockerFile = dockerFile.replaceAll(shaRegexAarch, "\$1${checksumAarch}") writeFile(file: 'Dockerfile.slim', text: dockerFile) } @@ -96,8 +94,8 @@ void commitAndPushChanges(String version) { runSafely "git diff --exit-code --cached || git commit -m 'Update IQ Server to ${version}.'" // pull and merge any new commits on main so that the push doesn't fail - runSafely 'git pull --no-rebase --no-edit origin main' - runSafely 'git push origin HEAD:main' + runSafely 'git pull --no-rebase --no-edit origin main' + runSafely 'git push origin HEAD:main' } } @@ -117,17 +115,11 @@ void pushImage(String imageName) { docker trust key load $NEXUS_IQ_SERVER_REPOSITORY_KEY docker trust key load $SONATYPE_KEY""" - // add signer - for this you need signers public key and repository keys password withCredentials([string(credentialsId: 'nexus-iq-server_dct_reg_pw', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { - runSafely "docker trust signer add sonatype ${imageName} --key $SONATYPE_PUB" - } - - runSafely "docker tag ${env.DOCKER_IMAGE_ID} ${imageName}:${env.VERSION}-slim" - runSafely "docker tag ${env.DOCKER_IMAGE_ID} ${imageName}:latest-slim" - - withCredentials([string(credentialsId: 'sonatype-password', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { - runSafely "docker image push ${imageName}:${env.VERSION}-slim" - runSafely "docker image push ${imageName}:latest-slim" + sh """ + docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit + ./build_and_push_images.sh ${env.VERSION} latest + """ } } } diff --git a/build_and_push_images.sh b/build_and_push_images.sh new file mode 100755 index 0000000..7e140c0 --- /dev/null +++ b/build_and_push_images.sh @@ -0,0 +1,65 @@ +#!/bin/bash - + +# This script expects that docker trust keys have already been loaded + +set -o nounset # Treat unset variables as an error + +# Enable for debugging +set -x + +# This is used by the notary tool for auth +export NOTARY_AUTH="$(printf "${DOCKERHUB_API_USERNAME}:${DOCKERHUB_API_PASSWORD}" | base64)" + +TRUST_DIR="${TRUST_DIR:-${HOME}/.docker/trust/}" + +# General args about the build +REPO="${OCI_REPO:-sonatype/nexus-iq-server}" +REF="${OCI_REGISTRY:-docker.io}/${REPO}" +TAGS="$@" +DOCKERFILE=${DOCKERFILE:-Dockerfile} + +ARM64_TAG=arm64-latest +AMD64_TAG=amd64-latest + +echo "Building images" +docker buildx build --progress=plain --platform=linux/arm64 -f ${DOCKERFILE} --push --provenance=false --tag "${REF}:${ARM64_TAG}" . +docker buildx build --progress=plain --platform=linux/amd64 -f ${DOCKERFILE} --push --provenance=false --tag "${REF}:${AMD64_TAG}" . + +for TAG in $TAGS; do + echo "Creating manifest" + docker manifest create "${REF}:${TAG}" "${REF}:${ARM64_TAG}" "${REF}:${AMD64_TAG}" --amend + + echo "Inspecting manifest" + docker manifest inspect "${REF}:${TAG}" + + echo "Pushing manifest" + docker manifest push "${REF}:${TAG}" --purge + + echo "Getting docker token" + DOCKER_TOKEN="$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${REPO}:pull" -H "Authorization: Basic ${NOTARY_AUTH}" | jq -r '.token')" + + echo "Parsing content-length and sha256 hash from manifest list response headers" + HEADERS=$(curl -I -s -H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' https://registry-1.docker.io/v2/${REPO}/manifests/${TAG} -H "Authorization: Bearer ${DOCKER_TOKEN}" -XGET) + BYTES_SIZE=$(echo "$HEADERS" | grep -i 'content-length' | awk '{print $2}' | tr -d '\r') + SHA_256=$(echo "$HEADERS" | grep -i 'docker-content-digest' | awk '{print $2}' | tr -d '\r' | sed 's/^sha256://') + + echo "Manifest SHA-256: ${SHA_256}" + echo "Manifest-inspect BYTES: ${BYTES_SIZE}" + echo "Sign ${SHA_256} with the notary" + + echo "Signing the manifest list" + # Notes: Through testing, this doesn't work with docker cli unless the roles is "targets". Other online sources had other values + echo "${DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE}" | notary -s https://notary.docker.io -d "${TRUST_DIR}" addhash "${REF}" "${TAG}" "${BYTES_SIZE}" --sha256 "${SHA_256}" --roles targets --publish --verbose + + docker trust inspect --pretty "${REF}:${TAG}" +done + +# Delete the temporary tags +HUB_TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d "{\"username\": \"${DOCKERHUB_API_USERNAME}\", \"password\": \"${DOCKERHUB_API_PASSWORD}\"}" https://hub.docker.com/v2/users/login/ | jq -r .token) +curl "https://hub.docker.com/v2/repositories/${REPO}/tags/${ARM64_TAG}" -H "Authorization: Bearer ${HUB_TOKEN}" -X DELETE +curl "https://hub.docker.com/v2/repositories/${REPO}/tags/${AMD64_TAG}" -H "Authorization: Bearer ${HUB_TOKEN}" -X DELETE + + +unset NOTARY_AUTH + +echo "Done!" From d2169e9f979d85c717add75916067d8ae317a118 Mon Sep 17 00:00:00 2001 From: Matt Johnson Date: Thu, 21 Nov 2024 08:06:00 -0500 Subject: [PATCH 2/3] Install notary --- Jenkinsfile.release | 5 +++++ Jenkinsfile.slim.release | 5 +++++ build_and_push_images.sh | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile.release b/Jenkinsfile.release index 887b283..abbc7cc 100644 --- a/Jenkinsfile.release +++ b/Jenkinsfile.release @@ -123,6 +123,11 @@ void pushImage(String imageName) { withCredentials([string(credentialsId: 'nexus-iq-server_dct_reg_pw', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { sh """ + curl -L https://go.dev/dl/go1.23.3.linux-amd64.tar.gz | tar -xzf - + export PATH=${env.PATH}:${env.WORKSPACE}/go/bin:${env.WORKSPACE}/bin + export GOPATH=${env.WORKSPACE} + go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary@latest + notary --help docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit ./build_and_push_images.sh ${env.VERSION} latest """ diff --git a/Jenkinsfile.slim.release b/Jenkinsfile.slim.release index 40fed78..fefa125 100644 --- a/Jenkinsfile.slim.release +++ b/Jenkinsfile.slim.release @@ -117,6 +117,11 @@ void pushImage(String imageName) { withCredentials([string(credentialsId: 'nexus-iq-server_dct_reg_pw', variable: 'DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE')]) { sh """ + curl -L https://go.dev/dl/go1.23.3.linux-amd64.tar.gz | tar -xzf - + export PATH=${env.PATH}:${env.WORKSPACE}/go/bin:${env.WORKSPACE}/bin + export GOPATH=${env.WORKSPACE} + go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary@latest + notary --help docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit ./build_and_push_images.sh ${env.VERSION} latest """ diff --git a/build_and_push_images.sh b/build_and_push_images.sh index 7e140c0..f392a9b 100755 --- a/build_and_push_images.sh +++ b/build_and_push_images.sh @@ -1,4 +1,19 @@ -#!/bin/bash - +#!/usr/bin/env bash +# +# Copyright (c) 2017-present Sonatype, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# # This script expects that docker trust keys have already been loaded From 4a6e9a15b3855df5d53a03a1a17ae8adbf03cfae Mon Sep 17 00:00:00 2001 From: Matt Johnson Date: Fri, 22 Nov 2024 11:33:06 -0500 Subject: [PATCH 3/3] Make script more portable. --- Jenkinsfile.release | 1 + Jenkinsfile.slim.release | 1 + build_and_push_images.sh | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile.release b/Jenkinsfile.release index abbc7cc..744b06c 100644 --- a/Jenkinsfile.release +++ b/Jenkinsfile.release @@ -129,6 +129,7 @@ void pushImage(String imageName) { go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary@latest notary --help docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit + export OCI_REPO="sonatype/nexus-iq-server" ./build_and_push_images.sh ${env.VERSION} latest """ } diff --git a/Jenkinsfile.slim.release b/Jenkinsfile.slim.release index fefa125..40a14df 100644 --- a/Jenkinsfile.slim.release +++ b/Jenkinsfile.slim.release @@ -123,6 +123,7 @@ void pushImage(String imageName) { go install -tags pkcs11 github.com/theupdateframework/notary/cmd/notary@latest notary --help docker buildx create --use --driver-opt image=${sonatypeDockerRegistryId()}/moby/buildkit + export OCI_REPO="sonatype/nexus-iq-server" ./build_and_push_images.sh ${env.VERSION} latest """ } diff --git a/build_and_push_images.sh b/build_and_push_images.sh index f392a9b..b4dd98d 100755 --- a/build_and_push_images.sh +++ b/build_and_push_images.sh @@ -28,7 +28,7 @@ export NOTARY_AUTH="$(printf "${DOCKERHUB_API_USERNAME}:${DOCKERHUB_API_PASSWORD TRUST_DIR="${TRUST_DIR:-${HOME}/.docker/trust/}" # General args about the build -REPO="${OCI_REPO:-sonatype/nexus-iq-server}" +REPO="${OCI_REPO}" REF="${OCI_REGISTRY:-docker.io}/${REPO}" TAGS="$@" DOCKERFILE=${DOCKERFILE:-Dockerfile}