Разработка и тестирование Jenkins Shared Library

от автора

В компаниях с большим количеством проектов часто возникает ситуация, когда при разработке пайплайнов мы начинаем повторять себя, добавляя в разные сервисы одинаковые конструкции. Это противоречит основному принципу программирования DRY (Don’t Repeat Yourself), а ещё усложняет внесение изменений в код. Справиться с описанными проблемами помогает Jenkins Shared Library.

Мы пообщались с Кириллом Борисовым, Infrastructure Engineer технологического центра Deutsche Bank, и узнали, какие задачи решает Jenkins Shared Library и что её внедрение даёт компании. А ещё рассмотрели кейс разработки и тестирования с примерами кода.

Что такое Jenkins Shared Library

Jenkins Shared Library — это библиотека для многократного использования кода, которая позволяет описать методы один раз и затем применять их во всех пайплайнах. Она решает проблемы дублирования кода и унификации. Благодаря ней нам не нужно держать в каждом пайплайне огромное количество кода и постоянно повторять его. Мы можем написать код в одном месте, а потом инклюдить и вызывать его в рамках самого пайплайна.

Как это работает? Первым делом во всех пайплайнах мы выделяем общие части, которые можем объединить. Предположим, мы заметили, что везде используем нотификацию в Telegram, и везде она реализована по-разному. Мы создаем библиотеку, в ней делаем модуль нотификации и внедряем его во все проекты.

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

Польза Jenkins Shared Library для компании

Jenkins Shared Library уменьшает количество кода в пайплайне и повышает его читаемость. Для бизнеса это не столь важно, его больше заботит скорость доставки новых версий продукта до конечных пользователей. А здесь многое зависит от скорости разработки. Если каждая команда компании перестанет придумывать своё и начнёт использовать наработки других, вероятнее всего, разработка пайплайнов пойдёт быстрее. Соответственно, процесс доставки обновлений тоже ускорится.

С точки зрения самой команды, Jenkins Shared Library одновременно и усложняет, и упрощает работу. Например, проводить онбординг оказывается проще — вы показываете всё в одном месте. В то же время командам становится сложнее взаимодействовать. Сложность заключается в необходимости постоянной коммуникации. Когда решаем изменить что-то у себя в библиотеке, мы сначала должны синхронизироваться с другой командой, чтобы убедиться, что у неё ничего не сломается. Но обычно это решается регулярными синками, ревью кода и пул-реквестами.

Как команда понимает, что пора внедрять Jenkins Shared Library

На прошлом проекте у нас было 20 микросервисов, очень похожих между собой. Они были написаны по одному скелету на Java, а ещё в каждом из них лежал Jenkins file, где был описан пайплайн сборки проектов. Всякий раз, когда нужно было что-то поменять в пайплайне (а делать это приходилось часто, так как проект динамичный и быстро развивался), мы проходились по 20 репозиториям и поправляли всё вручную. Это довольно геморройный процесс, и мы подумали: «А почему бы нам не сделать что-то общее?».

Так мы перешли на Jenkins Shared Library. У нас появился базовый пайплайн в библиотеке, и, когда требовалось что-то изменить, мы работали с ним. Добавляли какие-то правки, и они автоматически имплементировались в каждый микросервис.

Ограничения Jenkins Shared Library

Любой инструмент призван решать какую-то проблему. Jenkins Shared Library решает проблему дублирования кода и унификации. Если вам эта унификация не нужна, смысла тащить библиотеку в проект нет. 

Если, скажем, у вас небольшой проект, где всего 5 микросервисов, писать для него библиотеку с нуля не стоит. Единственный вариант — переиспользовать, если она уже написана. Jenkins Shared Library — всё-таки решение для более крупных проектов с большим количеством микросервисов.

Кейс: разработка и тестирование Jenkins Shared Library

Для разработки и тестирования Jenkins Shared Library нам необходимо установить на свой компьютер gradle. Вот инструкция по установке — https://gradle.org/install/.

Далее в рабочем каталоге мы выполняем команду инициализации «gradle init», говорим установщику, что хотим настроить base каталог с groovy-файлами, и получаем готовый проект.

Следующий шаг — создание каталогов для JSL:

  1. var

  2. src

  3. resources

  4. test

Директория src используется для Groovy классов, которые добавляются в classpath.

Директория resources содержит файлы, которые мы можем загружать в пайплайн.

Директория test содержит тесты для наших скриптом.

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

Затем заполянем build.gradle, добавляя в него описание наших каталогов:

–-- sourceSets {   main {         groovy {             srcDirs = ['src','vars']         }         resources {             srcDirs = ['resources']         }     }     test {         groovy {              srcDirs = ['test']         }     } } ---

И добавляем зависимости, которые понадобятся нам в работе. Полный файл можно посмотреть в репозитории: ссылка на репозиторий.

Пишем простой первый класс, который увеличивает возраст на указанное значение 🙂

package com.example  class SampleClass {    String name    Integer age     def increaseAge(Integer years) {       this.age += years    } }

Не забываем про тесты — создаем в директории test файл с именем SampleClassTest.groovy и содержимым.

В секции Before описываем действия, которые необходимо выполнить перед тестом — в нашем случае это объявление класса. Далее в секции Test описываем сам тест. Объявляем age = 7 и выполняем функцию increaseAge с параметром 3. В случае правильного выполнения ожидаем получить 10.

class SampleClassTest {   def sampleClass    @Before   void setUp() {     sampleClass = new SampleClass()   }    @Test   void testIncrease() {     sampleClass.age = 7       def expect = 10     assertEquals expect, sampleClass.increaseAge(3)   } }

 Тест готов, запускаем командой gradle test. Результат будет такой: 

Но это синтетические примеры, давайте рассмотрим реальный

Каждый раз для вызова GET или POST запроса в пайплайн нам нужно вызывать класс HttpsURLConnection, передавать в него правильные параметры и проверять валидность сертификата.

Последуем главному принципу программирования DRY и подготовим класс, который позволит нам вызывать get и post запросы в пайплайне.

Создаем в директории src/com/example HttpsRequest.groovy, в нём создаем два метода get и post. В параметрах методов передаём URL-запроса и в случае POST еще и body-запроса.

Корневым методом нашего класса будет метод httpInternal. В нём закладываем логику, добавляем параметры к HttpsURLConnection, отлавливаем ошибку и в результате возвращаем тело ответа и ошибку:

package com.example  import groovy.json.JsonSlurper import javax.net.ssl.HttpsURLConnection import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import java.security.cert.CertificateException import java.security.cert.X509Certificate  class HttpsRequest {    def get(String uri) {     httpInternal(uri, null, false)   }    def post(String uri, String body) {     httpInternal(uri, body, true)   }    def httpInternal(String uri, String body, boolean isPost) {     def response = [:]     def error     try {              def http = new URL(uri).openConnection() as HttpsURLConnection       if (isPost) {         http.setRequestMethod('POST')         http.setDoOutput(true)         if (body) {           http.outputStream.write(body.getBytes("UTF-8"))         }       }        http.setRequestProperty("Accept", 'application/json')       http.setRequestProperty("Content-Type", 'application/json')       http.connect()        if (http.responseCode == 200) {         response = new JsonSlurper().parseText(http.inputStream.getText('UTF-8'))       } else {         response = -1       }     } catch (Exception e) {       println(e)       error = e     }     return [response, error]   } }

Покрываем тестами и создаем файл в директории test/HttpsRequestTest.groovy.

Для примера делаем get запрос, получаем ответ и проверяем, что он соответствует нашим ожиданиям:

  void testGet() {     def expect = json.parseText('{"hello":"slurm"}')     def (result, error) = http.get("https://run.mocky.io/v3/0bd64f74-1861-4833-ad9d-80110c9b5f25")     if (error != null) {       println(error)     }     assertEquals "result:", expect, result   }

 Запускаем тесты:

Осталось дело за малым — подключить библиотеку в Jenkins.

Для этого в Jenkins нужно перейти: Manage Jenkins → Configure System (Настроить Jenkins → Конфигурирование системы). В блоке Global Pipeline Libraries, добавить наш репозиторий:

 Последний шаг — создаем наш пайплайн:

@Library('jenkins-shared-library') _  import com.example.HttpsRequest  pipeline {     agent any     stages {         stage('Demo') {             steps {                 script {                     def http = new HttpsRequest()                     def (result, error) = http.get("https://run.mocky.io/v3/0bd64f74-1861-4833-ad9d-80110c9b5f25")                     if (error != null) {                         println(error)                     } else {                         println result                     }                                      }             }         }     } }

Выполняем 🙂

Весь код можно найти в репозитории: ссылка на репозиторий.

Вместо заключения: о чём нужно помнить при работе с Jenkins Shared Library

Jenkins Shared Library — больше инструмент разработки в том плане, что нужно программировать. Чаще всего когда вы первый раз работаете с ней, если вы не разработчик, а администратор или DevOps, вы делаете это по наитию с точки зрения администратора. Это рабочий подход, но у него есть свои недостатки. Например, расширять проект дальше сложнее. 

Лучше сразу начинать мыслить как разработчик: выделять классы и методы, правильно их имплементировать в пайплайн и т.д. Если вы не из мира разработки, это осознание приходит с запозданием. И чем позже оно придёт, тем больше времени и сил вы потратите на то, чтобы переписать то, что уже сделали.

Для тех, кто хочет углубиться в тонкости работы с Jenkins Shared Library

6 сентября у нас стартует курс по Jenkins, автором которого выступил Кирилл Борисов, Infrastructure Engineer технологического центра Deutsche Bank. В курсе будет много кейсов и примеров из практики спикера.

Вы научитесь:

  • автоматизировать процесс интеграции и поставки;

  • ускорять цикл разработки и внедрять полезные инструменты;

  • настраивать плагины и создавать пайплайны Jenkins as a code;

  • работать с Jenkins Shared Library.

Ознакомиться с программой и записаться: https://slurm.club/3a6OEs8


ссылка на оригинал статьи https://habr.com/ru/company/southbridge/blog/674506/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *