Доброго времени суток, Хабр!


Сегодня я хотел бы поделиться одним из способов, как с помощью Active Choices Plugin сделать задачу в Jenkins наиболее унифицированной и понятной для пользователя.


Введение


Такая аббревиатура как DevOps уже давно не является чем-то новым для IT сообщества. У многих людей фраза «сделать DevOps» ассоциируется с какой-то волшебной кнопкой, при нажатии на которую код приложения автоматически превращается в развернутое и протестированное приложение (все на самом деле сложнее, но мы абстрагируемся от всех процессов).


Итак, нам поступил заказ сделать такую волшебную кнопку, чтобы администраторы могли развертывать приложение по одному щелчку. Имеются различные виды реализации данной задачи: начиная от написания бота для какого-либо из мессенджеров и заканчивая разработкой отдельного приложения. Тем не менее цель у всего этого одна – сделать запуск сборки и развертывания приложения наиболее прозрачным и удобным.


В нашем случае мы будем использовать Jenkins.



Задача


Создать удобную Jenkins job, которая будет запускать сборку и (или) деплой выбранного микросервиса определённой версии.



Входные данные


У нас имеются несколько репозиториев, в которых лежит исходный код различных микросервисов.


Определение параметров


На вход нашей джобе должны поступать следующие параметры:


  1. URL репозитория с кодом микросервиса, который мы хотим собрать и развернуть при запуске джобы.
  2. ID коммита, из которого будет происходить сборка.

AS IS


Самый простой способ выполнить поставленную задачу – создать два параметра с типом String.



В этом случае пользователю нужно будет вводить вручную путь до репозитория и id коммита, что, согласитесь, не совсем удобно.



AS TO BE


А теперь попробуем другой тип параметров, чтобы рассмотреть все его преимущества.
Создадим первый параметр с типом Choice Parameter, второй — Active Choices Reactive Reference Parameter. В параметр с типом Choice добавим вручную в поле Choices имена репозиториев, где хранится код наших микросервисов.



Если данная статья понравится аудитории, то в следующей статье опишу процесс конфигурирования задач в Jenkins, используя описание через код (Configuration as code), т.е. нам не нужно будет вручную вводить имена репозиториев и создавать параметры, все произойдет автоматически (наш код получит список репозиториев из SCM и создаст параметр с данным списком).

Значения второго параметра у нас будут наполняться динамически, в зависимости от того, какое значение примет первый параметр (test1 или test2), ведь у каждого репозитория имеется свой список коммитов.


Active Choices Reactive Reference Parameter имеет следующие поля для заполнения:


  1. Name – имя параметра.
  2. Script – код, который будет выполняться каждый раз, когда значение параметра из поля Referenced parameter будет изменено (в нашем случае, когда будем выбирать между test1 и test2).
  3. Description – краткое описание параметра.
  4. Choice Type – тип возвращаемого скриптом объекта (в нашем случае будем возвращать html код).
  5. Referenced parameter – имя параметра, при изменении значения которого будет выполняться код из секции Script.


Перейдем непосредственно к заполнению самого главного поля в данном параметре. На выбор нам предлагают два вида реализации: использование Groovy Script или Scriptler Script.
Выбираем первое, так как Scriptler – это всего лишь плагин, который сохраняет уже ранее написанные вами скрипты и позволяет использовать их в других задачах без повторного copy-past.


Groovy код для получения всех коммитов из выбранного репозитория:


AUTH = "логин и пароль в Base64"                           
GIT_URL = "url до вашей SCM (https://bitbucket.org/)"                       
PROJECT_NAME = "имя проектной области, где находятся репозитории"

def htmlBuild() {
    html = """
            <html>
            <head>
            <meta charset="windows-1251">
            <style type="text/css">
            div.grayTable {
            text-align: left;
            border-collapse: collapse;
            }
            .divTable.grayTable .divTableCell, .divTable.grayTable .divTableHead {
            padding: 0px 3px;
            }
            .divTable.grayTable .divTableBody .divTableCell {
            font-size: 13px;
            }
            </style>
            </head>
            <body>
        """

    def commitOptions = ""
    getCommitsForMicroservice(MICROSERVICE_NAME).each {
        commitOptions += "<option style='font-style: italic' value='COMMIT=${it.getKey()}'>${it}</option>"
    }
    html += """<p style="display: inline-block;">
        <select id="commit_id" size="1" name="value">
            ${commitOptions}
        </select></p></div>"""

    html += """
            </div>
            </div>
            </div>
            </body>
            </html>
         """
    return html
}

def getCommitsForMicroservice(microserviceRepo) {
    def commits = [:]
    def endpoint = GIT_URL + "/rest/api/1.0/projects/${PROJECT_NAME}/repos/${microserviceRepo}/commits"
    def conn = new URL(endpoint).openConnection()
    conn.setRequestProperty("Authorization", "Basic ${AUTH}")
    def response = new groovy.json.JsonSlurper().parseText(conn.content.text)
    response.values.each {
        commits.put(it.displayId, it.message)
    }
    return commits
}

return htmlBuild()

Если не вдаваться в детали, то данный код получает на вход имя микросервиса (MICROSERVICE_NAME), отправляет запрос в Bitbucket (метод getCommitsForMicroservice), используя его API, и получает id и commit message всех коммитов для данного микросервиса.
Как уже говорилось ранее, данный код должен возвращать html, который будет отображен на странице Build with Parameters в Jenkins, поэтому все полученные значения из Bitbucket мы оборачиваем в список и добавляем в select.


После выполнения всех действий мы должны получить вот такую красивую страничку Build with Parameters.


Если выбрали микросервис test1:



Если выбрали микросервис test2:



Согласитесь, что пользователю будет намного удобнее взаимодействовать с вашей задачей данным способом, чем каждый раз копировать url и искать нужный commit id.


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


P.S.S. Как уже писал ранее, если данная статья будет полезной, то следующая будет про динамическое конфигурирование задач Jenkins посредством кода.