X-Git-Url: https://gerrit.nordix.org/gitweb?a=blobdiff_plain;f=jjb%2Fnsm%2FJenkinsfile;h=914dbbb6f53da4cc8c14a315a401ce8db4f0bf64;hb=e39bbd7c0a8fe75fdfed904ba5ee07a51fa8b1e0;hp=4362fc663ea2302429a1b893a3d78499e478d166;hpb=a0fc705bac0a746b1777b5fe04082e14a7581456;p=infra%2Fcicd.git diff --git a/jjb/nsm/Jenkinsfile b/jjb/nsm/Jenkinsfile index 4362fc66..914dbbb6 100644 --- a/jjb/nsm/Jenkinsfile +++ b/jjb/nsm/Jenkinsfile @@ -24,13 +24,18 @@ in_progress = 'In Progress.' completed = 'Completed.' failed = 'Failed' -node ('nordix-nsm-build-ubuntu1804') { +exception_message_exec = 'failed to execute the following command: ' +exception_message_code_generation = 'Generated code verification failed' + +node('nordix-nsm-build-ubuntu2204') { build_number = env.BUILD_NUMBER workspace = env.WORKSPACE ws("${workspace}/${build_number}") { def image_names = params.IMAGE_NAMES.split(' ') def version = params.IMAGE_VERSION def e2e_enabled = params.E2E_ENABLED + def helm_chart_upload = params.HELM_CHART_UPLOAD + def security_scan_enabled = params.SECURITY_SCAN_ENABLED def git_project = params.GIT_PROJECT def current_branch = params.CURRENT_BRANCH def default_branch = params.DEFAULT_BRANCH @@ -38,37 +43,58 @@ node ('nordix-nsm-build-ubuntu1804') { def image_registry = params.IMAGE_REGISTRY def local_version = "${env.JOB_NAME}-${build_number}" - stage('Clone/Checkout') { - git branch: default_branch, url: git_project - checkout([ - $class: 'GitSCM', - branches: [[name: current_branch]], - extensions: [], - userRemoteConfigs: [[ - refspec: '+refs/pull/*/head:refs/remotes/origin/pr/*', - url: git_project - ]] - ]) - sh 'git show' - } - stage('Verify') { + timeout(30) { + stage('Clone/Checkout') { + git branch: default_branch, url: git_project + checkout([ + $class: 'GitSCM', + branches: [[name: current_branch]], + extensions: [], + userRemoteConfigs: [[ + refspec: '+refs/pull/*/head:refs/remotes/origin/pr/*', + url: git_project + ]] + ]) + sh 'git show' + } Verify().call() - } - stage('Docker login') { - wrap([$class: 'MaskPasswordsBuildWrapper', varPasswordPairs: [[password: env.HARBOR_USERNAME, var: 'HARBOR_USERNAME'], [password: env.HARBOR_PASSWORD, var: 'HARBOR_PASSWORD'], [password: image_registry, var: 'IMAGE_REGISTRY']]]) { - sh '''#!/bin/bash -eu - echo ${HARBOR_PASSWORD} | docker login --username ${HARBOR_USERNAME} --password-stdin ${IMAGE_REGISTRY} - ''' + stage('Docker login') { + if (env.DRY_RUN != 'true') { + withCredentials([usernamePassword(credentialsId: 'nordix-cicd-harbor-credentials', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) { + sh '''#!/bin/bash -eu + echo $HARBOR_PASSWORD | docker login --username $HARBOR_USERNAME --password-stdin $IMAGE_REGISTRY + ''' + } + } else { + echo 'Docker login' + } + } + stage('Base Image') { + BaseImage(version, build_steps, image_registry, local_version).call() + } + stage('Images') { + Images(image_names, version, build_steps, image_registry, local_version).call() + if (currentBuild.result == 'FAILURE') { + Error('Failed to build image(s)').call() + } + } + stage('Helm Chart') { + HelmChart(helm_chart_upload, version).call() + } + stage('Security Scan') { + if (security_scan_enabled == true) { + SecurityScan(current_branch, version).call() + } else { + Utils.markStageSkippedForConditional('Security Scan') + } + } + stage('E2E') { + if (e2e_enabled == true) { + E2e(current_branch, version).call() + } else { + Utils.markStageSkippedForConditional('E2E') + } } - } - stage('Base Image') { - BaseImage(version, build_steps, image_registry, local_version).call() - } - stage('Images') { - Images(image_names, version, build_steps, image_registry, local_version).call() - } - stage('E2E') { - E2e(e2e_enabled).call() } stage('Cleanup') { Cleanup() @@ -76,17 +102,12 @@ node ('nordix-nsm-build-ubuntu1804') { } } -// Static analysis: Runs the GeneratedCode function and then UnitTests and Linter in parallel +// Verify the Generated code, UnitTests and Linter def Verify() { return { GeneratedCode().call() // cannot generate code and run the linter and tests at the same time - // Linter().call() - // UnitTests().call() - def stages = [:] - stages.put('Unit Tests', UnitTests()) - stages.put('Linter', Linter()) - // stages.put('Generated code verification', GeneratedCode()) - parallel(stages) + Linter().call() + UnitTests().call() } } @@ -95,16 +116,14 @@ def UnitTests() { return { def context = 'Unit Tests' stage('Unit Tests') { + def command = 'make test' try { SetBuildStatus(in_progress, context, pending) - sh ''' - . \${HOME}/.profile - make test - ''' + ExecSh(command).call() SetBuildStatus(completed, context, success) } catch (Exception e) { SetBuildStatus(failed, context, failure) - Error(e).call() + Error("${exception_message_exec} ${command}").call() } } } @@ -115,16 +134,14 @@ def Linter() { return { def context = 'Linter' stage('Linter') { + def command = 'make lint' try { SetBuildStatus(in_progress, context, pending) - sh ''' - . \${HOME}/.profile - make lint - ''' + ExecSh(command).call() SetBuildStatus(completed, context, success) } catch (Exception e) { SetBuildStatus(failed, context, failure) - Error(e).call() + Error("${exception_message_exec} ${command}").call() } } } @@ -138,56 +155,20 @@ def Linter() { def GeneratedCode() { return { def context = 'Generated code verification' - def exception_message = 'Generated code verification failed' - SetBuildStatus(in_progress, context, pending) - stage('go mod tidy') { - try { - sh ''' - . \${HOME}/.profile - go mod tidy - ''' - if (GetModifiedFiles() != '') { - throw new Exception(exception_message) - } - } catch (Exception e) { - SetBuildStatus(failed, context, failure) - sh 'git diff' - sh 'git status -s' - Error(e).call() - } - } - stage('go generate ./...') { + stage('Generated code verification') { + def command = 'make go-generate manifests generate-controller' try { - sh ''' - . \${HOME}/.profile - make generate - ''' + SetBuildStatus(in_progress, context, pending) + ExecSh(command).call() if (GetModifiedFiles() != '') { - throw new Exception(exception_message) + throw new Exception(exception_message_code_generation) } + SetBuildStatus(completed, context, success) } catch (Exception e) { SetBuildStatus(failed, context, failure) - sh 'git diff' - sh 'git status -s' - Error(e).call() + Error(exception_message_exec + command).call() } } - stage('Proto') { - // TODO: protoc version could be different - Utils.markStageSkippedForConditional('Proto') - // try { - // sh 'make proto' - // if (GetModifiedFiles() != '') { - // throw new Exception(exception_message) - // } - // } catch (Exception e) { - // SetBuildStatus(failed, context, failure) - // sh 'git diff' - // sh 'git status -s' - // Error(e).call() - // } - } - SetBuildStatus(completed, context, success) } } @@ -216,35 +197,86 @@ def Build(image, version, build_steps, registry, local_version) { def in_progress_message = "${in_progress} (${build_steps})" def completed_message = "${completed} (${build_steps})" def failed_message = "${failed} (${build_steps})" + def command = "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}" try { SetBuildStatus(in_progress_message, context, pending) - sh "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}" + ExecSh(command).call() SetBuildStatus(completed_message, context, success) } catch (Exception e) { SetBuildStatus(failed_message, context, failure) - Error(e).call() + unstable "${exception_message_exec} ${command}" + currentBuild.result = 'FAILURE' } } } } +// Generate and upload the helm chart +def HelmChart(helm_chart_upload, version) { + return { + parallel( + 'Helm Chart': { + stage('Generate Helm Chart') { + def context = 'Generate Helm Chart' + def command = "make generate-helm-chart VERSION=${version}" + try { + SetBuildStatus(in_progress, context, pending) + ExecSh(command).call() + SetBuildStatus(completed, context, success) + } catch (Exception e) { + SetBuildStatus(failed, context, failure) + Error("${exception_message_exec} ${command}").call() + } + } + stage('Upload Helm Chart') { + if (helm_chart_upload == true) { + withCredentials([string(credentialsId: 'nsm-nordix-artifactory-api-key', variable: 'API_KEY')]) { + ExecSh(""" + charts=\$(cd _output/helm/ && ls *.tgz) + for chart in \$charts + do + curl -H 'X-JFrog-Art-Api:${API_KEY}' -T _output/helm/\$chart \"https://artifactory.nordix.org/artifactory/cloud-native/meridio/\$chart\" + done + """).call() + } + } else { + Utils.markStageSkippedForConditional('Upload Helm Chart') + } + } + } + ) + } +} + +// Run the security scan job +def SecurityScan(current_branch, version) { + return { + build job: 'meridio-periodic-security-scan', parameters: [ + string(name: 'IMAGE_VERSION', value: "$version"), + string(name: 'CURRENT_BRANCH', value: "$current_branch"), + string(name: 'DRY_RUN', value: env.DRY_RUN) + ], wait: true + } +} + // Run the E2e Tests // Currently skipped -def E2e(e2e_enabled) { - if (e2e_enabled == 'true') { - return { - echo 'make e2e' // todo - } - } else { - return { - Utils.markStageSkippedForConditional('E2E') - } +def E2e(current_branch, version) { + return { + build job: 'meridio-e2e-test-kind', parameters: [ + string(name: 'MERIDIO_VERSION', value: "$version"), + string(name: 'TAPA_VERSION', value: "$version"), + string(name: 'CURRENT_BRANCH', value: "$current_branch"), + string(name: 'DRY_RUN', value: env.DRY_RUN) + ], wait: true } } // Raise error in Jenkins job def Error(e) { return { + sh 'git diff' + sh 'git status -s' Cleanup() error e } @@ -255,17 +287,33 @@ def Cleanup() { cleanWs() } +// Execute command +def ExecSh(command) { + return { + if (env.DRY_RUN != 'true') { + sh """ + . \${HOME}/.profile + ${command} + """ + } else { + echo "${command}" + } + } +} + // Set the commit status on Github // https://plugins.jenkins.io/github/#plugin-content-pipeline-examples def SetBuildStatus(String message, String context, String state) { - step([ - $class: 'GitHubCommitStatusSetter', - reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'], - commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()], - contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context], - errorHandlers: [[$class: 'ChangingBuildStatusErrorHandler', result: 'UNSTABLE']], - statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ] - ]) + if (env.DRY_RUN != 'true') { + step([ + $class: 'GitHubCommitStatusSetter', + reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'], + commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()], + contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context], + errorHandlers: [[$class: 'ShallowAnyErrorHandler']], // Prevent GitHubCommitStatusSetter to set the job status to unstable + statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ] + ]) + } } // Return the current commit sha