Использование программного компонента#

Оглавление#

Сборка составного дистрибутива#

Использование составных дистрибутивов рекомендуется с целью:

  • Ускорения сборки в случае изменения конфигураций;

  • Уменьшения расхода места в Хранилище дистрибутивов;

  • Возможности использовать для разных сегментов одну кодовую базу с разными конфигурациями.

Артефакты составного дистрибутива#

  • Бинарные файлы - собранные приложения для дистрибутива.

  • Конфигурации - конфигурация дистрибутива. В данном артефакте прописана зависимость на бинарные (исполняемые) файлы.

Архивация в ZIP конечного дистрибутива производится через указание маски включаемых папок/файлов в стиле Ant. По умолчанию, в дистрибутив попадает только содержимое папки package

Сборка исполняемого файла#

Для того чтобы собрать исполняемый файл, необходимо:

  1. Создать отдельный файл pipeline.yml (отдельный файл или отдельный репозиторий с этим файлом);

  2. В нем включить расширение std5Binaries:

    extensions:
      - std5Binaries
    
  3. Запустить Jenkins job сборки, указав с помощью параметров расположение файла конфигурации (CONFIG_REPO/CONFIG_FILE/CONFIG_BRANCH/CONFIG_DIR);

  4. Отметить версию, с которой был выложен исполняемый файл, она потребуется для сборки конфигураций;

Сборка конфигураций#

  1. Аналогично пункту 1 раздела «Сборка исполняемого файла».

  2. В файле pipeline.yml добавить зависимость (дистрибутив, собранный согласно разделу «Сборка исполняемого файла») в профиле binaries:

    profiles:
      binaries:
        - groupId: my.group.id # groupId из сборки binaries
          artifactId: my-artifact-bin # artifactId из сборки binaries
          version: D-01.004.03-453 # В данном случае использована версия дистрибутива, указанная в шаге 4 сборки binary
          type: zip
          classifier: distrib
    
  3. В файле pipeline.yml добавить расширение std5Configuration

extensions:
  - std5Configuration
  1. аналогично пункту 3 раздела «Сборка исполняемого файла».

  2. В одном из начальных этапов сборки будет скачана зависимость из профиля binaries.

  3. В pom.xml дистрибутива появятся все профили со всеми зависимостями, описанные в разделе profiles

Ограничения#

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

  2. При сборке конфигураций из дистрибутива исключаются все исполняемые файлы (pl, bh и пр.).

Загрузка логических моделей данных (LDM) при сборке дистрибутива#

Для того чтобы при сборке дистрибутива выполнялась отправка логических моделей данных в сервис META, необходимо:

  • Разработать логические модели данных, содержащие ПДн, БТ, ДПК в формате JSON в соответствии с требованиями Разработки логических моделей данных АС;

  • Разместить модели в директории dataModel репозитория конфигурации проекта;

  • Зарегистрировать ТУЗ с правами загрузки моделей в META;

  • Выяснить код компонента используемого приложения в ARIS;

  • Добавить настройки для работы с сервисом МЕТА в файл pipeline.yml.

При размещении моделей в директории dataModel необходимо соблюдать правила именования:

  • model-pd.json - имя файла для модели ПДн;

  • model-bs.json - имя файла для модели БТ;

  • model-pcidss.json - имя файла для модели ДПК.

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

Рекомендации по заполнению полей code и name (замените CI00000000 на КЭ вашего приложения):

"name": "CI00000000 Логическая модель данных PD",
"code": "CI00000000_LDM_PD",
"name": "CI00000000 Логическая модель данных BS",
"code": "CI00000000_LDM_BS",
"name": "CI00000000 Логическая модель данных PCI_DSS",
"code": "CI00000000_LDM_PCI_DSS",

Настройка динамических параметров#

Для получения необходимых параметров сборки при установке в OpenShift предусмотрены placeholders вида ${build: <parameter>}, значения которых будут проставлены в процессе выполнения Pipeline:

  • jenkins_env.fp_artifact_version - версия текущей сборки. Пример шаблона:
    ${build: jenkins_env.fp_artifact_version }

  • jenkins_env.<image-name>.fp_image_hash - хэш сумма, получившегося при сборке образа приложения, где <image-name> - название deployment unit. Пример шаблона: image:https://nexus3.<...>.ru/efs/ci00428440_efs/ci01947988_integration_routing/ufs-routing@${build: jenkins_env.deployment_unit_folder.fp_image_hash }

При использовании данного функционала необходимо исключить символ - . - в названии deployment unit директории (<image-name>)

Использование sha-суммы обусловлено требованиями безопасности и новым стандартом формирования дистрибутива ОСЕ.

Обратите внимание, что в отличие от версии, sha-сумма отделяется от имени образа не двоеточием :, а символом @. Таким образом поле image имеет формат: https://registry_host/image_url@${build: jenkins_env.fp_image_hash }

Данный placeholder необходимо использовать только в поле image, которое соответствует ВАШЕМУ образу. В sidecar и остальных полях файла менять ничего не нужно!

Значения следующих placeholders вида ${build: <parameter>} проставляются из вашего pipeline.yml:

  • fp.groupId, fp.artifactId, fp.version - groupId, artifactId, version - координаты дистрибутива в Nexus. Пример шаблона: ${build: fp.version}

  • profiles.binaries.groupId, profiles.binaries.artifactId, profiles.binaries.version - аналогично - координаты бинарного артефакта, если текущий является конфигурацией. Пример шаблона: ${build: profiles.binaries.groupId}

Для определения значений переменных окружения среды необходимо в шаблонах использовать placeholders вида ${build: lookup('env', '<env_key>') }. Пример шаблона: ${build: lookup('env', 'BINARIES_VERSION') }, ${build: lookup('env', 'VERSION', default='1.0.0') }

Обратите внимание, переопределены placeholders для блока - ${build block: <block>}

Обратите внимание, что замена placeholders производится во всех текстовых (не бинарных) файлах рекурсивно относительно директории package/conf

Для работы функционала необходимо определить переменную окружения ANSIBLE_TEMPLATING_TOOL. Значением должна выступать метка инструмента ansible (подробнее можно прочитать в разделе Поиск инструментов сборки).

Также доступна Ansible-шаблонизация pipeline.yml посредством ${build: } и ${build block:}, при этом используется Jinja2-синтаксис. Предварительно необходимо задать ANSIBLE_TEMPLATING_TOOL (метка инструмента Ansible). Подробнее о настройке можно узнать в разделе Поиск инструментов сборки. Пример шаблона:

${build block: if not lookup('env', 'BINARIES_VERSION') and lookup('env', 'TEST_SPEC') == 'bin'}
fp:
  groupId: "<PROD_1>"
  artifactId: "<DEVOPS_PIPELINE_1>"
  version: "D-01.000.02"
${build block: elif lookup('env', 'BINARIES_VERSION') and lookup('env', 'TEST_SPEC') == 'conf'}
fp:
  groupId: "<PROD_2>"
  artifactId: "<DEVOPS_PIPELINE_2>"
  version: "P100"
${build block: endif}

Для использования lookup плагина „file“ рекомендуется применять следующую конструкцию:

${build: lookup('file', source_folder + '<relative_path>' ) }

Где:

  • source_folder - параметр пути до «package/conf» (менять этот параметр нельзя);

  • relative_path - относительный путь от «package/conf».

Также можно использовать и относительный путь от workspace (где вызывается шаблонизация):

${build: lookup('file', '<relative_path>' ) }

Сборка скриптов БД#

Скрипты БД, лежащие в дистрибутиве, должны быть сконфигурированы таким образом, чтобы при сборке получался архив следующей структуры:

db_archive.zip/
├─ <jdbc-driver>.jar
├─ <liquibase>.jar 
├─ 0001_changelog.xml
├─ <папки и файлы, на которые ссылается 0001_changelog.xml>

Файла такой структуры можно добиться, например, с помощью добавления в файл pom.xml зависимостей postgres вызова assembly plugin с соответствующим дескриптором:

<dependencies>
    <dependency>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-core</artifactId>
        <version>4.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.4.2</version>
    </dependency>

</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <id>make-db-archive</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <finalName>db_archive</finalName>
                        <appendAssemblyId>false</appendAssemblyId>
                        <descriptors>
                            <descriptor>db-assembly.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Важно: помимо скриптов текущего релиза в архив должны попасть liquibase-скрипты БД всех предыдущих. В данном примере все скрипты накопительным итогом хранятся в resources, и resources целиком попадает в дистрибутив.

и содержимое дескриптора db-assembly.xml (все файлы, которые нужно добавить, расположены в src/main/resources):

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0">
<id>make-liquibase-archive</id>
<formats>
    <format>zip</format>
</formats>

<dependencySets>
    <dependencySet>
        <includes>
            <include>org.postgresql:postgresql</include>
            <include>org.liquibase:liquibase-core</include>
        </includes>
        <useProjectArtifact>false</useProjectArtifact>
    </dependencySet>
</dependencySets>
<fileSets>
    <fileSet>
        <directory>src/main/resources/</directory>
        <outputDirectory>.</outputDirectory>
    </fileSet>
</fileSets>
``` Параметры конфигурации в pipeline.yml описаны в соответствующем [разделе](../developer-guide/configuration.md#заполнение-разделов-bh-db).

Сборка bh-артефактов#

Репозиторий bh - это обычный maven-проект. Для сборки используется maven, который настроен в соответствующем разделе в файле pipeline.yml. При этом к cmd добавляются следующие параметры (профили включаются, если не выключен сонар):

mvn <параметры из настройки args> -U -Dmaven.repo.local=<workspace>/.m2 -DFP_VERSION=<версия дистрибутива> -DDB_BRANCH_NAME=<ветка репозитория> -Pnexus-alpha,sonar,sonar-coverage 

Далее, если не включен параметр sonarSkip, запускается SonarQube сканирование. Для запуска сканирования используется следующая команда:

  • если настроен параметр sonar_cmd - просто выполняется то, что в нем указано (при этом в начало команды добавляются параметры из args)

  • иначе - выполняется команда sonar:sonar с параметрами (параметры указываются с префиксом -D - как стандартные свойства Java):

    • sonar.projectKey - указывается, если не включена опция sonarPomConfigured и если в pom.xml нет свойства sonar.projectKey. Равен этот параметр свойству name репозитория (в pipeline.yml).

    • sonar.branch.name - текущая ветка репозитория. Если она равна опции sonar.mainBranch - то параметр вообще не указывается.

    • sonar.branch.longLivedBranches.regex - берется из опции sonar.longLivingBranchesPattern, по умолчанию равен (<текущая ветка>|branch|release|develop).*

    • sonar.login - в качестве значения используется переменная окружения SONAR_TOKEN

Описание параметров конфигурации в файле pipeline.yml представлено в соответствующем разделе

Сборка Frontend#

Репозиторий pl - это nodejs-проект. Для сборки используется npm, который настроен в соответствующем разделе pipeline.yml. При этом перед запуском непосредственно команды, которая прописана в cmd, будет выполнена подгрузка зависимостей с помощью команды npm set strict-ssl false && env && npm ci -timeout=9999999.

Важно!

В корне репозитория должен располагаться файл package-lock.json

package-lock.json предназначен для блокировки зависимостей от определенного номера версии. В package-lock.json файле перечислены зависимости вашего приложения и зависимости всех его составляющих.

Генерация package-lock.json производится в процессе исполнения команды npm install [library]. При установке библиотек необходимо указывать версию (например, npm i express@4.18.2). Указание флагов –save-dev/–save-prod при установке зависимостей обязательно.

Рекомендуется сохранять именно подобное поведение, т.е. скачивание зависимостей с помощью команды npm ci, т.к. если использовать npm install, можно получить ситуацию, когда сборка внезапно начинает падать без каких-либо изменений - из-за того, что вышла новая версия какой-либо из зависимостей.

Параметры конфигурации в pipeline.yml описаны в соответствующем разделе.

Собственная реализация сборки#

Предоставляемая Jenkins библиотека программного компонента позволяет реализовывать собственные решения по сборке приложений для дистрибутива. Данный способ необходим в случае отсутствия той или иной сборочной функциональности внутри библиотеки и является рекомендуемой практикой, так как позволяет разработчикам собирать дистрибутивы удобными способами без привязки к pipeline. Как это сделать:

  1. В корне репозитория в собираемой ветке создается Jenkinsfile.

    В данный файл копируется обертка:     groovy     def onDistrib(app, distr) {     }     return wrapJenkinsfile(this)   

  2. Далее, при, например, сборке через maven, может быть добавлен wrapper:

    def onDistrib(app, distr) {
        withMaven(jdk: 'JDK_1.8_121_Linux', maven: 'Maven 3.6.1', mavenSettingsConfig: 'test.app.settings') {
            
        }
    }
    return wrapJenkinsfile(this)
    

    Параметр mavenSettingsConfig будет описан в соответствующем разделе Настройка конфигурационных файлов в Jenkins файла settings.xml.

  3. После этого осталось написать команду сборки вашего приложения (или любую другую).

    Важное замечание! Внутри обертки onDistrib можно указывать что угодно:

    • можно загружать файлы и копировать их в папки;

    • можно вызывать несколько команд maven и т.д. Для добавления артефактов в дистрибутив необходимо вызывать соответствующие методы объекта Distrib. Полный список методов описан ниже.

  4. Ниже описаны примеры сборки maven, gradle и npm приложений.

  5. Добавить dockerfile или, например, entrypoint-скрипт можно с помощью функции distr.addDockerItems('my-deployment-unit', 'Dockerfile') или distr.addDockerItems('my-deployment-unit', 'entrypoint.sh').

  6. После создания Jenkinsfile в репозитории, его нужно настроить в pipeline.yml в соответствии с разделом Заполнение раздела Jenkinsfiles.

Пример сборки maven-проекта#

def onDistrib(app, distr) {
    withMaven(jdk: 'JDK_1.8_121_Linux', maven: 'Maven 3.6.1', mavenSettingsConfig: 'test.app.settings') {
        sh "mvn clean package -Ddistrib.version=${distr.getVersion()}"
        sh 'mvn sonar:sonar'
    }

    distr.addBH ("target/*.war")  // добавляем в дистрибутив артефакт типа bh
}
return wrapJenkinsfile(this)

Пример сборки gradle-проекта#

def onDistrib(app, distr) {
    withEnv(["GRADLE_HOME=${tool name: 'gradle-6.5', type: 'gradle'}"]) {
        withCredentials([usernamePassword(credentialsId: 'my-credentials', usernameVariable: 'maven.user.name', passwordVariable: 'maven.user.password')]) {
            stage('Build with gradle') {  // допустим, вызов SonarQube встроен в build
                sh "$GRADLE_HOME/bin/gradle :my-app:build -Pversion=$distr.version"
            }
        }
    }
    distr.addBH("./my-app/build/libs/my-app-*.jar")  // добавляем в дистрибутив артефакт типа bh
}
return wrapJenkinsfile(this)

Пример сборки npm-проекта#

def onDistrib(app, distr) {
    nodejs(configId: 'test.app.npmrc', nodeJSInstallationName: 'v7.5.0-linux-x64') {
        sh 'npm ci'
        sh 'npm pack'
    }
    distr.addPL("*.tgz")  // добавляем в дистрибутив артефакт типа pl
}
return wrapJenkinsfile(this)

На этапах построения приложения происходит скачивание open-source библиотек и компонент из внешних репозиториев. Данная процедура производится с использованием инструмента, который проверяет соответствие установленных библиотек и компонентов требованиям кибербезопасности.

API дистрибутива#

У объекта Distrib существуют методы для добавления артефактов в дистрибутив:

Аргумент filePath во всех приведенных ниже методах - это путь к файлу, который необходимо добавить в дистрибутив. Допускается использование маски в bash-стиле.

// Добавить в дистрибутив артефакт типа bh
// Если задан аргумент bhName, то артефакт будет добавлен в папку bh/$bhName
def addBH(filePath, bhName = "")

// Добавить в дистрибутив артефакт типа pl
def addPL(filePath)

// Добавить в дистрибутив артефакт типа pl_mobile
def addPLMobile(filePath)

// Добавить в дистрибутив артефакт типа db
def addDB(filePath)

// Добавить в дистрибутив артефакт типа data
def addData(filePath)

// Добавить файл(ы) документации в дистрибутив
def addDocs(filePath)

// Добавить файлы конфигурации в дистрибутив
def addConf(filePath)

// Добавить Dockerfile (Dockerfile, entrypoint.sh, etc) в дистрибутив
def addDockerItems(deploymentUnit, filePath)

Расшифровка для типов артефактов приведена в разделе Термины и сокращения.

Методы объекта Distrib для добавления своих библиотек в дистрибутив (для пользования другими командами):

// Добавляет в дистрибутив Node.js-библиотеку
// coordinates - координаты библиотеки в npm-формате (имя библиотеки и ее версия)
def addSdkNpm(filePath, coordinates) // пример - distr.addSdkNpm('some-path/output.tgz', [name: 'some-lib', version: '1.2.3'])

// Добавляет в дистрибутив JAR-библиотеку (Java)
// pomPath - путь к файлу pom.xml библиотеки
def addSdkMvn(filePath, pomPath)

Чтобы получить версию дистрибутива, следует использовать метод getVersion:

def getVersion()

Сборка Pull Request#

Для того чтобы собрать Pull Request в том же окружении, в котором он будет собираться при подготовке дистрибутива (что дает некоторые гарантии того, что PR не сломает сборку дистрибутива), рекомендуется использовать предоставляемую библиотеку. Кроме того, данный подход позволяет версионировать логику сборки приложения вместе с кодом самого приложения.

Для этого необходимо добавить Jenkinsfile в корень репозитория и настроить автоматический запуск сборки. Ниже описаны требования к содержанию Jenkinsfile

Требования к Jenkinsfile#

Jenkinsfile – это стандартный pipeline-скрипт Jenkins.

  1. В данном файле должны быть реализованы методы:

    1. onPR(prInfo) - для обработки событий PR. prInfo - словарь с ключами:

      • id (Pull Request id),

      • url (ссылка на PR),

      • from (из какой ветки PR),

      • to (в какую ветку PR).

    2. onCommit(commitInfo) - для обработки commit-хуков, например, после слияния ветки в develop, master или release/*.
      commitInfo содержит ключ branch с наименованием ветки, в которую был выполнен commit (или merge).

  2. В скрипт должна быть подключена библиотека ufs-pipeline, на уровне папки, либо через динамическую загрузку.

  3. Скрипт должен заканчиваться строчкой return wrapJenkinsfile(this, context), где context - это необязательный параметр-map, содержащий ключи:

    • NODE - наименование slave или label (по умолчанию Linux_Default), на котором следует выполнять сборку,

    • COLOR_MAP - параметр для степа ansiColor (по умолчанию xterm).

  4. Использование node в скрипте Jenkinsfile не требуется и не рекомендуется.

  5. Использование stage - рекомендуется в onPR и не рекомендуется в onDistrib.

  6. Отправлять нотификации в Bitbucket не требуется и не рекомендуется.
    Данный функционал присоединяется вызовом wrapJenkinsfile.

Автоматический запуск сборки#

Существуют следующие варианты настройки автоматического запуска сборки:

  1. С помощью stash-плагина Pull Request Notifier :

    • Организовать первичный запуск Jenkins job.

    • Настроить выгрузку репозитория.

    • Добавить загрузку Jenkinsfile и вызов метода, соответствующего целям сборки (onPR или onCommit): load('Jenkinsfile').onPR().

    • Можно использовать пример ниже – Job с подгрузкой Jenkinsfile.

  2. С помощью jenkins-плагина Multibranch Pipeline. В этом случае в настройках multibranch pipeline необходимо подключить библиотеку ufs-pipeline c load implicity.

Для корректной работы функциональности notifyBitbucket, которая автоматически вызывается при сборке, рекомендуется добавить в Jenkins job следующие переменные окружения:

  • USERPASS_CREDS - ID credentials в Jenkins, у которых есть доступ в Bitbucket;

  • STASH_BASE_URL - ссылка на Bitbucket.

Примечания#

Решение проблем первой сборки pipeline с использованием Pull Request Notifier#

У многих команд имеются сложности с тем, что pipeline-job сборки Pull Request не запускается через hook Bitbucket. Для запуска, необходимо хотя бы одно успешное выполнение checkout этой Jenkins job.
Для этого рекомендуется выполнить следующие действия:

  1. Создать в репозитории ветку pull-requests/test.

  2. Запустить Jenkins job с отмеченной опцией Poll SCM и следующим кодом pipeline:

def creds = <credentials id>
def repo = <ссылка на репозиторий>
node('Linux_Default') {
    checkout([$class: 'GitSCM', branches: [[name: 'pull-requests/**']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: creds, url: repo]]])
}

После этого hook репозиториев должен заработать, и Jenkins job можно наполнять полезным функционалом.

Ветку pull-requests/test можно удалить.

Примеры Jenkinsfile#

Пример Jenkinsfile со сборкой maven#

mySonarUrl = 'https://sonar.<...>/sonar/dashboard?id=some-project'

def onPR(prInfo) {
    stage('Build') {
        build()
    }
    report()
}

def onCommit(commitInfo) {
    stage('Build') {
        build()
    }
    report()
}

def build() {
    withCredentials([usernamePassword(credentialsId: 'tech-creds', passwordVariable: 'TECH_PASSWORD', usernameVariable: 'TECH_USERNAME')]) {
        withMaven(jdk: 'jdk_installation', maven: 'maven_installation', mavenSettingsConfig: 'settings') {
            mvnBuild()
            if (!app.sonarSkip?.toBoolean()) {
                sonarScan()
            }
        }
        distributeArtifacts()
    }
}
def mvnBuild() {
    sh """
        mvn clean install -U -P build-config,spring
        touch test-db.zip
        touch pom-test.xml sdk-test.txt
    """
}

def sonarScan() {
    withSonarQubeEnv(credentialsId: 'your_cred', installationName: 'your_sonar_installation') {
        def sonarProjectKey = 'test.app.backend'
        sh """
            mvn sonar:sonar \
            -Dsonar.projectKey=${sonarProjectKey} \
            -Dsonar.host.url=${SONAR_HOST_URL} \
            -Dsonar.login=${SONAR_AUTH_TOKEN} \
            -Dsonar.branch.name=${SONAR_BRANCH} \
            --errors
            mv target/sonar ./
        """
    }
}

def report() {
    stage('Report') {
        junit '**/target/surefire-reports/*.xml'
        addBadge icon: '/plugin/sonar/images/waves_16x16.png', link: "$mySonarUrl&branch=$commitInfo.branch", text: 'Analyzed by SonarQube'
    }
}

def distributeArtifacts() {
    distr.addSdkMvn('pom-test.xml', 'sdk-test.txt')
    distr.addDB('test-db.zip')
    distr.addBH('target/*.war')
    distr.addBH('mod/module_test/*')
    distr.addConf('target/conf/*')
}

return wrapJenkinsfile(this)

Пример Jenkinsfile со сборкой gradle#

mySonarUrl = 'https://sonar.<...>/sonar/dashboard?id=some-project'
def onPR(prInfo) {
    withCredentials([usernamePassword(
        credentialsId: 'tech-creds', 
        passwordVariable: 'TECH_PASSWORD',
        usernameVariable: 'TECH_USERNAME'
    )]) {
        stage('Build') {
            sh './gradlew build'
        }
        stage('Sonar') {
            sh """./gradlew \
-Dsonar.login=$env.TECH_USERNAME \
-Dsonar.password=$env.TECH_PASSWORD \
-Dsonar.stash.login=$env.TECH_USERNAME \
-Dsonar.stash.password=$env.TECH_PASSWORD \
-Dsonar.pullrequest.key=$prInfo.id \
-Dsonar.pullrequest.branch=$prInfo.from \
-Dsonar.pullrequest.base=$prInfo.to \
            sonar """
        }
    }
    stage('Report') {
        junit '**/target/surefire-reports/*.xml'
        addBadge icon: '/plugin/sonar/images/waves_16x16.png',
                link: "$mySonarUrl&pullRequest=$prInfo.id",
                text: 'Analyzed by SonarQube'
    }
}
def onCommit(commitInfo) {
    withCredentials([usernamePassword(
        credentialsId: 'tech-creds', 
        passwordVariable: 'TECH_PASSWORD',
        usernameVariable: 'TECH_USERNAME'
    )]) {
        stage('Build') {
            sh './gradlew build'
        }
        stage('Sonar') {
            
                sh """./gradlew sonar \
    -Dsonar.login=$TECH_USERNAME \
    -Dsonar.password=$TECH_PASSWORD \
    -Dsonar.branch.name=$commitInfo.branch
    """
        }   
    }
    stage('Report') {
        junit '**/target/surefire-reports/*.xml'
        addBadge icon: '/plugin/sonar/images/waves_16x16.png',
                link: "$mySonarUrl&branch=$commitInfo.branch",
                text: 'Analyzed by SonarQube'
    }
}
return wrapJenkinsfile(this)

Jenkins job Pull Requests с подгрузкой Jenkinsfile#

def creds = 'git-creds'
def branch = 'master'
def repo = 'SSH ССЫЛКА НА РЕПОЗИТОРИЙ БИТБАКЕТ С ВАШИМ JENKINSFILE'

def merge(map, remoteConfig) {
  def branchFrom = map.from ?: 'origin/pull-requests/**'
  def branchTo = map.to

  return checkout([
        $class: 'GitSCM',
        branches: [[name: branchFrom]],
        extensions: [[$class: 'PreBuildMerge', options: [mergeRemote: 'origin', mergeTarget: branchTo]]],
        userRemoteConfigs: [remoteConfig]
  ])
}

library identifier: "ufs-pipeline@$branch", retriever: legacySCM([
        $class: 'GitSCM',
        branches: [[name: branch]],
        userRemoteConfigs: [[
                credentialsId: creds,
                url: 'SSH ССЫЛКА НА РЕПОЗИТОРИЙ БИТБАКЕТ С UFS-PIPELINE'
        ]]
])

node('Linux_Default') {
    ansiColor('xterm') {
        stage('Checkout') {
          merge (
            [to: env.PULL_REQUEST_TO_BRANCH],
            [
              credentialsId: creds,
              url: repo
            ]
          )
        }
        if( fileExists('Jenkinsfile') ) {
            load('Jenkinsfile').onPR()
        } else {
            echo "Jenkinsfile not found" // На время переходного периода, когда PR с Jenkinsfile уже есть
            manager.buildAborted()  // А в основных ветках его еще нет
        }
    }
}

Сборка и доставка Docker образов#

В случае, если необходимо выполнить установку в OpenShift, необходимо создать следующую иерархию в репозитории с конфигурациями:

test.app.config/
├─ k8s/base/
│  ├─ test.app.backend/
│  │  ├─ Dockerfile
│  │  ├─ dc.yaml
│  ├─ test.app.frontend/
│  │  ├─ Dockerfile
│  │  ├─ dc.yaml
├─ pipeline.yml

При наличии данной структуры, созданный pipeline найдет необходимые Dockerfile и запустит их сборку, после чего выложит образы в registry. Помимо этого он вставит hash образов в соответствующие конфигурационные файлы.

Обратите внимание, что в приведенной иерархии имя Docker образа будет соответствовать имени директории в которой расположен Dockerfile (в конкретном случае это test.app.backend, test.app.frontend), а тег это версия дистрибутива.

  1. В Dockerfile, вместо указания tag, можно использовать placeholder BASE_IMAGE_VERSION, который будет доставать последнюю доступную версию базового образа на текущий момент, например:

FROM registry.host/path/to/my/image:BASE_IMAGE_VERSION
  1. В Dockerfile необходимо указать параметр BASE_IMAGE, как показано в примере:

ARG BASE_IMAGE=registry.host/path/to/my/image:tag
FROM ${BASE_IMAGE}

Настройка Docker в файле конфигурации pipeline.yml описана в разделе Заполнение раздела docker.

Результат выкладки в registry отображается в логах Jenkins job (ссылка на docker-image), который можно дополнительно проверить, зайдя по адресу web-интерфейса docker-репозиториев https://registry.<...>.ru/docker/ под общим ТУЗом. В web-интерфейсе необходимо перейти по указанному пути в image_path (как пример - browse - efs/ci00428440_efs/ci00749857_efs_authorization) и /имя сервиса/tags.

Сборка приложений в Docker#

Декларативная сборка#

Декларативный подход к решению предполагает использование раздела with_docker в секции build. Данный раздел направлен на непосредственную работу с Dockerfile, в котором разработчик описывает последовательность инструкций по сборке приложений. Существует следующее соглашение, обязательное при использовании данной функциональности:

  • В корневой директории (/) финального Docker-образа должна располагаться папка с названием outputs;

  • В папке outputs должны располагаться директории, названия которых полностью соответствуют типу добавляемого артефакта (bh, pl, ...)

Для добавления Dockerfile (Dockerfile, entrypoint.sh, etc) в дистрибутив необходимо в outputs/docker определить директории с наименованием соответствующего deployment-unit, которые содержат соответствующие Dockerfile-артефакты.

Раздел with_docker используется для подключения сборки приложения в Docker. Каждый элемент списка в данном разделе должен содержать следующие обязательные параметры:

  • name - название элемента (рекомендуется использовать уникальные значения всех name во всех блоках);

  • repo (<…>/helloword.git) - репозиторий, из которого нужно взять собираемый Dockerfile;

  • branch (master) - ветка, из которой нужно взять расширение.

  • context - директория сборки Docker-образов;

  • dockerfile_path - путь до Docker-файла относительно директории сборки;

Также раздел может включать следующие необязательные параметры:

  • secrets - подраздел, отвечающий за передачу секретов (файлов, переменных) в Docker-сборку:

    • files - расширение для добавления конфигурационных файлов (данная опция включает список <config file id>);

  • prod_branch - это ветка, из которой был собран предыдущий релиз (можно также указать тег);

  • sonarSkip - если true, пропустить SonarQube-сканирование;

  • create_tag - значение типа boolean, по умолчанию false (если значение параметра выставлено в true, то создается аннотированный тег с версией, декларированной разделом fp);

  • parallel - опция использования параллельной сборки (если для двух приложений будет указано одинаковое значение parallel, то они будут запускаться в общем потоке);

  • outputs - список артефактов, которые необходимо скопировать в директорию относительно workspace;

    • source - путь до артефакта (может быть указана маска). Значение по умолчанию - outputs/*;

    • dest - директория назначения выбранного артефакта. Значение по умолчанию - package;

  • skip - если установить в true, сборка данного Dockerfile будет пропущена.

  • use_cache - при значении false позволяет производить сборку with_docker не используя cache. Значение по умолчанию - true;

Для использования данного раздела требуется заполнение секции docker (подробнее об этом можно прочитать в разделе Заполнение раздела docker).

build:
  with_docker:
    - name: "docker-app-1"
      repo: "project/dockerfile-repo.git"
      branch: "release/1.0.0"
      prod_branch: "master"
      dockerfile_path: Dockerfile
      context: ./
      parallel: "A"
      secrets:
        files:
          - <config file id>
      outputs:
        - source: "outputs/bh/*"
          dest: "package/bh"

Монтирование секретных файлов производится через флаг --mount=type=secret,id=maven_secret_id,dst=/project/settings.xml в инструкции RUN в вашем Dockerfile. Заданный параметр type (тип монтируемого файла) должен иметь значение secret; значение параметра id должно совпадать с , определенным в files в подразделе secrets вашего pipeline.yml; значение параметра dst определяет расположение конфигурационного файла (требуется указать полный путь и имя файла).

Добавленные через инструкцию files в подразделе secrets конфигурационные файлы по умолчанию (без указания параметра dst в инструкции RUN) располагаются в директории /run/secrets/ запущенного Docker- образа. Имена добавленных конфигурационных файлов соответствуют указанному <config file id> в списке, определенным files.

syntax - определяет расположение образа синтаксиса Dockerfile, используемого для сборки пользовательского Dockerfile. В данном контексте определение образа синтаксиса необходимо для использования инструкции RUN с флагом --mount=type=secret,id=maven_secret_id (пример официального образа интерфейса Dockerfile - docker/dockerfile:1.2).

Важно отметить, что сборка c кешированием в удаленный registry производится только при наличии установленного buildx плагина или buildctl контроллера BuildKit и заполненного подраздела cache секции docker.

Для указания ссылки на собственный BuildKit демон, переопределите переменную окружения BUILDKITD_HOST.

Пример Dockerfile:#

# syntax = registry.host/path/to/my/dockerfile/syntax/image:tag

FROM registry.host/path/to/my/maven/image:tag AS <target>

WORKDIR project/

COPY . .
RUN --mount=type=secret,id=<config file id>,dst=/project/settings.xml mvn clean package

FROM scratch

COPY --from=<target> /project/target/*.war /outputs/bh/

Сборка приложения с кешированием слоев:#

Кеширование слоев необходимо для увеличения скорости сборки проекта. Сборка проекта может быть разделена на несколько этапов, например - загрузка зависимостей и сборка приложения. Каждый этап может быть записан в отдельной инструкции в Dockerfile при сборке Docker-образа. Тогда для каждой инструкции создается новый слой образа, который сохраняется в кеше. В случае отсутствия изменений в исходных файлах проекта для отдельной инструкции, пересборка проекта или повторная загрузка зависимостей не осуществляется. Ниже приведены примеры сборок проектов в Dockerfile:

Пример сборки maven-проекта в Dockerfile#

Для настройки кеширования промежуточных слоев в удаленный репозиторий необходимо прописать название соответствующей стадии сборки в Dockerfile. Определение цели для последней результрующей стадии рекомендуется НЕ указывать, т.к. кешируется по умолчанию. Для данной стадии так же рекомендуется выставлять базовый образ scratch:

# syntax = registry.host/path/to/my/dockerfile/syntax/image:tag

FROM registry.host/path/to/my/maven/image:tag AS <target>

WORKDIR project/

# Копируем из исходного репозитория конфигурационные файлы проекта
COPY ./pom.xml ./
# Производим полную загрузку зависимотей
RUN --mount=type=secret,id=maven_secret_id mvn package -s /run/secrets/maven_secret_id

# Копируем проект целиком (pom.xml файл перезапишется)
COPY . .
# Производим сборку проекта
RUN --mount=type=secret,id=maven_secret_id mvn package -s /run/secrets/maven_secret_id

FROM scratch

COPY --from=<target> /project/target/*.war /outputs/bh/

При работе с кешированием необходимо убедиться, что каталоги/файлы, которые не участвуют в сборке проекта (например, .git, .idea, …), исключены из сборки Dockerfile. Инструментом для уточнения контекста сборки является файл .dockerignore

Пропуск кеширования отдельных инструкций#

Для пропуска кеширования отдельных инструкций рекомендуется использовать произвольный параметр внутри dockerfile, который во время сборки будет изменен, к примеру:

ARG CACHEBUST

RUN echo "$CACHEBUST" && mvn --settings /run/secrets/maven_secret_id package

Вместе с тем необходимо будет этот же параметр добавить в build_args секции docker:

docker:
  build_args:
    CACHEBUST: "$(date +%s)"

Экспорт переменных окружения Jenkins Job#

Для доступа к переменным окружения Jenkins Job нужно смонтировать секретный файл с ID envParams. Экспорт переменных в процесс сборки производится следующей инструкцией в Dockerfile - RUN --mount=type=secret,id=envParams,dst=/project/envParams . ./envParams || source ./envParams

# syntax = registry.host/path/to/my/dockerfile/syntax/image:tag

FROM registry.host/path/to/my/maven/image:tag

WORKDIR project/

COPY . .

RUN mvn package -s settings.xml

# Монтируем секретный файл и экспортируем переменные окружения
RUN --mount=type=secret,id=envParams,dst=/project/envParams \
    . ./envParams && \
    printenv

Передача секретных параметров#

Создайте конфигурационный файл с типом Custom file в Jenkins:

export EXAMPLE_KEY=$SECRET_TOKEN_ID
export ...

Смонтируйте секретный файл и произведите экспорт переменных, например - RUN --mount=type=secret,id=<custom file id>,dst=/project/<custom file name> . ./<custom file name> || source ./<custom file name>.

# syntax = registry.host/path/to/my/dockerfile/syntax/image:tag

FROM registry.host/path/to/my/maven/image:tag

WORKDIR project/

# Экспортируем переменные в окружение среды
RUN --mount=type=secret,id=envParams,dst=/envs \
  --mount=type=secret,id=custom_envs,dst=/custom_envs \
  . ./custom_envs && . ./envs && \
  printenv

Настройка SonarQube#

Для настройки сканирования приложений необходимо использовать переменные доступа к инструменту SonarQube, которые могут быть доступны в Dockerfile через инструкцию - RUN --mount=type=secret,id=envParams,dst=/project/envParams.

Существует следующее соглашение, обязательное для использования данного функционала:

  • В корневой директории (/) финального Docker-образа должна располагаться папка с названием sonar;

  • В директории sonar должны располагаться отчеты о сканировании вашего приложения. Например, SonarScanner плагин сборщика Maven после анализа приложения загружает отчеты о сканировании в директорию target/sonar относительно корня репозитория

Использование SonarQube при сборке Docker#

Для интеграции SonarQube в процессе сборки Docker, при условии заполненного раздела sonar в pipeline.yml, в контекст сборки передаются следующие переменные окружения, чтобы обеспечить корректное взаимодействие с сервером SonarQube:

  • SONAR_HOST_URL - URL сервера SonarQube.

  • SONAR_AUTH_TOKEN - токен для аутентификации.

После завершения сканирования SonarQube создаёт файл report-task.txt, содержащий информацию о задаче сканирования. Этот файл следует поместить в папку outputs/sonar внутри финального Docker-образа, чтобы обеспечить возможность дальнейшей обработки результатов. Более подробно про использование SonarQube при сборке Docker можно прочитать в документации SonarQube.

Пример сканирования maven-приложения в Docker#

# syntax = registry.host/path/to/my/dockerfile/syntax/image:tag

FROM registry.host/path/to/my/maven/image:tag as <target>

WORKDIR project/
COPY . .

RUN mvn package -s settings.xml

# Экспортируем переменные в окружение среды и производим сканирование
RUN --mount=type=secret,id=envParams,dst=/project/envParams \
    . ./envParams && \
    mvn sonar:sonar -s settings.xml

FROM scratch

COPY --from=<target> /project/target/*.war /outputs/bh/
COPY --from=<target> /project/target/sonar/* /sonar/

Существующие точки расширений#

analyzeDependencies#

Данное расширение подключается автоматически и выполняет анализ зависимостей включенных в дистрибутив образов.
Для того чтобы отменить автоматическое подключение расширения, нужно создать переменную окружения ANALYZE_DEP = false. Также это расширение использует сервис поиска образов по hash, который предоставляет Nexus. Запрос к сервису генерируется следующим образом: <registry_root>/<registry_search_api><hash>, где registry_root - это параметр docker.registry_address из pipeline.yml, а registry_search_api - путь сервиса поиска, по умолчанию docker/service/rest/v1/search?docker.contentDigest=sha256:, hash - хэш искомого образа

Запрос к сервису можно переопределить с помощью переменной окружения REGISTRY_SEARCH_API, причем, если она будет начинаться с https://, ссылка на сервис будет генерироваться так: <registry_search_api><hash> (т.е. без registry_root).

Узнать адрес сервиса поиска образов можно у владельца инсталляции Nexus

Если требуется использовать какую-либо специальную версию SYFT, можно ее указать с помощью переменной окружения ]]SYFT_TOOLING - но она должна быть подключена в Jenkins.

sequrityChecks#

Данное расширение подключается автоматически и выполняет запуск проверок SAST/OSS. Результирующие флаги проверок будут загружены в qgm средствами сервиса SAST/OSS. Запуск проверок можно регулировать выключением/включением сценариев в PLAYBOOKS sast-run и oss-run. Для использования конкретных метрик проверки необходимо указать опцию sast_check_keysв секции securityChecks файла pipeline.yml. Внутри этой опции определяются флаги и ожидаемые результаты. Например: SAST_PASS - для проверки на дефекты средней степени критичности. no_critical_defects - для проверки на отсутствие дефектов высокой критичности. Вы можете указать любые требуемые флаги, поддерживаемые SAST.

Пример заполнения:

- name: securityChecks
  devsec_repo: some_devsec_repo
  run_sast: true
  run_oss: true
  sast_check_keys:
    SAST_PASS: true
    no_critical_defects: true

makeReleaseNotes#

Данное расширение подключается автоматически, готовит ReleaseNotes и выкладывает его в QGM. Настройка данного расширения осуществляется через Заполнение раздела qgm qgm в файле pipeline.yml.

Формирование Release Notes для каждого из используемых репозиториев управляется следующими параметрами:

  • Релизная ветка или tag (prod_branch, между этой веткой будет браться diff. Если указывается tag, то в формате refs/tags/tagID).

  • Регулярное выражение для определения списка задач из JIRA (issue_pattern). Например:

    ### Ссылки на сборочные Jenkins-файлы ###
    jenkinsfiles:
      - name: "test.app.backend" # любое
        repo: "ufs_ci/test.app.backend.git" # url без хоста
        branch: "develop" # ветка сборки
        prod_branch: 'master',
        issue_pattern: '[A-Za-z][A-Za-z0-9]+-[0-9]+' # регулярное выражение по которому определяется идентификатор запроса в JIRA из commit.
    

updateVersionConf#

Данное расширение подключается автоматически и выполняет копирование файла version.conf в дистрибутив из репозитория version.conf.git того же проекта, из которого запускался ufs-pipeline. Ветка репозитория определяется по следующим правилам:

  • если релиз платформы !R19.5, то используется App19.5

  • если релиз платформы !R20.1, то используется App20.1

  • иначе используется ветка master

Кроме того, в случае наличия соответствующих переменных окружения PLATFORM_RELEASE и/или AT_BRANCH в файл version.conf добавятся параметры atBranch и platformRelease. Также, если в подготавливаемом дистрибутиве уже содержится файл version.conf (был в репозитории конфигураций или добавлен из Jenkinsfile), то все параметры в нем будут сохранены.

В случае, когда данный репозиторий version.conf.git уже есть в разделе downloads, расширение не выполняет копирование, но выводит предупреждение, если выбрана неверная ветка. При этом остальная логика (выставление параметров atBranch и platformRelease, сохранение уже существующих параметров) также не будет выполнена – будет скопирован тот файл, который указан в downloads.

AddDescription#

Данное расширение подключается автоматически и добавляет в описание сборки таблицу с координатами дистрибутива, а также ссылками, по которым он (и его pom.xml) был загружен в хранилище.

Если в вашей инфраструктуре используется какие-либо механизмы по «перекладыванию» дистрибутивов, ссылки могут быть устаревшими.

generateSWIDtagFiles#

Данная точка расширения выполняет генерацию файла SWIDTAG-файла.

Для работы точки необходима следующая структура pipeline.yml:

info:
  version: D-01.00.00 # версия компонента (если не заполнено, используется то же значение, что и для fp.version)
  code: wxyz # код компонента
  title: "Component title" # название собираемого компонента в учетной системе
  creator:
    name: "<NAME>" # название организации создателя тега
    regid: "<...>" # идентификатор организации создателя тега
  product:
    title: "Product title" # название продукта в учетной системе
    code: XYZ # код продукта в учетной системе, к которому относится собираемый компонент
    version: 1.0.0 # версия продукта в учетной системе, к которому относится собираемый компонент
  dependencies: # необязательный параметр, список зависимостей продукта на другие продукты
    - { unique_id: <unique_id> , tag_creator_regid: <tag_creator_regid> }
    - unique_id: cije-1.9.0
      tag_creator_regid: <...>

extensions:
  - name: generateSWIDtagFiles

Так же отдельные/все параметры можно определить в настройках Jenkins job с помощью String Parameter:

  • info.version;

  • info.code;

  • info.title;

  • info.creator.name;

  • info.creator.regid;

  • info.product.code;

  • info.product.version;

  • info.dependencies.

Данные определения будут иметь высший приоритет и перезаписывать значения из pipeline.yml. Для передачи параметра info.dependencies в значении следует указать JSON-массив, например: [] или [{ unique_id: "test fp dependency1 unique_id env" , tag_creator_regid: "<REG_ID>" }, { unique_id: "test fp dependency2 unique_id env" , tag_creator_regid: "<REG_ID>" }]

Генерация SWIDTAG-файла производится с помощью Ansible посредством модуля template.

Также есть возможность использовать шаблон, который уже находится в workspace сборки. Для этого необходимо прописать в pipeline.yml:

extensions:
  - name: generateSWIDtagFiles
    template: <template> # путь до файла с шаблоном

transformDistrib#

Данное расширение позволяет трансформировать получившийся дистрибутив в какой-либо другой формат. Для этого необходимо указать правила трансформации в параметре directories в виде <директория>: <файлы, которые надо в нее скопировать> Пример:

extensions: 
  - name: transformDistrib
    directories:
      modules: "package/bh/*"
      other/seap-lib/lib: "package/sdk/mvn/*"
      config: "package/conf/application.properties"
      install: "package/conf/deployment_pg.xml"
      db: "package/db/*"
      ./:
        - "package/ReleaseNotes.json"
        - "package/conf/pipeline.yml"

CheckDistrib#

Расширение для проверки состава получившегося дистрибутива. Настройка проверок осуществляется с помощью следующих параметров:

  • exists список регулярных выражений, по которым требуется проверять наличие файлов

  • not_exists список регулярных выражений, по которым требуется проверять отсутствие файлов

  • check_rn - необязательный параметр, по-умолчанию: false. Значение true указывается для запуска проверки ReleaseNotes.

  • checked_flags - список QGM флагов. Указанные QGM флаги проходят проверку (ОК статус) для выпущенного дистрибутива.

Пример (проверка дистрибутива бинарных файлов):

  extensions:
    - name: CheckDistrib
      exists:
        - package/conf/data/sup2/.*
        - package/docker/test.app.backend/.sha256
      not_exists:
        - documentation/.*
        - package/conf/openshift/test.app.backend/.sha256
        - package/conf/openshift/test.app.backend/Dockerfile
        - package/bh/.*
        - package/sdk/.*
        - package/db/.*
        - package/pl/.*
        - package/conf/data/analyze_dep/.*

generateJenkinsFile#

Данная точка расширения предназначена для упрощения разработки jenkinsfile для своих сборок. При ее выполнении в bh-, pl- и db-репозиториях будет создана ветка feature/conf/SUFSCI-126, в которой будет добавлен Jenkinsfile, реализующий сборку артефактов дистрибутива в соответствии с требованиями данной библиотеки сборки. Какой-либо настройки расширения не требуется, достаточно просто его подключить.

gettingConfigXml#

Данная точка расширения копирует файл config.xml Jenkins job сборки в дистрибутив. По умолчанию расширение скачивает config.xml под Credentials, указанными в USERPASS_CREDS. Также доступна настройка через свойство расширения creds, например:

extensions: 
  - name : gettingConfigXml
    creds: some_jenkins_token_cred 

MoveDockerfile#

Расширение переносит dockerfiles из package/conf/k8s/base/<deployment_unit> в package/docker/<deployment_unit> для выполнения требований стандарта STD-5. В большинстве случаев настройка расширения не требуется, достаточно только подключение:

extensions:
  - MoveDockerfile

Но если требуется помимо Dockerfile переместить еще какие-либо файлы, можно указать соотвутствующую маску в параметре mask:

extensions:
  - name: MoveDockerfile
    mask: *.sh

Если нужно указать несколько масок, следует указать их списком:

extensions:
  - name: MoveDockerfile
    mask:
     - *.sh
     - *.py

std5Configuration#

Расширение для сборки дистрибутива конфигураций. Детали работы описаны в соответствующем разделе

std5Binaries#

Расширение для сборки дистрибутива бинарных файлов. Детали работы описаны в соответствующем разделе.

Обратите внимание: при использовании данной точки расширения этап шаблонизации (templating) по умолчанию будет пропущен.

std19Sdk#

Расширение для сборки дистрибутива sdk. Данное расширение исключает все файлы/папки из дистрибутива, кроме /sdk.

shell#

Расширение для выполнения shell команд.
Доступные инструменты сборки: maven, node (если настроен раздел npm).

Данная точка расширения поддерживает следующие опции:

  • commands - список команд, которые будут выполнены

  • env - список переменных, которые будут доступны

Список экспортируемых переменных окружения:

  • CONFIGS_DIR - конфигурационными файлами

  • ARCHIVE_SOURCE_DIR - директория, из которой собирается архив дистрибутива

  • ARCHIVE_PATH - путь до архива, если он уже собран(расширение должно быть запущено после стадии pack)

Пример (скачивание зависимости maven и клонирование репозитория):

extensions:
  - name: shell
    env:
      collection-version: 4.4
    commands:
      - mvn --version
      - |
        mvn dependency:get -DgroupId=org.apache.commons -DartifactId=commons-collections4 -Dversion=$collection-version -Dpackaging=pom -Ddest=commons-collections4.pom
        cp commons-collections4.pom $ARCHIVE_SOURCE_DIR/package/bh
      - git clone <...>

generateSBOMCyclonedx#

Расширение для анализа зависимостей сборочных проектов инструментом cdxgen.

Данная точка расширения поддерживает следующие опции:

  • tools - список инструментов окружения для анализа зависимостей сборочных проектов

  • cdxgen - версия cdxgen (версия по умолчанию - 8.6.2)

  • cdxgen_path - путь до бинарного файла cdxgen (путь по умолчанию - cdxgen-linux-x64)

  • cyclonedx_cli - версия cyclonedx (версия по умолчанию - 0.27.1)

  • cyclonedx_cli_path - путь до бинарного файла cyclonedx_cli (путь по умолчанию - cyclonedx-linux-x64)

  • args - список дополнительных пользовательских параметров при исполнении команды cdxgen

  • classifier - maven classifier (значение по умолчанию - cyclonedx-distrib)

  • env - позволяет получать и перезаписывать environment в параметрах Jenkins job из pipeline.yml

Пример:

- name: generateSBOMCyclonedx
  tools:
    - '<метка инструмента Jenkins>'
  env:
    MVN_ARGS: "install -U -P build-config,spring"
    SOME_ENV: "somevalue"

При сканировании сборок инструментом maven, библиотека определяет следующие переменные окружения:

  • CDX_MAVEN_INCLUDE_TEST_SCOPE - отвечает за включение зависимостей тестовой области в сканирование, по умолчанию выставлена в false.

  • MVN_ARGS - отвечает за определение дополнительных аргументов в команде сканирования, по умолчанию выставлена в includeProvidedScope=false

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

Если в корне репозитория с исходным кодом присутствует sbom.json файл, то его содержимое будет использовано при формировании результирующего SBOM файла и сканирование данного репозитория текущей точкой расширения исполняться не будет.

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

generateSBOMTrivy#

Расширение для анализа зависимостей сборочных проектов инструментом trivy.

Данная точка расширения поддерживает следующие опции:

  • trivy - версия trivy (версия по умолчанию - 0.52.0)

  • trivy_path - путь до бинарного файла trivy (путь по умолчанию - trivy)

  • cyclonedx_cli - версия cyclonedx (версия по умолчанию - 0.27.1)

  • cyclonedx_cli_path - путь до бинарного файла cyclonedx_cli (путь по умолчанию - cyclonedx-linux-x64)

  • args - список дополнительных пользовательских параметров при исполнении команды trivy

  • classifier - maven classifier (значение по умолчанию - cyclonedx-distrib)

  • sbom_path - список путей к SBOM файлам. Путь указывается относительно корня репозитория. По умолчанию - sbom.json из корневой директории репозитория.

Пример:

extensions:
  - name: generateSBOMTrivy
    sbom_path:
      - "sbom.json"
      - "target/*-sbom.json"

Если ни один SBOM файл не найден, в лог точки расширения выводится сообщение уровня INFO о том, что в репозитории не найден SBOM файл и он будет сгенерирован

Если найден ровно один SBOM файл, то в лог точки расширения выводится сообщение уровня INFO о том, что в репозитории найден SBOM файл и он будет использован вместо генерации SBOM компонента

Если в репозитории по массиву sbom_path найдено несколько SBOM файлов, в лог точки расширения выводится сообщение уровня WARN о том, что в репозитории найдено несколько файлов, SBOM файл будет сгенерирован

generateSWIDtagAnnotations#

Опциональное расширение для добавления аннотаций к Docker-образам, собранным при использовании BuildKit. Аннотации формируются из данных SWIDTAG, что означает, что точка расширения может быть включена в сборку только если generateSWIDtagFiles исполнилась ранее, а также должна быть исполнена до stage docker.

Данная точка расширения поддерживает следующие опции:

  • level - уровень, на котором будут созданы аннотации. Может содержать единственное значение или список из допустимых: manifest, manifest-descriptor, index, index-descriptor. По умолчанию manifest.

Пример:

extensions:
  - name: generateSWIDtagFiles
  - name: generateSWIDtagAnnotations
    level: manifest

Разработка собственной точки расширения#

В предоставляемом pipeline для каждого этапа имеется поддержка точек расширения через контракт в Jenkinsfile.

Как и в случае onDistrib() необходимо написать groovy скрипт, реализовав в нем следующую обертку:

def run(extension, extensionAPI) {
}
return this

Объект из списка extensions это Map extension в функции run, то есть все поля этого объекта доступны вам внутри. Т.е. если расширение подключено так:

extensions:
  - name: SomeExtension
    param1: one
    param2: 2

В таком случае расширение может использовать объект extension следующим образом:

def run(extension, extensionAPI) {
  assert extension.name == 'SomeExtension'
  assert extension.branch == 'master' // Поскольку значение по умолчанию добавляется
  assert extension.param1 == 'one'
  assert extension.param2 == 2
}
return this

Объект extensionAPI можно использовать для получения данных из pipeline, доступные функции:

package ru.<...>.devops
class ExtensionAPI {
    def getPipelineConfig()  // прочитанный в объект pipeline.yml
    def getDistrib() // объект distrib, такой же, как и в onDistrib
}

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

Как использовать:

  1. Объявить переменные вне методов без ключевого слова def. В них подгружаем необходимые файлы.

  2. Далее эти переменные можем использовать в методах точки расширения. Пример реализации простой точки расширения, которая читает контент файла textfile и подгружает объект файла test.groovy, а далее в своих методах имеет доступ к содержимому textfile и методам test.groovy:

    // начало файла примера точки расширения
    fileContents = readFile("textfile") // загружаем файл в точке расширения, но вне ее методов без ключевого слова def
    testFile = load("test.groovy") // загружаем еще файл, путь к файлу указываем относительно пути где находится эта точка расширения
    
    def run(extension, extensionAPI) {
        testFile.call() // в методе точки расширения вызываем метод загруженного файла
        println fileContents // а также можем смотреть контент загруженного файла
    }
    // конец файла примера точки расширения
    

При создании новой точки расширения можно указать конфигурацию по умолчанию, чтобы не указывать ряд полей в файле pipeline.yml. Для этого надо создать новый метод getDefaultConfig, например:

def getDefaultConfig() {
    return [
        stage: 'upload',
        phase: 'before',
        description: 'Проверка состава дистрибутива'
    ]
}

Поля name, repo, branch и script_path возвращать в getDefaultConfig() не нужно, т.к. они используются до загрузки расширения.

Пример точки расширения, привязанной к finally stage:

def run(extension, extensionAPI) {
    println ("${currentBuild.number}") // Номер сборки в виде целого числа
    println ("${currentBuild.result}") // Результат выполнения
    println ("${currentBuild.absoluteUrl}") // URL-адрес индексной страницы сборки
}

def getDefaultConfig() {
    return [
            stage: 'finally',
            phase: 'after',
            description: 'Финальное расширение'
    ]
}

return this

Детали подключения расширения описаны в разделе Подключение и настройка расширений.

Настройка SAST/OSS#

В библиотеке ufs-pipeline существует возможность настроить анализ всех git-репозиториев на сканирование SAST и OSS. Для этого необходимо в pipeline.yml добавить настройки соответствующих инструментов:

### Настройки DevSecOps ###
### Общая информация о командах ###
team:
  - # Код команды в МУС
    # Если не сработает код команды в МУС, то можно попробовать прописать логин ТУЗа, который используется для сканирования
    mus_code: "command_code"
    # Название команды в МУС
    mus_name: "command_name"
    # Почтовый адрес Product Owner в МУС для уведомлений с результатами сканирования
    # !!! Почта должна быть валидной для текущего домена, чтобы корректно определить пользователя, которому впоследствии предоставятся права на доступ к АС в SALM !!!
    # !!! Возможно указание нескольких почт через знаки ";" или "," внутри одной строки !!!
    # Пример: "user1@mycompany.com; user2@mycompany.com, user3@mycompany.com"
    mus_po_mail: "user1@mycompany.com; user2@mycompany.com, user3@mycompany.com"
    # Возможно добавление нескольких команд
# !!! Вторую и последующие команды необходимо добавить по формату, аналогичному первой !!!
  # - mus_code: "00040018"
  #   mus_name: "Web"
  #   mus_po_mail: "user3@mycompany.com"


### Общая информация о приложении ###
app:
  # Конфигурационный элемент (КЭ) приложения / модуля ПО в Service Manager.
  # !!! КЭ должен быть строкой, состоящей исключительно из цифр без лидирующих нулей !!!
  sm_id: "number_id"
  # Название КЭ приложения / модуля ПО в Service Manager
  sm_name: "app_name"

### Настройки SAST ###
sast_cx:
  # Credentials ID в Jenkins для учетной записи одной команды, под которой будет проводиться сканирование
  creds_id: "creds"
  # Проектная область в JIRA для заведения и синхронизации дефектов
  jira_area: "area"
  # Маски файлов и директорий для включения (**/*) в скан и исключения (!**/*) из скана.
  # Пример: "!**/utils/**/*.xml" - исключение всех xml файлов в папке utils
  masks: ""
  # ID профиля сканирования, по умолчанию 36. 14 - для мобильного приложения. 100003 - Smoke
  preset_id: "36"
  # Максимальное время ожидания статуса QG в минутах
  wait_qg: "2"

### Настройки OSS
oss:
  # Маски файлов и директорий для исключения из сканирования
  # Пример: "**/utils/**/*.xml" - исключение всех xml файлов во всех папках utils
  # !!! Перечисление нескольких масок возможно через знаки "," или ";" внутри одной строки, например, "**/*test*/**; **/*.txt, .git/**" !!!
  # !!! Пропуски в каждой маске справа и слева автоматически удалятся (trim) !!!
  excludes: ""

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

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

jenkinsfiles:
  - name: bh-jf
    repo: cije/test.app.backend.git
    branch: develop
    sast_skip: true
pl:
  - name: my-pl
    repo: cije/test.app.frontend.git
    branch: develop
    cmd: 'npm pack'
    sast_skip: true
downloads:
  - source: git
    type: conf
    repo: cije/test.app.backend.git
    branch: develop
    files: pom.xml
    sast_skip: true

Более подробную информацию по настройке параметров sast/oss можно запросить у подразделения кибербезопасности компании.

Непосредственно запуск проверок осуществляется включением сценариев run-sast, run-oss.