Jenkins Job DSL - Création automatisée de pipelines

22 April 2015
| CI

Jenkins peut rapidement devenir une usine à jobs. Sans être nécessairement Netflix aux 1001 projets, l’application de certains paradigmes peut faire augmenter significativement le nombre de jobs.

Nous allons voir comment le plugin Job DSL peut résoudre cette problématique.

Introduction

L’application de certains paradigmes peut faire augmenter significativement le nombre de jobs:

  • La modularisation applicative
  • La réutilisation de briques techniques
  • Les utils que l’on peut être amené à développer
  • Le découpage du pipeline d’un projet en jobs jenkins associés aux étapes d’intégration et de déploiement continue: On part de compilation, test et deployment pour arriver à des projets associés à N jobs.

Suivant le dernier point, 10 projets vont générer au moins 30 jobs. Enjoy.

Objectif

L’idée face à cette configuration est de maintenir un pool de job type maitrisé et de ne pas multiplier les projets avec des configurations spécifiques.

Il reste tout de même la problématique de création et de maintenance des jobs. Même si créer un job à partir de reste simple (quoi que source du syndrome du copier/coller), configurer le tout sous la forme d’un pipeline reste fastidieux. Vient ensuite la maintenance de ces jobs où la modification d’un job type va toucher l’ensemble des jobs de ce type. Sans évoquer l’ajout d’un nouveau job type s’intercalant entre deux types.

Il existe plusieurs moyens qui apportent des solutions à des niveaux différents:

  • Template project plugin permet de partager les builders d’un job
  • Templates Plugin permet de définir des templates réutilisable de builder, job, folder, auxiliary. Il est disponible dans la version Enterprise de Cloudbees
  • Job DSL Plugin
  • Et sûrement d’autres plugins

Dont celui qui nous interesse, le Job DSL Plugin

The job-dsl-plugin allows the programmatic creation of projects using a DSL. Pushing job creation into a script allows you to automate and standardize your Jenkins installation, unlike anything possible before.

Le tout exprimé dans le langage Groovy qui propose des fonctionnalités poussées pour créer des DSL.

Par exemple le script ci-dessous (tiré de la page du plugin) permet de générer autant de job qu’il existe de branche sur le projet GitHub:

def project = 'quidryan/aws-sdk-test'
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
    job {
        name "${project}-${branchName}".replaceAll('/','-')
        scm {
            git("git://github.com/${project}.git", branchName)
        }
        steps {
            maven("test -Dproject.name=${project}/${branchName}")
        }
    }
}

Clair et concise, la configuration tient dans 1/3 d’écran avec juste ce qu’il y a à configurer.

Prerequisite

Obviously, un jenkins (au moins le LTS) et le Job DSL Plugin.

Définition des job types

Je vais prendre ici le cas le plus simple, 3 job types différents : compilation, test, package (pour s’abstraire du déploiement). L’objectif premier est de qualifier ces types et de définir la structure décrivant ces jobs.

Nous avons donc une liste de projets qui sont caractérisés par un identifiant, un nom et une url scm.

[
  {
    "id": "project1",
    "name": "Project 1",
    "scm": "https://github.com/nithril/jenkins-jobdsl-project1.git"
  },
  {
    "id": "project2",
    "name": "Project 2",
    "scm": "https://github.com/nithril/jenkins-jobdsl-project2.git"
  }
]
  • Le type compilation est un job maven qui lance une compilation.
  • Le type test est un job maven qui lance les tests.
  • Le type package est un freestyle job qui package l’artefact maven et copie le tout dans /dev/null

Création du job de génération

  1. Je crée donc un job de type Freestyle project que je nomme Generate Jobs
  2. Je lui ajoute un build step de type Process Job DSLs. Première option intéressante, le script peut être mis directement dans la configuration du job ou stocké sur le filesystem suite, par exemple, à un clone de son repository.
  3. J’ajoute le SCM Git sur l’url du projet contenant le script groovy JobDsl
  4. Et je paramêtre le chemin vers le fichier JobDsl

Description du script

Chargement de la structure des projets

J’utilise pour cela le JsonSlurper: JSON slurper parses text or reader content into a data structure of lists and maps.

import groovy.json.JsonSlurper

def projects = new JsonSlurper().parseText(readFileFromWorkspace("src/main/groovy/project.json"))

Iteration sur les projets et création des jobs

projects.each { project ->

    //Define projects name
    def compileProjectName = "${project.name} - Compile"
    def testProjectName = "${project.name} - Test"
    def packageProjectName = "${project.name} - Package"

    //Compile Job
    mavenJob(compileProjectName) {
        projectScm(delegate, project)
        goals "compile"
        publishers {
            downstream testProjectName
        }
    }

    //Test Job
    mavenJob(testProjectName) {
        projectScm(delegate, project)
        goals "test"
        publishers {
            downstream packageProjectName
        }
    }

    //Package Job
    freeStyleJob(packageProjectName) {
        projectScm(delegate, project)
        steps {
            maven {
                goals "package"
                mavenInstallation "Maven 3.2.2"
            }
            shell "cp submodule/target/*.jar /dev/null"
        }
    }
}

Les 2 projects ont respectivement leurs 3 jobs de définis avec leurs relations downstream. JobDsl

Description de projectScm

projectScm est la factorisation du bloc de configuration du SCM ci-dessous qui serait autrement à répeter dans chaque job:

scm {
    git {
        remote {
            url project.scm
        }
        branch "master"
        createTag false
    }
}

J’ai donc sorti ce bloc que j’ai mis dans une closure Groovy pour pouvoir l’utiliser dans la DSL Jenkins. Il est cependant nécessaire de définir par délégation le scope d’évalutation de cette closure qui est mis au scope du DSL Jenkins en cours d’execution. Pour plus d’information voir cet excellent article: Groovy Closures: this, owner, delegate Let’s Make a DSL.

   
def projectScm = { owner, project ->
    delegate = owner
    scm {
        git {
            remote {
                url project.scm
            }
            branch "master"
            createTag false
        }
    }
}

Et pour finir

Je crée une vue de type Pipeline que je mets en fin d’itération

 
//Create the Pipeline view
buildPipelineView("${project.name}") {
    selectedJob compileProjectName
    displayedBuilds 3
    showPipelineParameters true
    showPipelineParametersInHeaders true
    showPipelineDefinitionHeader true
}

JobDsl

En conclusion

En 55 lignes de code et 12 lignes de fichier de description, j’ai pu définir un process de génération d’un pipeline de projet qui dans notre exemple a créé 6 jobs et 2 vues de type pipeline.

Dans le prochain article, j’utiliserai un Job dsl pour générer les jobs de release.

Published 22 April 2015
blog comments powered by Disqus