Technotes

Technotes for future me

Jenkins pipeline DSL

Tutorial Using the Jenkins Job DSL

https://github.com/jenkinsci/job-dsl-plugin/wiki/Tutorial---Using-the-Jenkins-Job-DSL

Using Kubernetes

When running jobs as Kubernetes pods you need to specify/inherit podTemplates

def label = "mypod-${UUID.randomUUID().toString()}"    // ugly necessary workaround
podTemplate(label: label, containers: [
  // You can have as many containers as you want...
  containerTemplate(name: 'maven', image: 'maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),
  containerTemplate(name: 'golang', image: 'golang:1.8.0', ttyEnabled: true, command: 'cat')
]) {

  node(label) {
    container('maven') {
      // do something with Maven
    }
    container('golang') {
      // do something with go
    }
  }
}

Instead of explicitely specifying the pod template you can inherit templates and overwrite only some of the values…

podTemplate(label: 'mypod', inheritFrom: 'basepod) {
  ...
}

Understand the internals

Keep in mind the following:

  • there is always a container ‘jnlp’ which runs the Jenkins agent
  • when you do not use container('...') {} you are running inside the ‘jnlp’ container
  • you can override the ‘jnlp’ container by defining your own containerTemplate(name:'jnlp', ...)
  • SCM checkout always happens in the ‘jnlp’ container!

Docker-in-Docker

If you want to build docker images you have to run docker on-top of kubernetes. One possibility is Docker-in-Docker (DinD). Example:

podTemplate(label: 'dind', containers: [
  containerTemplate(name: 'docker', image: 'docker:dind', ttyEnabled: true, privileged: true,
    command: 'dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2375 --storage-driver=overlay')
  ],
  volumes: [emptyDirVolume(memory: false, mountPath: '/var/lib/docker')]) {
    container(docker) {
      docker.build()
    }
  }
}

Different DSL Syntax types

  • Declarative Pipeline (always has a ‘stages’ block, no loops/arithmetics/string processing)
  • Scripted Pipeline (always has a ’node’ block, full Groovy features)
  • Mixed Pipelines (using a ‘script’ block, do full Groovy inside a declarative pipeline)
  • JobDSL (always has something like a ‘pipelineJob’, otherwise full Groovy features)

Accessing the workspace

Check if a file exists

if (fileExists('file')) {
   // ...
}

Getting shell output

Either via reading from file

sh "echo something >output.txt";
def output =readFile('output.txt').trim()

or using the sh plugin

def output = sh(returnStdout: true, script: 'echo something').trim()

Handling the environment

Setting env variables

pipeline {
  environment {
     FOO = "BAR"
  }
}

Query all env variables

node {
  echo sh(returnStdout: true, script: 'env | sort')
}

Access an env variable

echo env.BUILD_ID
echo "My build is is ${env.BUILD_ID}"

SCM Checkout

For Jenkinsfiles located in the repo perform checkout like this

checkout scm

For advanced checkout specifics of the SCM provider can be overwritten like this

checkout([$class: 'GitSCM', 
    branches: [[name: '*/master']], 
    doGenerateSubmoduleConfigurations: false, 
    extensions: [], 
    submoduleCfg: [], 
    userRemoteConfigs: [[]]
])

For example git LFS support can be enabled like this

checkout([$class: 'GitSCM', 
    ...
    extensions = [
      [$class: 'GitLFSPull'],
      [$class: 'CloneOption',
      depth: 0,
      noTags: false,
      shallow: true,
      timeout: 10]
    ],
    ...
 ])

To get checkout details catch the result of ‘checkout’

 def scmVars = checkout scm
 
 println scmVars.GIT_BRANCH
 println scmVars.GIT_COMMIT
 println scmVars.GIT_URL
 ...

Alternatetivly checkout repos directly using the git plugin anywhere in your pipeline as needed

git credentialsId: 'github', url: ${REPO_URL}, branch: 'master'

Credentials

Username and Password

Fetch user and password into env variables

withCredentials([usernamePassword(
                   credentialsId: 'myCredentials',
                   usernameVariable: 'USER',
                   passwordVariable: 'PASSWORD'
               )]) {
  sh 'echo user "$USER" pasword "$PASSWORD"'
}

or

withCredentials([[$class: 'UsernamePasswordMultiBinding',
                  credentialsId: 'myCredentials', 
                  usernameVariable: 'USER', 
                  passwordVariable: 'PASSWORD'
                ]]) {
  sh 'echo user "$USER" pasword "$PASSWORD"'
}

Secret Files

withCredentials([file(credentialsId: 'myCredentials', variable: 'MY_SECRET_TOKEN')]) {
   sh 'echo token "$MY_SECRET_TOKEN"'
}

Docker Registry Credentials

Do this using the docker plugin, not via the credential-binding plugin!

docker.withRegistry('docker.example.com', 'myCredential') {
  docker.build(...)
}

Jenkins Shared Libraries

Script Security

  • Use global shared libraries to by-pass script security
  • Use per-folder libraries for enforced script security

Including Shared Libraries

From repo

 library(
   identifier: 'jenkins-shared-library@1.0',
   retriever: modernSCM([
     $class: 'GitSCMSource',
     remote: 'https://github.com/myrepo/jenkins-shared-library.git'
   ])
 )

From local Jenkins

For this to work you have to configure a global or per-folder shared library which you can then include by name

 @Library('my-shared-library') _
 @Library('my-shared-library@1.0') _
 @Library(['my-shared-library', 'otherlib@abc1234']) _

Note the underscore for importing in contrast to importing classes

 @Library('somelib')
 import com.mycorp.pipeline.somelib.Helper
Last updated on 31 Jan 2021
Published on 24 Dec 2019
Edit on GitHub