how to call parameters from groovy map script to Jenkins Pipeline - jenkins

simply I need to write jenkinsfile pipeline which contains choices and this choices come come from another groovy script that provides a map in this map I can add environment name and 3 values DB , APP , WEB so when I start build a new job I get parameter build with choices to pick the environment name DEV, QA , UAT and according to this choice it will pass map/list of the three IPs for DB,APP, WEB so I can use this values during build
/env.groovy
def DEV = [ DB : 10.0.0.5 , APP : 10.0.0.10 , WEB : 10.0.0.15 ]
def UAT = [ DB : 10.0.0.20 , APP : 10.0.0.25 , WEB : 10.0.0.30 ]
def QA = [ DB : 10.0.0.35 , APP : 10.0.0.40 , WEB : 10.0.0.45 ]
take this values from env.groovy file and pass it to choice in Jenkinsfile so I can get drop down menu with ( DEV - UAT - QA )
before I click build
I do not want to add this values inside the Jenkinsfile , I need to added it to separate groovy script (env.groovy)
this is for new pipeline Jenkinsfile which is running on ubuntu Jenkins server
String[] env = env()
pipeline {
agent any
properties([
parameters([
choice(
choices: env,
description: 'Select the environment',
name: 'ENV',
)
])
])
stages {
stage('deploy') {
steps {
sh "echo ${ENV}"
}
}
}
}
expecting to echo list of environment variables DB , APP and WEB , so I can be sure I can pass this values to another shell script that I will add later to deploy , but I didn't get this menu in the 1st place and I get this error
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 5: The ‘properties’ section has been renamed as of version 0.8. Use ‘options’ instead. # line 5, column 5.
properties([
^
1 error
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1085)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:603)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:581)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:558)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:133)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:126)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:561)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:522)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:320)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE

IMHO there is no possibility to use variable as maps name directly, but we have switch statement.
Try this one:
def map
pipeline {
agent any
parameters {
choice( name: 'env', choices: ['DEV', 'UAT', 'QA'] , description: "Choose ENV?" )
}
stages {
//I'm a bit lazy, in Your case use regular file :)
stage('create file') {
steps {
script {
sh "echo \"DEV = [ DB : '10.0.0.5' , APP : '10.0.0.10' , WEB : '10.0.0.15' ]\" > env.groovy"
sh "echo \"UAT = [ DB : '10.0.0.20' , APP : '10.0.0.25' , WEB : '10.0.0.30' ]\" >> env.groovy"
sh "echo \"QA = [ DB : '10.0.0.35' , APP : '10.0.0.40' , WEB : '10.0.0.45' ]\" >> env.groovy"
}
}
}
stage('switch time') {
steps {
script{
load "$JENKINS_HOME/workspace/box1/env.groovy"
switch (params.env) {
case 'DEV':
map = DEV
break
case 'UAT':
map = UAT
break
case 'QA':
map = QA
break
default:
map = []
break
}
}
}
}
stage('deploy') {
steps {
println map.DB
println map.APP
println map.WEB
}
}
}
}
Expected output:
Started by user 3sky
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] node
Running on Jenkins in /app/jenkins/home/workspace/box1
[Pipeline] {
[Pipeline] stage
[Pipeline] { (create file)
[Pipeline] script
[Pipeline] {
[Pipeline] sh
[box1] Running shell script
+ echo 'DEV = [ DB : '\''10.0.0.5'\'' , APP : '\''10.0.0.10'\'' , WEB : '\''10.0.0.15'\'' ]'
[Pipeline] sh
[box1] Running shell script
+ echo 'UAT = [ DB : '\''10.0.0.20'\'' , APP : '\''10.0.0.25'\'' , WEB : '\''10.0.0.30'\'' ]'
[Pipeline] sh
[box1] Running shell script
+ echo 'QA = [ DB : '\''10.0.0.35'\'' , APP : '\''10.0.0.40'\'' , WEB : '\''10.0.0.45'\'' ]'
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (switch time)
[Pipeline] script
[Pipeline] {
[Pipeline] load
[Pipeline] { (/app/jenkins/home/workspace/box1/env.groovy)
[Pipeline] }
[Pipeline] // load
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (deploy)
[Pipeline] echo
10.0.0.5
[Pipeline] echo
10.0.0.10
[Pipeline] echo
10.0.0.15
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

Related

Use exit code in if statement in Jenkins pipeline

Here's my Jenkins pipeline where I'm trying to to run a conditional statement on the basis of previous command's exit code.
pipeline{
agent any
stages{
stage('Test exitcode'){
steps{
script{
EX_CODE = sh(
script: 'echo hello-world',
returnStatus: true
)
if( env.EX_CODE == 0 ){
echo "Code is good with $EX_CODE"
}else{
echo "Code is Bad with $EX_CODE"
}
}
}
}
}
}
Here's its output
Started by user Yatharth Sharma
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/devops/syntax-testing-pipeline
[Pipeline] { (hide)
[Pipeline] stage
[Pipeline] { (Test exitcode)
[Pipeline] script
[Pipeline] {
[Pipeline] sh
+ echo hello-world
hello-world
[Pipeline] echo
Code is Bad with 0
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
I was expecting this to return Code is good but this prints out Code is Bad. Can someone help me here with a why?
You are assigning the int return of the exit code from the shell interpreter command from the sh step method to a variable EX_CODE. You then attempt to access the value from the env object member in the conditional as if it were an environment variable instead of a variable. You can either modify the variable to be an environment variable, or access the variable directly (simpler).
EX_CODE = sh(script: 'echo hello-world', returnStatus: true)
if (EX_CODE == 0) {
echo "Code is good with ${EX_CODE}"
}
else{
echo "Code is Bad with ${EX_CODE}"
}

Queue more than 2 jobs in a Jenkins pipeline

I need to be able to queue more than 2 jobs in a Jenkins pipeline.
In https://stackoverflow.com/a/24918670/8369030 it is suggested to use the Random String Parameter Plugin, how ever I can not find any documentation how to use it.
Alternatively I tried to do it with a random value like showed in https://stackoverflow.com/a/67110959/8369030, how ever this seems only to work in a Stage but not in a Parameter. Specifically, I always get null as default value when doing this:
pipeline {
environment {
max = 50
random_num = "${Math.abs(new Random().nextInt(max+1))}"
}
parameters {
string(name: 'JOB_ID', defaultValue: "${env.random_num}",
description: "Enter a random value to allow more than 2 jobs in the queue")
}
But it does not solve the problem with queues, because the parameter is overridden after execution, not before.
max = 50
random_num = "${Math.abs(new Random().nextInt(max+1))}"
println(random_num)
pipeline {
agent any
parameters {
string(name: 'rn', defaultValue: random_num,
description: "Enter a random value to allow more than 2 jobs in the queue")
}
stages {
stage('Randon number') {
steps {
println(random_num)
println(rn)
}
}
}
}
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] echo
31
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/test2
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Randon number)
[Pipeline] echo
31
[Pipeline] echo
25
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
Solution:
enter image description here

Expression depeding on Jenkins build boolean parameter doesn't work in pipeline

I've inherited some Jenkins pipeline and try to improve it. Jenkins and groovy is quite fresh topic for me, so most probably I'm doing something wrong.
I'm using Jenkins ver. 2.121.3
Main aim was to add build parameter to do some extra cleaning during build. So I've added parameter CLEAN_FIRST with Boolean type and default value false to a job configuration and did something like this in pipeline:
// CLEAN_FIRST = false
// def prefix = CLEAN_FIRST ? "" : "REM"
pipeline {
agent none
stages {
stage('Some step') {
steps {
script {
node('master') {
cleanWs()
try {
def prefix = CLEAN_FIRST ? "" : "REM"
echo "CLEAN_FIRST=$CLEAN_FIRST prefix=$prefix"
bat (label: 'build third party',
script: """
$prefix call cleanSomthing.bat
call doOtherStuff.bat
"""
} finally {
echo "some stuff"
}
} // node
} // script
} // steps
} // stage
} // stages
} // pipeline
Now this doesn't work as expected. "REM" prefix is not added.
Echo prints:
CLEAN_FIRST=false prefix=
And bat invokes cleanSomthing.bat which I wish to avoid (to save on build times).
I've tried to make prefix global, but with same result.
Most probably this is caused by some evaluation order or scoping issue, but I can't put finger on it.
Can someone give me a clue why it doesn't work? How to fix it?
Answered own question. Is this problem fixed on some version of Jenkins?
replace
def prefix = CLEAN_FIRST ? "" : "REM"
with
def prefix = params.CLEAN_FIRST ? "" : "REM"
Ok I've found source of problems. It is a bit funny.
When running this pipeline (tested on Mac machine since it had empty job queue):
pipeline {
agent none
stages {
stage('Some step') {
steps {
script {
node('Mac') {
cleanWs()
try {
def logic = true
def prefix = CLEAN_FIRST ? "Ole" : "REM"
def typeLogic = logic.getClass()
def typeParam = CLEAN_FIRST.getClass()
echo "typeLogic=$typeLogic typeParam=$typeParam"
echo "CLEAN_FIRST=$CLEAN_FIRST prefix=$prefix"
sh (script: """
echo prefix=$prefix
""")
} finally {
echo "some stuff"
}
} // node
} // script
} // steps
} // stage
} // stages
} // pipeline
I've got this outcome:
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] stage
[Pipeline] { (Some step)
[Pipeline] script
[Pipeline] {
[Pipeline] node
Running on master in /Users/builder/jenkins/workspace/EIbuild_MacOS
[Pipeline] {
[Pipeline] cleanWs
[WS-CLEANUP] Deleting project workspace...[WS-CLEANUP] done
[Pipeline] echo
typeLogic=class java.lang.Boolean typeParam=class java.lang.String
[Pipeline] echo
CLEAN_FIRST=false prefix=Ole
[Pipeline] sh
[EIbuild_MacOS] Running shell script
+ echo prefix=Ole
prefix=Ole
[Pipeline] echo
some stuff
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] End of Pipeline
Finished: SUCCESS
So now source the problem is obvious.
Jenkins in configuration promises variable of type Boolean, but in fact provides type String with values are "true" or "false" which are always evaluated as true when used as condition since both values are not empty strings :).

Calling multiple downstream jobs from an upstream Jenkins pipeline job

I have two pipeline based jobs
Parent_Job (has string parameters project1 & project2)
#NonCPS
def invokeDeploy(map) {
for (entry in map) {
echo "Starting ${entry.key}"
build job: 'Child_Job', parameters: [
string(name: 'project', value: entry.key),
string(name: 'version', value: entry.value)
], quietPeriod: 2, wait: true
echo "Completed ${entry.key}"
}
}
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
invokeDeploy(params)
}
}
}
}
}
Child_Job (has string parameters project & version)
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
echo "${params.project} --> ${params.version}"
}
}
}
}
}
Parent job output
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] script
[Pipeline] {
[Pipeline] echo
Starting project2
[Pipeline] build (Building Child_Job)
Scheduling project: Child_Job
Starting building: Child_Job #18
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
I expected the downstream job to be called twice, (for project1 and project2) but its invoked only once (for project2)
Is there something obviously wrong with this script?
It seems that the problem with wait: true option enabled for build job step. If you change it to wait: false it will execute 2 times. I tried it on this test pipeline:
#NonCPS
def invokeDeploy(map) {
for (entry in map) {
echo "Starting ${entry.key}"
build job: 'pipeline', quietPeriod: 2, wait: false
echo "Completed ${entry.key}"
}
}
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
def sampleMap = [first_job:'First', second_job:'Second']
invokeDeploy(sampleMap)
}
}
}
}
}

can not create file via Groovy code(or java code) in jenkinsfile of a pipeline job on Jenkins

pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
echo "whoami".execute().text
script {
File f = new File('/home/jenkins/test2.txt');
f.createNewFile();
}
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
Jenkins console log: (got exception: Started by user Edgar Yu Running
in Durability level: MAX_SURVIVABILITY [Pipeline] node Running on
Jenkins in /var/jenkins_home/workspace/test2 [Pipeline] { [Pipeline]
stage [Pipeline] { (Build) [Pipeline] echo Building.. [Pipeline] echo
jenkins
[Pipeline] script [Pipeline] { [Pipeline] } [Pipeline] // script
[Pipeline] } [Pipeline] // stage [Pipeline] stage [Pipeline] { (Test)
Stage 'Test' skipped due to earlier failure(s) [Pipeline] } [Pipeline]
// stage [Pipeline] stage [Pipeline] { (Deploy) Stage 'Deploy' skipped
due to earlier failure(s) [Pipeline] } [Pipeline] // stage [Pipeline]
} [Pipeline] // node [Pipeline] End of Pipeline
java.io.IOException: Permission denied at java.io.UnixFileSystem.createFileExclusively(Native Method) at
java.io.File.createNewFile(File.java:1012)
This is due to Jenkins not implementing Groovy itself but an interpreter (CPS) - https://github.com/cloudbees/groovy-cps
To help deal with the complexities introduced, there are some common Steps implemented to take the trouble out of tasks such as creating a file.
To use Jenkins pipeline steps out of the box, use writeFile:
https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/#code-writefile-code-write-file-to-workspace
writeFile([file: 'file.txt', text: filetxt])
If your deadset on writing your own, I suggest splitting it out into a Shared library, note this will probably cause ScriptSecurity alerts that will require approval:
final class PipelineUtils implements Serializable {
private script=null
private static final PipelineUtils instance = new PipelineUtils()
#NonCPS
String saveFile(String filename, String text) {
String PWD = script.pwd()
String filePath = "${PWD}/${filename}"
File file = new File(filePath)
file.text = text
}
}
See https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md for information regarding #NonCPS and nonserializable objects.

Resources