Obwohl jeder weiß, dass es wichtig und notwendig ist, Ihre Software zu testen, und viele dies schon lange automatisch tun, gibt es in den offenen Räumen von Habr kein einziges Rezept, um in dieser Nische eine Reihe so beliebter Produkte wie (unser Lieblings-) GitLab und JUnit einzurichten . Fülle diese Lücke!

Einführung
Zunächst werde ich den Kontext skizzieren:
- Da alle unsere Anwendungen in Kubernetes funktionieren, werden wir in Betracht ziehen, Tests in der entsprechenden Infrastruktur durchzuführen.
- Für die Montage und Bereitstellung verwenden wir werf (im Sinne von Infrastrukturkomponenten bedeutet dies auch automatisch, dass Helm beteiligt ist).
- Ich werde nicht auf die Details der direkten Erstellung von Tests eingehen: In unserem Fall schreibt der Client die Tests selbst und wir stellen nur sicher, dass sie ausgeführt werden (und der entsprechende Bericht in der Zusammenführungsanforderung verfügbar ist).
Wie sieht die gesamte Abfolge der Aktionen aus?
- Anwendungsassemblierung - Wir werden die Beschreibung dieser Phase weglassen.
- Stellen Sie die Anwendung in einem separaten Kubernetes-Cluster-Namespace bereit und starten Sie den Test.
- Suchen Sie nach Artefakten und analysieren Sie einen JUnit-Bericht von GitLab.
- Löschen Sie den zuvor erstellten Namespace.
Nun zur Implementierung!
Anpassung
Gitlab ci
Beginnen wir mit dem Fragment
.gitlab-ci.yaml
, das die Bereitstellung der Anwendung beschreibt und die Tests
.gitlab-ci.yaml
. Die Auflistung erwies sich als ziemlich umfangreich, daher wird sie gründlich durch Kommentare ergänzt:
variables:
Kubernetes
.helm/templates
nun im
.helm/templates
YAML mit Job -
tests-job.yaml
-, um die Tests und die benötigten Kubernetes-Ressourcen auszuführen. Erläuterungen siehe nach Auflistung:
{{- 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 }}
Welche Ressourcen werden in dieser Konfiguration beschrieben? Erstellen Sie bei der Bereitstellung einen eindeutigen Namespace für das Projekt (dies wird auch in
.gitlab-ci.yaml
-
tests-${CI_COMMIT_REF_SLUG}
) und rollen Sie ihn hinein:
- ConfigMap mit einem Testskript ;
- Job mit einer Beschreibung des Pods und der angegebenen
command
, die nur die Tests ausführt; - PV und PVC , mit denen Sie Testdaten speichern können.
Beachten Sie die Einführungsbedingung,
if
zu Beginn des Manifests - dementsprechend müssen andere YAML-Dateien des Helm-Diagramms mit der Anwendung in die
umgekehrte Konstruktion eingeschlossen werden, damit sie während des Tests nicht bereitgestellt werden. Also:
{{- if ne .Values.global.run_tests "yes" }} --- {{- end }}
Wenn für die Tests jedoch
eine Infrastruktur erforderlich ist (z. B. Redis, RabbitMQ, Mongo, PostgreSQL ...), können die YAMLs deaktiviert
werden . Stellen Sie sie in einer Testumgebung bereit ... natürlich nach Ihren Wünschen.
Letzte Berührung
Weil Die Montage und Bereitstellung mit werf funktioniert bisher
nur auf dem Build-Server (mit gitlab-Runner). Der Pod mit den Tests wird im Assistenten ausgeführt. Sie müssen das Verzeichnis
/mnt/tests
im Assistenten erstellen und es
beispielsweise über NFS an Runner
weitergeben . Ein detailliertes Beispiel mit Erläuterungen finden Sie in der
K8-Dokumentation .
Das Ergebnis wird sein:
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
Niemand verbietet es, einen NFS-Ball direkt auf dem Gitlab-Läufer herzustellen und ihn dann in Pods zu montieren.
Hinweis
Sie fragen sich vielleicht, warum alles mit der Erstellung von Job kompliziert wird, wenn Sie das Testskript einfach direkt auf dem Shell Runner ausführen können. Die Antwort ist ziemlich trivial ...
Einige Tests erfordern Zugriff auf die Infrastruktur (MongoDB, RabbitMQ, PostgreSQL usw.), um die Richtigkeit der Arbeit mit ihnen zu überprüfen. Wir vereinheitlichen das Testen - mit diesem Ansatz wird es einfach, solche zusätzlichen Entitäten einzubeziehen. Darüber hinaus erhalten wir einen
Standardansatz für die Bereitstellung (auch bei Verwendung von NFS, zusätzliche Verzeichnisbereitstellung).
Ergebnis
Was werden wir sehen, wenn wir die vorbereitete Konfiguration anwenden?
In der Zusammenführungsanforderung werden zusammenfassende Statistiken zu den in der letzten Pipeline gestarteten Tests angezeigt:

Sie können hier auf jeden Fehler klicken, um Details zu erhalten:
NB : Ein aufmerksamer Leser wird feststellen, dass wir eine NodeJS-Anwendung testen, und in den Screenshots - .NET ... Seien Sie nicht überrascht: Nur im Rahmen der Vorbereitung des Artikels gab es keine Fehler beim Testen der ersten Anwendung, aber sie wurden in einer anderen gefunden.Fazit
Anscheinend nichts kompliziertes!
Wenn Sie bereits einen Shell-Builder haben und dieser funktioniert und Sie keine Kubernetes benötigen, ist das Schrauben von Tests im Prinzip eine noch einfachere Aufgabe als hier beschrieben. In der
GitLab CI-Dokumentation finden Sie Beispiele für Ruby, Go, Gradle, Maven und einige andere.
PS
Lesen Sie auch in unserem Blog: