Παρά το γεγονός ότι όλοι γνωρίζουν πολύ καλά ότι είναι σημαντικό και απαραίτητο να δοκιμάζετε το λογισμικό σας, και πολλοί το κάνουν αυτόματα για μεγάλο χρονικό διάστημα, στην απεραντοσύνη του Habr δεν υπήρχε ούτε μία συνταγή για τη δημιουργία μιας δέσμης τόσο δημοφιλών προϊόντων σε αυτήν την εξειδικευμένη θέση όπως (το αγαπημένο μας) GitLab και JUnit. Ας καλύψουμε αυτό το κενό!

Εισαγωγικός
Καταρχάς, ας ορίσω το πλαίσιο:
- Δεδομένου ότι όλες οι εφαρμογές μας εκτελούνται σε Kubernetes, θα καλύψουμε την εκτέλεση δοκιμών στην αντίστοιχη υποδομή.
- Για τη συναρμολόγηση και την εγκατάσταση χρησιμοποιούμε (όσον αφορά τα στοιχεία υποδομής, αυτό σημαίνει αυτόματα ότι εμπλέκεται και το Helm).
- Δεν θα επεκταθώ σε λεπτομέρειες σχετικά με την πραγματική δημιουργία των δοκιμών: στην περίπτωσή μας, ο πελάτης γράφει ο ίδιος τις δοκιμές και εμείς διασφαλίζουμε μόνο ότι εκκινούνται (και ότι η αντίστοιχη αναφορά υπάρχει στο αίτημα συγχώνευσης).
Ποια θα είναι η γενική ακολουθία ενεργειών;
- Συναρμολόγηση της εφαρμογής - θα παραλείψουμε την περιγραφή αυτού του σταδίου.
- Αναπτύξτε την εφαρμογή σε ξεχωριστό χώρο ονομάτων συμπλέγματος Kubernetes και εκτελέστε δοκιμές.
- Αναζήτηση για τεχνουργήματα και ανάλυση της αναφοράς JUnit από το GitLab.
- Διαγραφή ενός χώρου ονομάτων που έχει δημιουργηθεί προηγουμένως.
Τώρα - στην υλοποίηση!
προσαρμογή
GitLab CI
Ας ξεκινήσουμε με ένα απόσπασμα .gitlab-ci.yaml, που περιγράφει την ανάπτυξη της εφαρμογής και την εκτέλεση δοκιμών. Η λίστα αποδείχθηκε αρκετά ογκώδης, επομένως συμπληρώθηκε πλήρως με σχόλια:
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
Τώρα στον κατάλογο .helm/templates ας δημιουργήσουμε YAML με το Job — tests-job.yaml — για την εκτέλεση δοκιμών και των πόρων Kubernetes που απαιτούνται. Για εξηγήσεις, δείτε μετά την καταχώριση:
{{- 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 }}
Τι είδους πόροι; περιγράφεται σε αυτήν τη διαμόρφωση; Κατά την ανάπτυξη, δημιουργούμε έναν μοναδικό χώρο ονομάτων για το έργο (αυτό καθορίστηκε στο .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) και το κυκλοφορούμε:
- ConfigMap με δοκιμαστικό σενάριο;
- Δουλειά με την περιγραφή του pod και την καθορισμένη οδηγία
command, το οποίο είναι αυτό που εκτελεί τις δοκιμές· - Φ/Β και PVC, το οποίο σας επιτρέπει να αποθηκεύετε δεδομένα δοκιμών.
Παρακαλώ σημειώστε την εισαγωγική συνθήκη με if στην αρχή του μανιφέστου - κατά συνέπεια, άλλα αρχεία YAML του γραφήματος Helm με την εφαρμογή πρέπει να είναι τυλιγμένα σε αντίστροφο σχεδιασμός τους έτσι ώστε να μην αναπτύσσονται κατά τη διάρκεια των δοκιμών. Ήτοι:
{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}
Ωστόσο, εάν οι δοκιμές απαιτούν κάποια υποδομή (π.χ. Redis, RabbitMQ, Mongo, PostgreSQL…) — τα YAML τους μπορούν να είναι όχι κλείνω. Αναπτύξτε τα και σε ένα δοκιμαστικό περιβάλλον... τροποποιώντας τα όπως κρίνετε κατάλληλο, φυσικά.
Τελική αφή
Επειδή η συναρμολόγηση και η ανάπτυξη χρησιμοποιώντας werf λειτουργούν προς το παρόν μόνο στον διακομιστή δημιουργίας (με το gitlab-runner) και το pod με τις δοκιμές ξεκινήσει στον κύριο κατάλογο, θα χρειαστεί να δημιουργήσετε έναν κατάλογο /mnt/tests στον αφέντη και δώσ' το στον δρομέα, για παράδειγμα, μέσω NFS. Ένα λεπτομερές παράδειγμα με επεξηγήσεις μπορεί να βρεθεί στο .
Το αποτέλεσμα θα είναι:
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 0
Κανείς δεν σας απαγορεύει να δημιουργήσετε ένα κοινόχρηστο στοιχείο NFS απευθείας στο gitlab-runner και στη συνέχεια να το προσαρτήσετε σε pods.
Σημείωση
Μπορεί να ρωτήσετε, γιατί να μπείτε στον κόπο να δημιουργήσετε μια Εργασία όταν μπορείτε απλώς να εκτελέσετε ένα σενάριο με δοκιμές απευθείας στον εκτελέστη κελύφους; Η απάντηση είναι αρκετά ασήμαντη...
Ορισμένες δοκιμές απαιτούν πρόσβαση στην υποδομή (MongoDB, RabbitMQ, PostgreSQL, κ.λπ.) για να ελεγχθεί η ορθότητα της εργασίας με αυτές. Κάνουμε τις δοκιμές ενοποιημένες - με αυτήν την προσέγγιση, γίνεται εύκολο να συμπεριληφθούν τέτοιες πρόσθετες οντότητες. Εκτός από αυτό, λαμβάνουμε πρότυπο προσέγγιση ανάπτυξης (ακόμα και με τη χρήση NFS, πρόσθετη προσάρτηση καταλόγων).
Αποτέλεσμα
Τι θα δούμε όταν εφαρμόσουμε την προετοιμασμένη διαμόρφωση;
Το αίτημα συγχώνευσης θα εμφανίζει συνοπτικά στατιστικά στοιχεία για τις δοκιμές που εκτελούνται στην πιο πρόσφατη σωλήνωση:

Μπορείτε να κάνετε κλικ σε κάθε σφάλμα εδώ για να λάβετε λεπτομέρειες:

NBΈνας προσεκτικός αναγνώστης θα παρατηρήσει ότι δοκιμάζουμε μια εφαρμογή NodeJS, ενώ τα στιγμιότυπα οθόνης δείχνουν .NET... Μην εκπλαγείτε: απλώς κατά την προετοιμασία του άρθρου, δεν βρήκαμε σφάλματα κατά τη δοκιμή της πρώτης εφαρμογής, αλλά τα βρήκαμε στην άλλη.
Συμπέρασμα
Όπως μπορείτε να δείτε, τίποτα περίπλοκο!
Κατ' αρχήν, εάν έχετε ήδη ένα πρόγραμμα δημιουργίας κελυφών και λειτουργεί, και δεν χρειάζεστε Kubernetes, η προσθήκη δοκιμών σε αυτό θα είναι μια ακόμη πιο απλή εργασία από αυτήν που περιγράφεται εδώ. Και σε Θα βρείτε παραδείγματα για Ruby, Go, Gradle, Maven και μερικά άλλα.
PS
Διαβάστε επίσης στο blog μας:
- «"?
- «"?
- «».
Πηγή: www.habr.com
