Jenkins - Stop concurrent job with same parameter - jenkins

I have a Jenkins job for a db rollback script that uses a choice parameter for each environment (using NodeLabel Parameter Plugin).
I want the jobs to able to be run concurrently, but only for different environments.
"Execute concurrent builds if necessary" is enabled.
E.g. If the job is running for LIVE, allow someone to run the job again for TEST (this works). However, if LIVE is already running and someone runs the job for LIVE again, then do not run.
This plugin seems to suit my needs but is not shown on the list of available plugins in Manage Jenkins.
https://wiki.jenkins-ci.org/display/JENKINS/Concurrent+Run+Blocker+Plugin
Are there any other ways around this?

There's a solution with existing Jenkins plugins:
Create a Freestyle project named like Starter for concurrent builds exclusively on nodes.
☑ This build is parameterized
Node [NodeLabel Parameter Plugin]
Name: NODE
Choice Parameter
Name: JOB
Choices: ... the jobs' names you'd like to start with this ...
Build
Conditional step (single) [Conditional BuildStep Plugin]
Run?: Not
!: Execute Shell
Command:
#!/bin/bash +x -e
# Bash 4 needed for associative arrays
# From http://stackoverflow.com/questions/37678188/jenkins-stop-concurrent-job-with-same-parameter
echo ' Build --> Conditional step (single) --> Execute Shell'
echo " Checking whether job '$JOB' runs on node '$NODE'"
echo ' Creating array'
declare -A computers
# ------------------------------------------------------------------------
# Declare your nodes and their executors here as mentioned, for instance,
# in the API URI 'http://<jenkins>/computer/(master)/executors/0/api/xml':
computers=( # ^^^^^^ ^
[master]="0 1 2 3"
[slave]="0 1"
)
# Note: Executor indices DO NOT conform to the numbers in Jenkins'
# Build Executor Status UI.
# ------------------------------------------------------------------------
echo " Checking executors of node '$NODE'"
for computer in ${!computers[#]} ; do
for executorIdx in ${computers[$computer]} ; do
if [[ $computer == $NODE ]] ; then
if [[ "$computer" == "master" ]] ; then
node="(${computer})"
else
node=$computer
fi
url="${JENKINS_URL}/computer/${node}/executors/${executorIdx}/api/xml?tree=currentExecutable\[url\]"
echo " $url"
xml=$(curl -s $url)
#echo $computer, $executorIdx, $xml
if [[ "$xml" == *"/job/${JOB}"* ]] ; then
echo " Job '$JOB' is already building on '$computer' executor index '$executorIdx'"
echo ' Exiting with 1'
exit 1
fi
fi
done
done
echo ' Exiting with 0'
Builder: Set the build result
Result: Aborted
Conditional step (single)
Run?: Current build status
Builder: Trigger/call build on other projects
Build Triggers:
Projects to build: $JOB [ignore the error message]
Node Label parameter
Name: NODE [or how you call it in your downstream job(s)]
Node: $NODE

Related

How to define multiple project keys for quality gate in jenkins pipeline

I have a jenkins pipeline in which one stage is sonarqube analysis.I have a project and multiple subprojects in sonarqube.for ex projectkey=xyz.com subprojectkey=abc.xyz.com. My requirement is if quality gates didn't pass my build should fails . How i can define multiple project keys. I wrote a shell script to do the task but in that also I'm able to define only one project key.
PROJECTKEY="%teamcity.project.id%"
QGSTATUS=`curl -s -u SONAR_TOKEN: http://SONAR_URL:9000/api/qualitygates/project_status?projectKey=$PROJECTKEY | jq '.projectStatus.status' | tr -d '"'`
if [ "$QGSTATUS" = "OK" ]
then
exit 0
elif [ "$QGSTATUS" = "ERROR" ]
then
exit 1
fi

Can we define a variable inside a Jenkins parameterized build

My scenario is, I have parameterized build and inside the build section, I have executed shell where I define a variable and then echo to print it. But it doesn't print anything in the console output.
I hope I have made myself clear. Could anyone please answer my question?
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
enter image description here
I'm using Jenkins ver. 2.32.3 and a simple freestyle job, running on mac OS, using an execute shell build step of:
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
Gives output of:
$ /bin/sh -xe /var/folders/kh/4fl0eeldofefmmsfd/T/hudson89388543547899686.sh
++ date +%Y%m%d-%H%M%S
+ current_folder=20180613-081712
+ echo 20180613-081712
20180613-081712
Finished: SUCCESS
In a similar fashion, setting the shell:
#!/bin/bash
current_folder=`date +%Y%m%d-%H%M%S`
echo $current_folder
Gives:
$ /bin/bash /var/folders/kh/by0kd93dfew5fgjhy000h6/T/hudson62702345565786787.sh
20180613-081655
Finished: SUCCESS
The same applies to a parameter that is defined as part of the Jenkins job, underneath the
This project is parameterized checkbox once set. For example, if you have a string parameter called userName with a default value of User1, then you can print it's value in an Execute Shell build step using:
echo $userName
echo ${userName}
echo "In a string ${userName}"
Giving:
User1
User1
In a string User1

Batch command job works in master but not in slave Jenkins

I have created free style project in jenkins to install msi installer. Free style project has
Parameterized job with string as parameter.
Restrict where this project can be run is enabled and selected the Label
Selected 'Executed windows batch command' in build step
Batch command
#ECHO OFF
IF NOT EXIST "C:\Build\Sample_%buidVersion%.msi" (
echo "The specified build does not exist in path"
EXIT /B 1
) ELSE (
echo "Installation of build" %buidVersion% "is started"
START "" /WAIT msiexec.exe /i "C:\\Build\\Sample_%buidVersion%.msi" /L*V "C:\package.log" ADDSOURCE=ALL /qn
)
IF %errorlevel% NEQ 0 (
echo "Error in installation, Please check C:\package.log for more details"
) ELSE (
echo "The build" %buidVersion% "installation is successful"
)
EXIT
When i execute this in master without applying 'Restrict where this project can be run is enabled and selected the Label' this option the job is successful by running in master but on enabling this and executing it in the slave says error as,
"The specified build does not exist in path."
Build step 'Execute Windows batch command' marked build as failure
Best way to debug is to echo the parameters you're using in order to see where the point of failure is.
Add in the beginning of your script:
echo "C:\Build\Sample_%buidVersion%.msi"
cd C:\Build
dir
and check if the file you're looking for is in the right location with the right name.
Good luck!

How to get the BUILD_USER in Jenkins when job triggered by timer?

I wanted to show the user who triggered a Jenkins job in the post job email. This is possible by using the plugin Build User Vars Plugin and the env variable BUILD_USER.
But this variable do not get initialized when the job is triggered by a scheduler.
How can we achieve this? I know we have a plugin called - EnvInject Plugin, and that can be used...
But I just want to know how we can use this and achieve the solution...
Build user vars plugin wasn't working for me so I did a quick-and-dirty hack:
BUILD_CAUSE_JSON=$(curl --silent ${BUILD_URL}/api/json | tr "{}" "\n" | grep "Started by")
BUILD_USER_ID=$(echo $BUILD_CAUSE_JSON | tr "," "\n" | grep "userId" | awk -F\" '{print $4}')
BUILD_USER_NAME=$(echo $BUILD_CAUSE_JSON | tr "," "\n" | grep "userName" | awk -F\" '{print $4}')
SIMPLE SOLUTIONS (NO PLUGINS) !!
METHOD 1: Via Shell
BUILD_TRIGGER_BY=$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | egrep '^userId>|^userName>' | sed 's/.*>//g' | sed -e '1s/$/ \//g' | tr '\n' ' ')
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
METHOD 2: Via Groovy
node('master') {
BUILD_TRIGGER_BY = sh ( script: "BUILD_BY=\$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | egrep '^userId>|^userName>' | sed 's/.*>//g' | sed -e '1s/\$/ \\/ /g'); if [[ -z \${BUILD_BY} ]]; then BUILD_BY=\$(curl -k --silent ${BUILD_URL}/api/xml | tr '<' '\n' | grep '^shortDescription>' | sed 's/.*user //g;s/.*by //g'); fi; echo \${BUILD_BY}", returnStdout: true ).trim()
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
}
METHOD 3: Via Groovy
BUILD_TRIGGER_BY = "${currentBuild.getBuildCauses()[0].shortDescription} / ${currentBuild.getBuildCauses()[0].userId}"
echo "BUILD_TRIGGER_BY: ${BUILD_TRIGGER_BY}"
OUTPUT:
Started by user Admin / user#example.com
Note: Output will be both User ID and User Name
This can be done using the Jenkins Build User Vars Plugin which exposes a set of environment variables, including the user who started the build.
It gives environment variables like BUILD_USER_ID, EMAIL, etc.
When the build is triggered manually by a logged-in user, that user's userid is available in the BUILD_USER_ID environment variable.
However, this environment variable won't be replaced / initialized when the build is automatically triggered by a Jenkins timer / scheduler.
Attached a screenshot for details
This can be resolved by injecting a condition to the Job by using Conditional Build Step Plugin / Run Condition Plugin,where in to each job we can add a condition to initialize the variable BUILD_USER_ID only when the build is caused or triggered by the Timer or scheduler, by setting a condition using the regular expression..
Without Plugin ->
def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
echo "userName: ${cause.userName}"
Install 'Build User Vars Plugin' and use like below:- [ See https://plugins.jenkins.io/build-user-vars-plugin ]
Be sure to check mark the Set jenkins user build variables checkbox under Build Environment for your Jenkins job's configuration.
I found similar but really working on Jenkins 2.1.x and easy for my understanding way.
And it works without any plugins.
if (currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')['userId']){
// Will be run only if someone user triggers build
// Because in other cases this contructions returns null
}
You can use in this construction any classes described here.
They will be returns maps with usable values.
This gets the username who clicked "Build Now" in a Jenkins pipeline job.
#NonCPS
def getBuildUser() {
return currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
}
I'm using a combination of the 'Execute Shell' and 'Env Inject' plugin as follows:
Create an 'Execute Shell' build step that uses shell parameter substitution to write default the value and echo that value into a file. Example highlighted in screen shot below.
Use the 'Env Inject' file to read that file as properties to set.
The token $BUILD_CAUSE from the email-ext plugin is what you are looking for.
You can see the full content token reference when you click the ? just after the Attach build log combobox at the email content configuration.
Some tokens get added by plugins, but this one should be aviable by default.
Edit: As pointed out by bishop in the comments, when using the EnvInject plugin, the $BUILD_CAUSE token gets changed to behave differently.
I have written a groovy script to extract the started by which would correctly get the source, regardless if user, scm or timer (could add more). It would recursively navigate the build tree to get the "original" 'started by' cause https://github.com/Me-ion/jenkins_build_trigger_cause_extractor
I wanted to trigger build initiator info to one of my slack/flock group so I used following way to get build initiator email and name by writing in Declarative fashion .
I am just printing here, you can use to store in some environment variable or write in one file giving file path according to your own convenience..
pipeline {
environment {
BRANCH_NAME = "${env.BRANCH_NAME}"
}
agent any
stages{
stage('Build-Initiator-Info'){
sh 'echo $(git show -s --pretty=%ae)'
sh 'echo $(git show -s --pretty=%an)'
}
}
}
Just to elaborate on Musaffir Lp's answer. The Conditional Build Step plugin now supports the Build Cause directly - it requires the Run Condition Plugin also.
If you wanted to detect when the build was started by a timer you can select a Run? value of Build Cause, with Build Cause of: TimerTrigger
This is a little simpler and more robust than using a regex. There are also other triggers you can detect, for example when the build was a result of Source Control Management commit, you can select: SCMTrigger.
This below is working for me.
Install "user build vars plugin"
Build Name = ${BUILD_NUMBER}_${TICKET}_${ENV,var="BUILD_USER_ID"}
I created a function that return the Triggered Job Name:
String getTriggeredJob(CURRENT_BUILD) {
if (CURRENT_BUILD.upstreamBuilds.size() > 0) {
TRIGGERED_JOB = CURRENT_BUILD.upstreamBuilds[0].projectName
if (!TRIGGERED_JOB.isEmpty()) {
return TRIGGERED_JOB
}
}
return "Self"
}
CURRENT_BUILD is env var currentBuild
How to return Username & UserId:
UserName: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserName()
UserId: currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
There is other way to get user_id, where you don't need to install anything.
BUILD_USER_ID = sh (
script: 'id -u',
returnStdout: true
).trim()
echo "bUILD USER: ${BUILD_USER_ID }"
For declarative pipeline syntax, here is a quick hack, base on #Kevin answer.
For declarative pipeline you need to enclose them in a node, else you will get an error/ build failure
node {
def BUILD_FULL = sh (
script: 'curl --silent '+buildURL+' | tr "{}" "\\n" | grep -Po \'"shortDescription":.*?[^\\\\]"\' | cut -d ":" -f2',
returnStdout: true
)
slackSend channel: '#ci-cd',
color: '#000000',
message: "The pipeline was ${BUILD_FULL} ${GIT_COMMIT_MSG} "
}
The output will be slack notification sent to your slack channel with the git short description

Getting the build status in post-build script

I would like to have a post-build hook or similar, so that I can have the same output as e. g. the IRC plugin, but give that to a script.
I was able to get all the info, except for the actual build status. This just doesn't work, neither as a "Post-build script", "Post-build task", "Parameterized Trigger" aso.
It is possible with some very ugly workarounds, but I wanted to ask, in case someone has a nicer option ... short of writing my own plugin.
It works as mentioned with the Groovy Post-Build Plugin, yet without any extra quoting within the string that gets executed. So I had to put the actual functionality into a shell script, that does a call to curl, which in turn needs quoting for the POST parameters aso.
def result = manager.build.result
def build_number = manager.build.number
def env = manager.build.getEnvironment(manager.listener)
def build_url = env['BUILD_URL']
def build_branch = env['SVN_BRANCH']
def short_branch = ( build_branch =~ /branches\//).replaceFirst("")
def host = env['NODE_NAME']
def svn_rev = env['SVN_REVISION']
def job_name = manager.build.project.getName()
"/usr/local/bin/skypeStagingNotify.sh Deployed ${short_branch} on ${host} - ${result} - ${build_url}".execute()
Use Groovy script in post-build step via Groovy Post-Build plugin. You can then access Jenkins internals via Jenkins Java API. The plugin provides the script with variable manager that can be used to access important parts of the API (see Usage section in the plugin documentation).
For example, here's how you can execute a simple external Python script on Windows and output its result (as well as the build result) to build console:
def command = """cmd /c python -c "for i in range(1,5): print i" """
manager.listener.logger.println command.execute().text
def result = manager.build.result
manager.listener.logger.println "And the result is: ${result}"
For this I really like the Conditional Build Step plugin. It's very flexible, and you can choose which actions to take based on build failure or success. For instance, here's a case where I use conditional build step to send a notification on build failure:
You can also use conditional build step to set an environment variable or write to a log file that you use in subsequent "execute shell" steps. So for instance, you might create a build with three steps: one step to compile code/run tests, another to set a STATUS="failed" environment variable, and then a third step which sends an email like The build finished with a status: ${STATUS}
Really easy solution, maybe not to elegant, but it works!
1: Catch all the build result you want to catch (in this case SUCCESS).
2: Inject an env variable valued with the job status
3: Do the Same for any kind of other status (in this case I catch from abort to unstable)
4: After you'll be able to use the value for whatever you wanna do.. in this case I'm passing it to an ANT script! (Or you can directly load it from ANT as Environment variable...)
Hope it can help!
Groovy script solution:-
Here I am using groovy script plugin to take the build status and setting it to the environmental variable, so the environmental variable can be used in post-build scripts using post-build task plugin.
Groovy script:-
import hudson.EnvVars
import hudson.model.Environment
def build = Thread.currentThread().executable
def result = manager.build.result.toString()
def vars = [BUILD_STATUS: result]
build.environments.add(0, Environment.create(new EnvVars(vars)))
Postscript:-
echo BUILD_STATUS="${BUILD_STATUS}"
Try Post Build Task plugin...
It lets you specify conditions based on the log output...
Basic solution (please don't laugh)
#!/bin/bash
STATUS='Not set'
if [ ! -z $UPSTREAM_BUILD_DIR ];then
ISFAIL=$(ls -l /var/lib/jenkins/jobs/$UPSTREAM_BUILD_DIR/builds | grep "lastFailedBuild\|lastUnsuccessfulBuild" | grep $UPSTREAM_BUILD_NR)
ISSUCCESS=$(ls -l /var/lib/jenkins/jobs/$UPSTREAM_BUILD_DIR/builds | grep "lastSuccessfulBuild\|lastStableBuild" | grep $UPSTREAM_BUILD_NR)
if [ ! -z "$ISFAIL" ];then
echo $ISFAIL
STATUS='FAIL'
elif [ ! -z "$ISSUCCESS" ]
then
STATUS='SUCCESS'
fi
fi
echo $STATUS
where
$UPSTREAM_BUILD_DIR=$JOB_NAME
$UPSTREAM_BUILD_NR=$BUILD_NUMBER
passed from upstream build
Of course "/var/lib/jenkins/jobs/" depends of your jenkins installation

Resources