И покрај фактот дека секој совршено добро знае дека тестирањето на вашиот софтвер е важно и неопходно, а многумина тоа го прават автоматски долго време, во пространоста на Хабр немаше ниту еден рецепт за поставување комбинација од такви популарни производи во оваа ниша како (нашиот омилен) GitLab и JUnit . Да ја пополниме оваа празнина!
Воведен
Прво, дозволете ми да дадам некој контекст:
Бидејќи сите наши апликации работат на Kubernetes, ќе размислиме да извршиме тестови на соодветната инфраструктура.
За склопување и распоредување користиме верф (во однос на инфраструктурните компоненти, ова исто така автоматски значи дека е вклучен и Хелм).
Нема да навлегувам во деталите за вистинското креирање тестови: во нашиот случај, клиентот сам ги пишува тестовите, а ние само го обезбедуваме нивното стартување (и присуството на соодветен извештај во барањето за спојување).
Како ќе изгледа генералниот редослед на дејства?
Градење на апликацијата - ќе го изоставиме описот на оваа фаза.
Распоредете ја апликацијата во посебен именски простор од кластерот Kubernetes и започнете со тестирање.
Пребарување артефакти и парсирање на извештаите на JUnit со GitLab.
Бришење на претходно креиран именски простор.
Сега - до имплементација!
прилагодување
GitLab CI
Да почнеме со фрагмент .gitlab-ci.yaml, кој го опишува распоредувањето на апликацијата и извршувањето на тестовите. Написот се покажа доста обемен, па затоа беше темелно дополнет со коментари:
variables:
# объявляем версию werf, которую собираемся использовать
WERF_VERSION: "1.0 beta"
.base_deploy: &base_deploy
script:
# создаем namespace в K8s, если его нет
- kubectl --context="${WERF_KUBE_CONTEXT}" get ns ${CI_ENVIRONMENT_SLUG} || kubectl create ns ${CI_ENVIRONMENT_SLUG}
# загружаем werf и деплоим — подробнее об этом см. в документации
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#deploy-stage)
- type multiwerf && source <(multiwerf use ${WERF_VERSION})
- werf version
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- werf deploy --stages-storage :local
--namespace ${CI_ENVIRONMENT_SLUG}
--set "global.commit_ref_slug=${CI_COMMIT_REF_SLUG:-''}"
# передаем переменную `run_tests`
# она будет использоваться в рендере Helm-релиза
--set "global.run_tests=${RUN_TESTS:-no}"
--set "global.env=${CI_ENVIRONMENT_SLUG}"
# изменяем timeout (бывают долгие тесты) и передаем его в релиз
--set "global.ci_timeout=${CI_TIMEOUT:-900}"
--timeout ${CI_TIMEOUT:-900}
dependencies:
- Build
.test-base: &test-base
extends: .base_deploy
before_script:
# создаем директорию для будущего отчета, исходя из $CI_COMMIT_REF_SLUG
- mkdir /mnt/tests/${CI_COMMIT_REF_SLUG} || true
# вынужденный костыль, т.к. GitLab хочет получить артефакты в своем build-dir’е
- mkdir ./tests || true
- ln -s /mnt/tests/${CI_COMMIT_REF_SLUG} ./tests/${CI_COMMIT_REF_SLUG}
after_script:
# после окончания тестов удаляем релиз вместе с Job’ом
# (и, возможно, его инфраструктурой)
- type multiwerf && source <(multiwerf use ${WERF_VERSION})
- werf version
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- werf dismiss --namespace ${CI_ENVIRONMENT_SLUG} --with-namespace
# мы разрешаем падения, но вы можете сделать иначе
allow_failure: true
variables:
RUN_TESTS: 'yes'
# задаем контекст в werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#infrastructure)
WERF_KUBE_CONTEXT: 'admin@stage-cluster'
tags:
# используем раннер с тегом `werf-runner`
- werf-runner
artifacts:
# требуется собрать артефакт для того, чтобы его можно было увидеть
# в пайплайне и скачать — например, для более вдумчивого изучения
paths:
- ./tests/${CI_COMMIT_REF_SLUG}/*
# артефакты старше недели будут удалены
expire_in: 7 day
# важно: эти строки отвечают за парсинг отчета GitLab’ом
reports:
junit: ./tests/${CI_COMMIT_REF_SLUG}/report.xml
# для упрощения здесь показаны всего две стадии
# в реальности же у вас их будет больше — как минимум из-за деплоя
stages:
- build
- tests
build:
stage: build
script:
# сборка — снова по документации по werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#build-stage)
- type multiwerf && source <(multiwerf use ${WERF_VERSION})
- werf version
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- werf build-and-publish --stages-storage :local
tags:
- werf-runner
except:
- schedules
run tests:
<<: *test-base
environment:
# "сама соль" именования namespace’а
# (https://docs.gitlab.com/ce/ci/variables/predefined_variables.html)
name: tests-${CI_COMMIT_REF_SLUG}
stage: tests
except:
- schedules
Кубернети
Сега во директориумот .helm/templates ајде да создадеме YAML со Job - tests-job.yaml — да изврши тестови и ресурсите на Кубернетес што му се потребни. Погледнете ги објаснувањата по набројувањето:
Какви ресурси опишано во оваа конфигурација? При распоредување, создаваме единствен именски простор за проектот (ова е означено во .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) и развлечете го:
ConfigMap со тест скрипта;
работа со опис на под и наведената директива command, кој само ги извршува тестовите;
PV и PVC, кои ви дозволуваат да складирате податоци од тестот.
Обрнете внимание на воведната состојба со if на почетокот на манифестот - соодветно, другите YAML-датотеки од табелата на Helm со апликацијата мора да бидат завиткани во обратна дизајнирајте така што тие да не се распоредат за време на тестирањето. Тоа е:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Меѓутоа, доколку тестовите бараат одредена инфраструктура (на пример, Redis, RabbitMQ, Mongo, PostgreSQL...) - нивните YAML може да бидат Нема Исклучи. Распоредете ги и во тест опкружување... прилагодувајќи ги како што ви одговара, се разбира.
Конечен допир
Бидејќи склопување и распоредување со користење на werf работи засега само на серверот за изградба (со gitlab-runner), и подлогата со тестови е лансирана на главниот, ќе треба да креирате директориум /mnt/tests на господарот и дај му го на тркачот, на пример, преку NFS. Детален пример со објаснувања може да се најде во K8s документација.
Никој не забранува правење NFS споделување директно на gitlab-runner, а потоа негово монтирање во pods.
Имајте на ум
Можеби се прашувате зошто да се комплицира сè со креирање на Job, ако едноставно можете да извршите скрипта со тестови директно на школката? Одговорот е прилично тривијален...
Некои тестови бараат пристап до инфраструктурата (MongoDB, RabbitMQ, PostgreSQL, итн.) за да се потврди дека тие работат правилно. Ние го правиме тестирањето унифицирано - со овој пристап, станува лесно да се вклучат такви дополнителни ентитети. Во прилог на ова, добиваме стандард пристап на распоредување (дури и ако користите NFS, дополнително монтирање на директориуми).
Резултира
Што ќе видиме кога ќе ја примениме подготвената конфигурација?
Барањето за спојување ќе прикаже збирна статистика за тестовите што се извршуваат во најновата линија:
Секоја грешка може да се кликне овде за детали:
NB: Внимателниот читател ќе забележи дека тестираме апликација NodeJS, а на сликите од екранот - .NET... Немојте да се чудите: исто како дел од подготовката на статијата, не беа пронајдени грешки при тестирањето на првата апликација, но биле пронајдени во друга.
Заклучок
Како што можете да видите, ништо комплицирано!
Во принцип, ако веќе имате колектор на школки и тој работи, но не ви треба Kubernetes, прикачувањето на тестирањето на него ќе биде уште поедноставна задача отколку што е опишано овде. И во GitLab CI документација ќе најдете примери за Ruby, Go, Gradle, Maven и некои други.