JUnit nan GitLab CI ak Kubernetes

Malgre lefèt ke tout moun konnen parfe byen ke tès lojisyèl ou a enpòtan ak nesesè, ak anpil moun yo te fè li otomatikman pou yon tan long, nan imansite a nan Habr pa te gen yon sèl resèt pou mete kanpe yon konbinezon de pwodwi sa yo popilè nan nich sa a kòm (pi renmen nou an) GitLab ak JUnit. Ann ranpli espas sa a!

JUnit nan GitLab CI ak Kubernetes

Entwodiksyon

Premyèman, kite m bay kèk kontèks:

  • Depi tout aplikasyon nou yo kouri sou Kubernetes, nou pral konsidere fè tès sou enfrastrikti apwopriye a.
  • Pou asanble ak deplwaman nou itilize werf (an tèm de eleman enfrastrikti, sa a tou otomatikman vle di ke Helm patisipe).
  • Mwen pa pral antre nan detay yo nan kreyasyon aktyèl la nan tès: nan ka nou an, kliyan an ekri tès yo tèt li, epi nou sèlman asire lansman yo (ak prezans nan yon rapò ki koresponn nan demann lan fizyone).


Ki jan sekans jeneral aksyon yo pral sanble?

  1. Bati aplikasyon an - nou pral omisyon deskripsyon etap sa a.
  2. Deplwaye aplikasyon an nan yon espas non separe nan gwoup Kubernetes la epi kòmanse tès la.
  3. Chèche zafè ak analize rapò JUnit ak GitLab.
  4. Efase yon espas non ki te kreye deja.

Koulye a, - nan aplikasyon!

ajisteman

GitLab CI

Ann kòmanse ak yon fragman .gitlab-ci.yaml, ki dekri deplwaye aplikasyon an ak fè tès yo. Lis la te tounen byen volumineuz, kidonk li te byen konplete ak kòmantè:

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

Koulye a, nan anyè a .helm/templates ann kreye YAML ak Job - tests-job.yaml — pou fè tès ak resous Kubernetes li bezwen yo. Gade eksplikasyon apre lis la:

{{- 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 }}

Ki kalite resous ki dekri nan konfigirasyon sa a? Lè deplwaye, nou kreye yon espas non inik pou pwojè a (sa a endike nan .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) epi woule li:

  1. ConfigMap ak script tès la;
  2. Job ak yon deskripsyon gous la ak direktiv espesifye a command, ki jis kouri tès yo;
  3. PV ak PVC, ki pèmèt ou estoke done tès yo.

Peye atansyon sou kondisyon entwodiksyon ak if nan kòmansman manifest la - kòmsadwa, lòt dosye YAML nan tablo Helm ak aplikasyon an dwe vlope nan ranvèse konsepsyon pou yo pa jwenn deplwaye pandan tès la. Sa vle di:

{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}

Sepandan, si tès yo mande kèk enfrastrikti (pa egzanp, Redis, RabbitMQ, Mongo, PostgreSQL...) - YAML yo ka pa gen okenn fèmen. Deplwaye yo nan yon anviwònman tès tou... ajiste yo jan ou wè anfòm, nan kou.

manyen final la

Paske asanble ak deplwaman lè l sèvi avèk werf travay pou kounye a sèlman sou sèvè bati a (ak gitlab-runner), epi gous la ak tès yo lanse sou mèt la, w ap bezwen kreye yon anyè. /mnt/tests sou mèt la epi bay kourè a li, pou egzanp, atravè NFS. Ou ka jwenn yon egzanp detaye ak eksplikasyon nan K8s dokiman.

Rezilta a pral:

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

Pèsonn pa entèdi fè yon pataje NFS dirèkteman sou gitlab-runner, ak Lè sa a, monte li nan gous.

Note

Ou ka mande poukisa konplike tout bagay lè w kreye yon travay si ou ka tou senpleman kouri yon script ak tès dirèkteman sou kourè koki a? Repons lan trè trivial...

Gen kèk tès ki mande aksè nan enfrastrikti a (MongoDB, RabbitMQ, PostgreSQL, elatriye) pou verifye ke yo travay kòrèkteman. Nou fè tès inifye - ak apwòch sa a, li vin fasil pou enkli antite adisyonèl sa yo. Anplis sa a, nou jwenn estanda apwòch deplwaman (menm si w ap itilize NFS, aliye adisyonèl nan repèrtwar).

Rezilta

Kisa nou pral wè lè nou aplike konfigirasyon prepare a?

Demann fizyon an pral montre estatistik rezime pou tès ki fèt nan dènye tiyo li yo:

JUnit nan GitLab CI ak Kubernetes

Chak erè ka klike isit la pou plis detay:

JUnit nan GitLab CI ak Kubernetes

NB: Lektè atantif la ap remake ke n ap teste yon aplikasyon NodeJS, ak nan ekran yo - .NET... Ou pa bezwen sezi: se jis ke pandan y ap prepare atik la, yo pa jwenn okenn erè nan tès premye aplikasyon an, men yo yo te jwenn nan yon lòt.

Konklizyon

Kòm ou ka wè, pa gen anyen konplike!

Nan prensip, si ou deja gen yon pèseptè koki epi li travay, men ou pa bezwen Kubernetes, tache tès li yo pral yon travay menm pi senp pase sa ki dekri isit la. Ak nan GitLab CI dokiman w ap jwenn egzanp pou Ruby, Go, Gradle, Maven ak kèk lòt.

PS

Li tou sou blog nou an:

Sous: www.habr.com

Add nouvo kòmantè