2 Copyright (c) 2022 Nordix Foundation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
17 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
22 base_image = 'base-image'
23 in_progress = 'In Progress.'
24 completed = 'Completed.'
27 exception_message_exec = 'failed to execute the following command: '
28 exception_message_code_generation = 'Generated code verification failed'
30 node('nordix-nsm-build-ubuntu2204') {
31 build_number = env.BUILD_NUMBER
32 workspace = env.WORKSPACE
33 ws("${workspace}/${build_number}") {
34 def image_names = params.IMAGE_NAMES.split(' ')
35 def version = params.IMAGE_VERSION
36 def e2e_enabled = params.E2E_ENABLED
37 def helm_chart_upload = params.HELM_CHART_UPLOAD
38 def security_scan_enabled = params.SECURITY_SCAN_ENABLED
39 def git_project = params.GIT_PROJECT
40 def current_branch = params.CURRENT_BRANCH
41 def default_branch = params.DEFAULT_BRANCH
42 def build_steps = params.BUILD_STEPS
43 def image_registry = params.IMAGE_REGISTRY
44 def local_version = "${env.JOB_NAME}-${build_number}"
47 stage('Clone/Checkout') {
48 git branch: default_branch, url: git_project
51 branches: [[name: current_branch]],
54 refspec: '+refs/pull/*/head:refs/remotes/origin/pr/*',
61 stage('Docker login') {
62 if (env.DRY_RUN != 'true') {
63 withCredentials([usernamePassword(credentialsId: 'nordix-cicd-harbor-credentials', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
65 echo $HARBOR_PASSWORD | docker login --username $HARBOR_USERNAME --password-stdin $IMAGE_REGISTRY
73 BaseImage(version, build_steps, image_registry, local_version).call()
76 Images(image_names, version, build_steps, image_registry, local_version).call()
77 if (currentBuild.result == 'FAILURE') {
78 Error('Failed to build image(s)').call()
82 HelmChart(helm_chart_upload, version).call()
84 stage('Security Scan') {
85 if (security_scan_enabled == true) {
86 SecurityScan(current_branch, version).call()
88 Utils.markStageSkippedForConditional('Security Scan')
92 if (e2e_enabled == true) {
93 E2e(current_branch, version).call()
95 Utils.markStageSkippedForConditional('E2E')
105 // Verify the Generated code, UnitTests and Linter
108 GeneratedCode().call() // cannot generate code and run the linter and tests at the same time
114 // Runs the unit tests and set the github commit status
117 def context = 'Unit Tests'
118 stage('Unit Tests') {
119 def command = 'make test'
121 SetBuildStatus(in_progress, context, pending)
122 ExecSh(command).call()
123 SetBuildStatus(completed, context, success)
124 } catch (Exception e) {
125 SetBuildStatus(failed, context, failure)
126 Error("${exception_message_exec} ${command}").call()
132 // Runs the linter and set the github commit status
135 def context = 'Linter'
137 def command = 'make lint'
139 SetBuildStatus(in_progress, context, pending)
140 ExecSh(command).call()
141 SetBuildStatus(completed, context, success)
142 } catch (Exception e) {
143 SetBuildStatus(failed, context, failure)
144 Error("${exception_message_exec} ${command}").call()
150 // Check if code has been generated correctly and set the github commit status:
151 // go.mod: runs "go mod tidy"
152 // go generate ./...: Code should be generated using "make genrate" command
153 // proto: skipped due to version of protoc
154 // If files are generated correctly then GetModifiedFiles function should return an empty string
155 def GeneratedCode() {
157 def context = 'Generated code verification'
158 stage('Generated code verification') {
159 def command = 'make go-generate manifests generate-controller'
161 SetBuildStatus(in_progress, context, pending)
162 ExecSh(command).call()
163 if (GetModifiedFiles() != '') {
164 throw new Exception(exception_message_code_generation)
166 SetBuildStatus(completed, context, success)
167 } catch (Exception e) {
168 SetBuildStatus(failed, context, failure)
169 Error(exception_message_exec + command).call()
175 def BaseImage(version, build_steps, registry, local_version) {
177 Build(base_image, version, build_steps, registry, local_version).call()
181 // Call Build function for every images in parallel
182 def Images(images, version, build_steps, registry, local_version) {
186 stages.put(i, Build(i, version, build_steps, registry, local_version))
192 // Build set the github commit status
193 def Build(image, version, build_steps, registry, local_version) {
195 stage("${image} (${version}): ${build_steps}") {
196 def context = "Image: ${image}"
197 def in_progress_message = "${in_progress} (${build_steps})"
198 def completed_message = "${completed} (${build_steps})"
199 def failed_message = "${failed} (${build_steps})"
200 def command = "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}"
202 SetBuildStatus(in_progress_message, context, pending)
203 ExecSh(command).call()
204 SetBuildStatus(completed_message, context, success)
205 } catch (Exception e) {
206 SetBuildStatus(failed_message, context, failure)
207 unstable "${exception_message_exec} ${command}"
208 currentBuild.result = 'FAILURE'
214 // Generate and upload the helm chart
215 def HelmChart(helm_chart_upload, version) {
219 stage('Generate Helm Chart') {
220 def context = 'Generate Helm Chart'
221 def command = "make generate-helm-chart VERSION=${version}"
223 SetBuildStatus(in_progress, context, pending)
224 ExecSh(command).call()
225 SetBuildStatus(completed, context, success)
226 } catch (Exception e) {
227 SetBuildStatus(failed, context, failure)
228 Error("${exception_message_exec} ${command}").call()
231 stage('Upload Helm Chart') {
232 if (helm_chart_upload == true) {
233 withCredentials([string(credentialsId: 'nsm-nordix-artifactory-api-key', variable: 'API_KEY')]) {
235 charts=\$(cd _output/helm/ && ls *.tgz)
236 for chart in \$charts
238 curl -H 'X-JFrog-Art-Api:${API_KEY}' -T _output/helm/\$chart \"https://artifactory.nordix.org/artifactory/cloud-native/meridio/\$chart\"
243 Utils.markStageSkippedForConditional('Upload Helm Chart')
251 // Run the security scan job
252 def SecurityScan(current_branch, version) {
254 build job: 'meridio-periodic-security-scan', parameters: [
255 string(name: 'IMAGE_VERSION', value: "$version"),
256 string(name: 'CURRENT_BRANCH', value: "$current_branch"),
257 string(name: 'DRY_RUN', value: env.DRY_RUN)
264 def E2e(current_branch, version) {
266 build job: 'meridio-e2e-test-kind', parameters: [
267 string(name: 'MERIDIO_VERSION', value: "$version"),
268 string(name: 'TAPA_VERSION', value: "$version"),
269 string(name: 'CURRENT_BRANCH', value: "$current_branch"),
270 string(name: 'DRY_RUN', value: env.DRY_RUN)
275 // Raise error in Jenkins job
291 def ExecSh(command) {
293 if (env.DRY_RUN != 'true') {
304 // Set the commit status on Github
305 // https://plugins.jenkins.io/github/#plugin-content-pipeline-examples
306 def SetBuildStatus(String message, String context, String state) {
307 if (env.DRY_RUN != 'true') {
309 $class: 'GitHubCommitStatusSetter',
310 reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'],
311 commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()],
312 contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context],
313 errorHandlers: [[$class: 'ShallowAnyErrorHandler']], // Prevent GitHubCommitStatusSetter to set the job status to unstable
314 statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ]
319 // Return the current commit sha
321 return sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
324 // Returns if any files has been modified/added/removed
325 def GetModifiedFiles() {
326 return sh(script: 'git status -s', returnStdout: true).trim()