Malgrat que tothom sap perfectament que provar el vostre programari és important i necessari, i molts ho fan automàticament des de fa molt de temps, a la immensitat d'Habr no hi havia ni una recepta per configurar una combinació de productes tan populars a aquest nínxol com (el nostre favorit) GitLab i JUnit . Omplim aquest buit!
Introducció
En primer lloc, permeteu-me donar una mica de context:
Com que totes les nostres aplicacions s'executen a Kubernetes, considerarem fer proves a la infraestructura adequada.
Per al muntatge i el desplegament fem servir werf (en termes de components d'infraestructura, això també significa automàticament que Helm està implicat).
No entraré en els detalls de la creació real de les proves: en el nostre cas, el client escriu les proves ell mateix i només ens assegurem el llançament (i la presència d'un informe corresponent a la sol·licitud de fusió).
Com serà la seqüència general d'accions?
Creació de l'aplicació: ometrem la descripció d'aquesta etapa.
Desplegueu l'aplicació en un espai de noms independent del clúster de Kubernetes i comenceu a provar.
Cercar artefactes i analitzar informes JUnit amb GitLab.
Eliminació d'un espai de noms creat anteriorment.
Ara, a la implementació!
ajust
GitLab CI
Comencem amb un fragment .gitlab-ci.yaml, que descriu el desplegament de l'aplicació i l'execució de proves. La llista va resultar ser força voluminosa, per la qual cosa es va complementar a fons amb comentaris:
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
Ara al directori .helm/templates creem YAML amb Job - tests-job.yaml — per executar proves i els recursos de Kubernetes que necessita. Vegeu les explicacions després de la llista:
Quin tipus de recursos descrit en aquesta configuració? Quan es desplega, creem un espai de noms únic per al projecte (això s'indica a .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) i desplega-ho:
ConfigMap amb guió de prova;
treball amb una descripció del pod i la directiva especificada command, que només executa les proves;
PV i PVC, que us permeten emmagatzemar dades de prova.
Preste atenció a la condició introductòria amb if al principi del manifest; en conseqüència, altres fitxers YAML del gràfic Helm amb l'aplicació s'han d'embolicar a al revés dissenyar de manera que no es despleguin durant les proves. Això és:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Tanmateix, si les proves requereixen d'alguna infraestructura (per exemple, Redis, RabbitMQ, Mongo, PostgreSQL...) - els seus YAML poden ser no tanca. Desplegueu-los també en un entorn de prova... ajustant-los com cregueu, és clar.
Tacte final
Perquè el muntatge i el desplegament amb werf funciona ara per ara només al servidor de compilació (amb gitlab-runner) i el pod amb proves es llança al mestre, haureu de crear un directori /mnt/tests sobre el mestre i donar-lo al corredor, per exemple, mitjançant NFS. Podeu trobar un exemple detallat amb explicacions a Documentació K8s.
Ningú prohibeix fer una compartició NFS directament a gitlab-runner i després muntar-la en pods.
Nota
Potser us preguntareu per què complicar-ho tot creant un treball si simplement podeu executar un script amb proves directament al shell runner? La resposta és bastant trivial...
Algunes proves requereixen accés a la infraestructura (MongoDB, RabbitMQ, PostgreSQL, etc.) per comprovar que funcionen correctament. Fem les proves unificades: amb aquest enfocament, és fàcil incloure aquestes entitats addicionals. A més d'això, aconseguim estàndard enfocament de desplegament (encara que utilitzeu NFS, muntatge addicional de directoris).
Resultat
Què veurem quan apliquem la configuració preparada?
La sol·licitud de combinació mostrarà estadístiques de resum de les proves executades a la seva última canalització:
Cada error es pot fer clic aquí per obtenir més informació:
NB: El lector atent s'adonarà que estem provant una aplicació NodeJS, i a les captures de pantalla - .NET... No us estranyeu: és que durant la preparació de l'article no s'han trobat errors en provar la primera aplicació, però sí es van trobar en un altre.
Conclusió
Com podeu veure, res complicat!
En principi, si ja teniu un col·lector d'intèrprets d'ordres i funciona, però no necessiteu Kubernetes, adjuntar-hi proves serà una tasca encara més senzilla que la que es descriu aquí. I en Documentació de GitLab CI trobareu exemples per a Ruby, Go, Gradle, Maven i alguns altres.