Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Currying groovy CPS closure for parallel execution

We do some dynamic creation of parallel steps in some of our jobs. Thanks to this thread I found how to dynamically create the map with parameters for use in the parallel step.

However now I wanted to reuse parts of the code which is used to create those parallel steps. For this I feel that I would need to curry the closures.

However currying seems not to work correctly. Referencing the loop variable (valueCopy) inside the closure does the right thing (as mentioned here) but currying does not do what I would expect.

Am I doing something wrong, is this just not (yet) supported, are there any workarounds? Is this probably a bug in Jenkins pipeline?

Hope anybody has a clue why this doesn't work and/or how to get it working.

Jenkins: LTS (2.32.1) & latest plugin updates as of 2017/01/19.

Solution:

After upgrading to Pipeline: Groovy plugin version 2.40 eveything is working as expected now.

Pipeline script executed:

def echoSome(val) {
    echo val
}

def buildClosures() {
    def someList = ["1", "2", "3"]
    def closures = [:]
    for (value in someList) {
        final valueCopy = value

        closures[value] = {val ->
                echo valueCopy.toString()
                echo val.toString()
            }.curry(value)
    }
    closures
}

parallel buildClosures()

Output:

[Pipeline] parallel
[Pipeline] [1] { (Branch: 1)
[Pipeline] [2] { (Branch: 2)
[Pipeline] [3] { (Branch: 3)
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] echo
[1] 3
[Pipeline] [1] }
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] echo
[2] 3
[Pipeline] [2] }
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS

Expected Output:

[Pipeline] parallel
[Pipeline] [1] { (Branch: 1)
[Pipeline] [2] { (Branch: 2)
[Pipeline] [3] { (Branch: 3)
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] }
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] }
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
like image 246
Joerg S Avatar asked Jan 19 '17 14:01

Joerg S


1 Answers

I'm not sure if it's the currying, or the for loop, but this function needs to be marked as NonCPS as described here: https://github.com/jenkinsci/pipeline-examples/blob/master/docs/BEST_PRACTICES.md#groovy-gotchas

Essentially, do this:

@NonCPS
def buildClosures() {
    def someList = ["1", "2", "3"]
    def closures = [:]
    for (value in someList) {
        final valueCopy = value

        closures[value] = {val ->
                echo valueCopy.toString()
                echo val.toString()
            }.curry(value)
    }
    closures
}

I think it's your for loop, but regardless, anytime you don't use classic "C Style" loops, you'll need to mark your function as NonCPS.

like image 193
Spencer Malone Avatar answered Oct 14 '22 13:10

Spencer Malone