jenkins complicated buildflow, is it possible? - jenkins

I would like to have a Jenkins build flow that looks like this.
After the build is triggered all slaves run the same job in parallel (a setup job).
If any slaves fail this job they should not continue on.
For the all the slaves that to pass that job, they should grab a job out of a pool of jobs that need to be completed. And once a slave completes a job they should go back to complete another job in the pool.
I have only started working with Jenkins a few weeks ago and they way I have it setup now is as each job is picked up by a slave they have to run the setup job first. This really slows down build times because I have about 30 jobs and the setup takes ~2 minutes.
I am using Jenkins as an automated testing platform and all the jobs in the job pool can run independently of each other. I have 5 slaves currently and ~30 jobs.

The following should do the trick:
def jobPool = new ArrayDeque()
jobPool.add({
echo "Doing stuff on ${env.NODE_NAME}"
});
jobPool.add({
echo "Doing other stuff on ${env.NODE_NAME}, a little slower"
sleep 4
});
jobPool.add({
echo "Doing more stuff on ${env.NODE_NAME}, even slower"
sleep 10
});
jobPool.add({
echo "Doing stuff quick on ${env.NODE_NAME}"
});
jobPool.add({
echo "Doing stuff quicker on ${env.NODE_NAME}"
});
def par = [:]
for (x in ["master", "urban"]) {
def nodeName = x; // needed due to variable scoping
par[nodeName] = {
node (nodeName) {
try {
echo "Doing setup on ${env.NODE_NAME}!"
// Do you're setup
echo "Done with setup"
} catch (Exception e) {
echo "Will not use this node as it failed setup!"
return;
}
while (true) {
// echo "${jobPool.size()}"
def subTask = jobPool.poll()
//echo "${jobPool.size()} ${subTask}"
if (subTask == null) {
break;
}
// Might wan't try catch around the next line if you wan't to continue if a job fails
subTask()
}
}
}
}
parallel par
if (!jobPool.isEmpty()) {
error "Not all tasks was done!"
}
Simply add your "job pool jobs" to the jobPool variable and modify the setup part.

It seems like you want separate stages in the same job. This is made much easier in jenkins 2's pipelines. There are some pictures here:
https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Stage+View+Plugin
the [groovy] code ends up looking like this:
node {
stage 'Checkout'
svn 'https://svn.mycorp/trunk/'
stage 'Build'
sh 'make all'
stage 'Test'
sh 'make test'
}

Related

Create process dump when Jenkins pipeline step runs into timeout

We run unit tests on Jenkins and one of our tests freezes sometimes.
We have timeouts defined in the Jenkins pipeline and the freeze triggers the timeout and that kills the testing process.
Is there a way (via Jenkins pipelines, maybe via Groovy) to execute a command (e.g. create a process dump of the testing process) as soon as we run into a timeout, but (of course) before the timeout kills the testing process?
You can wrap our test execution with a try-catch and do whatever you need after catching the timeout exception. Here is a sample Pipeline.
pipeline {
agent any
stages {
stage('Hello') {
steps {
script{
try {
timeout(unit: 'SECONDS', time: 5) {
echo "Running your Tests here!!!!"
sleep 10
}
} catch (e){
echo "The tests erroredout!!!" + e.getCauses()
if(e.getCauses()[0] instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution$ExceededTimeout) {
echo "This is a timeout, do whatever you want..."
}
}
}
}
}
}
}

How to run a task if tests fail in Jenkins

I have a site in production. And I have a simple playwright test that browses to the site and does some basic checks to make sure that it's up.
I'd like to have this job running in Jenkins every 5 minutes, and if the tests fail I want to run a script that will restart the production server. If the tests pass, I don't want to do anything.
What's the easiest way of doing this?
I have the MultiJob plugin that I thought I could use, and have the restart triggered on the failed test step, but it doesn't seem to have the ability to trigger specifically on fail.
Something like the following will do the Job for you. I'm assuming you have a second Job that will take care of the restart.
pipeline {
agent any
triggers{
cron('*/5 * * * *')
}
stages {
stage("Run the Test") {
steps{
echo "Running the Test"
// I'm returning exit code 1 so jenkins will think this failed
sh '''
echo "RUN SOMETHING"
exit 1
'''
}
}
}
post {
success {
echo "Success: Do nothing"
}
failure {
echo 'I failed :(, Execute restart Job'
// Executing the restart Job.
build job: 'RestartJob'
}
}
}

Jenkins: Schedule Particular Stage with single pipeline

I have a single pipeline where it' declarative and I've several stages such as below triggers via webhook.
I would like to execute and scheduled Stage B at a certain time which can also run without trigger via webhook. Clearly it needs to run when triggers via webhook and also run when it will be schedule. Can I handle this without creating seperate job or pipeline in Jenkins ?
stage('A'){
when{
beforeAgent true
expression{return env.GIT_BRANCH == "origin/development"}
}
steps{
script{
//Do something
}
stage ('B'){
when {
beforeAgent true
expression{return env.GIT_BRANCH == "origin/development"}
steps {
script {
//Run Tests
}
}
}
stage('C'){
when{
beforeAgent true
expression{return env.GIT_BRANCH == "origin/development"}
}
steps{
script{
//Do something
}
You can discover what caused your pipeline to run. This may be cron trigger, manual trigger, code commit trigger, webhook trigger, comment on GitHub, upstream job, etc. (depending on plugins installed, the list may be long.)
Here's and example of code to understand what the trigger was. This example sets the environment variable TRIGGERED_BY.
def checkForTrigger() {
def timerCause = currentBuild.rawBuild.getCause(hudson.triggers.TimerTrigger.TimerTriggerCause)
if (timerCause) {
echo "Build reason: Build was started by timer"
env.TRIGGERED_BY = 'timer'
return
}
def userCause = currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause)
if (userCause) {
echo "Build reason: Build was started by user"
env.TRIGGERED_BY = 'user'
return
}
def remoteCause = currentBuild.rawBuild.getCause(hudson.model.Cause$RemoteCause)
if (remoteCause) {
echo "Build reason: Build was started by remote script"
env.TRIGGERED_BY = 'webhook'
return
}
// etc.
println "We haven't caught any of triggers, might be a new one, here is the dump"
def causes = currentBuild.rawBuild.getCauses()
println causes.dump()
}
I think that a build might have more than one trigger, if so the order of your clauses is important.
Once you have this figured out, you can run your stages only when the trigger fits your definition.
stage ('B'){
when {
beforeAgent true
anyOf {
expression{return env.GIT_BRANCH == "origin/development"}
environment name: 'TRIGGERED_BY', value: 'timer'
environment name: 'TRIGGERED_BY', value: 'webhook'
}
Not in a clean or first-class manner, but yes you can do it effectively.
For any job which has been run at least once, you can click "replay" for the previous run.
You will then be presented with the Jenkinsfile in a text edit box. At this point you can perform any edit you want to the Jenkinsfile (including pasting in a completely unrelated Jenkinsfile if you wanted) and Jenkins will execute the modified version. For your specific case, you can delete all the stages you don't want to re-run and just leave behind the one (or two, etc) you want.

How to kill a stage of Jenkins Pipeline?

I've a Jenkins pipeline with multiple stages but because of some issue, in one of the stages, it is likely to run longer unnecessarily.
Instead of aborting entire pipeline build and skip next stages, I want to kill that specific stage on which other stages are not dependent.
Is there a way to kill specific stage of Jenkins pipeline?
There are ways to skip a stage. But I'm not sure if there are options to kill a long running stage. I'd simply add a conditional expression to run the stage or not OR maybe you could put a timeout condition wrapped in a try..catch block for the long running unnecessary stage to skip and proceed to other stages you want like as below.
pipeline {
agent any
stages {
stage('stage1') {
steps {
script {
try {
timeout(time: 2, unit: 'NANOSECONDS')
echo "do your stuff"
} catch (Exception e) {
echo "Ended the never ending stage and proceeding to the next stage"
}
}
}
}
stage('stage2') {
steps {
script {
echo "Hi Stage2"
}
}
}
}
}
OR Check this page for conditional step/stage.
You can try using "try/catch" block in the scripted pipeline. Even if there is error in a particular stage, Jenkins will continue to execute the next stage.
node {
stage('Example') {
try {
sh 'exit 1'
}
catch (exc) {
echo 'Something failed, I should sound the klaxons!'
throw
}
}
}
You can refer documentation here: https://jenkins.io/doc/book/pipeline/syntax/

Re run Jenkins job on a different slave upon failure

I have a job which is compatible with 2 slaves(configured on the different locations). I often experience connectivity issues due to VPN session timeout so I am trying to figure out a way to automatically run a job on the slave 2 if the job gets fail on slave 1. Please let me know if there is any plugin or any way to accomplish it.
I think with a free style project, it would be hard to implement your requirement.
Pipeline script
Check this if you don't know this plugin : How create a pipeline script
According to this answer, the Pipeline Plugin allows you to write jobs that run on multiple slave nodes using labels:
node('linux') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
sh "make"
step([$class: 'ArtifactArchiver', artifacts: 'build/program', fingerprint: true])
}
node('windows && amd64') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
sh "mytest.exe"
}
I created this simple pipeline script and work (this example does not have label, but you could use it):
def exitStatusInMasterNode = 'success';
node {
echo 'Hello World in node master'
echo 'status:'+exitStatusInMasterNode
exitStatusInMasterNode = 'failure'
}
node {
echo 'Hello World in node slave'
echo 'master status:'+exitStatusInMasterNode
}
exitStatusInMasterNode variable could be shared across nodes.
So if your slave1 fail, you could set exitStatusInMasterNode to failure. And at the start of your slave2, you could validate if exitStatusInMasterNode is failure in order to execute the same build but in this slave.
Example:
def exitStatusInMasterNode = 'none';
node {
try{
echo 'Hello World in Slave-1'
throw new Exception('Simulating an error')
exitStatusInMasterNode = 'success'
} catch (err) {
echo err.message
exitStatusInMasterNode = 'failure'
}
}
node {
if(exitStatusInMasterNode == 'success'){
echo 'Job in slave 1 was success. Slave-2 will not be executed'
currentBuild.result = 'SUCCESS'
return;
}
echo 'Re launch the build in Slave-2 due to failure on Slave-1'
// exec simple tasks or stages
}
Log of simulated error in slave1
Running on Jenkins in .../multiple_nodes
Hello World in Slave-1
Simulating an error
Running on Jenkins in .../multiple_nodes
Re launch the build in Slave-2 due to failure on Slave-1
Finished: SUCCESS
Log when there is not error in slave1 (comment this line: throw new Exception)
Running on Jenkins in .../multiple_nodes
Hello World in Slave-1
Running on Jenkins in .../multiple_nodes
Job in slave 1 was success. Slave-2 will not be executed
Finished: SUCCESS

Resources