Basically I can't pass build properties to Library var call without extra nonsense.
jenkinsfile relevant chunk:
tc_test{
repo = 'test1'
folder = 'test2'
submodules = true
refs = params.GitCheckout
}
That results in error
java.lang.NullPointerException: Cannot get property 'GitCheckout' on
null object
This, however, works:
def a1 = params.GitCheckout
tc_test{
repo = 'test1'
folder = 'test2'
submodules = true
refs = a1
}
The contents of the vars/tc_test.groovy in shared library :
def call ( body ) {
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
try {
body()
} catch(e) {
currentBuild.result = "FAILURE";
throw e;
} finally {
config.each{ k, v -> println "${k}:${v}" }
}
}
I'm not really good with groovy, so it might be something obvious.
Got the answer from Jenkins JIRA.
Small workaround is using maps instead of closures:
tc_test ([
repo: 'test1',
folder: 'test2',
submodules: true,
refs = params.GitCheckout
])
May have drawbacks, but for me that worked perfectly.
Still have to transfer params as argument to have access to them, but at least the code makes more sense now.
Suppose you have a sharedLibrary to call a Rundeck Job,
Parameters:
1 runDeckJobId - Rundeck unique job id thats available in settings.
2 role - AD Group associated with Rundeck Job
3 runDeckProject - Name of the project configured in rundeck.
4 optional - All optional parameters as a Map.
- rundeckInstanceType - Rundeck instances are currently in UK or HK.
- retries - Number of retries for checking job status once started (Default value=100)
- timeout - Number of seconds to be waited before each retry (Default value=15)
- verbose - If verbose calls need to be made in the rundeck api calls (Default value=false)
- rundeckArgs - All rundeck parameters as a map. Eg: Name of the playBook, location of inventory file.
Example Usage in JenkinsFile:
if (isRundeckDeployment == "true") {
def optional = [
rundeckInstance : "UK",
timeout : 10,
rundeckArgs : [
artifactPack : "${artifactPath}",
DEPLOYMENT_ENVIRONMENT: "${deploymentEnvironment}",
EXTRA_ARGS : "-e deployment_serial=1"
]
]
triggerRundeckJob("job-id", "AD-group-id", "BitbucketKey", optional)
}
Shared Library Function with filename : triggerRundeckJob in vars folder
def call(String rundeckJobId, String role, String rundeckProject, Map optional) {
String jobUserId
wrap([$class: 'BuildUser']) {
jobUserId = "${BUILD_USER_ID}"
}
// Determine rundeck instance type, by default instance is UK (rundeckAuthToken)
String mainRundeckId = optional.rundeckInstance == "HK" ? "rundeckAuthTokenHK": "rundeckAuthToken"
String rundeckBaseURL = optional.rundeckInstance == "HK" ? "https://rundeckUrl/selfservice" : "https://rundeckUrl:9043/selfservice"
withCredentials([string(credentialsId: mainRundeckId, variable: 'mainRundeckIdVariable')]) {
int retries = optional.retries ?: 100
int timeout = optional.timeout ?: 15
String verbose = optional.verbose? "-v" : "-s"
String rundeckArgsString = optional.rundeckArgs.collect{ "-${it.key} \\\"${it.value}\\\"" }.join(" ")
def tokenResponse = sh(returnStdout: true, script: "curl -k ${verbose} -X POST -d '{\"user\": \"${jobUserId}\",\"roles\":\"${role}\",\"duration\":\"30m\"}' -H Accept:application/json -H 'Content-Type: application/json' -H X-Rundeck-Auth-Token:${mainRundeckIdVariable} ${rundeckBaseURL}/api/19/tokens")
def tokenResponseJson = readJSON text: tokenResponse
def rundeckResponse = sh(returnStdout: true, script: "curl -k ${verbose} --data-urlencode argString=\"${rundeckArgsString}\" -H Accept:application/json -H X-Rundeck-Auth-Token:${tokenResponseJson.token} ${rundeckBaseURL}/api/19/job/${rundeckJobId}/run")
def rundeckResponseJson = readJSON text: rundeckResponse
if(!rundeckResponseJson.error){
while(true){
if(retries==0) {
currentBuild.result = "FAILURE"
echo "Rundeck Job Timedout, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
break;
}
def jobStateResponse = sh(returnStdout: true, script:"curl -k ${verbose} -H Accept:application/json -H X-Rundeck-Auth-Token:${tokenResponseJson.token} ${rundeckBaseURL}/api/19/execution/${rundeckResponseJson.id}/state")
def jobStateResponseJson = readJSON text: jobStateResponse
if(jobStateResponseJson.completed) {
if(jobStateResponseJson.executionState == "FAILED") {
currentBuild.result = "FAILURE"
echo "Rundeck Job FAILED, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
break
}else{
currentBuild.result = "SUCCESS"
echo "Rundeck Job SUCCESS, See: ${rundeckBaseURL}/project/${rundeckProject}/job/show/${rundeckJobId}"
break
}
}
else{
sleep timeout
}
retries--
}
}else{
echo "******************Rundeck Job Error: ${rundeckResponseJson.message} ******************"
currentBuild.result = "FAILURE"
}
}
}
Related
Please check what to write for just update password change not replace file kindly check.
Not all passwords change every time so it should only change passwords which are different
2)password file should be provided along with email
That's here I am stuck that how it is possible to do that because every time I run this it gets back with replacing the passwords file. It should have to update the changes just.
def json = 'initial_value'
def token = 'initial_value'
def count = 0
def csvContent = 'initial_value'
def ocpUrl ="https://api.nonprod.ocp.dev.ppr.gvv:6443"
def ocpRegistry="registry.apps.nonprod.ocp.dev.ppr.gvv"
pipeline {
agent any
environment {
VAULT_ADDR = credentials('VAULT_ADDR')
ROLE_ID = credentials('ROLE_ID')
SECRET_ID = credentials('SECRET_ID')
}
stages {
stage('Upload parameters and FILE') {
steps {
script {
//Load the csv file
def inputCSVPath = input message: 'Upload file', parameters: [file(name: 'Credentials.csv', description: 'Upload only CSV file')]
csvContent = readFile "${inputCSVPath}"
//Delete csv file
sh(""" rm -rf "$inputCSVPath" """)
json = '{"data":{'
csvContent.split('\n').each { line ->
if (count > 0) {
def fields = line.split(',',2)
def key = fields[0]
def pass = fields[1]
key = key.replaceAll(' ', '')
key=key.replaceAll('"', '')
pass=pass.replaceAll('"', '')
def item_data = '"'+key.trim()+'":"'+pass.trim()+'",'
json += item_data
}
count += 1
enter code here
}
count -= 1
json = json.substring(0, json.length() - 1)
json += '},"options":{}}'
//println(json)
//delete file
}
}
}
stage('Validate file'){
steps{
script {
csvContent = csvContent.trim()
csvContent = csvContent.replaceAll("\\S","")
println csvContent.length()
if(csvContent.equals("")){
currentBuild.result = "FAILURE"
echo "error"
exit o
}
}
}
}
stage('Authentication into vault'){
when {
expression { currentBuild.result != "FAILURE" }
}
steps{
script {
//Authentication Method
def response = sh(script: """curl --insecure -X PUT -H "X-Vault-Request: true" -d '{"role_id":"${ROLE_ID}","secret_id":"${SECRET_ID}"}' ${VAULT_ADDR}/v1/auth/approle/login""", returnStdout: true)
def responseObject = readJSON text: response
token = "$responseObject.auth.client_token"
}
}
}
stage('Update secrets into vault'){
when {
expression { currentBuild.result != "FAILURE" }
}
steps{
script{
// Updating the secret into vault
sh """
set +x
curl --insecure -X PUT -H "X-Vault-Request: true" -H "X-Vault-Token: "${token}"" -d '${json}' ${VAULT_ADDR}/v1/dev/data/db
set -x
"""
}
}
}
stage('Openshift delete Pods') {
steps{
withCredentials([usernamePassword(credentialsId: 'ocp-dev', usernameVariable: 'OCP_CREDS_USR', passwordVariable: 'OCP_CREDS_PSW')]) {
sh """
/usr/local/bin/oc login --insecure-skip-tls-verify -u '${OCP_CREDS_USR}' -p '${OCP_CREDS_PSW}' ${ocpUrl}
sudo docker login -u system -p \$(/usr/local/bin/oc whoami -t) ${ocpRegistry}
/usr/local/bin/oc delete --all pods --namespace md-dev
"""
}
}
}
}
post{
success{
emailext to: "${email}",
subject: "Jenkins Result",
body: "Password updated successfully",
attachLog: true
}
failure{
emailext to: "${email}",
subject: "Jenkins Result",
body: "Password updated failed with the error",
attachLog: true
}
}
}
I'm trying to convert my jenkins pipeline to a shared library since it can be reusable on most of the application. As part of that i have created groovy file in vars folder and kept pipeline in jenkins file in github and able to call that in jenkins successfully
As part of improving this i want to pass params, variables, node labels through a file so that we should not touch jenkins pipeline and if we want to modify any vars, params, we have to do that in git repo itself
pipeline {
agent
{
node
{
label 'jks_deployment'
}
}
environment{
ENV_CONFIG_ID = 'jenkins-prod'
ENV_CONFIG_FILE = 'test.groovy'
ENV_PLAYBOOK_NAME = 'test.tar.gz'
}
parameters {
string (
defaultValue: 'test.x86_64',
description: 'Enter app version',
name: 'app_version'
)
choice (
choices: ['10.0.0.1','10.0.0.2','10.0.0.3'],
description: 'Select a host to be delpoyed',
name: 'host'
)
}
stages {
stage("reading properties from properties file") {
steps {
// Use a script block to do custom scripting
script {
def props = readProperties file: 'extravars.properties'
env.var1 = props.var1
env.var2 = props.var2
}
echo "The variable 1 value is $var1"
echo "The variable 2 value is $var2"
}
In above code,i used pipeline utility steps plugin and able to read variables from extravars.properties file. Is it same way we can do for jenkins parameters also? Or do we have any suitable method to take care of passing this parameters via a file from git repo?
Also is it possible to pass variable for node label also?
=====================================================================
Below are the improvements which i have made in this project
Used node label plugin to pass the node name as variable
Below is my vars/sayHello.groovy file content
def call(body) {
// evaluate the body block, and collect configuration into the object
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent
{
node
{
label "${pipelineParams.slaveName}"
}
}
stages {
stage("reading properties from properties file") {
steps {
// Use a script block to do custom scripting
script {
// def props = readProperties file: 'extravars.properties'
// script {
readProperties(file: 'extravars.properties').each {key, value -> env[key] = value }
//}
// env.var1 = props.var1
// env.var2 = props.var2
}
echo "The variable 1 value is $var1"
echo "The variable 2 value is $var2"
}
}
stage ('stage2') {
steps {
sh "echo ${var1}"
sh "echo ${var2}"
sh "echo ${pipelineParams.appVersion}"
sh "echo ${pipelineParams.hostIp}"
}
}
}
}
}
Below is my vars/params.groovy file
properties( [
parameters([
choice(choices: ['10.80.66.171','10.80.67.6','10.80.67.200'], description: 'Select a host to be delpoyed', name: 'host')
,string(defaultValue: 'fxxxxx.x86_64', description: 'Enter app version', name: 'app_version')
])
] )
Below is my jenkinsfile
def _hostIp = params.host
def _appVersion = params.app_version
sayHello {
slaveName = 'master'
hostIp = _hostIp
appVersion = _appVersion
}
Now Is it till we can improve this?Any suggestions let me know.
I'm trying to use Groovy and curl to create a ServiceNow change ticket using their REST API. I get the below error every time I run the Jenkins pipeline
{"error":{"message":"Exception while reading request","detail":"Cannot decode: java.io.StringReader#90e4d8"},"status":"failure"}
What am I doing wrong here?
Jenkins version 2.150.2
{
node(){
stage ('Create Change Request') {
echo("Creating Change Request")
sh(script: """curl ${SERVICENOW_URL}/table/change_request \
--request POST \
--header 'Accept:application/json' \
--header 'Content-Type:application/json' \
--data '{"requested_by": "${params.requested_by}",
"u_verifier":"${params.u_verifier}",
"assigned_to":"${params.assigned_to}",
"reason":"${params.reason}",
"type":"${params.type}",
"start_date":"${params.start_date}",
"end_date":"${params.end_date}",
"change_plan":"${params.change_plan}",
"short_description":"${SHORT_DESCRIPTION}",
"description":"${DESCRIPTION}",
"backout_plan":"${BACKOUT_PLAN}",
"u_verification_plan":"${U_VERIFICATION_PLAN}",
"u_department_subsidiary":"${U_DEPARTMENT_SUBSIDIARY}",
"u_tested":"${U_TESTED}",
"u_have_verification_plan":"${U_HAVE_VERIFICATION_PLAN}",
"u_have_implementation_plan":"${U_HAVE_IMPLEMENTATION_PLAN}",
"u_have_backout_plan":"${U_HAVE_BACKOUT_PLAN}",
"assignment_group":"${U_ASSIGNMENT_GROUP}",
"category":"${CATEGORY}",
"cmdb_ci":"${CMDB_CI}",
"u_approval_group":"${U_APPROVAL_GROUP}",
"approval":"requested",
"state":"${_STATE}"
}' \
--user 'xxxx':'password' > CREATE_CHG_REQUEST_OUTPUT
""")
}
stage ('Parsing Change Result') {
def REQUEST_OUTPUT = ""
REQUEST_OUTPUT = readFile ('CREATE_CHG_REQUEST_OUTPUT').trim()
//var jsonStr = JSON.stringify(REQUEST_OUTPUT);
echo "REQUEST_OUTPUT:"
echo REQUEST_OUTPUT
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText(REQUEST_OUTPUT)
NEW_CHANGE_NUMBER = object.result.number
NEW_SYS_ID = object.result.sys_id
echo("New Change Number is : " + NEW_CHANGE_NUMBER )
echo("New sys_id for Change Number : " + NEW_SYS_ID )
}
}
} catch(e) {
echo e.message
} finally {
}
The data has certain variables declared using . in the variable name. Groovy doesn't support the replacement of the variables declared inside the double quotes "". Replace all the variable names having . in them with single quotes ''.
Hope so this helps
We have a Jenkins job that uses a declarative pipeline.
This job can be triggered by different other builds.
In the declarative pipeline how can I find out which build has triggered the pipeline?
Code sample below
pipeline {
agent any
stages {
stage('find upstream job') {
steps {
script {
def causes = currentBuild.rawBuild.getCauses()
for(cause in causes) {
if (cause.class.toString().contains("UpstreamCause")) {
println "This job was caused by job " + cause.upstreamProject
} else {
println "Root cause : " + cause.toString()
}
}
}
}
}
}
}
You can check the job's REST API to get extra information like below
{
"_class" : "org.jenkinsci.plugins.workflow.job.WorkflowRun",
"actions" : [
{
"_class" : "hudson.model.ParametersAction",
"parameters" : [
]
},
{
"_class" : "hudson.model.CauseAction",
"causes" : [
{
"_class" : "hudson.model.Cause$UpstreamCause",
"shortDescription" : "Started by upstream project \"larrycai-sto-46908390\" build number 7",
"upstreamBuild" : 7,
"upstreamProject" : "larrycai-sto-46908390",
"upstreamUrl" : "job/larrycai-sto-46908390/"
}
]
},
Reference:
https://jenkins.io/doc/pipeline/examples/#get-build-cause
Get Jenkins upstream jobs
I realize that this is a couple years old, but the previous response required some additional security setup in my Jenkins instance. After a bit of research, I found that there was a new feature request completed in 11/2018 that addresses this need and exposes build causes in currentBuild. Here is a little lib I wrote that returns the cause with the string "JOB/" prepended if the build was triggered by another build:
def call(body) {
if (body == null) {body = {DEBUG = false}}
def myParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = myParams
body()
def causes = currentBuild.getBuildCauses()
if (myParams.DEBUG) {
echo "causes count: " + causes.size().toString()
echo "causes text : " + causes.toString()
}
for(cause in causes) {
// echo cause
if (cause._class.toString().contains("UpstreamCause")) {
return "JOB/" + cause.upstreamProject
} else {
return cause.toString()
}
}
}
To use this, I place it in a library in a file named "buildCause.groovy". Then I reference the library at the top of my Jenkinsfile:
library identifier: 'lib#master', retriever: modernSCM(
[$class: 'GitSCMSource', remote: '<LIBRARY_REPO_URL>',
credentialsId: '<LIBRARY_REPO_CRED_ID', includes: '*'])
Then I can call it as needed within my pipeline:
def cause=buildCause()
echo cause
if (!cause.contains('JOB/')) {
echo "started by user"
} else {
echo "triggered by job"
}
Larry's answer didn't quite work for me.
But, after I've modified it slightly with the help of these docs and this version works:
def causes = currentBuild.getBuildCauses()
for(cause in causes) {
if (cause._class.toString().contains("UpstreamCause")) {
println "This job was caused by job " + cause.upstreamProject
} else {
println "Root cause : " + cause.toString()
}
}
P.S. Actually, Daniel's answer mentions this method, but there's too much clutter, I only noticed it after I wrote my solution.
I am using the Pipeline plugin in Jenkins by Clouldbees (the name was Workflow plugin before), I am trying to get the user name in the Groovy script but I am not able to achieve it.
stage 'checkout svn'
node('master') {
// Get the user name logged in Jenkins
}
Did you try installing the Build User Vars plugin? If so, you should be able to run
node {
wrap([$class: 'BuildUser']) {
def user = env.BUILD_USER_ID
}
}
or similar.
To make it work with Jenkins Pipeline:
Install user build vars plugin
Then run the following:
pipeline {
agent any
stages {
stage('build user') {
steps {
wrap([$class: 'BuildUser']) {
sh 'echo "${BUILD_USER}"'
}
}
}
}
}
Here's a slightly shorter version that doesn't require the use of environment variables:
#NonCPS
def getBuildUser() {
return currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
}
The use of rawBuild requires that it be in a #NonCPS block.
It is possible to do this without a plugin (assuming JOB_BASE_NAME and BUILD_ID are in the environment):
def job = Jenkins.getInstance().getItemByFullName(env.JOB_BASE_NAME, Job.class)
def build = job.getBuildByNumber(env.BUILD_ID as int)
def userId = build.getCause(Cause.UserIdCause).getUserId()
There is also a getUserName, which returns the full name of the user.
This works for me without the Build User plugin:
// get first entry of JSONArray
def buildCause = currentBuild.getBuildCauses()[0]
def buildPrincipal = [type:"unknown", name:""]
if (buildCause._class ==~ /.+BranchEventCause/) {
def branchCause = currentBuild.getRawBuild().getCause(jenkins.branch.BranchEventCause)
buildPrincipal = [type:"branch",name:buildCause.shortDescription]
} else
if (buildCause._class ==~ /.+TimerTriggerCause/) {
def timerCause = currentBuild.getRawBuild().getCause(hudson.triggers.TimerTrigger.TimerTriggerCause)
buildPrincipal = [type:"timer", name:"Timer event"]
} else
if (buildCause._class ==~ /.+UserIdCause/) {
def buildUserCause = currentBuild.getRawBuild().getCause(hudson.model.Cause.UserIdCause)
buildPrincipal = [type:"user", name:buildCause.userId]
} else
// ... other causes
def jobUserId, jobUserName
//then somewhere
wrap([$class: 'BuildUser']) {
jobUserId = "${BUILD_USER_ID}"
jobUserName = "${BUILD_USER}"
}
//then
println("Started By: ${jobUserName}")
We were using this plugin : Build User Vars Plugin. More variables are available.
//Below is a generic groovy function to get the XML metadata for a Jenkins build.
//curl the env.BUILD_URL/api/xml parse it with grep and return the string
//I did an or true on curl, but possibly there is a better way
//echo -e "some_string \c" will always return some_string without \n char
//use the readFile() and return the string
def GetUserId(){
sh """
/usr/bin/curl -k -s -u \
\$USERNAME:\$PASSWORD -o \
/tmp/api.xml \
\$BUILD_URL/api/xml || true
THE_USERID=`cat /tmp/api.xml | grep -oP '(?<=<userId>).*?(?=</userId>)'`
echo -e "\$THE_USERID \\c" > /tmp/user_id.txt
"""
def some_userid = readFile("/tmp/user_id.txt")
some_userid
}
I modified #shawn derik response to get it to work in my pipeline:
stage("preserve build user") {
wrap([$class: 'BuildUser']) {
GET_BUILD_USER = sh ( script: 'echo "${BUILD_USER}"', returnStdout: true).trim()
}
}
Then I can reference that variable later on by passing it or in the same scope as ${GET_BUILD_USER} . I installed the same plugin referenced.
Edit: I re-read the question - the below only gets you the user running the build (which technically is often more interesting), not the one triggering the build in the frontend (be it REST-API or WebUI).
If you have Jenkins impersonation enabled, then I believe the result should be equivalent, otherwise this will only get you the user who owns the jenkins agent on the build machine.
Original answer:
Another way would be to
sh 'export jenkins_user=$(whoami)'
Downside: Linux-dependent, difficult to port across multiple agents in a single build (but then, the auth context may be different on each slave)
Upside: No need to install plugins (which on shared/large Jenkins instances can be tricky)
The Build User Vars Plugin is useful when you are executing the stage on an agent.
The alternative is to use the current build clause (see https://code-maven.com/jenkins-get-current-user), which also works when your stage is set with agent none.
The following code is inspired by Juergen's solution but I added more possible trigger reason and display them in a formatted manner:
String getTriggerReason() {
def buildCause = currentBuild.getBuildCauses()[0]
if (buildCause._class ==~ /.+(BranchEventCause|BranchIndexingCause)/) {
if (env.JOB_BASE_NAME == 'master') {
return 'Triggered by master commit'
} else {
return "Triggered by ${buildCause.shortDescription}"
}
}
if (buildCause._class ==~ /.+TimerTriggerCause/) {
return 'Triggered by timer'
}
if (buildCause._class ==~ /.+BuildUpstreamCause/) {
return "Triggered by build #${buildCause.upstreamBuild}"
}
if (buildCause._class ==~ /.+UserIdCause/) {
def userName = buildCause.userName.replaceFirst(/\s?\(.*/, '')
return "Triggered by user ${userName}"
}
return 'Unknown trigger'
}