Ondanks het feit dat iedereen heel goed weet dat het testen van je software belangrijk en noodzakelijk is, en velen dit al heel lang automatisch doen, was er in de uitgestrektheid van Habr geen enkel recept voor het opzetten van een combinatie van zulke populaire producten in deze niche als (onze favoriet) GitLab en JUnit. Laten we deze leemte opvullen!
inleidend
Laat ik eerst wat context geven:
Omdat al onze applicaties op Kubernetes draaien, zullen we overwegen om tests uit te voeren op de juiste infrastructuur.
Voor montage en plaatsing gebruiken wij werf (qua infrastructuurcomponenten betekent dit ook automatisch dat Helm betrokken is).
Ik zal niet ingaan op de details van het daadwerkelijk maken van tests: in ons geval schrijft de klant de tests zelf en zorgen wij alleen voor de lancering ervan (en de aanwezigheid van een bijbehorend rapport in het samenvoegverzoek).
Hoe ziet de algemene volgorde van acties eruit?
Het bouwen van de applicatie - we laten de beschrijving van deze fase achterwege.
Implementeer de applicatie in een aparte naamruimte van het Kubernetes-cluster en begin met testen.
Zoeken naar artefacten en parseren van JUnit-rapporten met GitLab.
Een eerder gemaakte naamruimte verwijderen.
Nu - naar de implementatie!
afstelling
GitLab-CI
Laten we beginnen met een fragment .gitlab-ci.yaml, waarin wordt beschreven hoe u de applicatie implementeert en tests uitvoert. De lijst bleek behoorlijk omvangrijk en werd daarom grondig aangevuld met commentaar:
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
Nu in de map .helm/templates laten we YAML maken met Job - tests-job.yaml – om tests uit te voeren en de Kubernetes-bronnen die het nodig heeft. Zie uitleg na vermelding:
Wat voor middelen beschreven in deze configuratie? Bij de implementatie creëren we een unieke naamruimte voor het project (dit wordt aangegeven in .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) en rol het uit:
Configuratiekaart met testscript;
Jobomschrijving: met een beschrijving van de pod en de gespecificeerde richtlijn command, die alleen de tests uitvoert;
PV en PVC, waarmee u testgegevens kunt opslaan.
Let op de inleidende voorwaarde met if aan het begin van het manifest - dienovereenkomstig moeten andere YAML-bestanden van het Helm-diagram met de toepassing worden ingepakt achteruit zo te ontwerpen dat ze tijdens het testen niet worden ingezet. Dat is:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Echter, als de tests enige infrastructuur nodig (bijvoorbeeld Redis, RabbitMQ, Mongo, PostgreSQL...) - hun YAML's kunnen dat zijn geen uitschakelen. Implementeer ze ook in een testomgeving... en pas ze uiteraard naar eigen inzicht aan.
Laatste greep
Omdat montage en plaatsing voorlopig via werfwerken alleen op de buildserver (met gitlab-runner), en de pod met tests wordt gelanceerd op de master, zul je een map moeten aanmaken /mnt/tests op de meester en geef het aan de loper, bijvoorbeeld via NFS. Een gedetailleerd voorbeeld met uitleg vindt u in K8s-documentatie.
Niemand verbiedt om een NFS-share rechtstreeks op gitlab-runner te maken en deze vervolgens in pods te mounten.
Noot
U vraagt zich misschien af waarom alles ingewikkelder zou worden door een taak te maken als u eenvoudigweg een script met tests rechtstreeks op de shell-runner kunt uitvoeren? Het antwoord is nogal triviaal...
Sommige tests vereisen toegang tot de infrastructuur (MongoDB, RabbitMQ, PostgreSQL, enz.) om te verifiëren dat ze correct werken. Wij maken testen uniform: met deze aanpak wordt het eenvoudig om dergelijke extra entiteiten op te nemen. Daarnaast krijgen we standaard implementatiebenadering (zelfs bij gebruik van NFS, extra aankoppeling van mappen).
Resultaat
Wat zullen we zien als we de voorbereide configuratie toepassen?
Het samenvoegverzoek toont samenvattende statistieken voor tests die in de nieuwste pijplijn zijn uitgevoerd:
Elke fout kan hier worden geklikt voor meer informatie:
NB: De oplettende lezer zal merken dat we een NodeJS-applicatie testen, en in de screenshots - .NET... Wees niet verrast: het is alleen dat er tijdens het voorbereiden van het artikel geen fouten zijn gevonden bij het testen van de eerste applicatie, maar ze werden gevonden in een andere.
Conclusie
Zoals je kunt zien, niets ingewikkelds!
Als je al een shell-collector hebt en deze werkt, maar je hebt geen Kubernetes nodig, zal het koppelen van testen daaraan in principe een nog eenvoudiger taak zijn dan hier beschreven. En in GitLab CI-documentatie je vindt voorbeelden voor Ruby, Go, Gradle, Maven en enkele anderen.