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.
Related
In the jenkinsfile I have started using environment context in one of the stage and now there is a requirement to concatenate with one static value which is 'grafana-' with the variable declared in the Jenkins configuration and assign the output to a new variable.
CLUSTER_NAME is the key/variable and value is TEST under jenkins > configuration
Tried different ways but couldn't get value for variable CLUSTER_NAME
environment {
def INSTANCE_NAME='grafana-${env.CLUSTER_NAME}'
}
Use doudble quotes instead of single quotes.
environment {
def INSTANCE_NAME = "grafana-${env.CLUSTER_NAME}"
}
I'm using the Jenkins scripted pipeline and having trouble understanding how to nest environment variables within each other, here is a MWE:
// FROM https://jenkins.io/doc/pipeline/examples/#parallel-multiple-nodes
def labels = []
if (HOST == 'true') {
labels.add(<HOSTNAME>)
}
def builders = [:]
for (x in labels) {
def label = x
builders[label] = {
ansiColor('xterm') {
node(label) {
stage('cleanup') {
deleteDir()
}
stage('build') {
env.test = "TESTA"
env.intern = '''
TEST = "${env.test}"
'''
sh '''
echo $intern
printenv
'''
}
}
}
}
}
parallel builders
The idea here is that env.test contains the value TESTA, which sets env.intern to TEST = TESTA this is what I want to happen. After this the code is just to print out the values.
Sadly the result is TEST = "${env.test}".
How can I use nested string environment variables in Jenkins scripted pipeline?
The syntax difference here is literal strings versus interpolated strings in Groovy versus shell interpreters within shell step methods.
": interpolated string in Groovy
': literal string in Groovy and interpolated string in shell interpreter
Essentially, a Groovy variable is interpolated within " in the pipeline execution, and an environment variable is interpolated within " in the pipeline execution and within ' in the shell interpreter (and within the pipeline must also be accessed within the env object, but is a first class variable expression in the shell step method).
Therefore, we can fix the assigned value of env.intern with:
env.intern = "TEST = ${env.test}"
where the assigned value of env.test will be interpolated within the Groovy string and assigned to the env pipeline object at the intern key. This will then also be accessible to the shell interpreter within shell step methods, and the rest of your pipeline is already correct and will behave as expected.
Try the following:
env.intern = "TEST = ${env.test}"
As you're setting it now, the actual result of env.intern will be "TEST= ${env.test}". So long as you set env.test before you set env.intern you should be good. Also, good to note that if you change the value of env.test then you need to reset the value of env.intern or it's going to hold the original value of env.test that it was set to.
Let me preface this by saying that I don't yet fully understand how jenkins DSL / groovy handles namespace, scope, variables etc.
In order to keep my code DRY I put repeated command sequences into variables.
It turns out the variable script below is not readable by the code in doParallelStuff. Why is that? Is there a way to share global variables defined in the script (or elsewhere) among both the main pipleine steps and the doParallelStuff code?
def script = """\
#/bin/bash
python xyz.py
"""
def doParallelStuff() {
tests["1"] = {
node {
stage('ps1') {
sh script
}
}
}
tests["2"] = {
node {
stage('ps2') {
sh script
}
}
}
parallel tests
}
pipeline {
stages {
stage("myStage") {
steps {
script {
sh script
doParallelStuff()
}
}
}
}
}
The actual steps are a bit more complicated, but this causes an error like the following to be thrown:
hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: script for class: WorkflowScript
When you define a variable outside of the pipeline directive scope using the def keyword you are defining it in the local scope of the main script, because the pipeline keyword is actually a method that is executed in the main script it can access the variable is they are defined and executed in the same scope (they are actually transformed into a separated class).
When you define a function outside of the pipeline directive, that function has its own scope for variables which is separated from the scope of the main script and therefore it cannot access the defined variable in the top level.
To solve it you can define the variable without the def keyword which will affect the scope in which this variable is created, as without the def (in a groovy script, not class) the variable is added to the global variables of the script (The Binding) which makes it accessible from any function or code within the groovy script. You can read more on the following question: What is the difference between defining variables using def and without?
So in your case you want a variable that is available for both the pipeline code itself and for the defined functions - so it needs to be available anywhere in the script as a global variable and therefore just define it without the def keyword, and it should do the trick:
script = """\
#/bin/bash
python xyz.py
"""
I'm trying to generate Jenkins pipelines using the pipelineJob function in the jobDSL pluging, but cannot pass parameters from the DSL to the pipeline script. I have several projects that use what is essentially the same Jenkinsfile, with differences only in a few steps. I'm trying to use the JobDSL plugin to generate these pipelines on the fly, with the values I want changed in them interpreted to match the parameters to the DSL.
I've tried just about every combination of string interpretation that I can in the pipeline script, as well as in the DSL, but cannot get Jenkins/groovy to interpret variables in the pipeline script.
I'm calling the job DSL in a pipeline step:
def projectName = "myProject"
def envs = ['DEV','QA','UAT']
def repositoryURL = 'myrepo.com'
jobDsl targets: ['jobs/*.groovy'].join('\n'),
additionalParameters: [
project: projectName,
environments: envs,
repository: repositoryURL
],
removedJobAction: 'DELETE',
removedViewAction: 'DELETE'
The DSL is as follows:
pipelineJob("${project} pipeline") {
displayName('Pipeline')
definition {
cps {
script(readFileFromWorkspace(pipeline.groovy))
}
}
}
pipeline.groovy:
pipeline {
agent any
environment {
REPO = repository
}
parameters {
choice name: "ENVIRONMENT", choices: environments
}
stages {
stage('Deploy') {
steps {
echo "Deploying ${env.REPO} to ${params.ENVIRONMENT}..."
}
}
}
}
The variables that I pass in additionalParameters are interpreted in the jobDSL script; a pipeline with the correct name does get generated. The problem is that the variables are not passed to the pipeline script read from the workspace - the Jenkins configuration for the generated pipeline looks exactly the same as the file, without any interpretation on the variables.
I've made a number of attempts at getting the string to interpret, including a lot of variations of "${environments}", ${environments}, $environments, \$environments...I can't find any that work. I've also tried reading the file as a gstringImpl:
script("${readFileFromWorkspace(pipeline.groovy)}")
Does anyone have any ideas as to how I can make variables propagate down to the pipeline script? I know that I could just use a for loop to do string.replaceAll() on the script text, but that seems cumbersome; there's got to be a better way.
I've come up with a way to make this work. It's not what I'd prefer, which is having the string contents of the file implicitly interpreted during job creation, but it does work; it just adds an extra step.
import groovy.text.SimpleTemplateEngine
def fileContents = readFileFromWorkspace "pipeline.groovy"
def engine = new SimpleTemplateEngine()
template = engine.createTemplate(fileContents).make(binding.getVariables()).toString()
pipelineJob("${project} pipeline") {
displayName('Pipeline')
definition {
cps {
script(template)
}
}
}
This reads a file from your workspace, then uses it as a template with the binding variables. The other changes needed to make this work are escaping any variables used in your Jenkinsfile script, like \${VARIABLE} so that they are expanded at runtime, not at the time you build the job. Any variables you want to be expanded at job creation should be referenced as ${VARIABLE}.
You could achieve what you're trying to do by defining environment variables in the pipelineJob and then using those variables in your pipeline.
They are a bit limited because environment variables are strings, but it should work for basic stuff
Ex.:
//job-dsl
pipelineJob('example') {
environmentVariables {
// these vars could be specified by parameters of this job
env('repository', 'blah')
env('environments', "a,b,c"]) //comma separated string
}
displayName('Pipeline')
definition {
cps {
script(readFileFromWorkspace(pipeline.groovy))
}
}
}
}
And then in the pipeline:
//pipeline.groovy
pipeline {
agent any
environment {
REPO = env.repository
}
parameters {
choice name: "ENVIRONMENT", choices: env.environments.split(',')
//note the need to split the comma separated string above
}
}
You need to use the complete job name as a variable without the quotes. E.g., if JOBNAME is a parameter containing the entire job name:
pipelineJob(JOBNAME) {
displayName('Pipeline')
definition {
cps {
script(readFileFromWorkspace(pipeline.groovy))
}
}
}
I need to accept all kinds of global Jenkins variables as strings (basically as parameters to ansible like system - a template stored in \vars).
def proof = "\"${params.REPOSITORY_NAME}\""
echo proof
def before = "\"\${params.REPOSITORY_NAME}\""
echo before
def after = Eval.me(before)
echo after
The result is:
[Pipeline] echo
"asfd"
[Pipeline] echo
"${params.REPOSITORY_NAME}"
groovy.lang.MissingPropertyException: No such property: params for class: Script1
the first echo proves that the param value actually exists.
the second echo is the what the input actually looks like.
the third echo should have emitted asdf instead I get the exception.
Any ideas? I'm hours into this :-(
You may want to check:
groovy: Have a field name, need to set value and don't want to use switch
1st Variant
In case you have: xyz="REPOSITORY_NAME" and want the value of the parameter REPOSITORY_NAME you can simply use:
def xyz = "REPOSITORY_NAME"
echo params."$xyz" // will print the value of params.REPOSITORY_NAME
In case if your variable xyz must hold the full string including params. you could use the following solution
#NonCPS
def split(string) {
string.split(/\./)
}
def xyz = "params.REPOSITORY_NAME"
def splitString = split(xyz)
echo this."${splitString[0]}"."${splitString[1]}" // will print the value of params.REPOSITORY_NAME
2nd Variant
In case you want to specify an environment variable name as parameter you can use:
env.“${params.REPOSITORY_NAME}”
In plain groovy env[params.REPOSITORY_NAME] would work but in pipeline this one would not work inside the sandbox.
That way you first retrieve the value of REPOSITORY_NAME and than use it as key to a environment variable.
Using directly env.REPOSITORY_NAME will not be the same as it would try to use REPOSITORY_NAME itself as the key.
E.g. say you have a job named MyJob with the following script:
assert(params.MyParameter == "JOB_NAME")
echo env."${params.MyParameter}"
assert(env."${params.MyParameter}" == 'MyJob')
This will print the name of the job (MyJob) to the console assuming you did set the MyParameter parameter to JOB_NAME. Both asserts will pass.
Please don’t forget to open a node{} block first in case you want to retrieve the environment of that very node.
After trying all those solutions, found out that this works for my problem (which sounds VERY similar to the question asked - not exactly sure though):
${env[REPOSITORY_NAME]}