(error) pipeline jenkins, invoke sh in a loop + serialization json

Viewed 386

I don't understand why an error occurs:

stage('name') {
    steps {
        script {
            def jsonObj = new JsonSlurper().parseText( env.work_servers )
            for(item in jsonObj){
                cmd("my command --param1=${item.srv} --param2=${item.port}")
            }
        }
    }
}

getting an error, error stack https://pastebin.com/V09ZXrif

even if you replace the cmd method call with sh, nothing changes

If you invoke commands without a loop, everything works

stage('name') {
    steps {
        script {
            def srv = "..."
            def port = "..."
            cmd("my command --param1=${srv} --param2=${port}")

            srv = "..."
            port = "..."
            cmd("my command --param1=${srv} --param2=${port}")
        }
    }
}

this also works

stage('name') {
    steps {
        script {
            def jsonObj = new JsonSlurper().parseText( env.work_servers )
            for(item in jsonObj){
                echo item 
            }
        }
    }
}
def cmd(command) {
    result = ""
    if (isUnix()) {
        result = sh(
            script: command,
            returnStdout: true
        ).trim()
    } else {
        bat "chcp 65001\n${command}"
    }
    return result
}
1 Answers

You see this problem, because new JsonSlurper().parseText(text) returns a map of type groovy.json.internal.LazyMap, which is not serializable. Any code you run inside stages has to operate on serializable objects. (An exception can applied with extracted methods annotated with @NonCPS.)

The solution to this problem is simple - replace JsonSlurper with JsonSlurperClassic which returns HashMap from the parseText(text) method.

stage('name') {
    steps {
        script {
            def jsonObj = new JsonSlurperClassic().parseText( env.work_servers )
            for(item in jsonObj){
                cmd("my command --param1=${item.srv} --param2=${item.port}")
            }
        }
    }
}

Alternatively, if you need to stay with JsonSlurper you can extract the part that parses JSON and does any operations with the JSON-object to a separate method and annotate it with @NonCPS. You can consider this alternative option if your JSON is a huge one and using LazyMap makes some sense. Otherwise, switching to JsonSlurperClassic is the best way to solve your problem.

Related