A Hacky Hacker’s Guide To Hacking Together Jenkins Scripted Pipelines and Getting Them To Do Things Part 2

Inputs, Conditionals and Doing Stuff In Parallel

So, now that we have a basic pipeline pieced together, lets talk about the components that really make it useful: providing input, running things conditionally, and doing multiple things at once to save on time.

There aren’t many useful workflows I can think of that wouldn’t need at least one of these components, so let’s get right to it.

Inputs

One of the most important things you’re going to need to spice up your pipelines, probably, is going to be input. Maybe you need to choose which environment you’re deploying to or which modules to deploy, or perhaps you just want to stop the pipeline until a human comes in and tells it to proceed.

Let’s go over some examples.

First, the most basic input box:

input 'Continue the pipeline?'

By default, that’s all you need. Jenkins will stop the pipeline, and when you click on the stage it’ll pop up saying “Continue the pipeline?” (or whatever text you put there) with two buttons — “Proceed” and “Abort.”

The default can be nice, but not terribly useful. Thankfully, there’s a ton of options. Less thankfully, the documentation is kind a mess, but here’s two use cases I’ve had come in handy so far.

A drop-down box:

userInput = input(id: 'userInput',    
message: 'Choose an environment',
parameters: [
[$class: 'ChoiceParameterDefinition', choices: "Dev\nQA\nProd", name: 'Env']
]
)

You’ll see in the code that all three of these choices are in a single block, separated by newline characters ( /n ), which is how Jenkins registers them as different options. There’s also a name there, which is how you’ll access the choice in the code. More on that, and variables in general, later.

A checkbox (or bunch of check boxes):

chooseOptions = input(id: 'chooseOptions',
message: 'Select options',
parameters: [
[$class: 'BooleanParameterDefinition', defaultValue: true, description: '', name: 'Option A'],
[$class: 'BooleanParameterDefinition', defaultValue: true, description: '', name: 'Option B'],
[$class: 'BooleanParameterDefinition', defaultValue: true, description: '', name: 'Option C']
]
)

Important to note: Not only do inputs not need to be run on a node, they realistically shouldn’t be! Since the pipeline pauses while waiting for human interaction, it’ll tie up the node it’s running on until someone moves it on to the next stage. Keep your input code outside of the node block in a stage (or put it in its own stage and don’t use a node at all).

You can also set a timeout, so the pipeline isn’t sitting and waiting for input forever. Just wrap the whole thing in it:

stage('Input') {
timeout(60) {
input 'Keep going?'
}
}

If nobody hits the “Proceed” button before the timeout expires, this pipeline will fail.

The default unit of time is minutes (so, above, it’s a timeout of 60 minutes). Alternatively, you can define it like so:

stage('Input') {
timeout(time: 60 units: 'MINUTES')

The full list of available and units of time is NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS,andDAYS.

Conditionals

You’re probably not going to want all your pipeline code to run all of the time, so lets add in some conditionals. They’re especially helpful when you’ve got input to act on.

Keeping on with the environment selection example (the first one above), the next stage’s code might look somewhat like this:

if (userInput.Env == "Dev") {
// deploy dev stuff
} else if (userInput.Env == "QA"){
// deploy qa stuff
} else {
// deploy prod stuff
}

Parallel

Depending on your workflow, you might come to a part of the pipeline where you’re running tasks one at a time even though they don’t depend on each other, which can start taking up a lot of time. If you’ve got the spare nodes for it, why not run them in parallel?

stage('Deploy to all environments'){
parallel(
dev: (
node('deploy') {
// code
}
),
qa: (
node('deploy') {
// code
}
),
prod: (
node('deploy') {
// code
}
)
)
}

What’s going on here:

  • inside the parallel block, there are three more blocks: “dev”, “qa” and “prod”. These are arbitrary names, and can be called whatever makes sense for what you’re doing. They just help you know what’s going on.
  • all three of these are running on different ‘deploy’ nodes. If you only have one, each task will wait for the one before it to be finished, thus not actually running in parallel. Alternatively, maybe you’re running tasks in parallel that are doing things to a ‘windows’ node and a ‘centos’ node, or a ‘java7’ node and a ‘java8’ node. They don’t have to be the same type at all.
  • don’t put input in your parallel blocks; it gets weird. Put it first, right after the opening brace for the stage, and then proceed with the parallel blocks.

And that about wraps up part two! Stay tuned for a deeper look into setting and passing variables, continuing or taking action on failure and Slack notifications.

Did I miss anything? See something incorrect, or that could be done better? Have requests for what else you’d like to see covered? Let me know in the comments!