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

Creating a CI/CD Pipeline for Your Shared Libraries

Creating a CI/CD Pipeline for Your Shared Libraries

At Capital One we run tens of thousands of CI/CD pipelines on Jenkins, leveraging the Jenkins Pipeline shared libraries extension to enable code reuse and reduce time to market for dev teams. A code change to our shared library goes live immediately and is consumed the next time a team triggers their project's pipeline. So, why do we have such confidence that a code change to our library won't break a team's pipeline? The answer: we've developed an automated CI/CD pipeline for our shared library.

During this talk, you will learn how to create an automated pipeline for your shared libraries including how to develop tests, create canary releases, monitor for issues, and quickly rollback changes to achieve rapid delivery while minimizing the impact on dev teams.

Roderick Randolph

August 14, 2019
Tweet

More Decks by Roderick Randolph

Other Decks in Technology

Transcript

  1. © 2019 All Rights Reserved. 4 // Jenkinsfile stage('Build') {

    node('docker') { sh "docker build -t <registry>/my-repo/my-app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my-repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my-app:${version}" } } } } * Source code shown is simulated for demo purposes only
  2. © 2019 All Rights Reserved. 6 // Jenkinsfile stage('Build') {

    node('docker') { sh "docker build -t <registry>/my-repo/my-new-app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my-repo/my-new-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my-new-app:${version}" } } } } * Source code shown is simulated for demo purposes only
  3. © 2019 All Rights Reserved. 7 // Jenkinsfile stage('Build') {

    node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } Project A Project B Project C * Source code shown is simulated for demo purposes only
  4. © 2019 All Rights Reserved. 9 // Jenkinsfile stage('Build') {

    node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg http_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg http_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } Project A Project B Project C * Source code shown is simulated for demo purposes only
  5. © 2019 All Rights Reserved. 12 // Jenkinsfile stage('Build') {

    node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } // Jenkinsfile stage('Build') { node('docker') { sh "docker build -t <registry>/my-repo/my- app:${version} --build-arg https_proxy=http://<proxy>:8080 ." def imageId = sh( script: "docker inspect <registry>/my- repo/my-app:${version} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) withCredentials([usernamePassword(...)]) { retry(3) { sh "docker push <registry>/my-repo/my- app:${version}" } } } } * Source code shown is simulated for demo purposes only
  6. © 2019 All Rights Reserved. 16 ➜ ~ git init

    shared-libraries Initialized empty Git repository in shared-libraries/.git/ ➜ ~ cd shared-libraries ➜ shared-libraries git:(master) mkdir vars ➜ shared-libraries git:(master) vi vars/dockerBuild.groovy Creating A Shared Libraries Repository
  7. © 2019 All Rights Reserved. 17 // vars/dockerBuild.groovy def call(Map

    params) { def registry = "<registry>" def image = params.image def proxy = params.withProxy ? '--build-arg https_proxy=http://<proxy>:8080' : '' node('docker') { sh "docker build -t ${registry}/${image} ${proxy} ." if (params.withScan) { def imageId = sh( script: "docker inspect ${registry}/${image} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) } if (params.pushToRegistry) { withCredentials([usernamePassword(credentialsId: params.credentialsId)]) { retry(3) { sh "docker push ${registry}/${image}" } } } } } * Source code shown is simulated for demo purposes only
  8. © 2019 All Rights Reserved. 19 // Jenkinsfile library('shared-libraries') stage('Build')

    { dockerBuild( image: "my-repo/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } * Source code shown is simulated for demo purposes only
  9. © 2019 All Rights Reserved. 20 // Jenkinsfile library('shared-libraries') stage('Build')

    { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } // Jenkinsfile library('shared-libraries') stage('Build') { dockerBuild( image: "my-namespace/my-app:${version}", withProxy: true, withScan: true, credentialsId: "...", pushToRegistry: true, ) } * Source code shown is simulated for demo purposes only
  10. © 2019 All Rights Reserved. 26 Shared Jenkins Pipeline Library

    https://www.cloudbees.com/blog/ensuring-corporate-standards-pipelines-custom-marker-files
  11. © 2019 All Rights Reserved. 27 Shared Jenkins Pipeline Library

    https://www.cloudbees.com/blog/ensuring-corporate-standards-pipelines-custom-marker-files
  12. © 2019 All Rights Reserved. 28 Shared Jenkins Pipeline Library

    def libVersion = getLibraryVersion() echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline * Source code shown is simulated for demo purposes only the bootstrap script
  13. © 2019 All Rights Reserved. 29 def libVersion = getLibraryVersion()

    echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline Shared Jenkins Pipeline Library * Source code shown is simulated for demo purposes only
  14. © 2019 All Rights Reserved. 30 def libVersion = getLibraryVersion()

    echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline Shared Jenkins Pipeline Library * Source code shown is simulated for demo purposes only
  15. © 2019 All Rights Reserved. 31 def libVersion = getLibraryVersion()

    echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline Shared Jenkins Pipeline Library * Source code shown is simulated for demo purposes only this is the developer's Jenkinsfile
  16. © 2019 All Rights Reserved. 32 def libVersion = getLibraryVersion()

    echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline Shared Jenkins Pipeline Library * Source code shown is simulated for demo purposes only
  17. © 2019 All Rights Reserved. 33 TO PIN, OR NOT

    TO PIN, that is the question
  18. © 2019 All Rights Reserved. 34 Benefits of Latest... •

    Bugs and other major issues are resolved quicker • New features and capabilities are available for immediate use • Reduces the painful software upgrade "headaches"
  19. © 2019 All Rights Reserved. 36 Shared Library Guiding Principles

    Keep changes small & digestible Always maintain backwards compatibility Design clean API interfaces
  20. © 2019 All Rights Reserved. 37 // Jenkinsfile library('shared-libraries') stage('Build')

    { dockerBuild( image: "my-repo/my-app:${version}", pushToRegistry: true, ) } * Source code shown is simulated for demo purposes only
  21. © 2019 All Rights Reserved. 38 // Jenkinsfile library('shared-libraries') stage('Build')

    { dockerBuild( image: "my-repo/my-app:${version}", pushToRegistry: true, withArtifactoryRegistry: true, ) } * Source code shown is simulated for demo purposes only
  22. © 2019 All Rights Reserved. 39 Shared Library Branching Strategy

    master v0.1.0 Merge the PR Run the Jenkins pipeline Create release Address PR feedback Open a Pull Request Run the Jenkins pipeline Add some commits
  23. © 2019 All Rights Reserved. 40 Build Test Release Rollback

    Checkpoint End Start The CI/CD Pipeline
  24. © 2019 All Rights Reserved. 41 The CI/CD Pipeline Build

    Test Release Rollback Checkpoint End Start
  25. © 2019 All Rights Reserved. 42 The CI/CD Pipeline -

    Build // build.gradle plugins { id 'groovy' id 'jacoco' id 'codenarc' } sourceSets { main { groovy { srcDirs = ['src'] } resources { srcDirs = ['resources'] } } } dependencies { testCompile(group: 'com.lesfurets', name: 'jenkins-pipeline-unit', version: '1.0') testCompile(group: 'junit', name: 'junit', version: '4.12') testCompile(group: 'org.assertj', name: 'assertj-core', version: '3.13.2') } * Source code shown is simulated for demo purposes only
  26. © 2019 All Rights Reserved. 43 The CI/CD Pipeline -

    Build // Jenkinsfile stage('Build') { steps { sh './gradlew assemble' } } * Source code shown is simulated for demo purposes only
  27. © 2019 All Rights Reserved. 44 The CI/CD Pipeline Build

    Test Release Rollback Checkpoint End Start
  28. © 2019 All Rights Reserved. 45 // vars/dockerBuild.groovy def call(Map

    params) { def registry = "<registry>" def image = params.image def proxy = params.withProxy ? '--build-arg https_proxy=http://<proxy>:8080' : '' node('docker') { sh "docker build -t ${registry}/${image} ${proxy} ." if (params.withScan) { def imageId = sh( script: "docker inspect ${registry}/${image} -f '{{.ID}}'", returnStdout: true, ) scanImageForVulns(imageId: imageId) } if (params.pushToRegistry) { withCredentials([usernamePassword(credentialsId: params.credentialsId)]) { retry(3) { sh "docker push ${registry}/${image}" } } } } } * Source code shown is simulated for demo purposes only
  29. © 2019 All Rights Reserved. 46 The CI/CD Pipeline -

    Test https://github.com/jenkinsci/JenkinsPipelineUnit
  30. © 2019 All Rights Reserved. 47 // src/test/jenkinsfiles/dockerBuild/Jenkinsfile #!groovy stage('Build')

    { dockerBuild( image: 'my-namespace/my-app:0.1.0', pushToRegistry: true, ) } The CI/CD Pipeline - Test * Source code shown is simulated for demo purposes only this is a mocked Jenkinsfile
  31. © 2019 All Rights Reserved. 48 // src/test/jenkinsfiles/dockerBuild/Jenkinsfile #!groovy stage('Build')

    { dockerBuild( image: 'my-namespace/my-app:0.1.0', pushToRegistry: true, ) } The CI/CD Pipeline - Test * Source code shown is simulated for demo purposes only
  32. © 2019 All Rights Reserved. 49 // src/test/groovy/DockerBuildTest.groovy import static

    com.lesfurets.jenkins.unit.MethodCall.callArgsToString import static org.assertj.core.api.Assertions.assertThat import com.lesfurets.jenkins.unit.BasePipelineTest import org.junit.Test public class DockerBuildTest extends BasePipelineTest { // ... @Test void shouldPushToRegistry() { loadScript('dockerBuild/Jenkinsfile') printCallStack() assertThat(helper.callStack.findAll { call -> call.methodName == 'sh' }.any { call -> callArgsToString(call).contains('docker push <registry>/my-repo/my-app:0.1.0') }).isTrue() assertJobStatusSuccess() } } * Source code shown is simulated for demo purposes only
  33. © 2019 All Rights Reserved. 50 // src/test/groovy/DockerBuildTest.groovy import static

    com.lesfurets.jenkins.unit.MethodCall.callArgsToString import static org.assertj.core.api.Assertions.assertThat import com.lesfurets.jenkins.unit.BasePipelineTest import org.junit.Test public class DockerBuildTest extends BasePipelineTest { // ... @Test void shouldPushToRegistry() { loadScript('dockerBuild/Jenkinsfile') printCallStack() assertThat(helper.callStack.findAll { call -> call.methodName == 'sh' }.any { call -> callArgsToString(call).contains('docker push <registry>/my-repo/my-app:0.1.0') }).isTrue() assertJobStatusSuccess() } } * Source code shown is simulated for demo purposes only loads our mocked Jenkinsfile
  34. © 2019 All Rights Reserved. 51 // src/test/groovy/DockerBuildTest.groovy import static

    com.lesfurets.jenkins.unit.MethodCall.callArgsToString import static org.assertj.core.api.Assertions.assertThat import com.lesfurets.jenkins.unit.BasePipelineTest import org.junit.Test public class DockerBuildTest extends BasePipelineTest { // ... @Test void shouldPushToRegistry() { loadScript('dockerBuild/Jenkinsfile') printCallStack() assertThat(helper.callStack.findAll { call -> call.methodName == 'sh' }.any { call -> callArgsToString(call).contains('docker push <registry>/my-repo/my-app:0.1.0') }).isTrue() assertJobStatusSuccess() } } * Source code shown is simulated for demo purposes only prints the call stack to make troubleshooting easier
  35. © 2019 All Rights Reserved. 52 // src/test/groovy/DockerBuildTest.groovy import static

    com.lesfurets.jenkins.unit.MethodCall.callArgsToString import static org.assertj.core.api.Assertions.assertThat import com.lesfurets.jenkins.unit.BasePipelineTest import org.junit.Test public class DockerBuildTest extends BasePipelineTest { // ... @Test void shouldPushToRegistry() { loadScript('dockerBuild/Jenkinsfile') printCallStack() assertThat(helper.callStack.findAll { call -> call.methodName == 'sh' }.any { call -> callArgsToString(call).contains('docker push <registry>/my-repo/my-app:0.1.0') }).isTrue() assertJobStatusSuccess() } } * Source code shown is simulated for demo purposes only confirms the docker push command would be invoked correctly
  36. © 2019 All Rights Reserved. 53 // src/test/groovy/DockerBuildTest.groovy import static

    com.lesfurets.jenkins.unit.MethodCall.callArgsToString import static org.assertj.core.api.Assertions.assertThat import com.lesfurets.jenkins.unit.BasePipelineTest import org.junit.Test public class DockerBuildTest extends BasePipelineTest { // ... @Test void shouldPushToRegistry() { loadScript('dockerBuild/Jenkinsfile') printCallStack() assertThat(helper.callStack.findAll { call -> call.methodName == 'sh' }.any { call -> callArgsToString(call).contains('docker push <registry>/my-repo/my-app:0.1.0') }).isTrue() assertJobStatusSuccess() } } * Source code shown is simulated for demo purposes only confirms the job completed without an error
  37. © 2019 All Rights Reserved. 54 ➜ shared-libraries git:(master) ./gradlew

    test DockerBuildTest > shouldPushToRegistry STANDARD_OUT Loading shared library shared-libraries with version master Jenkinsfile.run() Jenkinsfile.stage(Build, groovy.lang.Closure) Jenkinsfile.dockerBuild({credentialsId=..., image=my-namespace/my-app:0.1.0, pushToRegistry=true, withProxy=true, withScan=true}) dockerBuild.node(docker, groovy.lang.Closure) dockerBuild.sh(docker build -t <registry>/my-repo/my-app:0.1.0 --build-arg https_proxy=http://<proxy>:8080 .) dockerBuild.sh({returnStdout=true, script=docker inspect <registry>/my-repo/my-app:0.1.0 -f '{{.ID}}'}) dockerBuild.usernamePassword({credentialsId=...}) dockerBuild.withCredentials([null], groovy.lang.Closure) dockerBuild.retry(3, groovy.lang.Closure) dockerBuild.sh(docker push <registry>/my-repo/my-app:0.1.0) Gradle Test Executor 47 finished executing tests. > Task :test Generating HTML test report… Finished generating test html results (0.002 secs) BUILD SUCCESSFUL in 4s 4 actionable tasks: 4 executed Stopped 1 worker daemon(s). * Source code shown is simulated for demo purposes only
  38. © 2019 All Rights Reserved. 55 The CI/CD Pipeline -

    Test // Jenkinsfile stage('Build') { steps { sh './gradlew assemble' } } stage('Test') { steps { sh './gradlew test' } } * Source code shown is simulated for demo purposes only
  39. © 2019 All Rights Reserved. 57 The CI/CD Pipeline -

    Test // Jenkinsfile library("shared-libraries@${env.BRANCH_NAME}") stage('Test') { steps { gradleBuild(tasks: 'test') ... } } * Source code shown is simulated for demo purposes only
  40. © 2019 All Rights Reserved. 58 The CI/CD Pipeline -

    Test // Jenkinsfile library("shared-libraries@${env.BRANCH_NAME}") stage('Test') { steps { gradleBuild(tasks: 'test') ... } } * Source code shown is simulated for demo purposes only
  41. © 2019 All Rights Reserved. 59 // Jenkinsfile library("shared-libraries@${env.BRANCH_NAME}") stage('Test')

    { steps { gradleBuild(tasks: 'test') ... } } The CI/CD Pipeline - Test * Source code shown is simulated for demo purposes only gradleBuild() is a custom library function!
  42. © 2019 All Rights Reserved. 60 // Jenkinsfile library("shared-libraries@${env.BRANCH_NAME}") stage('Test')

    { steps { gradleBuild(tasks: 'test') evaluate(readTrusted('test/dockerBuild/Jenkinsfile')) } } The CI/CD Pipeline - Test * Source code shown is simulated for demo purposes only executes a Jenkinsfile with library methods against the library itself!
  43. © 2019 All Rights Reserved. 61 // Jenkinsfile library("shared-libraries@${env.BRANCH_NAME}") stage('Test')

    { steps { gradleBuild(tasks: 'test') evaluate(readTrusted('test/dockerBuild/Jenkinsfile')) } } The CI/CD Pipeline - Test * Source code shown is simulated for demo purposes only executes a Jenkinsfile with library methods against the library itself!
  44. © 2019 All Rights Reserved. 62 The CI/CD Pipeline Build

    Test Release Rollback Checkpoint End Start
  45. © 2019 All Rights Reserved. 64 The CI/CD Pipeline -

    Release ➜ curl -s https://<ghe>/api/v3/repos/<org>/<repo>/releases/latest | jq '.tag_name' "v0.3.95" * Source code shown is simulated for demo purposes only
  46. © 2019 All Rights Reserved. 65 The CI/CD Pipeline -

    Release * Source code shown is simulated for demo purposes only def libVersion = getLibraryVersion() echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline the bootstrap script
  47. © 2019 All Rights Reserved. 66 The CI/CD Pipeline -

    Release def getLibraryVersion() { def response = httpRequest( url: 'https://<ghe>/api/v3/repos/<org>/<repo>/releases/latest', httpMode: 'GET', contentType: 'APPLICATION_JSON', validResponseCodes: '200', ) return readJSON(text: response.content).tag_name } * Source code shown is simulated for demo purposes only
  48. © 2019 All Rights Reserved. 67 The CI/CD Pipeline -

    Release * Source code shown is simulated for demo purposes only def libVersion = getLibraryVersion() echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline the bootstrap script
  49. © 2019 All Rights Reserved. 68 def libVersion = "v0.3.95"

    echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline The CI/CD Pipeline - Release * Source code shown is simulated for demo purposes only the bootstrap script
  50. © 2019 All Rights Reserved. 70 The CI/CD Pipeline -

    Release master v0.1.0 v0.1.1 latest
  51. © 2019 All Rights Reserved. 71 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 latest master
  52. © 2019 All Rights Reserved. 72 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 latest master
  53. © 2019 All Rights Reserved. 73 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 latest master
  54. © 2019 All Rights Reserved. 74 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 latest master
  55. © 2019 All Rights Reserved. 75 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 latest master
  56. © 2019 All Rights Reserved. 76 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 latest master
  57. © 2019 All Rights Reserved. 77 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 latest master
  58. © 2019 All Rights Reserved. 78 The CI/CD Pipeline -

    Release Canary release is a technique to reduce the risk of introducing a new software version in production by slowly rolling out the change to a small subset of users before rolling it out to the entire infrastructure and making it available to everybody. https://martinfowler.com/bliki/CanaryRelease.html “ “
  59. © 2019 All Rights Reserved. 79 The CI/CD Pipeline -

    Release Users Jenkins Instances 30% 70%
  60. © 2019 All Rights Reserved. 80 The CI/CD Pipeline -

    Release Users Jenkins Instances latest stable
  61. © 2019 All Rights Reserved. 81 The CI/CD Pipeline -

    Release v0.1.0 latest stable master
  62. © 2019 All Rights Reserved. 82 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 latest stable master
  63. © 2019 All Rights Reserved. 83 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 latest - 30% stable master
  64. © 2019 All Rights Reserved. 84 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 latest - 30% stable - 70% master
  65. © 2019 All Rights Reserved. 85 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  66. © 2019 All Rights Reserved. 86 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  67. © 2019 All Rights Reserved. 87 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  68. © 2019 All Rights Reserved. 88 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 latest - 30% stable - 70% master
  69. © 2019 All Rights Reserved. 89 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 latest - 30% stable - 70% master
  70. © 2019 All Rights Reserved. 90 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 latest - 30% stable - 70% master
  71. © 2019 All Rights Reserved. 91 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 latest - 30% stable - 70% master
  72. © 2019 All Rights Reserved. 92 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 latest - 30% stable - 70% master
  73. © 2019 All Rights Reserved. 93 The CI/CD Pipeline -

    Release v0.1.0 v0.1.1 latest - 30% stable - 70% v0.1.2 v0.1.3 v0.1.4 master
  74. © 2019 All Rights Reserved. 94 The CI/CD Pipeline -

    Release * Source code shown is simulated for demo purposes only def libVersion = getLibraryVersion() echo "Using shared library version: ${libVersion}" library("jenkins-pipeline-library@${libVersion}") def pipeline = readTrusted 'Jenkinsfile' evaluate pipeline the bootstrap script
  75. © 2019 All Rights Reserved. 95 The CI/CD Pipeline -

    Release def getLibraryVersion() { if (env.JENKINS_URL =~ /<canaryjenkins1>|<canaryjenkins2>/) { def response = httpRequest( url: 'https://<ghe>/api/v3/repos/<org>/<repo>/releases/latest', httpMode: 'GET', contentType: 'APPLICATION_JSON', validResponseCodes: '200', ) return readJSON(text: response.content).tag_name } return 'stable' } * Source code shown is simulated for demo purposes only
  76. © 2019 All Rights Reserved. 96 The CI/CD Pipeline Build

    Test Release Rollback Checkpoint End Start
  77. © 2019 All Rights Reserved. 97 The CI/CD Pipeline -

    Rollback Checkpoint // Jenkinsfile stage('Rollback Checkpoint') { steps { timeout(time: 4, unit: 'HOURS') { input(message: "Rollback v${version}?", ok: 'Yes') } ... } } * Source code shown is simulated for demo purposes only
  78. © 2019 All Rights Reserved. 99 The CI/CD Pipeline -

    Rollback Checkpoint def sendDatadogMetrics(def metrics) { try { httpRequest( url: 'https://app.datadoghq.com/api/v1/series, httpMode: 'POST', contentType: 'APPLICATION_JSON', requestBody: "{[\"series\": ${metrics}]}", validResponseCodes: '200,201,202,204', ) } catch (e) { null } } [ "job_name:${env.JOB_NAME}", "job_status:${currentBuild.currentResult}", "library_version:${libVersion}", "failure_exception_class:${error.getClass().getName()}", ] Tags * Source code shown is simulated for demo purposes only
  79. © 2019 All Rights Reserved. 100 The CI/CD Pipeline -

    Rollback Checkpoint v0.3.95 released hudson.AbortException
  80. © 2019 All Rights Reserved. 105 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 latest - 30% stable - 70% master
  81. © 2019 All Rights Reserved. 106 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 latest - 30% stable - 70% 4 hours master
  82. © 2019 All Rights Reserved. 107 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  83. © 2019 All Rights Reserved. 108 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  84. © 2019 All Rights Reserved. 109 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% 4 hours master
  85. © 2019 All Rights Reserved. 110 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  86. © 2019 All Rights Reserved. 114 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  87. © 2019 All Rights Reserved. 115 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  88. © 2019 All Rights Reserved. 117 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  89. © 2019 All Rights Reserved. 118 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  90. © 2019 All Rights Reserved. 119 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% master
  91. © 2019 All Rights Reserved. 120 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 master
  92. © 2019 All Rights Reserved. 121 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 4 hours master
  93. © 2019 All Rights Reserved. 122 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 master
  94. © 2019 All Rights Reserved. 123 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 master
  95. © 2019 All Rights Reserved. 124 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 v0.1.4 master
  96. © 2019 All Rights Reserved. 125 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 v0.1.4 master
  97. © 2019 All Rights Reserved. 126 The CI/CD Pipeline -

    Rollback Checkpoint v0.1.0 v0.1.1 v0.1.2 latest - 30% stable - 70% v0.1.3 v0.1.4 master
  98. © 2019 All Rights Reserved. 127 The CI/CD Pipeline Build

    Test Release Rollback Checkpoint End Start
  99. © 2019 All Rights Reserved. 130 In closing... Build Test

    Release Rollback Checkpoint End Start
  100. © 2019 All Rights Reserved. 132 Roderick R. Randolph linkedin.com/in/roderickrandolph

    capitalonecareers.com Distinguished Engineer Reimagining the software delivery experience for our engineers.