Til tross for at alle godt vet at det å teste programvaren din er viktig og nødvendig, og mange har gjort det automatisk i lang tid, var det ikke en eneste oppskrift for å sette opp en kombinasjon av slike populære produkter i Habrs vidder. denne nisjen som (vår favoritt) GitLab og JUnit . La oss fylle dette gapet!
Innledende
Først, la meg gi litt kontekst:
Siden alle applikasjonene våre kjører på Kubernetes, vil vi vurdere å kjøre tester på riktig infrastruktur.
For montering og distribusjon bruker vi werf (i forhold til infrastrukturkomponenter betyr dette også automatisk at Helm er involvert).
Jeg vil ikke gå inn på detaljene i den faktiske opprettelsen av tester: i vårt tilfelle skriver klienten testene selv, og vi sikrer bare lanseringen av dem (og tilstedeværelsen av en tilsvarende rapport i sammenslåingsforespørselen).
Hvordan vil den generelle handlingsrekkefølgen se ut?
Bygge applikasjonen - vi vil utelate beskrivelsen av dette stadiet.
Distribuer applikasjonen til et eget navneområde i Kubernetes-klyngen og begynn å teste.
Søker etter artefakter og analyserer JUnit-rapporter med GitLab.
Sletter et tidligere opprettet navneområde.
Nå - til implementering!
justering
GitLab CI
La oss starte med et fragment .gitlab-ci.yaml, som beskriver distribusjon av applikasjonen og kjøring av tester. Oppføringen viste seg å være ganske omfangsrik, så den ble grundig supplert 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
Nå i katalogen .helm/templates la oss lage YAML med Job - tests-job.yaml — å kjøre tester og Kubernetes-ressursene den trenger. Se forklaringer etter oppføringen:
Hva slags ressurser beskrevet i denne konfigurasjonen? Ved distribusjon oppretter vi et unikt navneområde for prosjektet (dette er indikert i .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) og rull den ut:
ConfigMap med testskript;
Jobb med en beskrivelse av poden og det spesifiserte direktivet command, som bare kjører testene;
PV og PVC, som lar deg lagre testdata.
Vær oppmerksom på den innledende betingelsen med if i begynnelsen av manifestet - følgelig må andre YAML-filer i Helm-diagrammet med applikasjonen pakkes inn omvendt design slik at de ikke blir distribuert under testing. Det er:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Men hvis testene krever litt infrastruktur (for eksempel Redis, RabbitMQ, Mongo, PostgreSQL...) - deres YAML-er kan være no skru av. Distribuer dem i et testmiljø også... juster dem etter eget ønske, selvfølgelig.
Endelig berøring
Fordi montering og distribusjon ved hjelp av werf fungerer foreløpig bare på byggeserveren (med gitlab-runner), og poden med tester er lansert på masteren, må du opprette en katalog /mnt/tests på mesteren og gi den til løperen, for eksempel via NFS. Et detaljert eksempel med forklaringer finner du i K8s dokumentasjon.
Ingen forbyr å lage en NFS-andel direkte på gitlab-runner, og deretter montere den i pods.
Note
Du spør kanskje hvorfor komplisere alt ved å lage en jobb hvis du bare kan kjøre et skript med tester direkte på shell-løperen? Svaret er ganske trivielt...
Noen tester krever tilgang til infrastrukturen (MongoDB, RabbitMQ, PostgreSQL, etc.) for å bekrefte at de fungerer som de skal. Vi gjør testing enhetlig - med denne tilnærmingen blir det enkelt å inkludere slike tilleggsenheter. I tillegg til dette får vi standard distribusjonstilnærming (selv om du bruker NFS, ekstra montering av kataloger).
Resultat
Hva vil vi se når vi bruker den forberedte konfigurasjonen?
Sammenslåingsforespørselen vil vise sammendragsstatistikk for tester som er kjørt i sin siste pipeline:
Hver feil kan klikkes her for detaljer:
NB: Den oppmerksomme leseren vil legge merke til at vi tester en NodeJS-applikasjon, og i skjermbildene - .NET... Ikke bli overrasket: det er bare at under utarbeidelsen av artikkelen ble det ikke funnet noen feil ved testing av den første applikasjonen, men de ble funnet i en annen.
Konklusjon
Som du kan se, ingenting komplisert!
I prinsippet, hvis du allerede har en skallsamler og den fungerer, men du ikke trenger Kubernetes, vil det være en enda enklere oppgave å feste testing til den enn beskrevet her. Og i GitLab CI-dokumentasjon du finner eksempler for Ruby, Go, Gradle, Maven og noen andre.