Разработка собственных pipelines и шагов на основе SMDL#
Важно! Прикладные разработчики производят разработку своих pipelines и шагов в репозитории с конфигурациями SMDL.Configs. Разработку и тестирование новых pipelines рекомендуется производить вначале в тестовой ветке репозитория и только после завершения разработки и успешного тестирования передавать доработки в основную ветку.
Процесс создания собственного Jenkins Job#
Для того чтобы не было пересечений с основной библиотекой SMDL, развернутой в пространстве Jenkins, в пространстве Jenkins необходимо создать папку, в которой будет располагаться Jenkins Job на время разработки и тестирования.
Создание каталога:
В пространстве Jenkins нажать кнопку 'New Item'.

Рисунок. Создание нового элемента в Jenkins
Указать имя папки, задать тип 'Folder' и нажать 'OK'.

Рисунок. Указание имени и типа создаваемого элемента в Jenkins
Подключить библиотеки в Jenkins, но при подключении указать свои ветки в репозитории разработки (процесс подключения библиотек описан в разделе «Установка» документа «Руководство по установке»).
Следующим шагом необходимо определиться с параметрами, которые будут на экране запуска Jenkins Job в Jenkins.
Если среди готового списка параметров запуска, представленного в jobPropertiesConfig.yml в репозитории ядра SMDL.Core по пути
resources/ru/sbrf/devops/jobPropertiesConfig.yml, нет подходящих для решения вашей задачи, то можно сформировать новый параметр. Для формирования новых параметров, которые будут отображаться на экране запуска Jenkins Job, необходимо:Перейти в любой Jenkins Job и открыть Pipeline Syntax.

Рисунок. Переход в Pipeline Syntax
Выбрать Sample Step: properties.

Рисунок. Выбор пункта properties
Выбрать параметр «Это — параметризованная сборка» и после этого создать все необходимые параметры, которые планируете размещать на экране запуска Jenkins Job. Пример:

Рисунок. Подготовка к созданию параметров
После создания всех необходимых параметров нажать "Generate Pipeline Script".

Рисунок. Кнопка "Generate Pipeline Script"
Будет сформирована конфигурация Jenkins Job из которой нужно извлечь только часть, отвечающую за параметры.
string(defaultValue: 'default value', description: 'Тестовый параметр', name: 'Test', trim: false)
Рисунок. Пример сгенерированных параметров.
В данной конструкции для простых типов (string, boolean, choice) необходимо заменить значение по умолчанию. defaultValue на конструкцию вида: PipelineManager.defaultValuesConfig().имяПараметраDV. Пример: PipelineManager.defaultValuesConfig().needCheckMetricsDV. Итоговый параметр будет выглядеть следующим образом:
string(defaultValue: PipelineManager.defaultValuesConfig().nexusLinkDV, description: '''<font color=\'red\' size=\'3em\'>*</font>URL для скачивания дистрибутива<br> Пример: <font color=\'blue\'><i> https://sbrf-nexus.ru/nexus/service/local/repositories/Nexus_PROD/content/Nexus_PROD/CI02001129_Synapse/SYNAPSE/SP-02.255.00-01-SYNAPSE-3586/SYNAPSE-SP-02.255.00-01-SYNAPSE-3586-distrib.tar.gz</i></font>''', name: 'nexusLink', trim: false)Каждый параметр нужно будет вставить в секцию jobProperties создаваемого Jenkins Job в конфигурационном файле.
В секции Jenkins Job:
jobs: TestPipeline: properties: jobProperties: TestParam: |- string(defaultValue: PipelineManager.defaultValuesConfig().TestDV, description: 'Тестовый параметр', name: 'Test', trim: false)Также прописать добавленные параметры в секции Jenkins Job properties: params: [] и определить значения по умолчанию для соответствующих параметров. Задание значения по умолчанию можно произвести в секции defaultValues Jenkins Job. Если значение по умолчанию будет определено в параметр согласно пункту 6, но при этом не задано, то оно автоматически будет определяться либо как пустая строка либо как false.
Пример:
jobs: TestPipeline: properties: params: [TestParam] defaultValues: TestDV: "Test DefaultValue"Каждый параметр, который присутствует на экране запуска Jenkins Job, должен пройти валидацию. Тип валидации прописывается для параметра в секции Jenkins Job (Validation.groovy - описан в подразделе «Шаги» раздела «Дополнительная документация» текущего документа).
Пример:
jobs: TestPipeline: validate: requiredNotEmpty: [TestParam]Пример итогового варианта описания параметров Jenkins Job с одним параметром:
jobs: TestPipeline: validate: requiredNotEmpty: [TestParam] properties: params: [TestParam] defaultValues: TestDV: "Test DefaultValue" jobProperties: TestParam: |- string(defaultValue: PipelineManager.defaultValuesConfig().TestDV, description: 'Тестовый параметр', name: 'Test', trim: false)
Создать pipeline в папке resources/custom/pipelines. Создать необходимые шаги в папке resources/custom/steps]. Перед созданием нового шага, убедитесь в том, что еще не существует шага, который бы выполнял похожий функционал. Если данный шаг имеется, то рекомендуется использовать его, а не реализовывать близкий по функционалу шаг.
Базовый pipeline и Jenkins Job, который будет использоваться как шаблон - Example, расположенный в репозитории ядра SMDL.Core по пути
resources/ru/sbrf/devops/pipelines/Example.groovyПример pipeline (pipeline вызывает только шаги, он не должен содержать никакой сложной логики):
import ru.sbrf.devops.Helper import ru.sbrf.devops.PipelineManager def config = initPipeline(parameters, [ agentLabel: 'dind', ]) pipeline { agent { label config.agentLabel } options { timestamps() ansiColor('xterm') skipDefaultCheckout(true) } stages { stage('Пример шага') { steps{ runStep('ExampleStep') } } } stages { stage('Пример шага 2') { steps{ runStep('ExampleStep2', [varTo : PipelineManager.projectConfig().general.varFrom]) } } } post { always { script { PipelineManager.saveLog(this) if (!config.skipCleanWs) { cleanWs() } } } } }В рамках pipeline можно передавать переменные, которые могли получиться в рамках выполнения предыдущих шагов. Для этого необходимо вторым аргументом в
runStepпередать словарь (ключ — значение) в котором отразить переменные и принимаемые ими значения. Для того чтобы обратиться к переменным изconfigв рамках pipeline необходимо использовать конструкциюPipelineManager.projectConfig().general.varName. Итоговый пример вызова такого шага:runStep('ExampleStep2', [varTo : PipelineManager.projectConfig().general.varFrom]).Создать Jenkins Job в которой прописать вызов создаваемого pipeline процесс создания Jenkins Job описан в разделе «Установка» документа «Руководство по установке».
После завершения разработки pipeline и шагов, можно переходить к запуску Jenkins Job и тестированию функциональности. Если Jenkins Job новая, то параметров при запуске у нее нет. Для того чтобы параметры на экране запуска Jenkins Job создались/обновились на основе занесенных параметров в пункте 4, нужно в настройках Jenkins Job установить параметр updateParams: true и сохранить (также Jenkins Job сама автоматически произведет обновление при первом запуске). После чего на экране запуска Jenkins Job нажать 'Собрать сейчас'.

Рисунок. Запуск сборки Jenkins Job
Jenkins Job запустится и обновит параметры, в результате в Jenkins появятся параметры на экране запуска, и в рамках следующих запусков можно тестировать Jenkins Job с необходимыми тестовыми данными.
После завершения тестирования, можно передавать pull-request с изменениями в основную ветку и перемещать Jenkins Job в основное пространство в Jenkins. Для этого в Jenkins Job необходимо нажать 'Move', выбрать назначение и подтвердить перенос, нажатием 'Move'.

Рисунок. Перемещение Jenkins Job
Возможности при разработке собственных шагов#
Вызов шага в шаге. SMDL позволяет осуществлять вызов шагов не только в pipelines, но и в других шагах. Данная функциональность позволяет переиспользовать готовые шаги для решения задач. Пример кода шага, в котором происходит вызов другого шага.
package ru.sbrf.devops.steps // Обычный вызов шага runStep("DisableSpecificManagers") // Вызов шага с передачей переменных runStep("DisableSpecificManagers", config) // передача всех переменных в виде словаря runStep("DisableSpecificManagers", Helper.mergeMaps(config, result)) // передача переменных в виде объединения словарей runStep("DisableSpecificManagers", [var1: value1, var2: value2]) // передача конкретных переменных в виде словаряПри передаче в шаг переменных внутри вызываемого шага к ним необходимо обращаться через словарь
config. Например:config.var1.Передача переменных в другие шаги. В пункте выше было описано, что в шагах есть возможность взывать другие шаги, передавая им на вход необходимые переменные. Также есть возможность возвращать переменные выше из вызываемого шага в вызывающий. В данном случае вызов шага будет отличаться. Пример кода шага, в котором происходит вызов другого шага, производящего возврат переменных.
package ru.sbrf.devops.steps // Обычный вызов шага result.putAll(runStep("DisableSpecificManagers")) // Вызов шага с передачей переменных result.putAll(runStep("DisableSpecificManagers", config)) // передача всех переменных в виде словаря result.putAll(runStep("DisableSpecificManagers", Helper.mergeMaps(config, result))) // передача переменных в виде объединения словарей result.putAll(runStep("DisableSpecificManagers", [var1: value1, var2: value2])) // передача конкретных переменных в виде словаряВ данном случае в вызывающем шаге к возвращаемым переменных необходимо обращаться через словарь
result. Например:result.var1. В вызываемом шаге для возврата переменных, они должны записываться в словарьresult. Пример кода шага, производящего возврат переменных.package ru.sbrf.devops.steps // Возвращение переменной result.var1 = value1Использование методов из библиотек методов. Библиотеки содержат наборы простых методов для работы с данными, Jira, BitBucket, Confluence, которые можно использовать в шагах. Подробнее про подключение библиотек и использование методов можно ознакомиться в документации на библиотеки раздела «Библиотеки методов» текущего документа. Пример кода шага, использующего метод из библиотеки методов по чтению дополнительных файлов (additionalFiles).
package ru.sbrf.devops.steps import ru.sbrf.devops.Colors import ru.sbrf.devops.DevCommon import ru.sbrf.devops.Helper def dev = new DevCommon(this, config) additionalFileName = 'example.json' additionalFileContents = dev.useAdditionalFile(additionalFileName) Helper.echoColor(this, "Содержимое файла ${additionalFileName}: \n ${additionalFileContents}", Colors.GREENTEXT)