Jenkins Environment Variables in Groovy Init - shell

I am building a Docker image of Jenkins, and have passed ENV variables to the jenkins.sh initialization file:
Dockerfile
...
COPY ./jenkins.sh /usr/local/bin/jenkins.sh
jenkins.sh
echo ENV: "$ENV"
echo CLUSTER: "$CLUSTER"
echo REGION: "$REGION"
When I run the image, these values print out perfectly, but I would like to use them in Groovy scripts during the initialization of Jenkins.
The following throws an error during start:
import java.util.Arrays
import java.util.logging.Logger
Logger logger = Logger.getLogger("ecs-cluster")
logger.info("Loading Archeus-Midwayer...")
import jenkins.model.*
instance = Jenkins.getInstance()
def env = System.getenv()
println(env['CLUSTER'])
Error
WARNING: Failed to run script file:/var/jenkins_home/init.groovy.d/init_ecs.groovy
groovy.lang.MissingPropertyException: No such property: CLUSTER for class: init_ecs
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:307)
How can I capture the environment variables present in jenkins.sh?
Thanks!

Check the env vars with:
def env = System.getenv()
env.each {
println it
}
Export the env vars in jenkins.sh.
See also Access to build environment variables from a groovy script in a Jenkins build step ( Windows).

Related

How to extract command output from the multi lines shell in Jenkins

How to get the output of kubectl describe deployment nginx | grep Image in an environment variable?
My code:
stage('Deployment'){
script {
sh """
export KUBECONFIG=/tmp/kubeconfig
kubectl describe deployment nginx | grep Image"""
}
}
In this situation, you can access the environment variables in the pipeline scope within the env object, and assign values to its members to initialize new environment variables. You can also utilize the optional returnStdout parameter to the sh step method to return the stdout of the method, and therefore assign it to a Groovy variable (because it is within the script block in the pipeline).
script {
env.IMAGE = sh(script: 'export KUBECONFIG=/tmp/kubeconfig && kubectl describe deployment nginx | grep Image', returnStdout: true).trim()
}
Note you would also want to place the KUBECONFIG environment variable within the environment directive at the pipeline scope instead (unless the kubeconfig will be different in different scopes):
pipeline {
environment { KUBECONFIG = '/tmp/kubeconfig' }
}
You can use the syntax:
someVariable = sh(returnStdout: true, script: some_script).trim()

Jenkins Declarative Pipeline is not supporting shell/bash syntax

I have a shell script inside my jenkins pipeline which will call mvn. For that i have to pass variable value to mvn. The variable is not passing inside the Jenkins pipeline's shell. But when trying from local machine shell it is working fine as expected.
ARTIFACT_NAME="Sample_Artifact"
pipeline{
agent {
node{
label "${AGENT}"
}
}
stages{
stage("Setting MultiJob Properties"){
steps{
sh '''set +x
export VERSION=$(mvn -B -q -Dexec.executable=echo -Dexec.args=\${${ARTIFACT_NAME}} )
echo $VERSION
'''
}
}
}
}
Expected Process: export VERSION=$(mvn -B -q -Dexec.executable=echo -Dexec.args=${Sample_Artifact} )
Expected Output: 1.0001
ARTIFACT_NAME - I am passing it from Jenkins UI.
${${ARTIFACT_NAME}} - This variable is perfectly replace value in Freestyle jobs and it is throwing error in the Pipeline jobs.
Error Message: script.sh: 3: Bad substitution
Can Anyone please help me to resolve the issue?
As Ian wrote, you're passing the whole script as a literal (''') instead of an interpolated string ("""), so the variable name doesn't get substituted with its value:
pipeline{
agent {
node {
label AGENT
}
}
stages {
stage("Setting MultiJob Properties") {
steps {
sh """set +x
export VERSION=\$(mvn -B -q -Dexec.executable=echo -Dexec.args=\${$ARTIFACT_NAME})
echo \$VERSION"""
}
}
}
}

Env variable value got reset to original even after assigning the pom version number in jenkins script

I have a scenario where i have to read the maven pom versions for different components and assign the version to docker image(TAG). But after i read the pom, assigned it to some global variable it will reset to original value in groovy jenkins script. Below is the sample. HMAP_VERSION value will 1.2.1 but when it is used in the line: sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} test.com" the value will be UNINITIALISED.
Can somebody tell me what might have gone wrong? This will work with single maven file which is read in env block as below:
environment {
CLOADER_VERSION = readMavenPom().getVersion()
}
Below is the sample of what im tring to do.
#! groovy
environment {
HMAP_VERSION = "UNINITIALISED"
CLOADER_VERSION = "UNINITIALISED"
}
stages {
stage('Build Cloader') {
steps {
checkout([$class: 'GitSCM' "rest is removed")
dir('isa-casloader') {
script {
CLOADER_VERSION = readMavenPom().getVersion()
}
container('build') {
sh '/opt/apache-maven/bin/mvn -s settings.xml -B clean install -DskipTests=true'
}
}
}
}
stage ('Build Casloader Docker Image') {
steps {
dir('isa-casloader') {
container('tools') {
echo("CLOADER_VERSION=${CLOADER_VERSION}")
withCredentials() {
sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} testing.com"
sh 'docker build -t testing.com:${CLOADER_VERSION} .'
sh 'docker push testing.com:${CLOADER_VERSION}'
}
}
}
}
}
stage ('Build Heat Map Docker Image') {
steps {
checkout([$class: 'GitSCM', "rest is commented"])
dir('apps') {
container('tools') {
script {
def pom = readMavenPom file: 'pom-docker.xml'
HMAP_VERSION = pom.version
}
echo("HMAP_VERSION=${HMAP_VERSION}")
withCredentials() {
sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} test.com"
sh 'docker build -t test.com:${HMAP_VERSION} .'
sh 'docker push test.com:${HMAP_VERSION}'
}}}}}}}
By my read of your code, you're mixing environment variables with variables within the Groovy context.
These lines create environment variables, which are accessible in the shell as $HMAP_VERSION and $CLOADER_VERSION:
environment {
HMAP_VERSION = "UNINITIALISED"
CLOADER_VERSION = "UNINITIALISED"
}
However, you're populating a Groovy variable here:
script {
CLOADER_VERSION = readMavenPom().getVersion()
}
To instead populate the environment variable, you'd want to use env.CLOADER_VERSION instead.
This changes what context the variables are evaluated in when you're calling out to shell using the sh directive:
1-> sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} testing.com"
2-> sh 'docker build -t testing.com:${CLOADER_VERSION} .'
3-> sh 'docker push testing.com:${CLOADER_VERSION}'
In line number 1 above, the command is quoted using a double quotes (") which means that the variables ART_USERNAME and ART_PASSWORD are evaluating in the context of the Groovy script.
However, in lines 2 and 3 the commands are quoted using a single quote (') which means that those variables are being evaluated by the shell (likely /bin/sh) and therefore using the values from the environment.
The easiest fix would be to ensure that values you want exposed in the shell are always accessed using the env. prefix in the Groovy context:
// set environment for CLOADER_VERSION
env.CLOADER_VERSION = readMavenPom().getVersion()
// print value of environment variable CLOADER_VERSION
echo("CLOADER_VERSION=${env.CLOADER_VERSION}")
// set environment for HMAP_VERSION
env.HMAP_VERSION = pom.version
// print value of environment variable HMAP_VERSION
echo("HMAP_VERSION=${env.HMAP_VERSION}")
Cheers.
Thanks for the response. My issue got resolved. In docker context as shown below,
withCredentials() {
sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} testing.com"
sh 'docker build -t testing.com:${CLOADER_VERSION} .'
sh 'docker push testing.com:${CLOADER_VERSION}'
}
Login command is proper which is inside double quotes, but the next statements were in single quotes. So variables latest value was not getting resolved. When i change the statements to be inside double quotes, it worked!!
Below is the proper command:
withCredentials() {
sh "docker login -u ${ART_USERNAME} -p ${ART_PASSWORD} testing.com"
sh "docker build -t testing.com:${CLOADER_VERSION} ."
sh "docker push testing.com:${CLOADER_VERSION}"
}
Thanks you.

Change groovy variables inside shell executor in Jenkins pipeline

I have a Jenkins pipeline job where I am taking some build variables as input, and if the variables are not passed by the user, I execute a script and get the value of those variables. Later I have to use the value of these variables to trigger other jobs.
So my code looks something like this:
node {
withCredentials([[$class: 'StringBinding', credentialsId: 'DOCKER_HOST', variable: 'DOCKER_HOST']]) {
env.T_RELEASE_VERSION = T_RELEASE_VERSION
env.C_RELEASE_VERSION = C_RELEASE_VERSION
env.N_RELEASE_VERSION = N_RELEASE_VERSION
env.F_RELEASE_VERSION = F_RELEASE_VERSION
....
stage concurrency: 1, name: 'consul-get-version'
sh '''
if [ -z ${T_RELEASE_VERSION} ]
then
export T_RELEASE_VERSION=$(ruby common/consul/services_prod_version.rb prod_t_release_version)
aws ecr get-login --region us-east-1
aws ecr list-images --repository-name t-server | grep ${T_RELEASE_VERSION}
else
aws ecr get-login --region us-east-1
aws ecr list-images --repository-name t-server | grep ${T_RELEASE_VERSION}
fi
.......
't-integ-pipeline' : {
build job: 't-integ-pipeline', parameters: [[$class: 'StringParameterValue', name: 'RELEASE_VERSION', value: T_RELEASE_VERSION],
[$class: 'BooleanParameterValue', name: 'FASTFORWARD_TO_DEPLOY', value: true]]
},
......
The issue is when I am triggering the main job with empty T_RELEASE_VERSION, the child build job t-integ-pipeline is triggered with an empty value of the RELEASE_VERSION parameter.
How can I change a groovy parameter inside a shell executor and then access it again in the groovy executor with the modified value?
When using env-inject it was possible to store the values in the properties files and the inject them as environment variables. Couldn't find any easy way to do it in pipeline.
Here is a solution anyway, store the values to a file, and read the file from the pipeline. Then use eval or similar to transform it to an parsable object (hash).
Eval.me example: Serializing groovy map to string with quotes
Write/Read to file example:
https://wilsonmar.github.io/jenkins2-pipeline/
EDIT
Manish solution for readability:
sh 'ruby common/consul/services_prod_version.rb prod_n_release_version > status'
N_RELEASE_VERSION_NEW = readFile('status').trim()
sh 'ruby common/consul/services_prod_version.rb prod_q_release_version > status'
Q_RELEASE_VERSION_NEW = readFile('status').trim()
I found a way change the groovy variable in the shell, No need to store it in the file, There is a example here git-tag-message-plugin, I use this method like below:
script{
N_RELEASE_VERSION_NEW = getN_RELEASE_VERSION_NEW()
}
String getN_RELEASE_VERSION_NEW() {
return sh(script: "ruby common/consul/services_prod_version.rb prod_n_release_version ", returnStdout: true)?.trim()
}

Using Finagle's clientbuilder, how do I set the host externally?

I am building a simple proxy to point to another server. Everything works but I need to find a way to be able to set the hosts in a ClientBuilder externally most likely using Docker or maybe some sort of configuration file. Here is what I have:
import java.net.InetSocketAddress
import com.twitter.finagle.Service
import com.twitter.finagle.builder.{ServerBuilder, ClientBuilder}
import com.twitter.finagle.http.{Request, Http}
import com.twitter.util.Future
import org.jboss.netty.handler.codec.http._
object Proxy extends App {
val client: Service[HttpRequest, HttpResponse] = {
ClientBuilder()
.codec(Http())
.hosts("localhost:8888")
.hostConnectionLimit(1)
.build()
}
val server = {
ServerBuilder()
.codec(Http())
.bindTo(new InetSocketAddress(8080))
.name("TROGDOR")
.build(client)
}
}
If you know of a way to do this or have any ideas about it please let me know!
if you want running this simple proxy in a docker container and manage the target host ip dynamically, you can try to pass a target host ip through environment variable and change your code like this
import java.net.InetSocketAddress
import com.twitter.finagle.Service
import com.twitter.finagle.builder.{ServerBuilder, ClientBuilder}
import com.twitter.finagle.http.{Request, Http}
import com.twitter.util.Future
import org.jboss.netty.handler.codec.http._
object Proxy extends App {
val target_host = sys.env.get("TARGET_HOST")
val client: Service[HttpRequest, HttpResponse] = {
ClientBuilder()
.codec(Http())
.hosts(target_host.getOrElse("127.0.0.1:8888"))
.hostConnectionLimit(1)
.build()
}
val server = {
ServerBuilder()
.codec(Http())
.bindTo(new InetSocketAddress(8080))
.name("TROGDOR")
.build(client)
}
}
this will let your code read system environment variable TARGET_HOST. when you done this part, you can try to start your docker container by adding the following parameter to your docker run command:
-e "TARGET_HOST=127.0.0.1:8090"
for example docker run -e "TARGET_HOST=127.0.0.1:8090" <docker image> <docker command>
note that you can change 127.0.0.1:8090 to your target host.
You need a file server.properties and put your configuration inside the file:
HOST=host:8888
Now get docker to write your configuration with every startup with a docker-entrypoint bash script. Add this script and define environment variables inside your Dockerfile:
$ ENV HOST=myhost
$ ENV PORT=myport
$ ADD docker-entrypoint.sh /docker-entrypoint.sh
$ ENTRYPOINT ["/docker-entrypoint.sh"]
$ CMD ["proxy"]
Write out your docker-entrypoint.sh:
#!/bin/bash -x
set -o errexit
cat > server.properties << EOF
HOST=${HOST}:${PORT}
EOF
if [ "$1" = 'proxy' ]; then
launch server
fi
exec "$#"
Launch Docker with your configuration and the command "proxy":
$ docker run -e "HOST=host" -e "PORT=port" image proxy
You can also do linking when your not sure of your server container ip adress:
$ docker run -e "HOST=mylinkhost" -e "PORT=port" --link myservercontainer:mylinkhost image proxy

Resources