Using env variables in a Jenkinsfile function? - jenkins

I'm triggering one Jenkins job from the other, all via Jenkinsfiles.
stage("Trigger another job")
{
build([job:"Job2", wait:true, propagate:true, parameters: [string(name:'branch_my',value:"${env.ghprbActualCommit}")]])
}
Note that parameter branch_my is sent to Job2. However, the pipeline Job2 needs to work even when branch_my is NOT defined, for example when it is triggered manually.
Jenkinsfile for Job2 looks like:
pipeline
{
// ...
steps
{
customBranches()
// etc...
}
}
def customBranches()
{
if ( env.branch_my != null)
{
sh "switch_to ${env.branch_my}"
}
}
However, the customBranches() if statement never evaluates to true. When I do
sh "echo 'Env branch_my is: ${env.branch_my} '"
I get Env branch_my is: some_value , which is OK, and if statement should evaluate to true - but it does not.
I tried adding ${} like so: if ( ${env.branch_my} != null), but that failed completely: No such DSL method "$" found.
What's wrong with my customBranches()?

Problem isn't the Jenkinsfile syntax, but the Jenkins job configuration: it must be labeled as "parametrized" in the GUI and a string parameter branch_my needs to be defined:
Note, the parameters can be added via the Jenkinsfile itself:
parameters { string(name: 'branch_my', defaultValue: 'master', description: '') }
However, this just adds the parameter to the GUI, so you end up with the same thing.

Related

Using same Jenkinsfile for two separate jobs in same repo

I have two separate Jenkins jobs that will run on one repository: My Jenkinsfile has a step that will run with this property enabled: enableZeroDownTime. The purpose of the 2nd Jenkins Job is to run the step with this property enableZeroDownTime disabled. Does anyone know how I can control it using the same Jenkinsfile? Can I pass that using some parameter based on any properties file? I am really confused on this.
stage('CreateCustomer') {
steps {
script {
common.runStage("#CreateCustomer")
common.runStage("#SetOnboardingCustomerManifest")
common.runStage("#enableZeroDownTime")
}
}
}
Solution
I currently run multiple pipelines that use the same Jenkinsfile. The change to conditionally execute a stage is trivial.
stage('CreateCustomer') {
when {
environment name: 'enableZeroDownTime', value: 'true'
}
steps {
script {
common.runStage("#CreateCustomer")
common.runStage("#SetOnboardingCustomerManifest")
common.runStage("#enableZeroDownTime")
}
}
}
The CreateCustomer stage will only run when the enableZeroDownTime parameter is set to true ( it can be a String parameter with value true, or a boolean parameter ).
The trick here is that you cannot add the parameters{} block to your declarative pipeline. For example if you had the following
parameters {
string(name: 'enableZeroDownTime', defaultValue: 'true')
}
Both pipelines would default to true. If you had the following
parameters {
string(name: 'enableZeroDownTime', defaultValue: '')
}
Both pipelines would default to a blank default value.
Even if you manually save a different default value to the pipeline after creation it will be overwritten next run with a blank default value.
Instead you simply need to remove the parameters{} block altogether and manually add the parameters through the web interface
Additionally...
Additionally it is possible to have two pipelines use the same Jenkinsfile with different parameters. For example, lets say Pipeline A had a enableZeroDownTime parameter defaulted to true and Pipeline B had no parameters at all. In this case you can add an environment variable of the same name and set the value equal to the following ternary expression
environment {
enableZeroDownTime = "${params.enableZeroDownTime != null ? "${params.enableZeroDownTime}" : false}"
}
You can then reference this parameter in the when declarative ( or anywhere in the pipeline ) without fear of the pipeline throwing a null pointer exception.

Accessing choice parameter values from within Jenkins declarative pipelines

In my declarative pipeline I have a choice parameter as follows:
parameters {
choice(name: 'sleep_time',
choices: ['2.5m', '2m', '15s', '50s', '4m', '1m', '1.5m', '1.5s', 'random'],
description: "the sleep time to execute after the command")
}
How can I get all the values of the parameter from within my declarative pipeline?
In this example I am expecting to get the list of 2.5m, 2m, 15s and so on.
During execution the parameter value will hold only the selected choice and not the entire list of options, however because the parameter is defined as part of the pipeline script, you can define the option list as a global parameter for your pipeline and use in in the parameter definition and in any other place in the pipeline.
Something like:
// Define the options as a global parameter
SLEEP_OPTIONS = ['2.5m', '2m', '15s', '50s', '4m', '1m', '1.5m', '1.5s', 'random']
pipeline {
agent any
parameters {
choice(name: 'sleep_time',
choices: SLEEP_OPTIONS, // Use the global parameter
description: "the sleep time to execute after the command")
}
stages {
stage('Use Global Parameter') {
steps {
script {
SLEEP_OPTIONS.each { // SLEEP_OPTIONS is available for all steps in the pipeline
println it
}
}
}
}
}
}

String interpolation and concatenation for Jenkinsfile

I am trying to setup a Jenkinsfile that gets a job to call other jobs, based on the parameters passed into itself.
Instead of having multiple when conditions, I'm thinking that it would be smarter (and manageable for future scaling) if the names of the jobs being called would ideally be concatenating a common prefix with the parameter being passed in, for example:
CICD_api-gateway
CICD_front-end
CICD_customer-service
I'm having difficulty mixing string interpolation with string concatenation to achieve this:
build job: 'CICD_"${params.SERVICE_NAME}"', wait : false
In Linux, we are able to use eval to achieve this. I'm not sure what the equivalent is in Jenkinsfile syntax.
The full code below:
pipeline {
agent any
parameters { string(name: 'SERVICE_NAME', defaultValue: '', description: 'Service being deployed.') }
stages {
stage('Build Trigger'){
steps{
echo "CICD_${params.SERVICE_NAME}"
build job: 'CICD_"${params.SERVICE_NAME}"', wait : false
}
}
}
}
Change it to be a Gstring from the beginning, no need for the single quotes:
build job: "CICD_${params.SERVICE_NAME}", wait : false

Dynamic variable in Jenkins pipeline with groovy method variable

I have a Jenkinsfile in Groovy for a declarative pipeline and two created Jenkins variables with names OCP_TOKEN_VALUE_ONE and OCP_TOKEN_VALUE_TWO and the corresponding values. The problem comes when I try to pass a method variable and use it in an sh command.
I have the next code:
private def deployToOpenShift(projectProps, environment, openshiftNamespaceGroupToken) {
sh """/opt/ose/oc login ${OCP_URL} --token=${openshiftNamespaceGroupToken} --namespace=${projectProps.namespace}-${environment}"""
}
The problem is, the method deployToOpenShift has in the openshiftNamespaceGroupToken variable, a value that is the name of variable that has been set in Jenkins. It needs to be dynamic and the problem is that Jenkins don't resolve the Jenkins variable value, just the one passed as String, I mean, the result is:
--token=OCP_TOKEN_VALUE_ONE
If I put in the code
private def deployToOpenShift(projectProps, environment, openshiftNamespaceGroupToken) {
sh """/opt/ose/oc login ${OCP_URL} --token=${OCP_TOKEN_VALUE_ONE} --namespace=${projectProps.namespace}-${environment}"""
}
works perfect but is not dynamic that is the point of the method variable. I have tried with the """ stuff as you can see, but not working.
Any extra idea?
Edited with the code that calls the method:
...
projectProps = readProperties file: './gradle.properties'
openShiftTokenByGroup = 'OCP_TOKEN_' + projectProps.namespace.toUpperCase()
...
stage ('Deploy-Dev') {
agent any
steps {
milestone ordinal : 10, label: "Deploy-Dev Milestone"
deployToOpenShift(projectProps, 'dev', openShiftTokenByGroup)
}
}
I have got two different ways to do that. One is using evaluate from groovy like this:
def openShiftTokenByGroup = 'OCP_TOKEN_' + projectProps.namespace.toUpperCase()
evaluate("${openShiftTokenByGroup}") //This will resolve the configured value in Jenkins
The second one is the same approach but in the sh command with eval escaping the $ character:
sh """
eval \$$openShiftTokenByGroup
echo "Token: $openShiftTokenByGroup
"""
This will do the magic too and you'll get the Jenkins configured value.

How to run downstream job conditionally based on a parameter in Jenkins pipeline?

Following on from my question How to trigger parameterized build on successful build in Jenkins?
I would like the invoke a downstream project but only if a boolean parameter is set to true. Is this possible? My pipeline looks like this:
node {
try {
echo "ConfigFilePath: ${ConfigFilePath}"
echo "Delete VM on Successful Build: ${DeleteOnSuccess}"
stage('checkout') {
deleteDir()
git 'http://my.git.lab/repo.git'
}
stage('deploy') {
bat 'powershell -nologo -file BuildMyVM.ps1 -ConfigFilePath "%ConfigFilePath%" -Verbose'
}
}
stage('test') {
// functional tests go here
}
}
catch (e) {
// exception code
} finally {
// finally code
}
} //node
stage('delete') {
if(DeleteOnSuccess)
{
bat 'SET /p VM_NAME=<DeleteVM.txt'
echo "Deleting VM_NAME: %VM_NAME%"
def job = build job: 'remove-vm', parameters: [[$class: 'StringParameterValue', name: 'VM_NAME', value: '${VM_NAME}']]
}
}
I get this error on the delete stage
Required context class hudson.FilePath is missing.
Perhaps you forgot to surround the code with a step that provides this, such as: node
If I wrap the above in a node, then the parameter values are lost. If I put the delete stage in the main node, then I take up two executors, which I'm trying to avoid because it will result in some deadlock conditions.
The problem is that the running of a script actually needs a node to run on, so in your case the cause of the error is that you try to run a bat command outside of a node context
node {
...
}
stage('delete') {
if(DeleteOnSuccess)
{
bat 'SET /p VM_NAME=<DeleteVM.txt' // <- this is actually causing the error
echo "Deleting VM_NAME: %VM_NAME%"
def job = build job: 'remove-vm', parameters: [[$class: 'StringParameterValue', name: 'VM_NAME', value: '${VM_NAME}']]
}
}
You can fix this by wrapping this part also inside a node by either putting it inside the first node or add a new one, depending on what you want
Besides that, if the DeleteOnSuccess variable is a build parameter, it will be a string. I am not sure, but I think this is because it is injected as an environment variable, which are also strings (even if it is of type BooleanParameter. I guess that is only a UI thing so it will show up as checkbox).
You can check that by echoing DeleteOnSuccess.class. This will tell you its class
if(DeleteOnSuccess) { ... }
will always run the conditional block. You can fix this by either converting it to a bool using the toBoolean() extension method, or checking it against the the string true: DeleteOnSuccess == "true", like you did.
The extension method has the advantage that it will also allow values "1" and "True" as true

Resources