Aller au contenu principal

Ecrire son premier Jenkinsfile

· 6 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Dans un précédent billet, je vous ai expliqué comment installer rapidement jenkins sur un cluster kubernetes en utilisant un chart helm. Voyons maintenant comment écrire le code d'un pipeline jenkins dans son mode déclaratif.

Structure d'un Jenkinsfile

Un fichier Jenkinsfile utilise une syntaxe appelée Jenkins Job DSL qui est fourni avec le plugin Pipeline.

Voici un exemple de Jenkinsfile :

pipeline {

    agent {
        node {
            label 'deploy'
        }
    }

    tools {
        ansible 'ansible-demo'
    }

    options {
        buildDiscarder logRotator(
                    daysToKeepStr: '15',
                    numToKeepStr: '10'
            )
    }

    environment {
        APP_ENV  = "DEV"
    }

    stages {

        stage('Cleanup Workspace') {
            steps {
                cleanWs()
                sh """
                echo "Cleaned Up Workspace for ${APP_NAME}"
                """
            }
        }

        stage('Code Checkout') {
            steps {
                checkout([
                    $class: 'GitSCM',
                    branches: [[name: '*/master']],
                    userRemoteConfigs: [[url: 'https://github.com/spring-projects/spring-petclinic.git']]
                ])
            }
        }

        stage('Code Build') {
            steps {
                 sh 'mvn install -Dmaven.test.skip=true'
            }
        }

        stage('Priting All Global Variables') {
            steps {
                sh """
                env
                """
            }
        }

    }
}

Voyons comment est construit ce Jenkinsfile.

Le bloc pipeline

Tous les pipelines possèdent un premier bloc de type pipeline :

pipeline {

---La déclaration du pipeline se fait dans un block pipeline---

}

C'est ce bloc qui va contenir des directives, qui sont pour les principales de type agent, tools, options, environment, post et stages. Nous verrons dans un autre billet le reste des directives.

La directive Agent (requis)

Dans le bloc agent où nous définissons sur quel agent va tourner notre pipeline. On peut ainsi indiquer n'importe quel agent avec la balise any, ou des spécifiques en indiquant un node portant un label.

pipeline {
    agent any

---Le reste du code---

}

Ou :

pipeline {
    agent {
        node {
            label 'ansible'
        }
    }
---Le reste du code---

}

Pour ajouter un label, étiquettes en français, à un node. Cela se fait dans la section configuration des nœuds de la partie administrations Jenkins. Ici, nous utilisons le nœud statique portant le label ansible.

Il est également possible d'utiliser des agents dynamiques de type docker, avec des images ou dockerfile ou encore kubernetes en installant les plugins au préalable.

pipeline {
    agent {
        docker {
            image "alpine:latest"
        }
    }
---Le reste du code---

}

La directive stages (requise)

Cette section va permettre de définir tous les stages d'un pipeline. Il en faut au minimum un.

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

Chaque stage va contenir des steps qui doit contenir au minimum un step.

Le code des steps

Composantes des stages, les steps permettent de lancer des fonctions, appelés step. Ces steps sont composés de celles fournies nativement avec Jenkins, appelés basic steps

Parmi les principales, on retrouve :

La directive post (optionnel)

Cette directive permet de définir un ou plusieurs steps qui sont exécutés à la fin de l'exécution d'un pipeline. Post peut prendre des post-condition permettent l'exécution des steps à l'intérieur de chaque condition en fonction de l'état d'exécution du pipeline : always, changed, fixed; regression, aborted, .... La liste complète des conditions

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post {
        always {
            echo 'I will always say Hello again!'
        }
    }
}

La directive tools (optionnel)

Ce bloc permet d'indiquer quels outils utiliser. Ces outils doivent être installés et configurés au préalable. L'installation se fait à travers des plugins. Par exemple le plugin Ansible.

pipeline {
    agent {
        node {
            label 'ansible'
        }
    }
    tools {
        ansible "ansible-demo"
    }
---Le reste du code---

}

Il existe d'autres méthodes pour installer ces outils, dans le pipeline, préinstallé sur le nœud (via le label ansible par exemple) ou en utilisant des images Docker.

La directive options (optionnel)

Il contient toutes les options requises par le job. Certaines de ces options sont natives et d'autres apportées par les plugins. Ces options ont une portée sur l'ensemble du pipeline. Nous verrons par la suite que ces options peuvent aussi être définies au niveau des stages.

pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS')
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

Toutes les options natives sont décrites ici

La directive environnement (optionnel)

Cette directive permet de définir des variables d'environnement utilisé dans tout le pipeline. Cette directive peut également être définie au niveau de chaque stage.

pipeline {
    agent any
    environment {
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment {
                AN_ACCESS_KEY = credentials('my-predefined-secret-text')
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

Écrire facilement son premier pipeline

Pour écrire mes pipelines, j'utilise vscode sur lequel j'ai activé deux extensions :

  • JenkinsFile Support qui apporte la coloration syntaxique, l'autocomplétion et des snippets
  • Jenkins Pipeline Linter Connector qui permet de valider la syntaxe de son jenkinsfile avant de l'exécuter. Pour que cela fonctionne, il suffit de rentrer dans ses paramètres l'url, le user et le mot de passe.

Si on reprend ce qui a été dit ci-dessus, nous avons en premier un bloc pipeline, dans lequel nous ajoutons des directives agent, stages et post :

pipeline{
    agent{
        label "node"
    }
    stages{
        stage("A"){
            steps{
                echo "========executing A========"
            }
            post{
                always{
                    echo "========always========"
                }
                success{
                    echo "========A executed successfully========"
                }
                failure{
                    echo "========A execution failed========"
                }
            }
        }
    }
    post{
        always{
            echo "========always========"
        }
        success{
            echo "========pipeline executed successfully ========"
        }
        failure{
            echo "========pipeline execution failed========"
        }
    }
}

Exemple de snippet apporté par le plugin JenkinsFile Support, appréciable pour démarrer.

Pousser ce Jenkinsfile dans un projet et exécuté le comme décris dans le précédent billet.

Plus loin

Comme je l'ai dit au début de ce billet, je découvre Jenkins. Donc pour le moment le billet permet de familiariser avec l'écriture de simples pipelines Jenkins en utilisant les blocs de base. Je vais enrichir ce billet comme j'ai pu le faire avec Ansible, au fil de mes découvertes. Donc revenez de temps en temps !