Sa kabila ng katotohanan na lubos na alam ng lahat na ang pagsubok sa iyong software ay mahalaga at kinakailangan, at marami ang awtomatikong ginagawa ito sa loob ng mahabang panahon, sa kalakhan ng Habr ay walang isang recipe para sa pag-set up ng kumbinasyon ng mga sikat na produkto sa niche na ito bilang (aming paborito) GitLab at JUnit . Punan natin ang puwang na ito!
Panimula
Una, hayaan mo akong magbigay ng ilang konteksto:
Dahil tumatakbo ang lahat ng aming application sa Kubernetes, isasaalang-alang namin ang pagpapatakbo ng mga pagsubok sa naaangkop na imprastraktura.
Para sa assembly at deployment ginagamit namin werf (sa mga tuntunin ng mga bahagi ng imprastraktura, awtomatiko rin itong nangangahulugan na kasama si Helm).
Hindi ako pupunta sa mga detalye ng aktwal na paglikha ng mga pagsubok: sa aming kaso, isinulat mismo ng kliyente ang mga pagsubok, at tinitiyak lamang namin ang kanilang paglulunsad (at ang pagkakaroon ng kaukulang ulat sa kahilingan ng pagsasama).
Ano ang magiging hitsura ng pangkalahatang pagkakasunud-sunod ng mga aksyon?
Pagbuo ng application - aalisin namin ang paglalarawan ng yugtong ito.
I-deploy ang application sa isang hiwalay na namespace ng Kubernetes cluster at simulan ang pagsubok.
Paghahanap ng mga artifact at pag-parse ng mga ulat ng JUnit sa GitLab.
Pagtanggal ng dating ginawang namespace.
Ngayon - sa pagpapatupad!
pag-aayos
GitLab CI
Magsimula tayo sa isang fragment .gitlab-ci.yaml, na naglalarawan sa pag-deploy ng application at pagpapatakbo ng mga pagsubok. Ang listahan ay naging napakalaki, kaya lubusan itong dinagdagan ng mga komento:
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
Kubernetes
Ngayon sa direktoryo .helm/templates gumawa tayo ng YAML kasama si Job - tests-job.yaml — upang magpatakbo ng mga pagsubok at ang mga mapagkukunan ng Kubernetes na kailangan nito. Tingnan ang mga paliwanag pagkatapos ng listahan:
Anong uri ng mga mapagkukunan inilarawan sa pagsasaayos na ito? Kapag nagde-deploy, gumagawa kami ng natatanging namespace para sa proyekto (ito ay ipinahiwatig sa .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) at ilunsad ito:
ConfigMap may test script;
Trabaho na may isang paglalarawan ng pod at ang tinukoy na direktiba command, na nagpapatakbo lamang ng mga pagsubok;
PV at PVC, na nagbibigay-daan sa iyong mag-imbak ng data ng pagsubok.
Bigyang-pansin ang panimulang kondisyon na may if sa simula ng manifest - nang naaayon, ang iba pang mga YAML file ng Helm chart na may application ay dapat na nakabalot sa baligtarin disenyo upang hindi sila ma-deploy sa panahon ng pagsubok. Yan ay:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Gayunpaman, kung ang mga pagsubok nangangailangan ng ilang imprastraktura (halimbawa, Redis, RabbitMQ, Mongo, PostgreSQL...) - ang kanilang mga YAML ay maaaring hindi patayin. I-deploy ang mga ito sa isang pagsubok na kapaligiran pati na rin... pagsasaayos sa kanila ayon sa nakikita mong akma, siyempre.
Huling ugnay
kasi assembly at deployment gamit ang werf works sa ngayon lamang sa build server (na may gitlab-runner), at ang pod na may mga pagsubok ay inilunsad sa master, kakailanganin mong lumikha ng isang direktoryo /mnt/tests sa panginoon at ibigay ito sa mananakbo, halimbawa, sa pamamagitan ng NFS. Ang isang detalyadong halimbawa na may mga paliwanag ay matatagpuan sa Dokumentasyon ng K8.
Walang sinuman ang nagbabawal sa direktang pagbabahagi ng NFS sa gitlab-runner, at pagkatapos ay i-mount ito sa mga pod.
Nota
Maaaring nagtatanong ka kung bakit ginagawang kumplikado ang lahat sa pamamagitan ng paglikha ng isang Trabaho kung maaari ka lamang magpatakbo ng isang script na may mga pagsubok nang direkta sa shell runner? Ang sagot ay medyo walang kuwenta...
Ang ilang mga pagsubok ay nangangailangan ng access sa imprastraktura (MongoDB, RabbitMQ, PostgreSQL, atbp.) upang ma-verify na gumagana ang mga ito nang tama. Ginagawa naming pinag-isa ang pagsubok - sa diskarteng ito, nagiging madaling isama ang mga naturang karagdagang entity. Bilang karagdagan dito, nakukuha namin pamantayan diskarte sa pag-deploy (kahit na gumagamit ng NFS, karagdagang pag-mount ng mga direktoryo).
Resulta
Ano ang makikita natin kapag inilapat natin ang inihandang pagsasaayos?
Ang kahilingan sa pagsasama ay magpapakita ng buod na istatistika para sa mga pagsubok na tumatakbo sa pinakabagong pipeline nito:
Ang bawat error ay maaaring i-click dito para sa mga detalye:
NB: Mapapansin ng matulungin na mambabasa na sinusubok namin ang isang NodeJS application, at sa mga screenshot -. ay natagpuan sa iba.
Konklusyon
Tulad ng nakikita mo, walang kumplikado!
Sa prinsipyo, kung mayroon ka nang isang shell collector at ito ay gumagana, ngunit hindi mo kailangan ng Kubernetes, ang pag-attach ng pagsubok dito ay magiging isang mas simpleng gawain kaysa inilarawan dito. At sa Dokumentasyon ng GitLab CI makakahanap ka ng mga halimbawa para kay Ruby, Go, Gradle, Maven at ilang iba pa.