På trods af at alle godt ved, at det er vigtigt og nødvendigt at teste din software, og mange har gjort det automatisk i lang tid, var der i Habrs vidder ikke en eneste opskrift på at opsætte en kombination af så populære produkter i denne niche som (vores favorit) GitLab og JUnit . Lad os udfylde dette hul!
Indledende
Lad mig først give lidt kontekst:
Da alle vores applikationer kører på Kubernetes, vil vi overveje at køre test på den relevante infrastruktur.
Til montering og udrulning bruger vi werf (i forhold til infrastrukturkomponenter betyder det også automatisk, at Helm er involveret).
Jeg vil ikke gå ind i detaljerne i den faktiske oprettelse af tests: I vores tilfælde skriver klienten testene selv, og vi sikrer kun deres lancering (og tilstedeværelsen af en tilsvarende rapport i fletteanmodningen).
Hvordan vil den generelle rækkefølge af handlinger se ud?
Opbygning af applikationen - vi udelader beskrivelsen af denne fase.
Implementer applikationen til et separat navneområde i Kubernetes-klyngen, og begynd at teste.
Søger efter artefakter og parser JUnit-rapporter med GitLab.
Sletning af et tidligere oprettet navneområde.
Nu - til implementering!
justering
GitLab CI
Lad os starte med et fragment .gitlab-ci.yaml, som beskriver implementering af applikationen og afvikling af tests. Listen viste sig at være ret omfangsrig, så den blev grundigt suppleret med kommentarer:
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 i mappen .helm/templates lad os skabe YAML med Job - tests-job.yaml — at køre test og de Kubernetes-ressourcer, det har brug for. Se forklaringer efter liste:
Hvilken slags ressourcer beskrevet i denne konfiguration? Ved implementering opretter vi et unikt navneområde for projektet (dette er angivet i .gitlab-ci.yaml — tests-${CI_COMMIT_REF_SLUG}) og rul den ud:
ConfigMap med testscript;
Job med en beskrivelse af poden og det specificerede direktiv command, som netop kører testene;
PV og PVC, som giver dig mulighed for at gemme testdata.
Vær opmærksom på den indledende betingelse med if i begyndelsen af manifestet - derfor skal andre YAML-filer i Helm-diagrammet med applikationen pakkes ind i baglæns design, så de ikke bliver implementeret under test. Det er:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Men hvis testene kræver noget infrastruktur (for eksempel Redis, RabbitMQ, Mongo, PostgreSQL...) - deres YAML'er kan være nej sluk. Implementer dem også i et testmiljø... juster dem, som du finder passende, selvfølgelig.
Endelig berøring
Fordi montering og udrulning ved hjælp af werf-værker for nu kun på build-serveren (med gitlab-runner), og poden med test lanceres på masteren, skal du oprette en mappe /mnt/tests på mesteren og giv den til løberen, for eksempel via NFS. Et detaljeret eksempel med forklaringer kan findes i K8s dokumentation.
Ingen forbyder at lave en NFS-share direkte på gitlab-runner og derefter montere den i pods.
Bemærk
Du spørger måske, hvorfor komplicere alt ved at oprette et job, hvis du blot kan køre et script med test direkte på shell-løberen? Svaret er ret trivielt...
Nogle test kræver adgang til infrastrukturen (MongoDB, RabbitMQ, PostgreSQL osv.) for at verificere, at de fungerer korrekt. Vi gør testning samlet - med denne tilgang bliver det nemt at inkludere sådanne yderligere enheder. Ud over dette får vi standard implementeringstilgang (selv hvis du bruger NFS, yderligere montering af mapper).
Outcome
Hvad vil vi se, når vi anvender den forberedte konfiguration?
Sammenfletningsanmodningen vil vise oversigtsstatistikker for test, der er kørt i dens seneste pipeline:
Hver fejl kan klikkes her for detaljer:
NB: Den opmærksomme læser vil bemærke, at vi tester en NodeJS-applikation, og i skærmbillederne - .NET... Bliv ikke overrasket: det er bare, at der under udarbejdelsen af artiklen ikke blev fundet fejl i test af den første applikation, men de blev fundet i en anden.
Konklusion
Som du kan se, intet kompliceret!
I princippet, hvis du allerede har en skalsamler, og den virker, men du ikke har brug for Kubernetes, vil det være en endnu enklere opgave at vedhæfte test til den end beskrevet her. Og i GitLab CI dokumentation du finder eksempler på Ruby, Go, Gradle, Maven og nogle andre.