Разработка собственных pipelines и шагов на основе SMDL#

Важно! Прикладные разработчики производят разработку своих pipelines и шагов в репозитории с конфигурациями SMDL.Configs. Разработку и тестирование новых pipelines рекомендуется производить вначале в тестовой ветке репозитория и только после завершения разработки и успешного тестирования передавать доработки в основную ветку.

Процесс создания собственного Jenkins Job#

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

Создание каталога:

  1. В пространстве Jenkins нажать кнопку 'New Item'.

    new_item_folder

    Рисунок. Создание нового элемента в Jenkins

  2. Указать имя папки, задать тип 'Folder' и нажать 'OK'.

    set_folder_name

    Рисунок. Указание имени и типа создаваемого элемента в Jenkins

  3. Подключить библиотеки в Jenkins, но при подключении указать свои ветки в репозитории разработки (процесс подключения библиотек описан в разделе «Установка» документа «Руководство по установке»).

  4. Следующим шагом необходимо определиться с параметрами, которые будут на экране запуска Jenkins Job в Jenkins.

    Если среди готового списка параметров запуска, представленного в jobPropertiesConfig.yml в репозитории ядра SMDL.Core по пути resources/ru/sbrf/devops/jobPropertiesConfig.yml, нет подходящих для решения вашей задачи, то можно сформировать новый параметр. Для формирования новых параметров, которые будут отображаться на экране запуска Jenkins Job, необходимо:

    1. Перейти в любой Jenkins Job и открыть Pipeline Syntax.

      pipeline_syntax

      Рисунок. Переход в Pipeline Syntax

    2. Выбрать Sample Step: properties.

      properties_generator

      Рисунок. Выбор пункта properties

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

      new_job_property

      Рисунок. Подготовка к созданию параметров

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

      generate_pipeline_script

      Рисунок. Кнопка "Generate Pipeline Script"

    5. Будет сформирована конфигурация Jenkins Job из которой нужно извлечь только часть, отвечающую за параметры.

      string(defaultValue: 'default value', description: 'Тестовый параметр', name: 'Test', trim: false)
      

      generated_property

      Рисунок. Пример сгенерированных параметров.

    6. В данной конструкции для простых типов (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)
      
    7. Каждый параметр нужно будет вставить в секцию jobProperties создаваемого Jenkins Job в конфигурационном файле.

      В секции Jenkins Job:

      jobs:
        TestPipeline:
          properties:
            jobProperties:
              TestParam: |-
                string(defaultValue: PipelineManager.defaultValuesConfig().TestDV, description: 'Тестовый параметр', name: 'Test', trim: false)
      
    8. Также прописать добавленные параметры в секции Jenkins Job properties: params: [] и определить значения по умолчанию для соответствующих параметров. Задание значения по умолчанию можно произвести в секции defaultValues Jenkins Job. Если значение по умолчанию будет определено в параметр согласно пункту 6, но при этом не задано, то оно автоматически будет определяться либо как пустая строка либо как false.

      Пример:

      jobs:
        TestPipeline:
          properties:
            params: [TestParam]
            defaultValues:
              TestDV: "Test DefaultValue"
      
    9. Каждый параметр, который присутствует на экране запуска Jenkins Job, должен пройти валидацию. Тип валидации прописывается для параметра в секции Jenkins Job (Validation.groovy - описан в подразделе «Шаги» раздела «Дополнительная документация» текущего документа).

      Пример:

      jobs:
        TestPipeline:
          validate:
            requiredNotEmpty: [TestParam]
      
    10. Пример итогового варианта описания параметров 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)
      
  5. Создать 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]).

  6. Создать Jenkins Job в которой прописать вызов создаваемого pipeline процесс создания Jenkins Job описан в разделе «Установка» документа «Руководство по установке».

  7. После завершения разработки pipeline и шагов, можно переходить к запуску Jenkins Job и тестированию функциональности. Если Jenkins Job новая, то параметров при запуске у нее нет. Для того чтобы параметры на экране запуска Jenkins Job создались/обновились на основе занесенных параметров в пункте 4, нужно в настройках Jenkins Job установить параметр updateParams: true и сохранить (также Jenkins Job сама автоматически произведет обновление при первом запуске). После чего на экране запуска Jenkins Job нажать 'Собрать сейчас'.

    job_build_now

    Рисунок. Запуск сборки Jenkins Job

    Jenkins Job запустится и обновит параметры, в результате в Jenkins появятся параметры на экране запуска, и в рамках следующих запусков можно тестировать Jenkins Job с необходимыми тестовыми данными.

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

    job_move

    Рисунок. Перемещение Jenkins Job

Возможности при разработке собственных шагов#

  1. Вызов шага в шаге. 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.

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

    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
    
  3. Использование методов из библиотек методов. Библиотеки содержат наборы простых методов для работы с данными, 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)