Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jenkins Automation

Jenkins Automation

When you want to guarantee code quality, do continuous deployments or any other automation you will need a good continuous integration server.

While there are many great (hosted) solutions like TravisCi, CircleCi, etc … when it comes to **full control** or the need for private hosting, **Jenkins** is the right candidate. However, Jenkins could be known to be hard to learn and to configure. But what if you could automate all that complexity via some **easy scripting**? We are developers after all ;).

This talk will give you an introduction on how to automate the setup and the configuration of your CI whether you are still using the old fashion way (freestyle jobs) or the newer recommended way (Pipelines).

Some examples that will be covered:

– Auto setup jenkins with plugins, credentials, …
– Generate jobs per branch fetched directly for your VCS (Bitbucket, Github, …) (the old fashion way)
– Arrange your jobs in folders
– Make reusable DSL functions
– Using conditionals (e.g. master => should deploy if build successful)
– generate (multi branch) pipeline jobs with, optionally, fully integration with Bitbucket or Github

Toni Van de Voorde

January 26, 2019
Tweet

More Decks by Toni Van de Voorde

Other Decks in Technology

Transcript

  1. ABOUT ME Toni Van de Voorde tonivdv [email protected] @tonivdv ▸

    CTO @ Adsdaq ▸ +10 years experience building web applications ▸ Love clean stuff and automations ▸ “Relax” with family
  2. BACKGROUND ▸ Written in Java ▸ First released in 2005

    ▸ Formerly known as the “Hudson Project” ▸ The name “Jenkins” was born in 2011
  3. FREESTYLE JOB Job DSL job(‘jobs-name’) {
 
 scm {
 git('[email protected]:adlogix/zf-symfony-container.git')


    } triggers {
 scm('H/15 * * * *')
 } steps {
 shell('composer install && vendor/bin/phpunit')
 }
 
 }
  4. FREESTYLE JOB Job DSL job(‘jobs-name’) {
 
 scm {
 git('[email protected]:adlogix/zf-symfony-container.git')


    } triggers {
 scm('H/15 * * * *')
 } steps {
 shell('composer install && vendor/bin/phpunit')
 }
 
 }
  5. FREESTYLE JOB Job DSL def gitUrl = 'git://github.com/jenkinsci/job-dsl-plugin.git’ def branches

    = ['master','dev','hotfix'] branches.each { branch -> 
 job(“demo-${branch}") { 
 scm { 
 git(gitUrl, branch) 
 } 
 }
 } Example: Static Branches
  6. FREESTYLE JOB Job DSL def project = 'Netflix/asgard’
 def branchApi

    = new URL("https://api.github.com/repos/${project}/branches")
 def branches = new groovy.json.JsonSlurper().parse(branchApi.newReader()) branches.each { def branchName = it.name 
 def jobName = "${project}-${branchName}".replaceAll('/','-') job(jobName) { 
 scm { 
 git("https://github.com/${project}.git", branchName) 
 } 
 }
 } Example: Dynamic Branches
  7. FREESTYLE JOB Job DSL def project = 'automate-jenkins-job-creation’
 def repo

    = "https://[email protected]/tonivdv/${project}.git" Process proc1 = "git ls-remote -h ${repo}".execute()
 Process proc2 = 'sed s/.*refs\\/heads\\///g'.execute()
 Process all = proc1 | proc2
 all.waitFor() def branches = "${all.in.text}".split('\\n') branches.each {
 …
 } Example: Dynamic Branches
  8. FREESTYLE JOB Job DSL # ./scripts/utilities/GitUtils.groovy
 
 package utilities
 


    public class GitUtils { 
 public static String[] getRemoteBranches(def repoUrl, def out) { 
 
 Process proc1 = "git ls-remote -h ${repoUrl}".execute() 
 Process proc2 = 'sed s/.*refs\\/heads\\///g'.execute() 
 Process all = proc1 | proc2 all.waitFor() 
 
 def branches = "${all.in.text}".split('\\n') 
 return branches }
 } Example: Functions
  9. FREESTYLE JOB Job DSL # ./scripts/demo.groovy import utilities.GitUtils
 
 def

    project = 'automate-jenkins-job-creation’
 def repo = https://[email protected]/tonivdv/${project}.git
 
 def branches = GitUtils.getRemoteBranches(repo, out) Example: Functions
  10. FREESTYLE JOB Job DSL ‣ Don’t store passwords in your

    DSL scripts! DON’T ! 
 ‣ Use the Credentials 
 Plugin Example: Credentials Plugin ID: credentials
  11. FREESTYLE JOB Job DSL job(‘jobs-name') {
 scm {
 git {


    remote {
 github('account/repo', 'ssh')
 credentials('github-ci-key')
 }
 }
 }
 } Example: Credentials
  12. FREESTYLE JOB Job DSL // assign the my-userpassword credentials to

    the PASSWORD build variable
 
 job(‘jobs-name') {
 wrappers {
 credentialsBinding { 
 usernamePassword('PASSWORD', ‘my-userpassword’)
 }
 }
 } Example: Credentials
  13. FREESTYLE JOB Job DSL ‣ Powerful & Easy ‣ Looks

    like a generic DSL ‣ Available in API Viewer ‣ But plugin must use following annotations: ‣ @DataBoundConstructor ‣ @DataBoundSetter
 Dynamic DSL
  14. FREESTYLE JOB Job DSL Dynamic DSL job(‘jobs-name’) {
 
 scm

    {
 git(‘[email protected]:...’)
 bitbucketWeb {
 repoUrl(‘https://...’)
 }
 }
 
 }
  15. FREESTYLE JOB Job DSL ‣ Gives access to underlying XML

    configuration ‣ Harder & Ugly ‣ Not fun ‣ BUT it works Configure Block
  16. FREESTYLE JOB Job DSL Configure Block job('example-1') {
 
 configure

    { node -> // node represents <project> }
 
 }
  17. PIPELINE Jenkins Pipeline (or simply "Pipeline" with a capital "P")

    is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins. © https://jenkins.io/doc/book/pipeline/
  18. Example PIPELINE Jenkinsfile pipeline {
 agent any 
 stages {


    stage('Checkout') { 
 steps {
 Checkout scm
 }
 }
 stage('Build') { 
 steps {
 sh 'make it-build’
 }
 }
 stage('Test') { 
 steps {
 sh 'make test’
 }
 }
 
 }
 }
  19. 2 Flavours … PIPELINE Jenkinsfile ‣ Declarative Pipeline ‣ Easier

    to use ‣ Limited ‣ Targets easy CD ‣ Scripted Pipeline ‣ Power Users ‣ Groovy is the limit
  20. Declarative Pipeline PIPELINE Jenkinsfile pipeline {
 agent any 
 stages

    {
 ...
 stage('Deploy') { 
 when {
 branch 'master'
 }
 steps {
 make deploy
 }
 }
 }
 }
  21. PIPELINE Jenkinsfile node { 
 stage('Deploy') { 
 if (env.BRANCH_NAME

    == 'master') { 
 make deploy 
 }
 }
 } Scripted Pipeline
  22. PIPELINE Jenkinsfile pipeline {
 agent none
 stages {
 stage('php 7.3')

    {
 agent {
 docker { image 'php:7.3-alpine' }
 }
 steps {
 sh 'php --version'
 }
 } stage('php 7.2') {
 agent {
 docker { image 'php:7.2-alpine' }
 }
 steps {
 sh 'php --version'
 }
 }
 }
 } Using Docker Blue Ocean Plugin
  23. PIPELINE Jenkinsfile pipeline {
 agent none
 stages {
 stage(‘php') {


    parallel {
 stage('php 7.3') {
 agent {
 docker { image 'php:7.3-alpine' }
 }
 steps {
 sh 'php --version'
 }
 } stage('php 7.2') {
 agent {
 docker { image 'php:7.2-alpine' }
 }
 steps {
 sh 'php --version'
 }
 }
 }
 }
 }
 } Parallel Blue Ocean Plugin
  24. PIPELINE multibranchPipelineJob('mypipelinejob') {
 branchSources {
 branchSource {
 source {
 bitbucket

    {
 id('bitbucket')
 serverUrl('https://bitbucket.org')
 repoOwner(‘my-user')
 repository('my-repository')
 credentialsId(‘bitbucket-ssh’)
 }
 }
 }
 }
 
 ...
 }
 
 Job DSL Multi-Branch Pipeline
  25. PIPELINE multibranchPipelineJob('mypipelinejob') {
 ...
 
 // discover Branches (workaround due

    to JENKINS-46202)
 configure { def traits = it / sources / data / 'jenkins.branch.BranchSource' / source / traits // Exclude branches that are also filed as PRs 
 traits << 'com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait' {
 strategyId(1)
 }
 
 // Discover each pull request once ... without merging
 traits << 'com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait' {
 strategyId(2)
 }
 }
 }
 
 Job DSL Multi-Branch Pipeline
  26. PIPELINE organizationFolder('example') {
 
 description(‘Some description')
 displayName(‘Other name')
 organizations {


    bitbucket { 
 serverUrl('https://bitbucket.org') 
 repoOwner('adqphpbenl19')
 credentialsId('bitbucket-tonivdv') 
 }
 }
 
 ...
 } Job DSL Organisation Folder
  27. PIPELINE organizationFolder('example') {
 
 ...
 
 configure {
 def traits

    = it / navigators / 'com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMNavigator' / traits
 traits << 'com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait' {
 strategyId(1)
 }
 traits << 'com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait' {
 strategyId(2)
 }
 }
 
 } Job DSL Organisation Folder
  28. JCASC JCasC or Jenkins Configuration as Code allows to define

    the “configuration” of plugins and parameters as a simple, human friendly, plain text Yaml syntax.
 
 © https://jenkins.io/projects/jcasc/
  29. JCASC Installation ‣ Define location of the casc configurations ‣

    Path to a folder containing a set of configs
 e.g. /var/jenkins_home/casc_configs ‣ Full path to single file
 e.g. /var/jenkins_home/casc_configs/jenkins.yaml ‣ A URL pointing to a file served on the web
 e.g. https://acme.org/jenkins.yaml ‣ Default: $JENKINS_ROOT/jenkins.yaml
  30. JCASC # casc_configs/realm.yaml
 
 jenkins:
 securityRealm:
 bitbucket:
 clientID: ${b_id}
 clientSecret:

    ${b_secret} Examples # casc_configs/authorization.yaml
 
 jenkins:
 authorizationStrategy:
 globalMatrix:
 grantedPermissions:
 - "Overall/Read:anonymous"
 - "Overall/Administer:authenticated"
  31. REFERENCES ‣ Doc ‣ https://jenkins.io/ ‣ https://github.com/jenkinsci/job-dsl-plugin/wiki ‣ https://jenkins.io/doc/book/pipeline/ ‣

    https://www.phparch.com/article/jenkins-automation/ ‣ Code Examples ‣ https://github.com/tonivdv/rnd-jenkins-as-code ‣ https://github.com/tonivdv/automate-jenkins-job-creation ‣ Various ‣ https://jenkins.io/artwork/ ‣ https://www.cloudbees.com/