| /* |
| Copyright (c) 2022 Nordix Foundation |
| |
| 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. |
| */ |
| |
| import org.jenkinsci.plugins.pipeline.modeldefinition.Utils |
| |
| pending = 'PENDING' |
| success = 'SUCCESS' |
| failure = 'FAILURE' |
| base_image = 'base-image' |
| in_progress = 'In Progress.' |
| completed = 'Completed.' |
| failed = 'Failed' |
| |
| 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 |
| def build_steps = params.BUILD_STEPS |
| def image_registry = params.IMAGE_REGISTRY |
| def local_version = "${env.JOB_NAME}-${build_number}" |
| |
| 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') { |
| 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('Cleanup') { |
| Cleanup() |
| } |
| } |
| } |
| |
| // 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() |
| } |
| } |
| |
| // Runs the unit tests and set the github commit status |
| def UnitTests() { |
| return { |
| def context = 'Unit Tests' |
| stage('Unit Tests') { |
| def command = 'make test' |
| 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() |
| } |
| } |
| } |
| } |
| |
| // Runs the linter and set the github commit status |
| def Linter() { |
| return { |
| def context = 'Linter' |
| stage('Linter') { |
| def command = 'make lint' |
| 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() |
| } |
| } |
| } |
| } |
| |
| // Check if code has been generated correctly and set the github commit status: |
| // go.mod: runs "go mod tidy" |
| // go generate ./...: Code should be generated using "make genrate" command |
| // proto: skipped due to version of protoc |
| // If files are generated correctly then GetModifiedFiles function should return an empty string |
| def GeneratedCode() { |
| return { |
| def context = 'Generated code verification' |
| stage('Generated code verification') { |
| def command = 'make go-generate manifests generate-controller' |
| try { |
| SetBuildStatus(in_progress, context, pending) |
| ExecSh(command).call() |
| if (GetModifiedFiles() != '') { |
| throw new Exception(exception_message_code_generation) |
| } |
| SetBuildStatus(completed, context, success) |
| } catch (Exception e) { |
| SetBuildStatus(failed, context, failure) |
| Error(exception_message_exec + command).call() |
| } |
| } |
| } |
| } |
| |
| def BaseImage(version, build_steps, registry, local_version) { |
| return { |
| Build(base_image, version, build_steps, registry, local_version).call() |
| } |
| } |
| |
| // Call Build function for every images in parallel |
| def Images(images, version, build_steps, registry, local_version) { |
| return { |
| def stages = [:] |
| for (i in images) { |
| stages.put(i, Build(i, version, build_steps, registry, local_version)) |
| } |
| parallel(stages) |
| } |
| } |
| |
| // Build set the github commit status |
| def Build(image, version, build_steps, registry, local_version) { |
| return { |
| stage("${image} (${version}): ${build_steps}") { |
| def context = "Image: ${image}" |
| 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) |
| ExecSh(command).call() |
| SetBuildStatus(completed_message, context, success) |
| } catch (Exception e) { |
| SetBuildStatus(failed_message, context, failure) |
| 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(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 |
| } |
| } |
| |
| // Cleanup directory |
| 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) { |
| 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 |
| def GetCommitSha() { |
| return sh(script: 'git rev-parse HEAD', returnStdout: true).trim() |
| } |
| |
| // Returns if any files has been modified/added/removed |
| def GetModifiedFiles() { |
| return sh(script: 'git status -s', returnStdout: true).trim() |
| } |