Nico
Nico

Reputation: 91

How to stop a step when another is completed in Jenkins Pipeline Parallel steps

I have a Jenkins pipeline with parallel steps.

Step A is building a spring-boot application, while step B is launching another spring-boot application (mvn spring-boot:run), meant to make a bridge between the tests and the database.

My goal is to close step B (spring-boot:stop ?) when step A is done (Success or Failure).

I'm trying to avoid to use a timeout, as it's not very optimized.

Do you have any solution ?

Thanks a lot.

I've tried to launch spring-boot:stop after my test passed, but to na avail. Same thing with setting a boolean variable to stop a while loop/

            parallel(
                a: {
                    Sonar: {
                        withSonarQubeEnv {
                            withMaven(maven: 'Apache Maven 3.3.9') {
                                sh '''
                                    echo "lauching sonar check"
                                    cd git-42c
                                    mvn -Dmaven.test.failure.ignore verify sonar:sonar
                                    cd ..
                                '''
                            }
                        }
                    }
                },
                b: {
                    // Run the maven build
                    withMaven(maven: 'Apache Maven 3.3.9') {
                        dir('git-proxy') {
                            echo "launching mvn spring-boot:run"
                            sh "mvn spring-boot:run -Dpmd.skip=true -Dcpd.skip=true -Dfindbugs.skip=true"
                        }
                    }
                }
            )
        }

I expect step B to stop when step A has finished (always), but my build stays suspended indefinitely as Step B is running the app.

Upvotes: 2

Views: 2708

Answers (2)

Nico
Nico

Reputation: 91

OK, found a solution.

Reminder : my goal was launching and stopping a spring boot app in parallel of my build.

As I could not find a way to remotely kill a parallel step from antoher step, I had to use a non-elegant way to do it : time out.

A step time-out does not work, as it times out only if the command does not launch, but spring-boot:run launches. DOES NOT WORK : timeout(time: 1, unit: 'MINUTES') { [...] }

So the timeout has to be in the command itself. This is what it looks like at first :
sh "timeout -s KILL 1m mvn spring-boot:run -Dpmd.skip=true -Dcpd.skip=true -Dfindbugs.skip=true

So after 1 minute, my run is killed. This implies a new problem, the kill fails the parallel step, so even if the job finishes with a build success, it is still considered failed, as one branch of the job has "failed".

Now, to avoid the fail, a solution would be to consider the spring-boot step to always be succesful. This is done by using command || true.

Like this :
sh "timeout -s KILL 1m mvn spring-boot:run -Dpmd.skip=true -Dcpd.skip=true -Dfindbugs.skip=true || true"

As for now, my parallel step finishes in a green success.

Per my sample in the question, this is the same sample working :

stage('Scan Sonar') {
            parallel(
                a: {
                    Sonar: {
                        withSonarQubeEnv {
                            withMaven(maven: 'Apache Maven 3.3.9') {
                                sh '''
                                    echo "lauching sonar check"
                                    cd git-42c
                                    mvn -Dmaven.test.failure.ignore verify sonar:sonar
                                    cd ..
                                '''
                            }
                        }
                    }
                },
                b: {
                    // Run the maven build
                    withMaven(maven: 'Apache Maven 3.3.9') {
                        dir('git-proxy') {
                            echo "launching mvn spring-boot:run"
                            sh "timeout -s KILL 1m mvn spring-boot:run -Dpmd.skip=true -Dcpd.skip=true -Dfindbugs.skip=true || true"
                        }
                    }
                }
            )
        }

Upvotes: 1

hakamairi
hakamairi

Reputation: 4678

An elegant solution I could think of would be sidecar container, but that requires docker and scripted pipeline.

node {
    checkout scm
    docker.image('mysql:5').withRun('-e "MYSQL_ROOT_PASSWORD=my-secret-pw"') { c ->
        docker.image('mysql:5').inside("--link ${c.id}:db") {
            /* Wait until mysql service is up */
            sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
        }
        docker.image('centos:7').inside("--link ${c.id}:db") {
            /*
             * Run some tests which require MySQL, and assume that it is
             * available on the host name `db`
             */
            sh 'make check'
        }
    }
}

There of course an option to synchronize on a flag like:

stop = false

parallel 'long': {
    sleep 20
    println "finished long process"
    stop = true
}, 'short': {
    while ( !stop ) {
        println "work"
        sleep 1
    }
    println "stopped by other branch"
}

But that wouldn't work for you as you don't have a loop anywhere.

Neither would failFast on parallel.

It would seem that even if you would cancel the stage from Jenkins REST API you would still fail the build.

So what is the outcome you are looking for? If you don't what the build failed you have to introduce some mechanism to synchronize the state on.

Upvotes: 1

Related Questions