5
votes

Basically I can't pass build properties to Library var call without extra nonsense.

jenkinsfile relevant chunk:

tc_test{
    repo = 'test1'
    folder = 'test2'
    submodules = true
    refs = params.GitCheckout
}

That results in error

java.lang.NullPointerException: Cannot get property 'GitCheckout' on null object

This, however, works:

def a1 = params.GitCheckout
tc_test{
    repo = 'test1'
    folder = 'test2'
    submodules = true
    refs = a1
}

The contents of the vars/tc_test.groovy in shared library :

def call ( body ) {

    def config = [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = config
    try {
        body()
    } catch(e) {
        currentBuild.result = "FAILURE";
        throw e;
    } finally {

        config.each{ k, v -> println "${k}:${v}" }

    }
}

I'm not really good with groovy, so it might be something obvious.

2
I had this exact problem and my solution has been to do something like you did. I assign a variable 'def localParams = params' and use it. Something to do with Groovy delegate and params defined outside the pipeline. - macg33zr
This seems like such a mess, though - Alexei Kiryanov

2 Answers

7
votes

Got the answer from Jenkins JIRA.

Small workaround is using maps instead of closures:

tc_test ([
  repo: 'test1',
  folder: 'test2',
  submodules: true,
  refs = params.GitCheckout
])

May have drawbacks, but for me that worked perfectly.

Still have to transfer params as argument to have access to them, but at least the code makes more sense now.

4
votes

Suppose you have a sharedLibrary to call a Rundeck Job,

Parameters:
1 runDeckJobId - Rundeck unique job id thats available in settings.
2 role - AD Group associated with Rundeck Job
3 runDeckProject - Name of the project configured in rundeck.
4 optional - All optional parameters as a Map.
- rundeckInstanceType - Rundeck instances are currently in UK or HK.
- retries - Number of retries for checking job status once started (Default value=100)
- timeout - Number of seconds to be waited before each retry (Default value=15)
- verbose - If verbose calls need to be made in the rundeck api calls (Default value=false)
- rundeckArgs - All rundeck parameters as a map. Eg: Name of the playBook, location of inventory file.

Example Usage in JenkinsFile:

if (isRundeckDeployment == "true") {
    def optional = [
        rundeckInstance : "UK",
        timeout : 10,
        rundeckArgs : [
            artifactPack      : "${artifactPath}",
            DEPLOYMENT_ENVIRONMENT: "${deploymentEnvironment}",
            EXTRA_ARGS            : "-e deployment_serial=1"
        ]
    ]
    triggerRundeckJob("job-id", "AD-group-id", "BitbucketKey", optional)
} 

Shared Library Function with filename : triggerRundeckJob in vars folder

def call(String rundeckJobId, String role, String  rundeckProject, Map optional) {

    String jobUserId
    wrap([$class: 'BuildUser']) {
        jobUserId = "${BUILD_USER_ID}"
    }
    // Determine rundeck instance type, by default instance is UK (rundeckAuthToken)
    String mainRundeckId  = optional.rundeckInstance == "HK" ? "rundeckAuthTokenHK": "rundeckAuthToken"
    String rundeckBaseURL = optional.rundeckInstance == "HK" ? "https://rundeckUrl/selfservice" : "https://rundeckUrl:9043/selfservice"

    withCredentials([string(credentialsId: mainRundeckId, variable: 'mainRundeckIdVariable')]) {
        int retries     = optional.retries ?: 100
        int timeout     = optional.timeout ?: 15
        String verbose  = optional.verbose? "-v" : "-s"
        String rundeckArgsString = optional.rundeckArgs.collect{ "-${it.key} \\\"${it.value}\\\"" }.join(" ")

        def tokenResponse = sh(returnStdout: true, script: "curl -k ${verbose} -X POST -d '{\"user\": \"${jobUserId}\",\"roles\":\"${role}\",\"duration\":\"30m\"}' -H Accept:application/json -H 'Content-Type: application/json' -H X-Rundeck-Auth-Token:${mainRundeckIdVariable} ${rundeckBaseURL}/api/19/tokens")
        def tokenResponseJson = readJSON text: tokenResponse

        def rundeckResponse =  sh(returnStdout: true, script: "curl -k ${verbose} --data-urlencode argString=\"${rundeckArgsString}\" -H Accept:application/json -H X-Rundeck-Auth-Token:${tokenResponseJson.token} ${rundeckBaseURL}/api/19/job/${rundeckJobId}/run")
        def rundeckResponseJson = readJSON text: rundeckResponse

        if(!rundeckResponseJson.error){
            while(true){
                if(retries==0) {
                    currentBuild.result = "FAILURE"
                    echo "Rundeck Job Timedout, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
                    break;
                }
                def jobStateResponse = sh(returnStdout: true, script:"curl -k ${verbose} -H Accept:application/json -H X-Rundeck-Auth-Token:${tokenResponseJson.token} ${rundeckBaseURL}/api/19/execution/${rundeckResponseJson.id}/state")
                def jobStateResponseJson = readJSON text: jobStateResponse

                if(jobStateResponseJson.completed) {
                    if(jobStateResponseJson.executionState == "FAILED") {
                        currentBuild.result = "FAILURE"
                        echo "Rundeck Job FAILED, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
                        break
                    }else{
                        currentBuild.result = "SUCCESS"
                        echo "Rundeck Job SUCCESS, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
                        break
                    }
                }
                else{
                    sleep timeout
                }
                retries--
            }
        }else{
            echo "******************Rundeck Job Error: ${rundeckResponseJson.message} ******************"
            currentBuild.result = "FAILURE"
        }
    }
}