Remove outdated integration tests and openstack image building
[infra/cicd.git] / jjb / nsm / Jenkinsfile
index 4362fc663ea2302429a1b893a3d78499e478d166..914dbbb6f53da4cc8c14a315a401ce8db4f0bf64 100644 (file)
@@ -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