Jenkins Pipeline для АТ

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В данной статье хочу поделиться содержанием pipeline.jenkinsfile с минимальной необходимостью для организации автоматизированного тестирования. Установку, настройку самого Jenkins мы рассматривать не будем, только pipeline и его содержание для АТ.

Автотесты в нашем случае будут написаны с использованием:

  • java

  • maven

  • junit5

  • allure

Бонусом расскажу как сделать запуск сборки по действию в GitLab.

Что мы хотим от Jenkins?

  • Автоматический запуск тестов по расписанию (ночные прогоны регресса)

  • Хранение самого файла с pipeline в репозитории с исходным кодом автотестов

  • Использование переменных, параметров и сredentials

  • Шаги 

    • клонирование репозитория

    • запуск автотестов с возможностью выбора сьюта

    • формирование allure-отчета

  • Нотификация на почту о завершении прогона

pipeline.jenkinsfile

И так, сначала сразу результат, а потом посмотрим на него более детально.

pipeline {
    agent {
        node {
            label 'jenkins-vm.company.ru'
        }
    }
    triggers {
            cron('H 7 * * *')
    }
    parameters{
        choice(choices: ['regress', 'smoke'], description: 'Выбор сьюта для запуска', name: 'suiteToRun')
    }
    environment{
        mailRecipients = 'dminakova@cinimex.ru'
        TEST_CREDS = credentials('444444444')
    }
    stages {
        stage('clone repo') {
            steps{
                checkout([$class                           : 'GitSCM', branches: [[name: '*/master']],
                          doGenerateSubmoduleConfigurations: false,
                          extensions                       : [],
                          submoduleCfg                     : [],
                          userRemoteConfigs                : [[credentialsId: '22222222',
                                                               url          :  'https://git.company.ru/java_example.git']]
                          ])
            }
        }
        stage('run tests') {
            steps{
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    withMaven(jdk: 'JDK 8u172', maven: 'Maven 3.6.3') {
                        sh 'mvn clean install -Dgroups=${suiteToRun}'
                    }
                }
            }
        }
        stage('run allure reports') {
            steps {
                allure([includeProperties: true,
                                      jdk: '',
                               properties: [],
                        reportBuildPolicy: 'ALWAYS',
                                  results: [[path: '**/allure-results']]
                ])
            }
        }
    }
    post {
        always{
            echo 'Pipeline is complete'
            emailext (
            subject: "CMXQA.TESTS Отчет прогона тестов [${env.BUILD_NUMBER}] ",
            body:"""Подробный allure-отчет: "<a href='${env.BUILD_URL}allure/'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
            to: "${env.mailRecipients}"
            )
        }
    }
}

Ссылка на GitHub: https://github.com/DaryaMin/jenkins_pipeline_at/tree/master


Pipeline детально

Рассмотрим pipeline автотестов более детально.

Сборка автотестов чаще всего содержит следующие блоки:

  1. Переменные

  2. Параметры

  3. Credentials

  4. Триггеры сборки

  5. Шаги

    • клонирование репозитория

    • запуск автотестов

    • формирование отчета

  6. Действия после шагов: нотификация


Переменные

Pipeline поддерживает объявление переменных, которые можно в нем дальше использовать.

Для этого используется кодовое слово environment. Переменные перечисляются через запятую по принципу ключ = 'значение'.

В дальнейшем вызываются при помощи ${env.<название переменной>}, например: ${env.mailRecipients}

environment {
        allureResults = 'target/allure-results',
        allureReportPolicy = 'ALWAYS',
        mailRecipients = 'test@cinimex.ru, dminakova@cinimex.ru'
    }

к содержанию

Параметры сборки

В Pipeline можно указать некие параметры сборки с возможностью выбора при ручном старте сборки и дефолтным значением для автоматического старта по триггеру. Значения параметров могут использоваться как значения параметров самого приложения, например: url стенда, название сьюта для запуска и т.п.

Кодовое слово - parameters.
Параметры имеют тип, название, значение по умолчанию и описание. 
Параметры бывают разных типов:

  1. string

    Поддерживает строчный параметр, например:
    parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }

  2. text
    Может содержать несколько строк, например:
    parameters { text(name: 'DEPLOY_TEXT', defaultValue: 'One\nTwo\nThree\n', description: '') }

  3. booleanParam
    Параметр принимающий значения истина/ложь, например:
    parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }

  4. choice
    Параметр, который содержит уже конкретные значения к выбору. Часто используется для выбора сьюта (регресс, смок, дебаг), т.к. названия сьютов редко меняются.
    parameters { choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '') }

  5. password
    Параметр предназначенный для пароля.
    parameters { password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password') 

  6. отдельным типом заслуживающим особенного внимания является gitParameter. Этот тип не входит в базовую версию Jenkins и добавляется плагином Git Parametr. parameters { gitParameter(name: 'BRANCH', defaultValue: 'develop', description: 'Ветка, из которой деплоимся', branchFilter: 'origin/(.*)', type: 'PT_BRANCH', selectedValue: 'DEFAULT')}

Пример синтаксиса параметров:
pipeline {
    agent any
    parameters {
        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')

        text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')

        booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')

        choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')

        password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
    }
    stages {
        stage('Example') {
            steps {
                echo "Hello ${params.PERSON}"

                echo "Biography: ${params.BIOGRAPHY}"

                echo "Toggle: ${params.TOGGLE}"

                echo "Choice: ${params.CHOICE}"

                echo "Password: ${params.PASSWORD}"
            }
        }
    }
}
Отображение при ручном запуске сборки
Отображение при ручном запуске сборки
Результат вывода в консоль jenkins
Результат вывода в консоль jenkins

к содержанию


Credentials

Обратим внимание, что в выводе в консоль Jenkins нас предупреждает о том, что выводить значение пароля в echo используя Groovy String interpolation не является безопасным. 

Как же избежать возможной утечки пароля в данном случае?!

Самый простой вариант - это заменить двойные кавычки на одинарные.
О данной особенности не стоит забывать и при работе с методом credentials().

Результат выполнения

Метод credentials() поддерживает несколько вариантов:

  • secret text

  • usernames and passwords

  • secret files

  • Jenkins' Snippet Generator для других типов

Создание credential в Jenkins
  1. Перейдем в раздел credentials

  2. Выберем папку

  3. Выбираем домен

  4. Здесь отображаются уже существующие credentials. Их можно отредактировать. Нас интересует команда меню: Add credentials

  5. В нашем случае выберем тип Username and Password. Также существует плагин для Jenkins для интеграции с Vault.  Подробней https://plugins.jenkins.io/hashicorp-vault-plugin/

    Чек-бокс Treat username as secret не отображает username на списке credentials
    ID если не будет заполнено, сформируется автоматически

Для того чтобы credentials можно было использовать, необходимо объявить переменную:

environment {

        TEST_CREDS = credentials('4444444444444')

    }

здесь TEST_CREDS - это название переменной, а в скобках указан ID credentials

Также стоит отметить, что при обращении по имени переменной данные получаются в формате user:password. При необходимости получения непосредственно user или password к названию переменной добавляется _USR или _PSW соответственно. Объявлять эти переменные дополнительно не требуется.

Обращение к значению переменной осуществляется за счет указания знака$ перед названием переменной.

Пример

Добавим в наш пайплайн credentials и выведем их значение:

pipeline {
    agent {
        node {
            label 'jenkins-vm.company.ru'
        }
    }
 
    parameters {
        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
 
        text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
 
        booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
 
        choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
 
        password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
    }
    environment {
        TEST_CREDS = credentials('4444444444')
    }
    stages {
        stage('Example') {
            steps {
                echo "Hello ${params.PERSON}"
 
                echo "Biography: ${params.BIOGRAPHY}"
 
                echo "Toggle: ${params.TOGGLE}"
 
                echo "Choice: ${params.CHOICE}"
 
                echo 'Password: ${params.PASSWORD}'
                 
                echo 'from credentials: $TEST_CREDS'
                 
                echo "user from credentials: $TEST_CREDS_USR"
                 
                echo "password from credentials: $TEST_CREDS_PSW"
            }
        }
    }
}

Результат выполнения:

В данном случае даже использование двойных кавычек не выводит конкретное значение в консоль. Но все равно интерполяция является небезопасной, о чем нас предупреждает Jenkins. И рекомендуется использовать одинарные кавычки.

к содержанию


Триггеры сборки

Существует несколько вариантов триггеров сборки:
- Удаленный запуск сборки 
- Старт сборки после окончания сборки другого проекта
- Запуск периодически
- По изменениям в GitLab
- Триггер из артифактори
- Триггеры GitHub
- Scm изменения 

Самый популярный для запуска автотестов - это периодический запуск, например: ежедневно в 7 утра. Чтобы по началу рабочего дня было актуальное состояние прогона тестов.

Пример, ежедневный запуск в рабочие дни в 7.00: H 7 * * 1-5 

Синхронизация с GitLab

Полностью строка звучит "Build when a change is pushed to GitLab. GitLab webhook URL: https://company.jenkins.ru/project/TEST/ExampleMVN" 
Из названия нам потребуется URL. Именно на этот адрес GitLab будет отправлять события.

По каким же событиям Jenkins стартует сборку?

  1. любой Push в репозиторий

  2. Push в случае удалении ветки

  3. Создание любого Merge Request

  4. Создание Merge Request содержащего новые  коммиты

  5. Принятие  Merge Request

  6. Закрытие Merge Request

  7. Подтверждение Merge Request

  8. Если Merge Request содержит конкретный коммент

А теперь посмотрим,  что нужно сделать со стороны Gitlab.

В меню "Настройки" следует выбрать пункт "Webhooks"
В меню "Настройки" следует выбрать пункт "Webhooks"

Нам понадобится URL и Secret Token.
URL - указан в Jenkins когда мы проставляли чек-бокс по триггеру сборки. В нашем случае это https://company.jenkins.ru/project/TEST/ExampleMVN

Secret Token  - также берем из Jenkins из настроек триггера сборки по изменениям в Gitlab. Точнее, то значение которое мы укажем в Gitlab должно совпадать с указанным в Jenkins.

Генерация Secret Token 

  1. Необходимо перейти в Расширенные настройки триггер:

  2.  Нажать Generate 

  3. Скопировать сгенерированный код и вставить его в GitLab

После добавления webhook он отобразится внизу страницы

Можно проверить что все настроено корректно, нажав на кнопку Test. В случае если все сделано корректно, то появится уведомление 

к содержанию


Этапы/stages 

Основное описание того что проиcходит в Pipeline заключается как раз в блоке Stages. Блок Stages должен содержать минимум один этап (stage).  Рекомендуется делать этапы дискретными, например: Build, Test, and Deploy.

Каждый этап (stage) содержит:

  1. название, которое отображается в консоле при выполнении. Указывается в круглых скобках в одинарных кавычках

  2. непосредственно шаги выполнения (steps)

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

GIT

Одним из основных этапов сборки что для разработчиков, что для тестирования является интеграция с репозиторием для получения актуального состояния исходного кода. https://www.jenkins.io/doc/pipeline/steps/git/

В данном случае нам помогает плагин Jenkins https://plugins.jenkins.io/git/

Существует два варианта клонирования репозитория:

  • git step

  • checkout step

git step является более простым вариантом и соответственно менее функциональным.
checkout step лучше использовать с SCM checkout method. 

Рассмотрим простейший вариант git step. Подробнее про SCM checkout method можно прочитать https://plugins.jenkins.io/git/#plugin-content-pipeline-examples

Пример Pipeline, в котором клонируюется репозиторий
pipeline {
    agent {
        node {
            label 'jenkins-vm.company.ru'
        }
    }
    stages {
        stage('clone repo') {
            steps {
                git url: 'https://git.company.ru/java_example.git',
                credentialsId: '22222222',
                branch: 'master'
            }
        }
    }
}
Результат выполнения Pipeline
Результат выполнения Pipeline

Пример c checkout:

Stage('clone repo') {
    steps{
        checkout([$class : 'GitSCM', branches: [[name: '*/master']],
        doGenerateSubmoduleConfigurations: false,
        extensions                       : [],
        submoduleCfg                     : [],
        userRemoteConfigs                : [[credentialsId: '2222222222',
        url          :  'https://git.company.ru/java_example.git']]]
                )
            }
        }

Запуск тестов

После того как мы клонировали репозиторий с нашими автотестами, необходимо их запустить.

Запускаются они стандартной командой mvn (в данном случае) mvn clean install. Также можно указать версию jdk, maven. Команда запуска полностью дублируется той что мы запускаем у себя локально из консоли. Например: можно добавить запуск конкретного сьюта.

stage('run tests') {
     steps{
          withMaven(jdk: 'JDK 8u172', maven: 'Maven 3.6.3') {
                             sh 'mvn clean install'
           }
     }
 }
Запуск конкретного сьюта автотестов
  1. Для этого в тестах у нас имеется аннотация Tag.

Значения SMOKE и REGRESS вынесены в отдельный класс как константы. И уже на их значение настраивается Pipeline

public class SuiteName {
    public static final String SMOKE = "smoke";
    public static final String REGRESS= "regress";
}
  1. В пайплайн добавим параметр:

parameters{choice(choices: ['regress', 'smoke',],
                  description: 'Выбор сьюта для запуска',
                  name: 'suiteToRun')}
  1. Отредактируем команду запуска:

sh 'mvn clean install -Dgroups=${suiteToRun}'

В итоге при запуске сборки запустятся только те автотесты, которые помечены соответствующей аннотацией.

Формирование отчета

После прогона тестов нам бы хотелось получить некий отчет о результатах выполнения автотестов.

Jenkins по умолчанию формирует отчет об упавших тестах, из которого видно:
- Кем был осуществлен запуск
- Сколько по времени выполнялось
- В каком репозитории лежат исходники
- Результаты теста в виде количества упавших тестов и с какой ошибкой, если провалиться в конкретный тест

Данный вариант не особо удобен в использовании и мало информативен для сотрудников, не связанных с написанием автотестов. Более читабельный формат с описанием непосредственно шагов автотеста позволяет сформировать allure report.  Jenkins умеет также с ним работать  https://www.jenkins.io/doc/pipeline/steps/allure-jenkins-plugin/

Чтобы Allure отчет был информативен нужно уделять внимание исходному коду и аннотациям Allure.  

Подробнее про Allure: официальная документация: https://docs.qameta.io/allure/#_junit_5

Для того, чтобы jenkins формировал allure-отчет, необходимо добавить в pipline соответствующий этап:


stage('run allure reports') {
            steps {
                allure([includeProperties: true,
                                      jdk: '',
                               properties: [],
                        reportBuildPolicy: 'ALWAYS',
                                  results: [[path: '**/allure-results']]
                ])
            }
        }

В результате у нас появился дополнительный этап и значок allure у сборки

к содержанию

Формирование отчета

После прогона тестов нам бы хотелось получить некий отчет о результатах выполнения автотестов.

Jenkins по умолчанию формирует отчет об упавших тестах, из которого видно:
- Кем был осуществлен запуск
- Сколько по времени выполнялось
- В каком репозитории лежат исходники
- Результаты теста в виде количества упавших тестов и с какой ошибкой, если провалиться в конкретный тест

Данный вариант не особо удобен в использовании и мало информативен для сотрудников, не связанных с написанием автотестов. Более читабельный формат с описанием непосредственно шагов автотеста позволяет сформировать allure report.  Jenkins умеет также с ним работать  https://www.jenkins.io/doc/pipeline/steps/allure-jenkins-plugin/

Чтобы Allure отчет был информативен нужно уделять внимание исходному коду и аннотациям Allure.  

Подробнее про Allure: официальная документация: https://docs.qameta.io/allure/#_junit_5

Для того, чтобы jenkins формировал allure-отчет, необходимо добавить в pipline соответствующий этап:


stage('run allure reports') {
            steps {
                allure([includeProperties: true,
                                      jdk: '',
                               properties: [],
                        reportBuildPolicy: 'ALWAYS',
                                  results: [[path: '**/allure-results']]
                ])
            }
        }

В результате у нас появился дополнительный этап и значок allure у сборки, нажав на который мы перейдем непосредственно в allure отчет

к содержанию


Нотификация

Post блок состоит из одного или нескольких шагов выполняющихся либо после конкретного stage либо после всех stage, в зависимости от его расположения в pipeline.

Блок post может содержать следующие условия выполнения:

  • always

  • changed

  • fixed

  • regression

  • aborted

  • failure

  • success

  • unstable

  • unsuccessfulcleanup

Подробнее https://www.jenkins.io/doc/book/pipeline/syntax/#post

Мы прогнали автотесты, сформировали отчет, но пока никто не знает об этом. 
Наша следующая задача - рассказать об этом всем заинтересованным лицам.
Варианты тут могут быть разные:

  • нотификация о начале тестирования (не в блоке Post)

  • нотификация только в случае упавших тестов

  • нотификация всегда по завершении

  • прочие варианты, которые можно придумать)

Но все они базируются на процессе нотификации.  Рассмотрим вариант нотификации по почте.

Отправка письма на почту

Для отправки письма на почту есть команда emailext(). Подробнее https://plugins.jenkins.io/email-ext/

Добавим в наш pipeline отправку сообщения, указав:

  • тему сообщения

  • тело (в теле добавим сразу ссылку на allure отчет)

  • адресатов

post {
    always{
        echo 'Pipeline is complete'
        emailext (
            subject: "CMXQA.TESTS Отчет прогона тестов [${env.BUILD_NUMBER}] ",
            body:"""Подробный allure-отчет: "<a href='${env.BUILD_URL}allure/'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
            to: "dminakova@cinimex.ru"
        )
    }
}

Pipeline from SCM

Ну и в завершении, хотелось бы отметить: bestPractice является хранение файла jenkins.pipeline вместе с кодом

В проекте создаем файл в корневом каталоге и называем его piplene.jenkinsfile. И это же название указываем в Script Path.

В самом файле указываем все то что у нас было в jenkins

Источник: https://habr.com/ru/companies/cinimex/articles/782486/


Интересные статьи

Интересные статьи

Всем привет! Меня зовут Иван Чечиков, я QA-инженер в МТС Digital, работаю в проекте WASD.TV. В этой статье я расскажу о своем способе автоматизации iOS-сборки в TestFlight через Jenkins. С помощью так...
В Arenadata мы используем Jenkins для CI. Почему? Как бы банально это ни звучало — так исторически сложилось. Мы хранили код в GitHub, когда там ещё не было Actions, и продолжаем хранить, потому что м...
Эта статья — способ заглянуть в курс «Ansible: от первых шагов до большого проекта». Всеволод Севостьянов, Lead Engineer в Vene, отвечающий за пайплайны и deployment, показал, как настраивать Jen...
В последнее десятилетие достигнуты значительные успехи в сфере разработки инструментов непрерывной интеграции (Continuous Integration, CI) и непрерывного развёртывания (Continuous Del...
фото с Unsplash по запросу "pipeline" Общий подход Привет! Я начинаю серию постов о пайплайнах в разработке и не только, которые помогают удостовериться в качестве разрабатываемых мобильных п...