În ciuda faptului că toată lumea știe perfect că testarea software-ului dvs. este importantă și necesară și mulți o fac automat de mult timp, în vastitatea Habr nu exista o singură rețetă pentru a crea o combinație de produse atât de populare în această nișă ca (preferatul nostru) GitLab și JUnit. Să umplem acest gol!
introductiv
Mai întâi, permiteți-mi să ofer un context:
Deoarece toate aplicațiile noastre rulează pe Kubernetes, vom lua în considerare efectuarea de teste pe infrastructura adecvată.
Pentru asamblare și desfășurare folosim werf (în ceea ce privește componentele infrastructurii, acest lucru înseamnă și automat că Helm este implicat).
Nu voi intra în detalii despre crearea efectivă a testelor: în cazul nostru, clientul scrie el însuși testele, iar noi asigurăm doar lansarea acestora (și prezența unui raport corespunzător în cererea de fuziune).
Cum va arăta secvența generală de acțiuni?
Construirea aplicației - vom omite descrierea acestei etape.
Implementați aplicația într-un spațiu de nume separat al clusterului Kubernetes și începeți testarea.
Căutarea artefactelor și analizarea rapoartelor JUnit cu GitLab.
Ștergerea unui spațiu de nume creat anterior.
Acum - la implementare!
ajustare
GitLab CI
Să începem cu un fragment .gitlab-ci.yaml, care descrie implementarea aplicației și rularea testelor. Lista sa dovedit a fi destul de voluminoasă, așa că a fost complet completată cu comentarii:
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
Acum în director .helm/templates să creăm YAML cu Job - tests-job.yaml — pentru a rula teste și resursele Kubernetes de care are nevoie. Vedeți explicațiile după listare:
Ce fel de resurse descrise în această configurație? La implementare, creăm un spațiu de nume unic pentru proiect (acest lucru este indicat în .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) și lansați-l:
ConfigMap cu script de testare;
Loc de munca cu o descriere a podului și directiva specificată command, care doar rulează testele;
PV și PVC, care vă permit să stocați date de testare.
Atenție la starea introductivă cu if la începutul manifestului - în consecință, alte fișiere YAML ale diagramei Helm cu aplicația trebuie să fie împachetate în verso proiectați astfel încât să nu fie implementate în timpul testării. Acesta este:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Cu toate acestea, dacă testele necesită o anumită infrastructură (de exemplu, Redis, RabbitMQ, Mongo, PostgreSQL...) - YAML-urile lor pot fi nu opriți. Implementați-le și într-un mediu de testare... ajustându-le după cum credeți de cuviință, desigur.
Efectul final
Deoarece asamblarea și implementarea folosind werf funcționează deocamdată numai pe serverul de compilare (cu gitlab-runner), iar podul cu teste este lansat pe master, va trebui să creați un director /mnt/tests pe stăpân și dă-l alergătorului, de exemplu, prin NFS. Un exemplu detaliat cu explicații poate fi găsit în documentația K8s.
Nimeni nu interzice să faci o partajare NFS direct pe gitlab-runner și apoi să o montezi în pod-uri.
Nota
S-ar putea să vă întrebați de ce complicați totul prin crearea unui Job dacă pur și simplu puteți rula un script cu teste direct pe shell runner? Raspunsul este destul de banal...
Unele teste necesită acces la infrastructură (MongoDB, RabbitMQ, PostgreSQL etc.) pentru a verifica dacă funcționează corect. Facem testarea unificată - cu această abordare, devine ușor să includeți astfel de entități suplimentare. Pe lângă asta, primim standard abordare de implementare (chiar dacă se utilizează NFS, montare suplimentară a directoarelor).
Rezultat
Ce vom vedea când vom aplica configurația pregătită?
Solicitarea de îmbinare va afișa statistici rezumate pentru testele rulate în cel mai recent canal:
Fiecare eroare poate fi făcută clic aici pentru detalii:
NB: Cititorul atent va observa că testăm o aplicație NodeJS, iar în capturi de ecran - .NET... Nu vă mirați: tocmai în cadrul pregătirii articolului, nu au fost găsite erori la testarea primei aplicații, dar au fost găsite în alta.
Concluzie
După cum puteți vedea, nimic complicat!
În principiu, dacă aveți deja un colector de shell și funcționează, dar nu aveți nevoie de Kubernetes, atașarea testării la acesta va fi o sarcină și mai simplă decât cea descrisă aici. Si in Documentația GitLab CI veți găsi exemple pentru Ruby, Go, Gradle, Maven și alții.