[REX] Migrez efficacement vos Jobs CI Maven en Pipeline Docker avec Jenkins 2

Maxime GréauSenior Sofwtare Engineer eXo Platform

About me, eXo, me@eXo

Maxime Gréau - @mgreau

  • Senior Software Engineer
    • @ eXo since 2015
  • 2010-2015 - Architect DGFIP
  • Before 2010 - Software Developer
  • Book Author
    • 2011: Apache Maven 3
  • Open Source
    • Asciidoctor
  • Speaker
    • Devoxx, DevNation, DevFest, JUG…​
@mgreau profile

eXo at a glance

eXo at a glance

What is eXo Platform?

eXo Platform

Software Factory Overview

eXo SWF

What do we have to build?

Platform Components & Add-ons

eXo PLF Components
  • More than 20 components to build
    • Platform projects
    • GateIn projects
    • Juzu projects
  • Lots of Add-ons
    • 15+ supported Add-ons
    • 100+ community Add-ons
  • Native Mobile projects
    • Android Application
    • iOS Application
Note
1 component = 1 multi-module Maven project

eXo Component Example - Social Maven Project

eXo Social

Platform versions and Clients

Git Workflow

eXo git workflow
  • develop
  • stable:
    • stable/1.x stable/4.3.x stable/4.2.x
  • feature
    • feature/<feature-name>
  • fix
    • fix/<issue-id>
  • integration
  • …​

http://developer.exoplatform.org

Jobs type

Remembered how to create a Maven Jenkins Job?

Jenkins Maven Job
Note
Yes, this is only ONE job!

Me when I had to use the Jenkins UI

jenkins UI

If you start to feel like your are a toaster…​AUTOMATE!! toaster

Quentin Adam Clever Cloud

Jenkins DSL and Pipeline jobs with Docker to the rescue!

jenkins docker

Overview

1- Create your own CI Docker Images

Build stacks

eXo Docker GitHub
  • What we need?
    • Git
    • Maven / Gradle / Android SDK
  • eXo CI Images
    • exoplatform/ci:jdk6-maven30
    • exoplatform/ci:jdk7-maven30
    • exoplatform/ci:jdk7-maven32
    • exoplatform/ci:jdk8-maven30
    • exoplatform/ci:jdk8-maven32
    • exoplatform/ci:jdk8-maven33
    • exoplatform/ci:jdk8-gradle2
    • exoplatform/ci:gradle2-android

https://github.com/exo-docker/exo-ci

Docker Image for Jenkins (1/4)

Define the locale

# Set the locale
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

Docker Image for Jenkins (2/4)

Configure the user

# eXo CI User
ARG EXO_CI_USER_UID=13000
ENV EXO_CI_USER ciagent
ENV EXO_GROUP ciagent
...
# Create user and group with specific ids
RUN useradd --create-home --user-group -u ${EXO_CI_USER_UID} --shell /bin/bash ${EXO_CI_USER}
$ docker run -t -d -u 13000:13000
-v m2-cache-ecms-develop-ci:/home/ciagent/.m2/repository
-v /srv/ciagent/workspace/ecms-develop-ci:/srv/ciagent/workspace/ecms-develop-ci:rw
-v /srv/ciagent/workspace/ecms-develop-ci@tmp:/srv/ciagent/workspace/ecms-develop-ci@tmp:rw
...  -e ******** --entrypoint cat exoplatform/ci:jdk8-maven32

Docker Image for Jenkins (3/4)

entrypoint and cat command

$ docker run ... -e ******** exoplatform/ci:jdk8-maven32 cat
$ docker run ... -e ******** --entrypoint cat exoplatform/ci:jdk8-maven32
COPY docker-entrypoint.sh /usr/bin/docker-entrypoint
# Workaround to be able to execute others command than "mvn" as entrypoint
ENTRYPOINT ["/usr/bin/docker-entrypoint"]

CMD ["mvn", "--help"]
# Hack for Jenkins Pipeline: authorize cat without absolute path
if [[ "$1" == "/"* ]] || [[ "$1" == "cat" ]]; then
  exec "$@"
fi

exec mvn "$@"

Docker Image for Jenkins (4/4)

eXo Dockerfiles

2- Use Jenkins Pipeline & Docker plugins

Pipelines are a series of steps that allow you to orchestrate the work required to build, test and deploy applications.

Jenkins Wiki CloudBees

Plugins to use

Note
Take a look at my Jenkins2 Dockerfile/plugins.txt samples https://github.com/mgreau/dockerfiles/tree/master/jenkins2

eXo Pipeline example

Maven CI job in Docker

Jenkins Job Pipeline
node('ci-docker'){
    exoCI{
        dockerImage = 'exoplatform/ci:jdk8-maven32'
        gitUrl = 'git@github.com:exodev/platform-ui.git'
        gitBranch = 'develop'
    }
}

Pipeline Shared Groovy Libraries

  • Create your Groovy scripts
    • src/org/exoplatform/swf/ExoSWFCommands.groovy
    • vars/exoCI.groovy
    • vars/exoCI.txt (doc)
    • vars/exoSonar.groovy
    • vars/exoSonar.txt (doc)
    • vars/mailNotification.groovy
  • Manage as a code
    • external github repository for now
eXo Pipeline libs

Pipeline Shared Groovy Libraries

vars/exoCI.groovy (1/3)

init
    def exoSWF = new org.exoplatform.swf.ExoSWFCommands()
    def utils = new org.exoplatform.swf.Utils()

    //init values
    def M2_REPO_IN_CONTAINER = utils.getValue('m2RepositoryPath', '/home/ciagent/.m2/repository', config, env)
    def DOCKER_IMAGE = utils.getValue('dockerImage', '', config, env)
Stage Pull Docker Image
    def eXoJDKMaven = docker.image(DOCKER_IMAGE);
    def pipelineError = null

    stage(DOCKER_IMAGE){
      eXoJDKMaven.pull()
    }
Stage Checkout SCM via Git
    stage('Checkout ' + GIT_BRANCH){
      // checkout scm with credentials
      git branch: GIT_BRANCH, credentialsId: GIT_CREDENTIALS_ID, url: GIT_URL
    }

Pipeline Shared Groovy Libraries

vars/exoCI.groovy (2/3)

Stage Maven Build
    stage('Build'){
      // Create m2 cache (use docker volume)
      def m2Cache = exoSWF.createMavenCacheVolume(JOB_NAME)
      // Use custom settings.xml file on project workspace directory
      configFileProvider(
                  [configFile(fileId: "${MAVEN_SETTINGS_FILE_ID}",  targetLocation: 'settings.xml')]) {
        try {
          eXoJDKMaven.inside("${DOCKER_RUN_PARAMS} -v ${m2Cache}:${M2_REPO_IN_CONTAINER}") {
            sh "mvn ${MAVEN_GOALS} -P${MAVEN_PROFILES} -DdeployAtEnd=${DEPLOY_AT_END} -s settings.xml"
          }
        } catch (error) {
          currentBuild.result = 'FAILURE'
          pipelineError = error
        } finally {
          // Delete temporary settings file
          sh 'rm -f settings.xml'
        }
      }
    }

Pipeline Shared Groovy Libraries

vars/exoCI.groovy (3/3)

Stage Publish Reports
    stage('Publish Reports'){
      junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
    }
Stage Send Notifications
    stage('Send Notifications'){

      // Send notification to inform about Build status
      mailNotification(env,currentBuild, mailTo)
      // Add comment to JIRA
      jiraNotification(env,currentBuild)

      // Clean up the workspace at the end (except in failure, and unstable cases)
      step([$class: 'WsCleanup', cleanWhenSuccess: true, cleanWhenFailure: false, cleanWhenUnstable: false])
    }
Note
See: Directions for Pipeline by Jesse Glick at Jenkins World 2016

DEMO

Pipeline Docker for CI Builds / Gradle-Android Build / Sonar Analysis

eXo Pipeline Sonar

3- Generate your Pipeline jobs with DSL jobs

      triggers {
            scm("${scmValue}")
            cron("${cronValue}")
      }

      definition {
        cps {
          script('''#!groovy
node('docker'){
    exoSonar{}
}
          ''')
        }
      }
    }
}

Pipeline, DSL, Shared Groovy libs

exo swf dsl pipeline

DEMO

Generate all pipeline jobs with DSL jobs

DSL

Now how to create new Jobs?

$ git commit -am "Create CI/Sonar jobs..."
$ git push
DSL Commit

7 seed jobs to generate 350+ jobs

then you feel more like…​

Jobs DSL Happy

Notes about Pipeline

postBuild {
  always {
    sh 'echo "This will always run"'
  }
  success {
    sh 'echo "This will run only if successful"'
  }
...
}

What about local builds?

use Docker for Mac /Docker for Windows

Docker4Mac

Use your CI Images on your laptop

.bash_profile

mvn(){
      mvnInContainer "exoplatform/ci:jdk8-maven32" "$@"
}
jdk7mvn30(){
      mvnInContainer "exoplatform/ci:jdk7-maven30" "$@"
}
# Run Maven commands in a container
mvnInContainer(){
      local dockerImage=$1

      docker run --rm \
        -v $(pwd):/srv/ciagent/workspace \
        -v ~/.gnupg/gpg.conf:/home/ciagent/.gnupg/gpg.conf:ro \
        -v ~/.gnupg/pubring.gpg:/home/ciagent/.gnupg/pubring.gpg:ro \
        -v ~/.gnupg/secring.gpg:/home/ciagent/.gnupg/secring.gpg:ro \
        -v ~/.gnupg/trustdb.gpg:/home/ciagent/.gnupg/trustdb.gpg:ro \
        -v ~/.gnupg:/home/ciagent/.gnupg \
        -v ~/.m2/repository:/home/ciagent/.m2/repository \
        -v ~/.m2/settings.xml:/home/ciagent/.m2/settings.xml \
        --workdir /srv/ciagent/workspace \
        ${dockerImage} "${@:2}"
}

Use your CI Images on your laptop

$ mvn -v
Apache Maven 3.2.5 (12a6b3acb947671f09b81f49094c53f426d8cea1; 2014-12-14T17:29:23+00:00)
Maven home: /usr/share/maven
Java version: 1.8.0_92, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0_92-oracle-x64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.4.22-moby", arch: "amd64", family: "unix"
$ jdk7mvn30 -v
Apache Maven 3.0.5 (r01de14724cdef164cd33c7c8c2fe155faf9602da; 2013-02-19 13:51:28+0000)
Maven home: /usr/share/maven
Java version: 1.7.0_79, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.7.0_79-oracle-x64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.4.22-moby", arch: "amd64", family: "unix"

Tip: Rebuild the image locally with your user ID if necessary

$ docker build --build-arg EXO_CI_USER_UID="1100" \
 -t exoplatform/ci:base https://github.com/exo-docker/exo-ci.git#master:base

To sum up

<Thank You!>

twitter: @mgreau - github: @mgreau - mgreau.com