I have hostname that is found in a xml file but would need to use that hostname to get the node name in Jenkins to be able to connect to that node and do the scripting.
The hostname is example.co.uk and would like to use script to find the hostname in jenkins and get the node name of the Jenkins and use that as find-hostname. Is this possible?
////
stage('use_nodename_found') {
node("$find-hostname") {
unstash 'db-process'
sh """
sudo su - postgres -c 'echo -e "-------------------------------System Information of current node running --------------------"'
""".stripIndent()
You can use the following script to retrieve the Node name passing the Host. If you want to get the labels instead of node.getNodeName() call node.getLabelString(). Read more here.
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
if(node.getLauncher().getHost() == host){
return node.getNodeName()
}
}
return null
}
Note: This is a basic script, make sure you d proper error handling.
Update: Print all Hosts
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
println node.getNodeName()
println node.getLauncher().getHost()
}
return null
}
Following is a full scripted Pipeline.
node {
stage('Test') {
echo getNodeName("172.17.0.3")
}
}
def getNodeName(def host) {
for(def node : Jenkins.instance.getNodes()) {
if(node.getLauncher().getHost() == host){
return node.getNodeName()
}
}
return null
}
Related
I have two different Linux servers (prod and dev) with different $HOSTNAME and different certificates, which are by default named after the hostname.
Now I want to determine within the Jenkins-Pipeline on which host I am and thus use the right certificate.
To do so I wrote the following test script:
def labels = []
labels.add('jenkins_<slavehost>_x86')
def builders = [:]
for (x in labels) {
def label = x
builders[label] = {
ansiColor('xterm') {
node (label) {
stage('cleanup') {
deleteDir()
}
stage('test') {
sh """
echo $HOSTNAME
"""
}
}
}
}
}
parallel builders
Which does not work since the $HOSTNAME is not defined.
groovy.lang.MissingPropertyException: No such property: HOSTNAME for class: groovy.lang.Binding
How can I get the hostname of the jenkins-slave within a sh in a pipeline?
Since you can name the node in any way you like, you can't just use the NODE_NAME, it does not have to be the same as the $HOSTNAME you would get from echo $HOSTNAME on a bash on the slave machine.
def getJenkinsMaster() {
return env.BUILD_URL.split('/')[2].split(':')[0]
}
You can get the hostname from the url
sh "echo ${env.NODE_NAME}"
You can add this shell command and get the hostname from the environment variable.
not sh step, but you can use groovy:
import java.security.MessageDigest
import org.jenkinsci.plugins.workflow.cps.CpsThread
import hudson.FilePath
#NonCPS
def get_current_pipeline_node() {
def thread = CpsThread.current()
def cv = thread.contextVariables
def fp
try {
fp = cv.get(FilePath, null, null)
} catch(MissingMethodException) {
fp = cv.get(FilePath)
}
return fp?.toComputer()?.node
}
node = get_current_pipeline_node()
print(node.nodeName)
I have a Jenkins pipeline that runs on docker agents and everytime it enters a stage with a different agent it changes Jenkins node. How can I force it to run always on the same node?
I have 3 nodes: master, slave-1 and slave-2. My pipeline sometimes, just an example, starts by using master, then when it calls agent image-docker-1 it uses slave-1 and then when it calls agent image-docker-2 it uses master again.
How can I force it to use always slave-1? I know that, if I weren't using docker as agent, I could use something like:
node (label: "slave-1") {
(...)
pipeline {
agent { label "slave-1 }
(...)
But I think this is not the case.
Here's my pipeline:
node {
properties([
pipelineTriggers(
[cron('H 00 * * 1-5') ]
)]
)
workloadPipeline = load("ImagePull.groovy")
workloadPipeline
}
pipeline {
options {
ansiColor('xterm')
timestamps()
}
agent none
environment {
TOKEN = credentials("token")
HOME = '.'
}
stages {
stage("initiating"){
agent {
docker {
image 'image-docker-1'
args '--entrypoint="" -u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
stages {
stage('docker 1 scanning') {
steps {
script {
workloadPipeline.loopImages(Images)
}
}
}
stage ('docker 1 test'){
(...)
}
}
}
stage('docker 2 scanning') {
agent {
docker {
image 'image-docker-2'
args '--entrypoint="" -u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
steps {
script {
workloadPipeline.Scanning()
}
}
}
}
}
found an easy solution from this example by using reuseNode true
pipeline {
agent none
stages {
stage("Fix the permission issue") {
agent any
steps {
sh "sudo chown root:jenkins /run/docker.sock"
}
}
stage('Step 1') {
agent {
docker {
image 'nezarfadle/tools'
reuseNode true
}
}
steps {
sh "ls /"
}
}
}
}
Use:
agent {
docker {
image 'image-docker-1'
args '--entrypoint="" -u root -v /var/run/docker.sock:/var/run/docker.sock'
label 'slave-1'
}
}
Put that or at the pipeline level, for having all your stages using it, or at each stage if you want to seperate by stage
Thanks João for the small correction :)
Thanks for the answer #Washwater. In fact I needed to make a little change.
If I use what you suggested, it returns an error "No agent type specified. Must be one of [any, docker, dockerfile, label, none]"
agent {
node { label "slave-1" }
docker {
image 'image-docker-1'
args '--entrypoint="" -u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
So, the correct syntax must be:
agent {
docker {
image 'image-docker-1'
args '--entrypoint="" -u root -v /var/run/docker.sock:/var/run/docker.sock'
label "slave-1"
}
}
-The following code works for me when i have multiple nodes labeled with 'slaves'
'init' stage will pick one node from 'slaves', following stages will use the same node with env.NODE_NAME( set by the init state)
pipeline {
agent {
node {
labe 'slaves'
}
stages {
stage ('init') { steps {echo "node is $NODE_NAME"} }
stage ( 'test1') {
agent {
docker {
label env.NODE_NAME
image nginx
}
steps {
echo "test done"
}
}
}
}
}
I am trying to choose a different docker agent from a private container registry based on an a parameter in Jenkins pipeline. For my example let's say I have 'credsProd' and 'credsTest' saved in the credentials store. My attempt is as follows:
pipeline {
parameters {
choice(
name: 'registrySelection',
choices: ['TEST', 'PROD'],
description: 'Is this a deployment to STAGING or PRODUCTION environment?'
)
}
environment {
URL_VAR = "${env.registrySelection == "PROD" ? "urlProd.azure.io" : "urlTest.azure.io"}"
CREDS_VAR = "${env.registrySelection == "PROD" ? "credsProd" : "credsTest"}"
}
agent {
docker {
image "${env.URL_VAR}/image:tag"
registryUrl "https://${env.URL_VAR}"
registryCredentialsId "${env.CREDS_VAR}"
}
}
stages{
stage('test'){
steps{
echo "${env.URL_VAR}"
echo "${env.CREDS_VAR}"
}
}
}
}
I get error:
Error response from daemon: Get https://null/v2/: dial tcp: lookup null on
If I hard code the registryUrl I get a similar issue with registryCredentialsId:
agent {
docker {
image "${env.URL_VAR}/image:tag"
registryUrl "https://urlTest.azure.io"
registryCredentialsId "${env.CREDS_VAR}"
}
}
ERROR: Could not find credentials matching null
It is successful if I hardcode both registryUrl and registryCredentialsId.
agent {
docker {
image "${env.URL_VAR}/image:tag"
registryUrl "https://urlTest.azure.io"
registryCredentialsId "credsTest"
}
}
It appears that the docker login stage of the agent{docker{}} cannot access/resolve environment variables.
Is there a way around this that does not involve code duplication? I manage changes with multi branch pipeline so ideally do not want to have separate Prod and test groovy files or different sets sequential steps in the same file.
Try running a scripted pipeline before declarative:
URL_VAR = null
CREDS_VAR = null
node('master') {
stage('Choose') {
URL_VAR = params.registrySelection == "PROD" ? "urlProd.azure.io" : "urlTest.azure.io"
CREDS_VAR = params.registrySelection == "PROD" ? "credsProd" : "credsTest"
}
}
pipeline {
agent {
docker {
image "${URL_VAR}/image:tag"
registryUrl "https://${URL_VAR}"
registryCredentialsId "${CREDS_VAR}"
}
}
...
Alternatively, you can define two stages (with hard-coded url and creds) but run only one of them, using when in both.
Is it possible to get a shared library to retune a docker run command?
I have the following,
scr/docker_run.groovy
def ubuntu() {
echo "docker run --rm " +
'--env APP_PATH="`pwd`" ' +
'--env RELEASE=true ' +
"-v \"`pwd`:`pwd`\" " +
"-v /var/run/docker.sock:/var/run/docker.sock " +
"ubuntu"
}
Jenkinsfile
#Library('pipeline-library-demo') _
pipeline {
agent {
node {
label params.SLAVE
}
}
parameters {
string(name: 'SLAVE', defaultValue: 'so_slave')
}
stages {
stage('ubuntu') {
steps {
script {
sh docker_run.ubuntu ls -lah
}
}
}
}
}
I have tried different things inside the groovy file, such as echo, sh, call, all returning errors.
Any help would be great
If you are using DSL style pipeline you should define Steps in your shared library not directly a function in the src/ directory. Should follow the directory structure defined here https://jenkins.io/doc/book/pipeline/shared-libraries/
Then you can define a Step like
vars/mystep.groovy
def call() {
this.sh("docker_run.ubuntu ls -lah");
}
And calling from you pipeline as:
#Library('pipeline-library-demo') _
pipeline {
agent {
node {
label params.SLAVE
}
}
parameters {
string(name: 'SLAVE', defaultValue: 'so_slave')
}
stages {
stage('ubuntu') {
steps {
mystep
}
}
}
}
Best
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'
}