Search code examples
sonarqubejenkins-pipelinemultibranch-pipeline

Work around global webhook for sonarQube Quality Gate


I am interested in including quality gate for my projects. Once built at Jenkins, I have added a script that creates a project on SonarQube server and decides if quality of testing is sufficient (code origin can be found here). Script is bellow

stage('SonarCloud') {
      steps {
        withSonarQubeEnv('SonarQube') {

            sh 'mvn clean package sonar:sonar '

        }
      }
    }
stage("Quality Gate") {
      steps {
        timeout(time: 15, unit: 'MINUTES') { // If analysis takes longer than indicated time, then build will be aborted
            waitForQualityGate abortPipeline: true
            script{
                def qg = waitForQualityGate() // Waiting for analysis to be completed
                if(qg.status != 'OK'){ // If quality gate was not met, then present error
                    error "Pipeline aborted due to quality gate failure: ${qg.status}"
                }
            }
        }
      }
    }

The problem is that my build never finishes itself (it gets aborted after timeout. Removing timeout makes it run forever it seems. That being said, the analysis gets created and placed on SonarQube server). This is the message that I see before timeout:

SonarQube task 'AXUx0uqFm6myUuXQbknO' status is 'IN_PROGRESS'

Cancelling nested steps due to timeout

My problem is that I am not an admin of our SonarQube server, so I cannot add a webhook that would solve this problem. I wonder if there is a work around?

I tried replacing

sh 'mvn clean package sonar:sonar '

with

sh 'mvn clean package sonar:sonar -Dsonar.webhooks.project=https://my-jenkins-server.com/sonarqube-webhook/'

but didn't get anywhere.

I also tried to adapt this code, discussed in the similar question, but didn't get far either.


Solution

  • There actually is a way to get around this problem. The proper fix for this is installing the webhook back to Jenkins, but you apparently can't fix that.

    The next "proper" fix is to use the SonarQube REST api, but first I need to explain what happens in "waitForQualityGate()".

    The first thing it does is use the REST api endpoint "/api/ce/task" to see if the background task is complete. It gets the task id to check for from the "report-task.txt" file, which is formatted like a properties file. If that first check is not SUCCESS or ERROR, then it goes into a wait loop, waiting for the webhook to be called.

    So, you could implement a really cheap fix by simply adding a "sleep" of more seconds than it ever takes the background task to take, and then calling "waitForQualityGate()".

    A more proper fix would do something like this:

    def reportFilePath = "target/sonar/report-task.txt"
    def reportTaskFileExists = fileExists "${reportFilePath}"
    if (reportTaskFileExists) {
        echo "Found report task file"
        def taskProps = readProperties file: "${reportFilePath}"
        echo "taskId[${taskProps['ceTaskId']}]"
        while (true) {
            sleep 20
            def taskStatusResult    =
                sh(returnStdout: true,
                   script: "curl -s -X GET -u ${authString} \'${sonarProps['sonar.host.url']}/api/ce/task?id=${taskProps['ceTaskId']}\'")
                echo "taskStatusResult[${taskStatusResult}]"
            def taskStatus  = new JsonSlurper().parseText(taskStatusResult).task.status
            echo "taskStatus[${taskStatus}]"
            // Status can be SUCCESS, ERROR, PENDING, or IN_PROGRESS. The last two indicate it's
            // not done yet.
            if (taskStatus != "IN_PROGRESS" && taskStatus != "PENDING") {
                break;
            }
        }
    }