A pesar de que todo el mundo sabe perfectamente que probar su software es importante y necesario, y muchos lo han estado haciendo automáticamente durante mucho tiempo, en la inmensidad de Habr no existía una receta única para configurar una combinación de productos tan populares en este nicho como (nuestro favorito) GitLab y JUnit. ¡Llenemos este vacío!
Introductorio
Primero, déjame darte un poco de contexto:
Dado que todas nuestras aplicaciones se ejecutan en Kubernetes, consideraremos realizar pruebas en la infraestructura adecuada.
Para el montaje y despliegue utilizamos patio (En términos de componentes de infraestructura, esto también significa automáticamente que Helm está involucrado).
No entraré en detalles sobre la creación real de pruebas: en nuestro caso, el cliente escribe las pruebas él mismo y nosotros solo nos aseguramos de su lanzamiento (y la presencia del informe correspondiente en la solicitud de fusión).
¿Cómo será la secuencia general de acciones?
Creación de la aplicación: omitiremos la descripción de esta etapa.
Implemente la aplicación en un espacio de nombres separado del clúster de Kubernetes y comience a probar.
Búsqueda de artefactos y análisis de informes JUnit con GitLab.
Eliminar un espacio de nombres creado previamente.
Ahora, ¡a la implementación!
Ajuste
CI de GitLab
Empecemos con un fragmento. .gitlab-ci.yaml, que describe la implementación de la aplicación y la ejecución de pruebas. La lista resultó ser bastante voluminosa, por lo que se complementó minuciosamente con comentarios:
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
Ahora en el directorio .helm/templates creemos YAML con Job - tests-job.yaml – para ejecutar pruebas y los recursos de Kubernetes que necesita. Ver explicaciones después del listado:
¿Qué tipo de recursos descrito en esta configuración? Al implementar, creamos un espacio de nombres único para el proyecto (esto se indica en .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) y extiéndalo:
Mapa de configuración con guión de prueba;
Trabajos con una descripción del pod y la directiva especificada command, que simplemente ejecuta las pruebas;
Fotovoltaico y PVC, que le permiten almacenar datos de prueba.
Preste atención a la condición introductoria con if al comienzo del manifiesto; en consecuencia, otros archivos YAML del gráfico Helm con la aplicación deben estar empaquetados en inversa diseñar para que no se implementen durante las pruebas. Eso es:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Sin embargo, si las pruebas Requiere alguna infraestructura (por ejemplo, Redis, RabbitMQ, Mongo, PostgreSQL...): sus YAML pueden ser no apagar. Implementarlos también en un entorno de prueba... ajustándolos como mejor le parezca, por supuesto.
Toque final
Porque montaje e implementación utilizando werf works por ahora sólo en el servidor de compilación (con gitlab-runner) y el pod con pruebas se inicia en el maestro, deberá crear un directorio /mnt/tests en el maestro y dárselo al corredor, por ejemplo, a través de NFS. Un ejemplo detallado con explicaciones se puede encontrar en documentación de K8.
Nadie prohíbe compartir NFS directamente en gitlab-runner y luego montarlo en pods.
Nota
Quizás se pregunte por qué complicar todo creando un trabajo si simplemente puede ejecutar un script con pruebas directamente en el shell runner. La respuesta es bastante trivial...
Algunas pruebas requieren acceso a la infraestructura (MongoDB, RabbitMQ, PostgreSQL, etc.) para verificar que funcionan correctamente. Hacemos que las pruebas sean unificadas; con este enfoque, resulta fácil incluir dichas entidades adicionales. Además de esto, obtenemos estándar enfoque de implementación (incluso si se utiliza NFS, montaje adicional de directorios).
resultado
¿Qué veremos cuando apliquemos la configuración preparada?
La solicitud de fusión mostrará estadísticas resumidas de las pruebas ejecutadas en su última canalización:
Se puede hacer clic en cada error aquí para obtener más detalles:
NB: El lector atento notará que estamos probando una aplicación NodeJS, y en las capturas de pantalla - .NET... No se sorprenda: es solo que mientras preparamos el artículo, no se encontraron errores al probar la primera aplicación, pero fueron encontrados en otro.
Conclusión
Como puedes ver, ¡nada complicado!
En principio, si ya tiene un recopilador de shell y funciona, pero no necesita Kubernetes, adjuntarle pruebas será una tarea aún más sencilla que la que se describe aquí. Y en Documentación de GitLab CI encontrará ejemplos para Ruby, Go, Gradle, Maven y algunos otros.