diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index acacd49aacb..fc7514de6a7 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -2,15 +2,24 @@ @Library('apm@current') _ +import groovy.transform.Field + +/** + This is required to store the test suites we will use to trigger the E2E tests. +*/ +@Field def e2eTestSuites = [] + pipeline { agent none environment { - BASE_DIR = 'src/github.com/elastic/beats' + REPO = 'beats' + BASE_DIR = "src/github.com/elastic/${env.REPO}" JOB_GCS_BUCKET = 'beats-ci-artifacts' JOB_GCS_BUCKET_STASH = 'beats-ci-temp' JOB_GCS_CREDENTIALS = 'beats-ci-gcs-plugin' DOCKERELASTIC_SECRET = 'secret/observability-team/ci/docker-registry/prod' DOCKER_REGISTRY = 'docker.elastic.co' + GITHUB_CHECK_E2E_TESTS_NAME = 'E2E Tests' SNAPSHOT = "true" PIPELINE_LOG_LEVEL = "INFO" } @@ -119,6 +128,7 @@ pipeline { release() pushCIDockerImages() } + prepareE2ETestForPackage("${BEATS_FOLDER}") } } stage('Package Mac OS'){ @@ -149,6 +159,13 @@ pipeline { } } } + stage('Run E2E Tests for Packages'){ + agent { label 'ubuntu && immutable' } + options { skipDefaultCheckout() } + steps { + runE2ETests() + } + } } } } @@ -156,37 +173,32 @@ pipeline { def pushCIDockerImages(){ catchError(buildResult: 'UNSTABLE', message: 'Unable to push Docker images', stageResult: 'FAILURE') { - if ("${env.BEATS_FOLDER}" == "auditbeat"){ - tagAndPush('auditbeat-oss') - } else if ("${env.BEATS_FOLDER}" == "filebeat") { - tagAndPush('filebeat-oss') - } else if ("${env.BEATS_FOLDER}" == "heartbeat"){ + if (env?.BEATS_FOLDER?.endsWith('auditbeat')) { + tagAndPush('auditbeat') + } else if (env?.BEATS_FOLDER?.endsWith('filebeat')) { + tagAndPush('filebeat') + } else if (env?.BEATS_FOLDER?.endsWith('heartbeat')) { tagAndPush('heartbeat') - tagAndPush('heartbeat-oss') } else if ("${env.BEATS_FOLDER}" == "journalbeat"){ tagAndPush('journalbeat') - tagAndPush('journalbeat-oss') - } else if ("${env.BEATS_FOLDER}" == "metricbeat"){ - tagAndPush('metricbeat-oss') + } else if (env?.BEATS_FOLDER?.endsWith('metricbeat')) { + tagAndPush('metricbeat') } else if ("${env.BEATS_FOLDER}" == "packetbeat"){ tagAndPush('packetbeat') - tagAndPush('packetbeat-oss') - } else if ("${env.BEATS_FOLDER}" == "x-pack/auditbeat"){ - tagAndPush('auditbeat') } else if ("${env.BEATS_FOLDER}" == "x-pack/elastic-agent") { tagAndPush('elastic-agent') - } else if ("${env.BEATS_FOLDER}" == "x-pack/filebeat"){ - tagAndPush('filebeat') - } else if ("${env.BEATS_FOLDER}" == "x-pack/metricbeat"){ - tagAndPush('metricbeat') } } } -def tagAndPush(name){ +def tagAndPush(beatName){ def libbetaVer = sh(label: 'Get libbeat version', script: 'grep defaultBeatVersion ${BASE_DIR}/libbeat/version/version.go|cut -d "=" -f 2|tr -d \\"', returnStdout: true)?.trim() + def aliasVersion = "" if("${env.SNAPSHOT}" == "true"){ + aliasVersion = libbetaVer.substring(0, libbetaVer.lastIndexOf(".")) // remove third number in version + libbetaVer += "-SNAPSHOT" + aliasVersion += "-SNAPSHOT" } def tagName = "${libbetaVer}" @@ -194,29 +206,127 @@ def tagAndPush(name){ tagName = "pr-${env.CHANGE_ID}" } - def oldName = "${DOCKER_REGISTRY}/beats/${name}:${libbetaVer}" - def newName = "${DOCKER_REGISTRY}/observability-ci/${name}:${tagName}" - def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}:${env.GIT_BASE_COMMIT}" dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - retry(3){ - sh(label:'Change tag and push', script: """ - docker tag ${oldName} ${newName} - docker push ${newName} - docker tag ${oldName} ${commitName} - docker push ${commitName} - """) + + // supported image flavours + def variants = ["", "-oss", "-ubi8"] + variants.each { variant -> + doTagAndPush(beatName, variant, libbetaVer, tagName) + doTagAndPush(beatName, variant, libbetaVer, "${env.GIT_BASE_COMMIT}") + + if (!isPR() && aliasVersion != "") { + doTagAndPush(beatName, variant, libbetaVer, aliasVersion) + } + } +} + +/** +* @param beatName name of the Beat +* @param variant name of the variant used to build the docker image name +* @param sourceTag tag to be used as source for the docker tag command, usually under the 'beats' namespace +* @param targetTag tag to be used as target for the docker tag command, usually under the 'observability-ci' namespace +*/ +def doTagAndPush(beatName, variant, sourceTag, targetTag) { + def sourceName = "${DOCKER_REGISTRY}/beats/${beatName}${variant}:${sourceTag}" + def targetName = "${DOCKER_REGISTRY}/observability-ci/${beatName}${variant}:${targetTag}" + + def iterations = 0 + retryWithSleep(retries: 3, seconds: 5, backoff: true) { + iterations++ + def status = sh(label: "Change tag and push ${targetName}", script: """ + docker tag ${sourceName} ${targetName} + docker push ${targetName} + """, returnStatus: true) + + if ( status > 0 && iterations < 3) { + error("tag and push failed for ${beatName}, retry") + } else if ( status > 0 ) { + log(level: 'WARN', text: "${beatName} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") + } + } +} + +def prepareE2ETestForPackage(String beat){ + if ("${beat}" == "filebeat" || "${beat}" == "x-pack/filebeat") { + e2eTestSuites.push('fleet') + e2eTestSuites.push('helm') + } else if ("${beat}" == "metricbeat" || "${beat}" == "x-pack/metricbeat") { + e2eTestSuites.push('ALL') + echo("${beat} adds all test suites to the E2E tests job.") + } else if ("${beat}" == "x-pack/elastic-agent") { + e2eTestSuites.push('fleet') + } else { + echo("${beat} does not add any test suite to the E2E tests job.") + return } } def release(){ withBeatsEnv(){ - dir("${env.BEATS_FOLDER}") { - sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + withEnv([ + "DEV=true" + ]) { + dir("${env.BEATS_FOLDER}") { + sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + } } publishPackages("${env.BEATS_FOLDER}") } } +def runE2ETests(){ + if (e2eTestSuites.size() == 0) { + echo("Not triggering E2E tests for PR-${env.CHANGE_ID} because the changes does not affect the E2E.") + return + } + + def suites = '' // empty value represents all suites in the E2E tests + + catchError(buildResult: 'UNSTABLE', message: 'Unable to run e2e tests', stageResult: 'FAILURE') { + def suitesSet = e2eTestSuites.toSet() + + if (!suitesSet.contains('ALL')) { + suitesSet.each { suite -> + suites += "${suite}," + }; + } + + triggerE2ETests(suites) + } +} + +def triggerE2ETests(String suite) { + echo("Triggering E2E tests for PR-${env.CHANGE_ID}. Test suites: ${suite}.") + + def branchName = isPR() ? "${env.CHANGE_TARGET}" : "${env.JOB_BASE_NAME}.x" + def e2eTestsPipeline = "e2e-tests/e2e-testing-mbp/${branchName}" + + def parameters = [ + booleanParam(name: 'forceSkipGitChecks', value: true), + booleanParam(name: 'forceSkipPresubmit', value: true), + booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), + string(name: 'runTestsSuites', value: suite), + string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), + string(name: 'GITHUB_CHECK_REPO', value: env.REPO), + string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), + ] + if (isPR()) { + def version = "pr-${env.CHANGE_ID}" + parameters.push(booleanParam(name: 'USE_CI_SNAPSHOTS', value: true)) + parameters.push(string(name: 'ELASTIC_AGENT_VERSION', value: "${version}")) + parameters.push(string(name: 'METRICBEAT_VERSION', value: "${version}")) + } + + build(job: "${e2eTestsPipeline}", + parameters: parameters, + propagate: false, + wait: false + ) + + def notifyContext = "${env.GITHUB_CHECK_E2E_TESTS_NAME}" + githubNotify(context: "${notifyContext}", description: "${notifyContext} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${e2eTestsPipeline.replaceAll('/','+')}") +} + def withMacOSEnv(Closure body){ withEnvMask( vars: [ [var: "KEYCHAIN_PASS", password: getVaultSecret(secret: "secret/jenkins-ci/macos-codesign-keychain").data.password],