Jenkins pass trigger block to Shared Library Pipeline - jenkins

I have a Shared Library containing a declarative pipeline which numerous jobs are using to build with.
However I want to be able to pass the trigger block in from the Jenkinsfile to the Shared Library as some jobs I want to trigger via Cron, others I want to trigger via SNS and others with an upstream job trigger.
Is there a way I can do this? Everything I have tried so fails
I have tried
#Jenkinsfile
#Library('build') _
buildAmi{
OS = "amazonlinux"
owners = "amazon"
filters = "\"Name=name,Values=amzn-ami-hvm-*-x86_64-gp2\""
template = "linux_build_template.json"
trigger = triggers {
cron('0 H(06-07) * * *')
}
#Shared Lib
pipeline {
$buildArgs.trigger
which fails with
Not a valid section definition
Have also tried passing just the cron schedule into the Shared lib e.g.
triggers {
cron("${buildArgs.cron}")
}
but that gives the error
Method calls on objects not allowed outside "script" blocks
Have tried various other thing but it seems the declarative style requires a trigger block with just triggers inside.
Does anyone know of a way to achieve what I am trying to do?

Too much code for a comment:
We wanted to merge some preset properties with jenkinsfile properties.
This is the way to get this to work since the properties method can only be called once.
We used an additional pipeline parameter with the trigger info:
Jenkinsfile:
mypipeline (
pipelineTriggers: [pollSCM(scmpoll_spec: '0 5,11,15,22 * * 1-5')],
)
then in the shared library
#Field def pipelineProperties = [
disableConcurrentBuilds(abortPrevious: true),
buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '0'))
]
def pipelineTriggersArgs = args.pipelineTriggers
def setJobProperties() {
def props = pipelineProperties
def str = ""
pipelineProperties.each { str += (blue + it.getClass().toString() + it.toString() + "\n") }
if (pipelineTriggersArgs) {
if (debugLevel > 29) {
def plTrig = pipelineTriggers(pipelineTriggersArgs)
str += (mag + "args: " + pipelineTriggersArgs.getClass().toString() + "\t\t" + pipelineTriggersArgs.toString() + "\n" + off)
str += (red + "expr: " + plTrig.getClass().toString() + "\t\t" + plTrig.toString() + "\n" + off)
echo str
}
props?.add(pipelineTriggers(pipelineTriggersArgs)) // pass param with list as the arg
}
properties(props)
}
That was the only way to merge preset and parameters since the properties cannot be overwritten. not exactly the answer but an approach to solve it I think.

Related

print pass or failed job in a parallel Jenkins code

I'm running the following code where the idea is to run as much "TestRunner" as possible during night. I've removed some unnecessary code if some variables aren't there the for easier reading.
I want to know each job if it was successful, aborted or failed and when I'm using parallel I'm not able to see it.
How can I modify my code so i can print each job state when its done? adding a variable is being wiped out to the last element in the for loop.
Thanks a lot
def numberOfRuns = 0
def availableExecutors = 5
def parallelRuns = [:]
// building executers for later use in parallel
for (int i = 0; i < availableExecutors; i++) {
parallelRuns[i] = {
waitUntil {
build job: 'TestRunner', parameters: [
string(name: 'randomBit', value: "${randomBit}"),
], propagate: false
numberOfRuns++
def now = new Date()
return (now > workDayMorning)
}
}
}
// Parallel stage - running al executers
parallel parallelRuns
I've tried to enter a variable to track job process, i tried to use the parallelRuns as object but didnt manage to get the result of each test passed or not.
A solution i've found is:
def Job = build job: 'TestRunner', parameters: [
string(name: 'randomBit', value: ${randomBit}")
], propagate: false
echo "Runner: ${Job.getDisplayName()} has ${Job.getResult()}
with duration: ${Job.getDuration()}"
This prints data of the job.

why is my for-each loop not working properly in parallel stages in jenkins scripted syntax? [duplicate]

In the context of Jenkins pipelines, I have some Groovy code that's enumerating a list, creating closures, and then using that value in the closure as a key to lookup another value in a map. This appears to be rife with some sort of anomaly or race condition almost every time.
This is a simplification of the code:
def tasks = [:]
for (platformName in platforms) {
// ...
tasks[platformName] = {
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
// ...
}
tasks.failFast = true
parallel(tasks)
platforms has two values. I will usually see two iterations and two tasks registered and the keys in tasks will be correct, but the echo statement inside the closure indicates that we're just running one of the platforms twice:
14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].
It's ridiculous.
What do I need to add or do differently?
It's the same issue as you'd see in Javascript.
When you generate the closures in a for loop, they are bound to a variable, not the value of the variable.
When the loop exits, and the closures are run, they will all be using the same value...that is -- the last value in the for loop before it exited
For example, you'd expect the following to print 1 2 3 4, but it doesn't
def closures = []
for (i in 1..4) {
closures << { -> println i }
}
closures.each { it() }
It prints 4 4 4 4
To fix this, you need to do one of two things... First, you could capture the value in a locally scoped variable, then close over this variable:
for (i in 1..4) {
def n = i
closures << { -> println n }
}
The second thing you could do is use groovy's each or collect as each time they are called, the variable is a different instance, so it works again:
(1..4).each { i ->
closures << { -> println i }
}
For your case, you can loop over platforms and collect into a map at the same time by using collectEntries:
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
Hope this helps!

Groovy script for modifying Jenkins nodes labels

All!!
Let's say I have Jenkins with few slave nodes (Slave1, Slave2 & Slave3).
Each slave have few labels associated with it, i.e Slave1 (test build python), Slave2 (develop update clean)...
I'm looking for groovy script that I can run from Jenkins Script Console to modify labels on given slaves at once by adding/removing "-offline" suffix for each label on each slave, i.e Slave1 will have (test-offline build-offline python-offline) & Slave2 (develop-offline update-offline clean-offline).
I need this functionality in order to prevent next job start running on these slaves (let's say I want to set them offline for maintenance, once all jobs finished on these nodes). And once I done with maintenance on these slaves, I need to run another groovy script which set back all labels as they was configured before, i.e Slave1 (test build python), Slave2 (develop update clean).
So far I've found old topic about similar issue. The bellow code can return all labels for chosen slave, but its adding "offline" as additional label and not as a suffix for each existing label:
hudson = hudson.model.Hudson.instance
hudson.slaves.findAll { it.nodeName.equals("slave1") }.each { slave ->
print "Slave $slave.nodeName : Labels: $slave.labelString"
slave.labelString = slave.labelString + " " + "offline"
println " --> New labels: $slave.labelString"
}
hudson.save()
So I have few questions:
How to use list of nodes (slave1,slave2, slave3...) and not single node slave1
How to add/remove"-offline" suffix to/from labels? It can be two different script for adding and removing labels suffix.
Any help & suggestions are much appreciated.
Regrads,
Igor.
I think there is some issue in the script.
hudson = hudson.model.Hudson.instance
hudson.slaves.findAll { it.nodeName.equals("slave1") }.each { slave ->
print "Slave $slave.nodeName : Labels: $slave.labelString"
labels = aSlave.getLabelString()
labels = labels + "-offline"
aSlave.setLabelString(labels)
println " --> New labels: $slave.labelString"
}
hudson.save()
Here too the assumption is that there is only one lable. Then only this will work.
if there are multiple labels
labels = aSlave.getLabelString()
labelList = labels.split(' ') //This is a list
labelList[0] = labelList[0]+"-offline"
labels = labelList.join(' ')
You will have to split the labelString by space and then work on them.
And to get all slaves you can create a loop as follows
for (aSlave in hudson.model.Hudson.instance.slaves) {
if(aSlave.name == "")//here you can pass a list or a single node for which changes needs to be done
println "${aSlave.name}"
}
I would do something like this for multiple nodes
hudson = hudson.model.Hudson.instance
for (aSlave in hudson.slaves) {
labels = aSlave.getLabelString()
labelList = labels.split(' ') //This is a list
i = 0
for (individualLabel in labelList) {
if(individualLabel == "python") {
labelList[i] = labelList[i]+"-offline"
}
i++
}
labels = labelList.join(' ')
aSlave.setLabelString(labels)
}
hudson.save()
You can also add a condition on name of the slave if this has to be done for a particular slave.
let me know if this works
So far, thanks to #pratikvasa, for single node with single label working code is:
hudson = hudson.model.Hudson.instance
hudson.slaves.findAll { it.nodeName.equals("slave1") }.each { slave ->
println " --> Slave $slave.nodeName : Labels: $slave.labelString"
slave.labelString = slave.labelString + "-offline"
println " --> New labels: $slave.labelString"
}
hudson.save()
And for single node & multiple labels working code is:
hudson = hudson.model.Hudson.instance
hudson.slaves.findAll { it.nodeName.equals("slave1") }.each { slave ->
println " --> Slave $slave.nodeName : Current Labels: $slave.labelString"
labels = slave.labelString
def list = ["$labels"]
labelList = labels.split(' ')
i = 0
for (item in labelList) {
print " --> $i Label In The List: "
println item
labelList[i] = labelList[i] + "-offline"
i++
}
labels = labelList.join(' ')
slave.labelString = labels
print " --> New Labels Are: $slave.labelString"
}
Using + "-offline" will append suffix to each label & using - "-offline" will remove suffix from each label.
This is simply worked for me, you have to provide in script approval for the first time
Jenkins.instance.getNode("${env.NODE_NAME}").setLabelString("mylabel") Jenkins.instance.save()

For Loop to call multiple variables from properties file

I have a properties file which I call inside my Jenkins Pipeline Script to get multiple variables.
BuildCounter = n
BuildName1 = Name 1
BuildName2 = Name 2
...
Buildnamen = Name n
I call my properties file with: def props = readProperties file: Path
Now I want to create a loop to print all my BuildNames
for (i = 0; i < BuildJobCounterInt; i++){
tmp = 'BuildName' + i+1
println props.tmp
}
But of course this is not working. ne last println call is searching for a variable called tmp in my properties file. Is there a way to perform this or am I completely wrong?
EDIT:
This is my .properties file:
BuildJobCounter = 1
BuildName1 = 'Win32'
BuildPath1 = '_Build/MBE3_Win32'
BuildName2 = 'empty'
BuildPath2 = 'empty'
TestJobCounter = '0'
TestName1 = 'empty'
TestPath1 = 'empty'
TestName2 = 'empty'
TestPath2 = 'empty'
In my Jenkins pipeline I want to have the possibility to check the ammount of Build/TestJobs and automatically calle the Jobs (each BuildName and BuildPath is a Freestyle Job) To call all these Job I thought of calling the variables inside a for loop. So for every istep I have the Name/Path pair.
Try the below:
Change from:
println props.tmp
To:
println props[tmp]
or
println props."$tmp"
EDIT : based on OP comment
change from:
tmp = 'BuildName' + i+1
To:
def tmp = "BuildName${(i+1).toString()}"

Select All Choices in Jenkins Groovy

I want to select all the choices in my Groovy script so it defaults to all. I am using the Active Choices Reactive Parameter because I am reading in the previous option. How do I make my "output" variable so it is all selected without having the user select them all?
def output = []
def line
def release = RELEASE_NUMBER
releaseNumber = release.replaceAll("\\n", "");
String[] number = releaseNumber.split("\\.");
def list = "cmd /c e:\\tools\\wget --no-check-certificate --http-user=username--http-password=password-qO- \"https://1.1.1.1:443/svn/Instructions/trunk/${number[0]}.${number[1]}.${number[2]}/ICAN/EI_${releaseNumber}.txt\"".execute().text
list.eachLine {
if (it.contains("- LH")) {
String newName = it.replaceAll("\\s", "");
String newName2 = newName.replaceAll("-", "");
output.add(newName2)
}
}
return output
I don't know anything about Jenkins, but reading the documentation for the plugin you mention you should be able to simply use output.add("${newName2}:selected").

Resources