diff --git a/Jenkinsfile b/Jenkinsfile index d6c83281..7637a31d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,9 +17,23 @@ LINT_OUTPUT = '' SCAN_OUTPUT = '' IMAGE_SIZE = 0 RPMversion = '' +GRAVITON3_IMAGE_ARCHIVE = 'marklogic-image.tar' +builtImage = '' +publishImage = '' +latestTag = '' +upgradeDockerImage = '' // Define local funtions +/** + * Determines if the current build is for an ARM image type. + * ARM workers (e.g., Graviton3) are only available for selected MarkLogic versions (currently 11 and 12). + * @return true if dockerImageType contains 'arm', false otherwise. + */ +@NonCPS +def isArmImage() { + return params.dockerImageType.toLowerCase().contains('arm') +} /** * Loads email configuration from the KUBE_NINJAS_PIPELINE_EMAILS Jenkins secret file credential. * The credential file must contain key=value lines for 'emailList' and 'emailSecList'. @@ -38,11 +52,26 @@ Map loadEmailConfig() { return result } +/** + * Returns the build branch value used in image metadata and tests. + * PR builds use PR-; non-PR builds use the branch name. + */ +String getBuildBranchValue() { + if (env.CHANGE_ID?.trim()) { + return "PR-${env.CHANGE_ID.trim()}" + } + if (env.BRANCH_NAME?.trim()) { + return env.BRANCH_NAME.trim() + } + return (env.GIT_BRANCH ?: 'local').toString().trim() +} + /** * Performs pre-build checks: * - Initializes parameters as environment variables. * - Extracts Jira ID from branch name or PR title. * - Checks if the PR is a draft or has requested changes (for PR builds). + * - Validates ARM image types are only used with MarkLogic 11 and 12. */ void preBuildCheck() { // Initialize parameters as env variables (workaround for https://issues.jenkins-ci.org/browse/JENKINS-41929) @@ -199,6 +228,10 @@ void resultNotification(status) { * Sets RPM, CONVERTERS, and marklogicVersion global variables. */ void copyRPMs() { + // Determine architecture suffix based on image type + def archSuffix = dockerImageType.contains('arm') ? 'aarch64' : 'x86_64' + def armRhelSuffix = (marklogicVersion == "12") ? 'rhel' : 'rhel9' + if (marklogicVersion == "11") { //if dockerImageType contains "ubi9" then use nightly-rhel9 suffix if (dockerImageType.contains("ubi9")) { @@ -217,25 +250,39 @@ void copyRPMs() { else { error "Invalid value in marklogicVersion parameter." } + sh """ cd src - if [ -z ${env.ML_RPM} ]; then - wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-tierpoint/${RPMbranch}/server/MarkLogic-${RPMversion}${RPMsuffix}.x86_64.rpm + ARM_DATE=\$(TZ=America/Los_Angeles date +%Y%m%d) + if [ -z "${env.ML_RPM}" ]; then + if [ "${archSuffix}" = "aarch64" ]; then + wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-dev-tierpoint/${RPMbranch}/server-arm/MarkLogic-${RPMversion}.\${ARM_DATE}-${armRhelSuffix}.aarch64.rpm + else + wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-tierpoint/${RPMbranch}/server/MarkLogic-${RPMversion}${RPMsuffix}.${archSuffix}.rpm + fi else - wget --no-verbose ${ML_RPM} + wget --no-verbose "${env.ML_RPM}" fi - if [ -z ${env.ML_CONVERTERS}]; then - wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-tierpoint/${RPMbranch}/converters/MarkLogicConverters-${RPMversion}${RPMsuffix}.x86_64.rpm + if [ -n "${env.ML_CONVERTERS}" ]; then + wget --no-verbose "${env.ML_CONVERTERS}" else - wget --no-verbose ${ML_CONVERTERS} + if [ "${archSuffix}" = "aarch64" ]; then + wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-dev-tierpoint/${RPMbranch}/converters-arm/MarkLogicConverters-${RPMversion}.\${ARM_DATE}-${armRhelSuffix}.aarch64.rpm + else + wget --no-verbose https://bed-artifactory.bedford.progress.com:443/artifactory/ml-rpm-tierpoint/${RPMbranch}/converters/MarkLogicConverters-${RPMversion}${RPMsuffix}.${archSuffix}.rpm + fi fi """ script { - // Get the RPM and Converters file names - RPM = sh(returnStdout: true, script: 'cd src;file MarkLogic-*.rpm | cut -d: -f1').trim() - CONVERTERS = sh(returnStdout: true, script: 'cd src;file MarkLogicConverters-*.rpm | cut -d: -f1').trim() - // Extract MarkLogic version from RPM file name - marklogicVersion = sh(returnStdout: true, script: "echo ${RPM}| awk -F \"MarkLogic-\" '{print \$2;}' | awk -F \".x86_64.rpm\" '{print \$1;}' | awk -F \"-rhel\" '{print \$1;}' ").trim() + // Get the RPM and Converters file names for the correct architecture (archSuffix already defined above) + // Use newest files so we don't accidentally pick a stale RPM left from a previous run. + RPM = sh(returnStdout: true, script: "cd src; ls -1t MarkLogic-*.${archSuffix}.rpm 2>/dev/null | head -1").trim() + CONVERTERS = sh(returnStdout: true, script: "cd src; (ls -1t MarkLogicConverters-*.${archSuffix}.rpm 2>/dev/null || ls -1t MarkLogicConverters-*.rpm 2>/dev/null) | head -1").trim() + // Extract MarkLogic version from RPM file name (handle both x86_64 and aarch64) + marklogicVersion = sh(returnStdout: true, script: "echo ${RPM} | awk -F 'MarkLogic-' '{print \$2;}' | awk -F '.x86_64.rpm' '{print \$1;}' | awk -F '.aarch64.rpm' '{print \$1;}' | awk -F '-rhel' '{print \$1;}'").trim() + echo "Selected server RPM: ${RPM}" + echo "Selected converters RPM: ${CONVERTERS}" + echo "Derived MarkLogic version from RPM: ${marklogicVersion}" } } @@ -249,10 +296,13 @@ void buildDockerImage() { publishImage="marklogic/marklogic-server-${dockerImageType}:${marklogicVersion}-${env.dockerImageType}" mlVerShort=marklogicVersion.split("\\.")[0] latestTag="marklogic/marklogic-server-${dockerImageType}:latest-${mlVerShort}" - timeStamp = new Date().format('yyyyMMdd') + // Use Los Angeles time (same as ARM_DATE in copyRPMs) to ensure consistency across UTC/PST boundaries + timeStamp = sh(returnStdout: true, script: "TZ=America/Los_Angeles date +%Y%m%d").trim() timestamptedTag = builtImage.replace('nightly', timeStamp) - sh "make build docker_image_type=${dockerImageType} dockerTag=${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} marklogicVersion=${marklogicVersion} dockerVersion=${env.dockerVersion} build_branch=${env.BRANCH_NAME} package=${RPM} converters=${CONVERTERS}" + def buildBranchValue = getBuildBranchValue() + sh "make build docker_image_type=${dockerImageType} dockerTag=${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} marklogicVersion=${marklogicVersion} dockerVersion=${env.dockerVersion} build_branch=${buildBranchValue} package=${RPM} converters=${CONVERTERS}" currentBuild.displayName = "#${BUILD_NUMBER}: ${marklogicVersion}-${env.dockerImageType} (${env.dockerVersion})" + echo "Built image: ${builtImage}" } /** @@ -265,6 +315,10 @@ void pullUpgradeDockerImage() { sh """ echo 'dockerImageType is set to ubi-rootless, skipping this stage and Docker upgrade test.' """ + } else if (isArmImage()) { + sh """ + echo 'ARM image type detected. Skipping upgrade test (no previous ARM images available for upgrade testing).' + """ } else { if (upgradeDockerImage != "" ) { sh """ @@ -285,10 +339,17 @@ void pullUpgradeDockerImage() { * Runs container structure tests using the 'make structure-test' target. */ void structureTests() { + def buildBranchValue = getBuildBranchValue() sh """ - #install container-structure-test 1.16.0 binary - curl -s -LO https://storage.googleapis.com/container-structure-test/v1.16.0/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && mv container-structure-test-linux-amd64 container-structure-test - make structure-test current_image=marklogic/marklogic-server-${dockerImageType}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} marklogicVersion=${marklogicVersion} dockerVersion=${env.dockerVersion} build_branch=${env.BRANCH_NAME} docker_image_type=${env.dockerImageType} Jenkins=true + #install container-structure-test 1.16.0 binary (detect architecture) + ARCH=\$(uname -m) + if [ "\$ARCH" = "aarch64" ]; then + PLATFORM="arm64" + else + PLATFORM="amd64" + fi + curl -s -LO https://storage.googleapis.com/container-structure-test/v1.16.0/container-structure-test-linux-\${PLATFORM} && chmod +x container-structure-test-linux-\${PLATFORM} && mv container-structure-test-linux-\${PLATFORM} container-structure-test + make structure-test current_image=marklogic/marklogic-server-${dockerImageType}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} marklogicVersion=${marklogicVersion} dockerVersion=${env.dockerVersion} build_branch=${buildBranchValue} docker_image_type=${env.dockerImageType} Jenkins=true """ } @@ -296,8 +357,9 @@ void structureTests() { * Runs Docker functional tests using the 'make docker-tests' target. */ void dockerTests() { + def buildBranchValue = getBuildBranchValue() sh "make docker-test-ids" - sh "make docker-tests current_image=marklogic/marklogic-server-${dockerImageType}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} upgrade_image=${upgradeDockerImage} marklogicVersion=${marklogicVersion} build_branch=${env.BRANCH_NAME} dockerVersion=${env.dockerVersion} docker_image_type=${dockerImageType} DOCKER_TEST_LIST=\"${params.DOCKER_TEST_LIST}\"" + sh "make docker-tests current_image=marklogic/marklogic-server-${dockerImageType}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} upgrade_image=${upgradeDockerImage} marklogicVersion=${marklogicVersion} build_branch=${buildBranchValue} dockerVersion=${env.dockerVersion} docker_image_type=${dockerImageType} DOCKER_TEST_LIST=\"${params.DOCKER_TEST_LIST}\"" } /** @@ -346,39 +408,23 @@ void vulnerabilityScan() { * Requires Artifactory and Azure ACR credentials. */ void publishToInternalRegistry() { + // Use the discovered image tag if available (handles date/time mismatches across day boundaries) + def imageToPublish = env.IMAGE_TO_PUBLISH ?: builtImage + echo "Publishing image: ${imageToPublish}" + withCredentials([usernamePassword(credentialsId: 'builder-credentials-artifactory', passwordVariable: 'docker_password', usernameVariable: 'docker_user')]) { sh """ docker logout ${dockerRegistry} echo "${docker_password}" | docker login --username ${docker_user} --password-stdin ${dockerRegistry} - docker tag ${builtImage} ${dockerRegistry}/${builtImage} - docker tag ${builtImage} ${dockerRegistry}/${publishImage} - docker tag ${builtImage} ${dockerRegistry}/${latestTag} - docker tag ${builtImage} ${dockerRegistry}/${timestamptedTag} - docker push ${dockerRegistry}/${builtImage} + docker tag ${imageToPublish} ${dockerRegistry}/${publishImage} + docker tag ${imageToPublish} ${dockerRegistry}/${latestTag} + docker tag ${imageToPublish} ${dockerRegistry}/${timestamptedTag} docker push ${dockerRegistry}/${publishImage} docker push ${dockerRegistry}/${latestTag} docker push ${dockerRegistry}/${timestamptedTag} """ } - // Publish to private ECR repository that is used by the performance team. (only ML11) - // (disabled since it's not needed) - // if ( params.marklogicVersion == "11" ) { - // withCredentials( [[ - // $class: 'AmazonWebServicesCredentialsBinding', - // credentialsId: "aws-engineering-ct-ecr", - // accessKeyVariable: 'AWS_ACCESS_KEY_ID', - // secretKeyVariable: 'AWS_SECRET_ACCESS_KEY' - // ]]) { - // sh """ - // aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 713759029616.dkr.ecr.us-west-2.amazonaws.com - // docker tag ${builtImage} 713759029616.dkr.ecr.us-west-2.amazonaws.com/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - // docker tag ${builtImage} 713759029616.dkr.ecr.us-west-2.amazonaws.com/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType} - // docker push 713759029616.dkr.ecr.us-west-2.amazonaws.com/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - // docker push 713759029616.dkr.ecr.us-west-2.amazonaws.com/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType} - // """ - // } - // } // Publish to private ACR repositories that are used by PDC. if ( params.marklogicVersion == "12" ) { @@ -386,49 +432,48 @@ void publishToInternalRegistry() { withCredentials([usernamePassword(credentialsId: 'PDC_SANDBOX_USER', passwordVariable: 'docker_password', usernameVariable: 'docker_user')]) { sh """ echo "${docker_password}" | docker login --username ${docker_user} --password-stdin ${pdcSbRegistry} - docker tag ${builtImage} ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - docker tag ${builtImage} ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType} + docker tag ${imageToPublish} ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} + docker tag ${imageToPublish} ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType} docker push ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} docker push ${pdcSbRegistry}/ml-docker-nightly:${marklogicVersion}-${env.dockerImageType} """ } } - if ( params.marklogicVersion == "11" || params.marklogicVersion == "12" ) { - // Publish to Dev PDC registry - withCredentials([usernamePassword(credentialsId: 'pdc-azure-cr', passwordVariable: 'docker_password', usernameVariable: 'docker_user')]) { - sh """ - echo "${docker_password}" | docker login --username ${docker_user} --password-stdin ${pdcDevRegistry} - docker tag ${builtImage} ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - docker tag ${builtImage} ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType} - docker push ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - docker push ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType} - """ - } + + // Publish to Dev PDC registry + withCredentials([usernamePassword(credentialsId: 'pdc-azure-cr', passwordVariable: 'docker_password', usernameVariable: 'docker_user')]) { + sh """ + echo "${docker_password}" | docker login --username ${docker_user} --password-stdin ${pdcDevRegistry} + docker tag ${imageToPublish} ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} + docker tag ${imageToPublish} ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType} + docker push ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} + docker push ${pdcDevRegistry}/marklogicdb-custom:${marklogicVersion}-${env.dockerImageType} + """ + } + + // Publish to Kubernetes ECR for testing on EKS + withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', + credentialsId: 'KUBE_NINJAS_OPS_AWS_JENKINS', + accessKeyVariable: 'AWS_ACCESS_KEY_ID', + secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { + // Resolve account ID via STS - no account number is hardcoded in this file. + def awsAccountId = sh(returnStdout: true, + script: 'aws sts get-caller-identity --region us-west-1 --query Account --output text').trim() + def kubeNinjasEcrRegistry = "${awsAccountId}.dkr.ecr.us-west-1.amazonaws.com" + def ecrRepo = "${kubeNinjasEcrRegistry}/jenkins-kube-ninjas/marklogic-server-${dockerImageType}" + sh """ + aws ecr get-login-password --region us-west-1 | \\ + docker login --username AWS --password-stdin ${kubeNinjasEcrRegistry} + docker tag ${imageToPublish} ${ecrRepo}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} + docker tag ${imageToPublish} ${ecrRepo}:latest-${mlVerShort} + docker push ${ecrRepo}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} + docker push ${ecrRepo}:latest-${mlVerShort} + """ } - if ( params.marklogicVersion == "12" ) { - // Publish to Kubernetes ECR for testing on EKS - withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', - credentialsId: 'KUBE_NINJAS_OPS_AWS_JENKINS', - accessKeyVariable: 'AWS_ACCESS_KEY_ID', - secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { - // Resolve account ID via STS — no account number is hardcoded in this file. - def awsAccountId = sh(returnStdout: true, - script: 'aws sts get-caller-identity --region us-west-1 --query Account --output text').trim() - def kubeNinjasEcrRegistry = "${awsAccountId}.dkr.ecr.us-west-1.amazonaws.com" - def ecrRepo = "${kubeNinjasEcrRegistry}/jenkins-kube-ninjas/marklogic-server-${dockerImageType}" - sh """ - aws ecr get-login-password --region us-west-1 | \\ - docker login --username AWS --password-stdin ${kubeNinjasEcrRegistry} - docker tag ${builtImage} ${ecrRepo}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - docker tag ${builtImage} ${ecrRepo}:latest-${mlVerShort} - docker push ${ecrRepo}:${marklogicVersion}-${env.dockerImageType}-${env.dockerVersion} - docker push ${ecrRepo}:latest-${mlVerShort} - """ - } - } currentBuild.description = "Published" } + /** * Triggers a BlackDuck scan job for the published image. * Runs asynchronously (wait: false). @@ -475,11 +520,7 @@ void scapScan() { } pipeline { - agent { - label { - label 'cld-docker' - } - } + agent none options { checkoutToSubdirectory '.' buildDiscarder logRotator(artifactDaysToKeepStr: '7', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '') @@ -497,31 +538,39 @@ pipeline { 00 02 * * * % marklogicVersion=12;dockerImageType=ubi-rootless;SCAP_SCAN=true 00 02 * * * % marklogicVersion=12;dockerImageType=ubi9 00 02 * * * % marklogicVersion=12;dockerImageType=ubi9-rootless;SCAP_SCAN=true - 30 05 * * 7 % marklogicVersion=11;dockerImageType=ubi;DOCKER_TEST_LIST=Initialized MarkLogic container with latency - 00 06 * * 7 % marklogicVersion=12;dockerImageType=ubi;DOCKER_TEST_LIST=Initialized MarkLogic container with latency''' : '') - } + 00 07 * * 7 % marklogicVersion=11;dockerImageType=ubi;DOCKER_TEST_LIST=D05 Initialized MarkLogic container with latency;PUBLISH_IMAGE=false + 00 08 * * 7 % marklogicVersion=12;dockerImageType=ubi;DOCKER_TEST_LIST=D05 Initialized MarkLogic container with latency;PUBLISH_IMAGE=false + 00 05 * * * % marklogicVersion=11;dockerImageType=ubi9-arm;GRAVITON3_AGENT=true + 30 05 * * * % marklogicVersion=11;dockerImageType=ubi9-rootless-arm;SCAP_SCAN=true;GRAVITON3_AGENT=true + 00 06 * * * % marklogicVersion=12;dockerImageType=ubi9-arm;GRAVITON3_AGENT=true + 30 06 * * * % marklogicVersion=12;dockerImageType=ubi9-rootless-arm;SCAP_SCAN=true;GRAVITON3_AGENT=true + 00 09 * * 7 % marklogicVersion=11;dockerImageType=ubi9-arm;DOCKER_TEST_LIST=D05 Initialized MarkLogic container with latency;PUBLISH_IMAGE=false;GRAVITON3_AGENT=true + 00 10 * * 7 % marklogicVersion=12;dockerImageType=ubi9-arm;DOCKER_TEST_LIST=D05 Initialized MarkLogic container with latency;PUBLISH_IMAGE=false;GRAVITON3_AGENT=true''' : '') + } environment { QA_LICENSE_KEY = credentials('QA_LICENSE_KEY') } parameters { string(name: 'dockerVersion', defaultValue: '2.2.6', description: 'ML Docker version. This value is used as part of the Docker image tag, which is built as ${marklogicVersion}-${dockerImageType}-${dockerVersion}', trim: true) - choice(name: 'dockerImageType', choices: 'ubi-rootless\nubi\nubi9-rootless\nubi9', description: 'Platform type for Docker image. Will be made part of the docker image tag') + choice(name: 'dockerImageType', choices: 'ubi-rootless\nubi\nubi9-rootless\nubi9\nubi9-arm\nubi9-rootless-arm', description: 'Platform type for Docker image. Will be made part of the docker image tag') string(name: 'upgradeDockerImage', defaultValue: '', description: 'Docker image for testing upgrades. Defaults to ubi image if left blank.\n Currently upgrading to ubi-rootless is not supported hence the test is skipped when ubi-rootless image is provided.', trim: true) choice(name: 'marklogicVersion', choices: '12\n11', description: 'MarkLogic Server Branch. used to pick appropriate rpm') string(name: 'ML_RPM', defaultValue: '', description: 'URL for RPM to be used for Image creation. \n If left blank nightly ML rpm will be used.\n Please provide Jenkins accessible path e.g. /project/engineering or /project/qa', trim: true) string(name: 'ML_CONVERTERS', defaultValue: '', description: 'URL for the converters RPM to be included in the image creation \n If left blank the nightly ML Converters Package will be used.', trim: true) booleanParam(name: 'PUBLISH_IMAGE', defaultValue: false, description: 'Publish image to internal registry') booleanParam(name: 'TEST_STRUCTURE', defaultValue: true, description: 'Run container structure tests') - booleanParam(name: 'DOCKER_TESTS', defaultValue: true, description: 'Run docker tests') - string(name: 'DOCKER_TEST_LIST', defaultValue: '', description: 'Comma separated list of test names to run (e.g Test one, Test two). Leave empty to run all tests.', trim: true) + booleanParam(name: 'DOCKER_TESTS', defaultValue: true, description: 'Run docker tests') + string(name: 'DOCKER_TEST_LIST', defaultValue: '', description: 'Comma separated list of test names to run (e.g Test one, Test two). Leave empty to run all tests.', trim: true) booleanParam(name: 'SCAP_SCAN', defaultValue: false, description: 'Run Open SCAP scan on the image.') + booleanParam(name: 'GRAVITON3_AGENT', defaultValue: false, description: '[ARM only] Run ARM-only stages on Graviton3 agent') string(name: 'emailList', defaultValue: '', description: 'Optional override for the build notification email list. If left blank, the list is loaded from the KUBE_NINJAS_PIPELINE_EMAILS Jenkins credential file. Specify a comma-separated list only to send notifications to additional or different recipients for a specific build run.', trim: true) } stages { // Stage: Remove stale test results from previous builds stage('Clean-Previous-Results') { + agent { node { label 'cld-docker' } } steps { sh ''' rm -f container-structure-test.xml @@ -532,41 +581,59 @@ pipeline { // Stage: Perform initial checks (PR status, Jira ID) stage('Pre-Build-Check') { + agent { node { label 'cld-docker' } } steps { preBuildCheck() } } - // Stage: Download MarkLogic Server and Converters RPMs + // Stage: Download MarkLogic Server and Converters RPMs (ARM builds on x86) stage('Copy-RPMs') { + agent { node { label 'cld-docker' } } steps { copyRPMs() + stash name: 'rpms', includes: 'src/*.rpm' } } // Stage: Build the Docker image + // Save image archive to workspace and stash for cross-agent stages. stage('Build-Image') { + agent { node { label 'cld-docker' } } steps { + unstash 'rpms' buildDockerImage() + script { + // Always save image for cases where agents might differ + sh """ + echo "Saving ${builtImage} to ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}..." + docker image save ${builtImage} -o ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE} + ls -lh ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE} + """ + stash name: 'built-image-archive', includes: "${GRAVITON3_IMAGE_ARCHIVE}", allowEmpty: false + } } } // Stage: Pull the base image needed for upgrade testing stage('Pull-Upgrade-Image') { + agent { node { label 'cld-docker' } } steps { pullUpgradeDockerImage() } } - // Stage: Lint Dockerfile and startup scripts + // Stage: Lint Dockerfile and startup scripts (x86 only) stage('Lint') { + agent { node { label 'cld-docker' } } steps { lint() } } - // Stage: Scan the image for vulnerabilities + // Stage: Scan the image for vulnerabilities (x86 only) stage('Scan') { + agent { node { label 'cld-docker' } } steps { echo 'Skipping vulnerability scan due to compatibility issues.' // vulnerabilityScan() @@ -575,43 +642,156 @@ pipeline { // Stage: Run OpenSCAP compliance scan (conditional) stage('SCAP-Scan') { + agent { + node { + label isArmImage() ? 'cld-docker-graviton' : 'cld-docker' + } + } when { + beforeAgent true expression { return params.SCAP_SCAN } } steps { + script { + unstash 'built-image-archive' + // Load image from tar if not already available (applies to all build types) + def imageSource = "${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}" + sh """ + if ! docker image inspect ${builtImage} &>/dev/null; then + echo "Loading image from ${imageSource} for SCAP scan..." + docker image load -i ${imageSource} + else + echo "Image ${builtImage} already available locally" + fi + """ + } scapScan() + stash name: 'scap-results', includes: 'scap/**', allowEmpty: true + } + } + + // Stage: Load image from tar archive (ARM builds only) + stage('Load-Image') { + agent { label 'cld-docker-graviton' } + when { + beforeAgent true + expression { return isArmImage() && params.GRAVITON3_AGENT } + } + steps { + script { + unstash 'built-image-archive' + sh """ + if ! docker image inspect ${builtImage} &>/dev/null; then + echo "Loading image from ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}..." + docker image load -i ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE} + else + echo "Image ${builtImage} already loaded (likely by SCAP-Scan stage)" + fi + docker images | head -5 + """ + } } } // Stage: Run container structure tests (conditional) stage('Structure-Tests') { + agent { + node { + label isArmImage() ? 'cld-docker-graviton' : 'cld-docker' + } + } when { + beforeAgent true expression { return params.TEST_STRUCTURE } } steps { + script { + unstash 'built-image-archive' + // Load image from tar if not already available (applies to all build types) + def imageSource = "${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}" + sh """ + if ! docker image inspect ${builtImage} &>/dev/null; then + echo "Loading image from ${imageSource} for Structure-Tests..." + docker image load -i ${imageSource} + else + echo "Image ${builtImage} already available locally" + fi + """ + } structureTests() + stash name: 'structure-test-results', includes: 'container-structure-test.xml', allowEmpty: true } } // Stage: Run Docker functional tests (conditional) stage('Docker-Run-Tests') { + agent { + node { + label isArmImage() ? 'cld-docker-graviton' : 'cld-docker' + } + } when { + beforeAgent true expression { return params.DOCKER_TESTS } } steps { + script { + unstash 'built-image-archive' + // Load image from tar if not already available (applies to all build types) + def imageSource = "${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}" + sh """ + if ! docker image inspect ${builtImage} &>/dev/null; then + echo "Loading image from ${imageSource} for Docker-Run-Tests..." + docker image load -i ${imageSource} + else + echo "Image ${builtImage} already available locally" + fi + """ + } dockerTests() + stash name: 'docker-test-results', includes: 'test/test_results/**', allowEmpty: true } } // Stage: Publish image to internal registries (conditional) stage('Publish-Image') { + agent { node { label 'cld-docker' } } when { + beforeAgent true anyOf { branch 'develop' expression { return params.PUBLISH_IMAGE } } } steps { + script { + unstash 'built-image-archive' + // Load image from tar if not already available (applies to all build types) + sh """ + if ! docker image inspect ${builtImage} &>/dev/null; then + echo "Image not found locally, loading from ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE}..." + docker image load -i ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE} + else + echo "Image ${builtImage} already available locally" + fi + """ + + // If builtImage doesn't exist, find the loaded image by repo pattern + def actualImage = sh( + returnStdout: true, + script: """docker images --format 'table {{.Repository}}:{{.Tag}}' | grep "marklogic/marklogic-server-${dockerImageType}:" | head -1""" + ).trim() + + if (!actualImage) { + actualImage = builtImage + echo "Using builtImage tag: ${actualImage}" + } else { + echo "Found loaded image: ${actualImage}" + } + + // Store for use in publishToInternalRegistry + env.IMAGE_TO_PUBLISH = actualImage + } publishToInternalRegistry() // Trigger downstream QA image build job build job: 'KubeNinjas/docker/docker-nightly-builds-qa', wait: false, parameters: [string(name: 'dockerImageType', value: "${dockerImageType}"), string(name: 'marklogicVersion', value: "${RPMversion}")] @@ -620,6 +800,7 @@ pipeline { // Stage: Trigger BlackDuck security scan (conditional) stage('BlackDuck-Scan') { + agent { node { label 'cld-docker' } } when { anyOf { branch 'develop' @@ -631,31 +812,78 @@ pipeline { } } + // Stage: Cleanup ARM agent (ARM builds only) + stage('Cleanup-ARM') { + agent { label 'cld-docker-graviton' } + when { + beforeAgent true + expression { return isArmImage() && params.GRAVITON3_AGENT } + } + steps { + sh ''' + echo "Cleaning up ARM agent..." + # Stop all running containers + docker stop $(docker ps -a -q) || true + # Docker cleanup + docker system prune --force --all --volumes + docker system df + ''' + } + } + } post { always { - // Clean up the workspace and Docker resources - sh ''' - cd src - rm -rf *.rpm NOTICE.txt - docker stop $(docker ps -a -q) || true - docker system prune --force --all --volumes - docker system df - ''' - publishTestResults() + node('cld-docker') { + // Clean up the workspace and Docker resources + sh """ + # Remove any stale test artifacts before unstash + rm -rf test/test_results scap container-structure-test.xml + # Remove ARM image tar archive + rm -f ${WORKSPACE}/${GRAVITON3_IMAGE_ARCHIVE} + # Remove ARM image if it was built + if [ -n "${builtImage}" ]; then + docker rmi ${builtImage} || true + fi + # Clean up RPMs + if [ -d src ]; then + cd src + rm -rf *.rpm NOTICE.txt + cd .. + fi + # Docker cleanup applies to both agents + docker stop \$(docker ps -a -q) || true + docker system prune --force --all --volumes + docker system df + """ + script { + try { unstash 'structure-test-results' } catch (e) { echo 'No structure test results to unstash.' } + try { unstash 'docker-test-results' } catch (e) { echo 'No docker test results to unstash.' } + try { unstash 'scap-results' } catch (e) { echo 'No SCAP results to unstash.' } + } + publishTestResults() + } } success { - resultNotification('✅ Success') + node('cld-docker') { + resultNotification('✅ Success') + } } failure { - resultNotification('❌ Failure') + node('cld-docker') { + resultNotification('❌ Failure') + } } unstable { - resultNotification('⚠️ Unstable') + node('cld-docker') { + resultNotification('⚠️ Unstable') + } } aborted { - resultNotification('🚫 Aborted') - } + node('cld-docker') { + resultNotification('🚫 Aborted') } + } + } } \ No newline at end of file diff --git a/Makefile b/Makefile index df6451a0..4045cdcd 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -# Copyright © 2018-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +# Copyright © 2018-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. dockerTag?=internal package?=MarkLogic.rpm repo_dir=marklogic -docker_build_options=--compress --platform linux/amd64 +docker_build_options=--compress build_branch?=local docker_image_type?=ubi upgrade_docker_image_type?=ubi @@ -11,6 +11,17 @@ current_image?=${repo_dir}/marklogic-server-${docker_image_type}:${dockerTag} # Latest release tag can be found here: https://github.com/ComplianceAsCode/content/releases open_scap_version?=0.1.79 +#*************************************************************************** +# set docker platform based on the docker image type +#*************************************************************************** +ifeq ($(findstring arm,$(docker_image_type)),arm) + docker_build_options += --platform linux/arm64 + export DOCKER_PLATFORM=linux/arm64 +else + docker_build_options += --platform linux/amd64 + export DOCKER_PLATFORM=linux/amd64 +endif + #*************************************************************************** # build docker image #*************************************************************************** @@ -18,6 +29,13 @@ build: # NOTICE file need to be in the build context to be included in the built image cp NOTICE.txt src/NOTICE.txt +# Install ARM64 emulation support on Linux (assuming Jenkins environment which is not aarch64) +ifeq ($(findstring arm,$(docker_image_type)),arm) +ifeq ($(shell uname -s),Linux) + docker run --privileged --rm tonistiigi/binfmt --install arm64 +endif +endif + # rootless images use the same dependencies as ubi image so we copy the file ifeq ($(docker_image_type),ubi9) cp dockerFiles/marklogic-server-ubi\:base dockerFiles/marklogic-server-ubi9\:base @@ -27,10 +45,15 @@ ifeq ($(findstring rootless,$(docker_image_type)),rootless) cp dockerFiles/marklogic-deps-ubi9\:base dockerFiles/marklogic-deps-ubi9-rootless\:base cp dockerFiles/marklogic-server-ubi-rootless\:base dockerFiles/marklogic-server-ubi9-rootless\:base endif +# ubi9-rootless-arm needs deps from ubi9-arm and server template from ubi-rootless +ifeq ($(docker_image_type),ubi9-rootless-arm) + cp dockerFiles/marklogic-deps-ubi9-arm\:base dockerFiles/marklogic-deps-ubi9-rootless-arm\:base + cp dockerFiles/marklogic-server-ubi-rootless\:base dockerFiles/marklogic-server-ubi9-rootless-arm\:base +endif # retrieve and copy open scap hardening script ifeq ($(findstring rootless,$(docker_image_type)),rootless) - [ -f scap-security-guide-${open_scap_version}.zip ] || curl -Lo scap-security-guide-${open_scap_version}.zip https://github.com/ComplianceAsCode/content/releases/download/v${open_scap_version}/scap-security-guide-${open_scap_version}.zip + ([ -f scap-security-guide-${open_scap_version}.zip ] && unzip -t scap-security-guide-${open_scap_version}.zip > /dev/null 2>&1) || (rm -f scap-security-guide-${open_scap_version}.zip && curl -Lso scap-security-guide-${open_scap_version}.zip https://github.com/ComplianceAsCode/content/releases/download/v${open_scap_version}/scap-security-guide-${open_scap_version}.zip) #UBI9 needs a different version of the remediation script ifeq ($(findstring ubi9,$(docker_image_type)),ubi9) unzip -p scap-security-guide-${open_scap_version}.zip scap-security-guide-${open_scap_version}/bash/rhel9-script-cis.sh > src/rhel-script-cis.sh @@ -45,7 +68,7 @@ endif cd src/; docker build ${docker_build_options} -t "${repo_dir}/marklogic-server-${docker_image_type}:${dockerTag}" --build-arg BASE_IMAGE=${repo_dir}/marklogic-deps-${docker_image_type}:${dockerTag} --build-arg ML_RPM=${package} --build-arg ML_USER=marklogic_user --build-arg ML_DOCKER_VERSION=${dockerVersion} --build-arg ML_VERSION=${marklogicVersion} --build-arg ML_CONVERTERS=${converters} --build-arg BUILD_BRANCH=${build_branch} --build-arg ML_DOCKER_TYPE=${docker_image_type} -f ../dockerFiles/marklogic-server-${docker_image_type}:base . # remove temporary files - rm -f dockerFiles/marklogic-deps-ubi-rootless\:base dockerFiles/marklogic-deps-ubi9-rootless\:base dockerFiles/marklogic-server-ubi9-rootless\:base dockerFiles/marklogic-server-ubi9\:base src/NOTICE.txt src/rhel-script-cis.sh + rm -f dockerFiles/marklogic-deps-ubi-rootless\:base dockerFiles/marklogic-deps-ubi9-rootless\:base dockerFiles/marklogic-server-ubi9-rootless\:base dockerFiles/marklogic-server-ubi9\:base dockerFiles/marklogic-deps-ubi9-rootless-arm\:base dockerFiles/marklogic-server-ubi9-rootless-arm\:base src/NOTICE.txt src/rhel-script-cis.sh #*************************************************************************** # strcture test docker images @@ -139,15 +162,21 @@ endif # security scan docker images #*************************************************************************** scap-scan: + # Clean up any existing scap-scan container from previous runs + docker rm -f scap-scan 2>/dev/null || true mkdir -p scap - [ -f scap-security-guide-${open_scap_version}.zip ] || curl -Lo scap-security-guide-${open_scap_version}.zip https://github.com/ComplianceAsCode/content/releases/download/v${open_scap_version}/scap-security-guide-${open_scap_version}.zip + ([ -f scap-security-guide-${open_scap_version}.zip ] && unzip -t scap-security-guide-${open_scap_version}.zip > /dev/null 2>&1) || (rm -f scap-security-guide-${open_scap_version}.zip && curl -Lso scap-security-guide-${open_scap_version}.zip https://github.com/ComplianceAsCode/content/releases/download/v${open_scap_version}/scap-security-guide-${open_scap_version}.zip) #UBI9 needs a different version of the evaluation profile ifeq ($(findstring ubi9,$(current_image)),ubi9) unzip -p scap-security-guide-${open_scap_version}.zip scap-security-guide-${open_scap_version}/ssg-rhel9-ds.xml > scap/ssg-rhel-ds.xml else unzip -p scap-security-guide-${open_scap_version}.zip scap-security-guide-${open_scap_version}/ssg-rhel8-ds.xml > scap/ssg-rhel-ds.xml endif - docker run -itd --name scap-scan -v $(PWD)/scap:/scap ${current_image} + docker run -itd --name scap-scan --entrypoint /bin/bash -v $(PWD)/scap:/scap ${current_image} -c "sleep infinity" + # Wait a moment for container to be fully up + sleep 2 + # Verify container is running + docker ps | grep scap-scan || (docker logs scap-scan; exit 1) docker exec -u root scap-scan /bin/bash -c "microdnf update -y; microdnf install -y openscap-scanner" # ensure the file is owned by root in order to avoid permission issues docker exec -u root scap-scan /bin/bash -c "chown root:root /scap/ssg-rhel-ds.xml" diff --git a/NOTICE.txt b/NOTICE.txt index 116953d9..ede40c0f 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,6 +1,6 @@ MarkLogic® Docker Container Image v2 -Copyright © 2018-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +Copyright © 2018-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. This project is licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at diff --git a/dockerFiles/marklogic-deps-ubi9-arm:base b/dockerFiles/marklogic-deps-ubi9-arm:base new file mode 100644 index 00000000..7fc89fde --- /dev/null +++ b/dockerFiles/marklogic-deps-ubi9-arm:base @@ -0,0 +1,30 @@ +############################################################### +# +# Copyright © 2018-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +# +############################################################### + +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7-1775623882 +LABEL "com.marklogic.maintainer"="docker@marklogic.com" + +############################################################### +# install libnsl rpm package +############################################################### + +RUN microdnf -y upgrade glibc \ + && rpm -i --nodeps https://download.rockylinux.org/pub/rocky/9.8/BaseOS/aarch64/os/Packages/l/libnsl-2.34-270.el9_8.aarch64.rpm \ + && microdnf clean all + +############################################################### +# install gdb and dependencies for stack traces, networking, base deps and tzdata for timezone +############################################################### +# hadolint ignore=DL3006 +RUN echo "NETWORKING=yes" > /etc/sysconfig/network \ + && microdnf -y install --setopt install_weak_deps=0 gdb python3-rpm nss libcap procps-ng python3 libtool-ltdl cpio initscripts tzdata glibc libstdc++ util-linux hostname \ + && microdnf clean all + + +############################################################### +# Enable FIPS Mode +############################################################### +RUN update-crypto-policies --set FIPS \ No newline at end of file diff --git a/dockerFiles/marklogic-server-ubi-rootless:base b/dockerFiles/marklogic-server-ubi-rootless:base index 708c9349..c2b9b2cf 100644 --- a/dockerFiles/marklogic-server-ubi-rootless:base +++ b/dockerFiles/marklogic-server-ubi-rootless:base @@ -59,9 +59,15 @@ RUN touch /etc/marklogic.conf \ # Add TINI to serve as PID 1 process ############################################################### ENV TINI_VERSION=v0.19.0 -ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini -RUN chown ${ML_USER}:users /tini \ - && chmod +x /tini +ARG ML_DOCKER_TYPE=ubi +RUN if [ "${ML_DOCKER_TYPE}" = "ubi9-rootless-arm" ]; then \ + TINI_BIN="tini-arm64"; \ + else \ + TINI_BIN="tini"; \ + fi && \ + curl -fsSL https://github.com/krallin/tini/releases/download/${TINI_VERSION}/${TINI_BIN} -o /tini && \ + chown ${ML_USER}:users /tini && \ + chmod +x /tini ############################################################### # second stage for flattening layers @@ -149,7 +155,17 @@ RUN touch /.dockerenv \ ############################################################### WORKDIR / COPY ${ML_CONVERTERS} /tmp/converters.rpm -RUN chown ${ML_USER}:users /tmp/converters.rpm +RUN if [ -s /tmp/converters.rpm ]; then chown ${ML_USER}:users /tmp/converters.rpm; else rm -f /tmp/converters.rpm; fi + +############################################################### +# Configure GDB for debugging and set capabilities for non-root usage +############################################################### +RUN microdnf -y install libcap \ + && setcap cap_sys_ptrace+ep $(readlink -f /usr/bin/gdb) \ + && echo "set auto-load safe-path /" > /home/${ML_USER}/.gdbinit \ + && chown ${ML_USER}:users /home/${ML_USER}/.gdbinit \ + && chmod 644 /home/${ML_USER}/.gdbinit \ + && microdnf clean all ############################################################### # Configure GDB for debugging and set capabilities for non-root usage diff --git a/dockerFiles/marklogic-server-ubi9-arm:base b/dockerFiles/marklogic-server-ubi9-arm:base new file mode 100644 index 00000000..c91a3b96 --- /dev/null +++ b/dockerFiles/marklogic-server-ubi9-arm:base @@ -0,0 +1,151 @@ +############################################################### +# +# Copyright © 2018-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +# +############################################################### + +ARG BASE_IMAGE=marklogic-ubi/marklogic-deps-ubi9-arm:11-internal +FROM ${BASE_IMAGE} AS builder + +############################################################### +# set build args +############################################################### + +ARG ML_RPM=marklogic.rpm +ARG ML_USER="marklogic_user" +ARG ML_VERSION=11-internal +ARG ML_CONVERTERS=marklogic.converters +#################################################### +# inject init, start and clustering scripts +############################################################### + +COPY scripts/start-marklogic.sh /usr/local/bin/start-marklogic.sh + +############################################################### +# install MarkLogic server, sudo, and remove mlcmd packages +############################################################### +COPY ${ML_RPM} /tmp/marklogic-server.rpm +RUN rpm -i /tmp/marklogic-server.rpm \ + && rm /tmp/marklogic-server.rpm \ + && microdnf -y install --setopt install_weak_deps=0 sudo \ + && microdnf -y clean all \ + && rm -rf ./opt/MarkLogic/mlcmd/lib/* \ + && rm -rf ./opt/MarkLogic/mlcmd/ext/* + +############################################################### +# Add TINI to serve as PID 1 process +############################################################### +ENV TINI_VERSION=v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-arm64 /tini +RUN chmod +x /tini + +############################################################### +# Copy converters package +############################################################### +WORKDIR / +COPY ${ML_CONVERTERS} converters.rpm +############################################################### +# create system user +############################################################### + +RUN adduser --gid users --uid 1000 ${ML_USER} \ + && echo ${ML_USER}" ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +############################################################### +# second stage for flattening layers +############################################################### +FROM ${BASE_IMAGE} + +COPY --from=builder / / + +ARG ML_USER="marklogic_user" +ARG ML_VERSION=11-internal +ARG ML_DOCKER_VERSION=local +ARG BUILD_BRANCH=local +ARG ML_DOCKER_TYPE=ubi +############################################################### +# define docker labels +############################################################### + +LABEL "com.marklogic.maintainer"="docker@marklogic.com" +LABEL "com.marklogic.name"="MarkLogic Server ${ML_VERSION}" +LABEL "com.marklogic.docker-version"="${ML_DOCKER_VERSION}" +LABEL "com.marklogic.release-version"="${ML_VERSION}" +LABEL "com.marklogic.build-branch"="${BUILD_BRANCH}" +LABEL "com.marklogic"="MarkLogic" +LABEL "com.marklogic.release-type"="production" +LABEL "com.marklogic.license"="MarkLogic EULA" +LABEL "com.marklogic.license.description"="By subscribing to this product, you agree to the terms and conditions outlined in MarkLogic's End User License Agreement (EULA) here https://developer.marklogic.com/eula " +LABEL "com.marklogic.license.url"="https://developer.marklogic.com/eula" +LABEL "com.marklogic.description"="MarkLogic is the only Enterprise NoSQL database. It is a new generation database built with a flexible data model to store, manage, and search JSON, XML, RDF, and more - without sacrificing enterprise features such as ACID transactions, certified security, backup, and recovery. With these capabilities, MarkLogic is ideally suited for making heterogeneous data integration simpler and faster, and for delivering dynamic content at massive scale. The current release of the MarkLogic Server Developer Docker image includes all features and is limited to developer use." +LABEL docker.cmd="docker run -it -p 7997-8010:7997-8010 -e MARKLOGIC_INIT=true -e MARKLOGIC_ADMIN_USERNAME= -e MARKLOGIC_ADMIN_PASSWORD= --mount src=MarkLogic,dst=/var/opt/MarkLogic progressofficial/marklogic-db:${ML_VERSION}" + +############################################################### +# copy notice file +############################################################### +COPY --chown=${ML_USER}:users NOTICE.txt /home/${ML_USER}/NOTICE.txt + +############################################################### +# set env vars +############################################################### + +ENV MARKLOGIC_INSTALL_DIR=/opt/MarkLogic \ + MARKLOGIC_DATA_DIR=/var/opt/MarkLogic \ + MARKLOGIC_USER=${ML_USER} \ + MARKLOGIC_PID_FILE=/var/run/MarkLogic.pid \ + MARKLOGIC_UMASK=022 \ + LD_LIBRARY_PATH=/lib64:$LD_LIBRARY_PATH:/opt/MarkLogic/lib \ + MARKLOGIC_VERSION="${ML_VERSION}" \ + MARKLOGIC_DOCKER_VERSION="${ML_DOCKER_VERSION}" \ + MARKLOGIC_IMAGE_TYPE="$ML_DOCKER_TYPE" \ + MARKLOGIC_BOOTSTRAP_HOST=bootstrap \ + MARKLOGIC_ADMIN_USERNAME_FILE=mldb_admin_user \ + MARKLOGIC_ADMIN_PASSWORD_FILE=mldb_password_user \ + MARKLOGIC_WALLET_PASSWORD_FILE=mldb_wallet_password \ + BUILD_BRANCH=${BUILD_BRANCH} \ + MARKLOGIC_JOIN_TLS_ENABLED=false \ + OVERWRITE_ML_CONF=true \ + MARKLOGIC_EC2_HOST=0 + +################################################################ +# Set Timezone +################################################################ + +RUN microdnf -y reinstall tzdata + +############################################################### +# Remove optional packages that have known vulnerabilities +# (Excluding python/gdb dependencies needed for stack traces) +RUN for package in vim-minimal cups-client cups-libs tar avahi-libs binutils libarchive binutils-gold; \ + do rpm -e --nodeps $package || true; \ + done; + +############################################################### +# expose MarkLogic server ports +############################################################### + +EXPOSE 25 7997-8010 + +############################################################### +# set system user +############################################################### + +USER ${ML_USER} + +#################################################### +# Set Linux Language Settings +############################################################### + +ENV LANG=en_US.UTF-8 +ENV LC_ALL=C.UTF-8 + +############################################################### +# define volume for persistent MarkLogic server data +############################################################### + +VOLUME /var/opt/MarkLogic + +############################################################### +# set entrypoint +############################################################### +ENTRYPOINT ["/tini", "--", "/usr/local/bin/start-marklogic.sh"] diff --git a/test/keywords.resource b/test/keywords.resource index c24ff194..b224842a 100644 --- a/test/keywords.resource +++ b/test/keywords.resource @@ -8,7 +8,8 @@ Library Collections Library DateTime *** Variables *** -@{DOCKER DEFAULTS} -it -d -p 8000:8000 -p 8001:8001 -p 8002:8002 -p7997:7997 --platform linux/amd64 +${DOCKER_PLATFORM} %{DOCKER_PLATFORM=linux/amd64} +@{DOCKER DEFAULTS} -it -d -p 8000:8000 -p 8001:8001 -p 8002:8002 -p7997:7997 --platform ${DOCKER_PLATFORM} ${DEFAULT ADMIN USER} test_admin ${DEFAULT ADMIN PASS} test_admin_pass ${SPEC CHARS ADMIN PASS} Admin@2$s%^&*! @@ -50,7 +51,7 @@ Create container with latency ... --name ${container name} ... --cap-add NET_ADMIN --entrypoint /bin/bash ... ${TEST_IMAGE} - ... -c sudo microdnf -y install iproute iptables && sudo curl -s -O https://download.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os/Packages/i/iproute-tc-6.2.0-6.el8_10.x86_64.rpm && sudo rpm -i iproute-tc-6.2.0-6.el8_10.x86_64.rpm && sudo tc qdisc add dev lo root netem delay 30000ms && sudo tc qdisc show dev lo && /tini -- /usr/local/bin/start-marklogic.sh + ... -c sudo microdnf -y install iproute iptables && (sudo microdnf -y install iproute-tc || (sudo curl -fsS -O https://download.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os/Packages/i/iproute-tc-6.2.0-6.el8_10.x86_64.rpm && sudo rpm -Uvh --replacepkgs iproute-tc-6.2.0-6.el8_10.x86_64.rpm)) && ((command -v tc >/dev/null && sudo tc qdisc add dev lo root netem delay 30000ms && sudo tc qdisc show dev lo) || ([ -x /usr/sbin/tc ] && sudo /usr/sbin/tc qdisc add dev lo root netem delay 30000ms && sudo /usr/sbin/tc qdisc show dev lo) || ([ -x /sbin/tc ] && sudo /sbin/tc qdisc add dev lo root netem delay 30000ms && sudo /sbin/tc qdisc show dev lo)) && /tini -- /usr/local/bin/start-marklogic.sh ... stderr=test_results/stderr-${container name}.txt ... stdout=test_results/stdout-${container name}.txt ... timeout=15000 @@ -84,14 +85,16 @@ Create test container with Create upgrade container with [Arguments] @{input parameters} [Documentation] Creates a second test container for upgrade testing. + Skip If 'arm' in '${IMAGE_TYPE}' msg=Skipping upgrade test for ARM image (no previous ARM upgrade image is available). + Should Not Be Empty ${UPGRADE_TEST_IMAGE} msg=UPGRADE_TEST_IMAGE is empty; cannot run upgrade container. ${container name}= Remove spaces from ${TEST NAME} - Run Process docker run @{DOCKER DEFAULTS} @{input parameters} + ${result}= Run Process docker run @{DOCKER DEFAULTS} @{input parameters} ... --name ${container name}-2 ... --mount ${VOL_INFO} ${UPGRADE_TEST_IMAGE} ... stderr=test_results/stderr-${container name}-2.txt ... stdout=test_results/stdout-${container name}-2.txt ... timeout=${DOCKER TIMEOUT} - File Should Be Empty test_results/stderr-${container name}-2.txt + Should Be Equal As Integers ${result.rc} 0 Docker log should contain *Cluster config complete, marking this container as ready.* True Stop container