6

We're trying to add a Sonarqube scan as part of our Jenkins pipeline script. We have a multi-module maven project, and we're using the Maven Sonarqube plugin to run the aggregated scan. To initiate the scan we followed the instructions in Sonarqube's documentation as shown here (scroll down to the end of the page).

So the pipeline script looks something like this :

node {
    stage('SonarQube analysis') {
        withSonarQubeEnv('My SonarQube Server') {
            sh 'mvn clean package sonar:sonar'
        }
    }
}
stage("Quality Gate") {
    timeout(time: 1, unit: 'HOURS') { 
        def qg = waitForQualityGate() 
        if (qg.status != 'OK') {
            error "Pipeline aborted due to quality gate failure: ${qg.status}"
        }
    }
}

and it works as expected. But since we're using it in more than one file, we'd like to have as less duplicate code as possible, so we want to have it inside the node like this :

node {
    stage('SonarQube analysis') {
        withSonarQubeEnv('My SonarQube Server') {
            sh 'mvn clean package sonar:sonar'
        }
    }
    stage("Quality Gate") {
        timeout(time: 1, unit: 'HOURS') { 
            def qg = waitForQualityGate() 
            if (qg.status != 'OK') {
                error "Pipeline aborted due to quality gate failure: ${qg.status}"
            }
        }
    }
}

But for some reason, this gets the quality gate stuck on PENDING status until the timeout is reached. I'm trying to understand why this is happening, and what could be done to avoid moving the quality gate check outside the node.

Any help would be greatly appreciated!

Community
  • 1
  • 1
Eddie
  • 73
  • 1
  • 4
  • Putting a waiting step into a node is not a good idea. the build will be holding an agent unnecessarily (which could be used by another build during the wait stage). How are you getting less duplication just putting the call inside a `node`? – amuniz Feb 12 '18 at 11:04
  • We have a function call (from another file) inside the node to do the build, we wanted the wait stage to be part of the function cause as far as I know, we can't make a call to a function in a different file without having a node to load it. Please correct me if I'm wrong since I'm new to this. – Eddie Feb 13 '18 at 16:56
  • You can use `readTrusted` to load any file from the CSM checkout outside a `node`. – amuniz Feb 14 '18 at 08:02

3 Answers3

1

waitForQualityGate performs an HTTP call to the SonarQube server.

Make sure your build node has HTTP access to your SonarQube instance (the Jenkins master having access to it does not imply build nodes also have it).

Regardless, as I said in a comment, using a waiting step inside a node is not a good idea in general.

amuniz
  • 3,292
  • 2
  • 20
  • 22
0

I was also facing the same issue and I fixed it by adding sleep before waitForQualityGate stage

sleep 10
stage("Quality Gate") {
        timeout(time: 1, unit: 'HOURS') { 
            def qg = waitForQualityGate() 
            if (qg.status != 'OK') {
                error "Pipeline aborted due to quality gate failure: ${qg.status}"
            }
        }
    }
RAVI SINGH
  • 379
  • 2
  • 7
  • what if your analysis doesn't get completed in those 10 seconds? ;) It is best to add a retry mechanism with a wait of "x" – Pankaj Saini Sep 10 '20 at 08:42
0

This could be a better way to retry.

def retryForTimeoutExceeded(count = 3, Closure closure) {
    for (int i = 1; i <= count; i++) {
        try {
            closure()
            break
        } catch (FlowInterruptedException error) {
            int retriesLeft = count - i
            def hasTimeoutExceeded = error.causes[0].getClass().toString() == 'class org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout'
            println "Timeout Exceeded for clousre.\nRetries left: $retriesLeft"
            if (retriesLeft == 0 || !hasTimeoutExceeded) {
                throw error
            }
        }
    }
}

stage("Quality Gate") {
        retryForTimeoutExceeded {
            timeout(time: 5, unit: 'MINUTES') {
                // Just in case something goes wrong, pipeline will be killed after a timeout
                def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
                if (qg.status != 'OK') {
                    error "Pipeline aborted due to sonar quality gate failure: ${qg.status}"
                }
            }
        }
    }

Timeout can be configured accordingly

Pankaj Saini
  • 1,493
  • 8
  • 13