Trots att alla vet att det Àr viktigt och nödvÀndigt att testa sin mjukvara och att mÄnga redan gör detta automatiskt, har det inte funnits nÄgot recept pÄ Habr för att konfigurera kopplingen mellan sÄ populÀra produkter i denna nisch som (vÄr favorit) GitLab och JUnit. LÄt oss fylla detta tomrum!

Inledning
För att börja med, lÄt mig definiera kontexten:
- Eftersom alla vÄra applikationer körs i Kubernetes kommer vi att se pÄ hur tester körs i den aktuella infrastrukturen.
- För byggande och distribution anvÀnder vi (vilket ocksÄ automatiskt innebÀr att Helm anvÀnds i meningen infrastrukturkomponenter).
- Jag kommer inte att gÄ in pÄ detaljer om sjÀlva skapandet av tester: i vÄrt fall skriver kunden testerna sjÀlva, och vi sÀkerstÀller bara deras körning (och att det finns en motsvarande rapport i merge requesten).
Hur kommer den allmÀnna sekvensen av ÄtgÀrder att se ut?
- Byggande av applikationen â vi kommer att hoppa över beskrivningen av detta steg.
- Distribution av applikationen till ett separat namespace i Kubernetes-klustret och uppstart av testning.
- Sökning av artefakter och parsering av JUnit-rapporten av GitLab.
- Borttagning av det tidigare skapade namespace.
Nu â till genomförandet!
InstÀllning
GitLab CI
LÄt oss börja med fragmentet .gitlab-ci.yaml, som beskriver distributionen av applikationen och körningen av tester. Listan blev ganska omfattande, sÄ den har noggrant kompletterats med kommentarer:
variabler:
# deklarera versionen av werf som vi ska anvÀnda
WERF_VERSION: "1.0 beta"
.base_deploy: &base_deploy
skript:
# skapa namespace i K8s om det inte finns
- kubectl --context="${WERF_KUBE_CONTEXT}" get ns ${CI_ENVIRONMENT_SLUG} || kubectl create ns ${CI_ENVIRONMENT_SLUG}
# ladda ner werf och deploya â lĂ€s mer om detta i dokumentationen
# (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:-''}"
# överför variabeln `run_tests`
# den kommer att anvÀndas i renderingen av Helm-releasen
--set "global.run_tests=${RUN_TESTS:-no}"
--set "global.env=${CI_ENVIRONMENT_SLUG}"
# Àndra timeout (det kan ta tid med tester) och överför den till releasen
--set "global.ci_timeout=${CI_TIMEOUT:-900}"
--timeout ${CI_TIMEOUT:-900}
beroenden:
- Bygg
.test-base: &test-base
extends: .base_deploy
before_script:
# skapa en mapp för den framtida rapporten baserat pÄ $CI_COMMIT_REF_SLUG
- mkdir /mnt/tests/${CI_COMMIT_REF_SLUG} || true
# tvingad workaround eftersom GitLab vill ha artefakter i sin build-mapp
- mkdir ./tests || true
- ln -s /mnt/tests/${CI_COMMIT_REF_SLUG} ./tests/${CI_COMMIT_REF_SLUG}
after_script:
# efter testerna avslutas radera releasen tillsammans med jobben
# (och eventuellt dess infrastruktur)
- 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
# vi tillÄter misslyckanden, men du kan göra annorlunda
allow_failure: true
variabler:
RUN_TESTS: 'ja'
# stÀll in kontexten i werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#infrastructure)
WERF_KUBE_CONTEXT: 'admin@stage-cluster'
tags:
# anvÀnd en runner med taggen `werf-runner`
- werf-runner
artefakter:
# det Àr nödvÀndigt att samla artefakten sÄ den kan ses
# i pipelinen och laddas ner â till exempel för mer noggrant granskande
paths:
- ./tests/${CI_COMMIT_REF_SLUG}/*
# artefakter Àldre Àn en vecka kommer att raderas
expire_in: 7 dagar
# viktigt: dessa rader ansvarar för parsing av rapporten av GitLab
reports:
junit: ./tests/${CI_COMMIT_REF_SLUG}/report.xml
# för att förenkla visas hÀr bara tvÄ steg
# i verkligheten har du fler â Ă„tminstone pĂ„ grund av deployment
steg:
- bygg
- tester
bygg:
steg: bygg
skript:
# bygg â Ă„terigen enligt dokumentationen för 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
förutom:
- scheman
kör tester:
<<: *test-base
miljö:
# "sjÀlva hemligheten" i namngivningen av namespace
# (https://docs.gitlab.com/ce/ci/variables/predefined_variables.html)
name: tester-${CI_COMMIT_REF_SLUG}
steg: tester
förutom:
- schemanKubernetes
Nu i katalogen .helm/templates skapar vi en YAML med Job' som tests-job.yaml för att köra tester och dess nödvÀndiga resurser för Kubernetes. Se förklaringar efter listningen:
{{- if eq .Values.global.run_tests "yes" }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tests-script
data:
tests.sh: |
echo "======================"
echo "${APP_NAME} TESTS"
echo "======================"
cd /app
npm run test:ci
cp report.xml /app/test_results/${CI_COMMIT_REF_SLUG}/
echo ""
echo ""
echo ""
chown -R 999:999 /app/test_results/${CI_COMMIT_REF_SLUG}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Chart.Name }}-test
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "2"
"werf/watch-logs": "true"
spec:
activeDeadlineSeconds: {{ .Values.global.ci_timeout }}
backoffLimit: 1
template:
metadata:
name: {{ .Chart.Name }}-test
spec:
containers:
- name: test
command: ['bash', '-c', '/app/tests.sh']
{{ tuple "application" . | include "werf_container_image" | indent 8 }}
env:
- name: env
value: {{ .Values.global.env }}
- name: CI_COMMIT_REF_SLUG
value: {{ .Values.global.commit_ref_slug }}
- name: APP_NAME
value: {{ .Chart.Name }}
{{ tuple "application" . | include "werf_container_env" | indent 8 }}
volumeMounts:
- mountPath: /app/test_results/
name: data
- mountPath: /app/tests.sh
name: tests-script
subPath: tests.sh
tolerations:
- key: dedicated
operator: Exists
- key: node-role.kubernetes.io/master
operator: Exists
restartPolicy: OnFailure
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Chart.Name }}-pvc
- name: tests-script
configMap:
name: tests-script
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Chart.Name }}-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}
volumeName: {{ .Values.global.commit_ref_slug }}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: {{ .Values.global.commit_ref_slug }}
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Mi
local:
path: /mnt/tests/
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- kube-master
persistentVolumeReclaimPolicy: Delete
storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}
{{- end }} Vilka resurser beskrivs i denna konfiguration? Vid deployment skapar vi ett unikt namespace för projektet (detta anges ocksĂ„ i .gitlab-ci.yaml â tests-${CI_COMMIT_REF_SLUG}) och vi distribuerar till det:
- ConfigMap med testscriptet;
- Job med beskrivning av podden och angiven direktiv
command, som just startar testerna; - PV och PVC, vilket gör det möjligt att lagra testdata.
Observera det inledande villkoret med if i början av manifestet â dĂ€rmed mĂ„ste andra YAML-filer frĂ„n Helm-diagrammet med applikationen omslutas i omvĂ€nd struktur sĂ„ att de inte distribueras vid testning. Det vill sĂ€ga:
{{- if ne .Values.global.run_tests "yes" }}
---
jag annan yaml
{{- end }}Men om testerna krĂ€ver viss infrastruktur (till exempel Redis, RabbitMQ, Mongo, PostgreSQLâŠ) â deras YAML-filer kan inte stĂ€nga av. Utveckla och testa dem i en testmiljö⊠sjĂ€lvklart justera efter eget tycke.
Det sista strecket
Eftersom byggnation och distribution med werf för nÀrvarande fungerar bara pÄ byggservern (med gitlab-runner), och podden med tester startas pÄ master, behöver vi skapa en katalog /mnt/tests pÄ master och ge den till runnern, till exempel via NFS. Ett utökat exempel med förklaringar kan hittas i .
Resultatet blir:
user@kube-master:~$ cat /etc/exports | grep tests
/mnt/tests IP_gitlab-builder/32(rw,nohide,insecure,no_subtree_check,sync,all_squash,anonuid=999,anongid=998)
user@gitlab-runner:~$ cat /etc/fstab | grep tests
IP_kube-master:/mnt/tests /mnt/tests nfs4 _netdev,auto 0 0Ingen hindrar dig frÄn att skapa en NFS-delning direkt pÄ gitlab-runner, som sedan kan monteras i poddar.
AnmÀrkning
Du kanske undrar varför man ska komplicera det hela genom att skapa en Job, nÀr man kan köra testskript direkt pÄ shell-runnern? Svaret Àr ganska trivialt...
Vissa tester krĂ€ver tillgĂ„ng till infrastrukturer (MongoDB, RabbitMQ, PostgreSQL m.m.) för att kontrollera korrektheten i arbetet med dem. Vi gör testningen enhetlig â med denna metod blir det enkelt att inkludera sĂ„dana tillĂ€ggsenheter. Dessutom fĂ„r vi standard metod för distribution (Ă€ven om det gĂ€ller att anvĂ€nda NFS och ytterligare montering av kataloger).
Resultat
Vad kommer vi att se nÀr vi tillÀmpa den förberedda konfigurationen?
I merge-förfrÄgan kommer en sammanfattande statistik över de tester som kördes i den senaste pipelinen att visas:

För varje fel kan du klicka för att fÄ detaljer:

NB: En uppmÀrksam lÀsare kommer att notera att vi testar en NodeJS-applikation, medan skÀrmbilderna visar .NET⊠Oroa dig inte: helt enkelt för att det under artikelns förberedelse inte fanns nÄgra fel i testningen av den första applikationen, men vi hittade dem i den andra.
Slutsats
Som ni ser, inget komplicerat!
I princip, om du redan har en shell-builder och den fungerar, och Kubernetes inte Ă€r nödvĂ€ndigt för dig â att koppla till testningen blir en Ă€nnu enklare uppgift Ă€n den som beskrivs hĂ€r. Och i finns exempel för Ruby, Go, Gradle, Maven och nĂ„gra andra.
P.S.
LÀs Àven i vÄr blogg:
- «»;
- «»;
- «».
KĂ€lla: habr.com
