GitLab CI میں JUnit Kubernetes کے ساتھ

اس حقیقت کے باوجود کہ ہر کوئی بخوبی جانتا ہے کہ آپ کے سافٹ ویئر کی جانچ ضروری اور ضروری ہے، اور بہت سے لوگ ایک طویل عرصے سے یہ خود بخود کر رہے ہیں، حبر کی وسعت میں اس طرح کی مقبول مصنوعات کا مجموعہ ترتیب دینے کا کوئی نسخہ نہیں تھا۔ یہ جگہ بطور (ہماری پسندیدہ) GitLab اور JUnit۔ آئیے اس خلا کو پُر کریں!

GitLab CI میں JUnit Kubernetes کے ساتھ

تعارفی

پہلے، میں کچھ سیاق و سباق بتاتا ہوں:

  • چونکہ ہماری تمام ایپلیکیشنز Kubernetes پر چلتی ہیں، اس لیے ہم مناسب انفراسٹرکچر پر ٹیسٹ چلانے پر غور کریں گے۔
  • اسمبلی اور تعیناتی کے لیے ہم استعمال کرتے ہیں۔ werf (بنیادی ڈھانچے کے اجزاء کے لحاظ سے، اس کا خود بخود مطلب یہ ہے کہ ہیلم اس میں شامل ہے)۔
  • میں ٹیسٹوں کی اصل تخلیق کی تفصیلات میں نہیں جاؤں گا: ہمارے معاملے میں، کلائنٹ خود ٹیسٹ لکھتا ہے، اور ہم صرف ان کے لانچ (اور انضمام کی درخواست میں متعلقہ رپورٹ کی موجودگی) کو یقینی بناتے ہیں۔


اعمال کی عمومی ترتیب کیسی نظر آئے گی؟

  1. درخواست کی تعمیر - ہم اس مرحلے کی تفصیل کو چھوڑ دیں گے۔
  2. درخواست کو Kubernetes کلسٹر کے الگ نام کی جگہ پر تعینات کریں اور جانچ شروع کریں۔
  3. نمونے کی تلاش اور GitLab کے ساتھ JUnit رپورٹس کو پارس کرنا۔
  4. پہلے سے بنائی گئی نام کی جگہ کو حذف کرنا۔

اب - نفاذ کے لئے!

ایڈجسٹمنٹ

گٹ لیب سی آئی

آئیے ایک ٹکڑے سے شروع کرتے ہیں۔ .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 بنائیں - tests-job.yaml - ٹیسٹ چلانے کے لیے اور کوبرنیٹس کے وسائل کی ضرورت ہے۔ فہرست کے بعد وضاحتیں دیکھیں:

{{- 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}) اور اسے رول آؤٹ کریں:

  1. کنفیگ میپ ٹیسٹ اسکرپٹ کے ساتھ؛
  2. ایوب پوڈ کی تفصیل اور مخصوص ہدایت کے ساتھ command, جو صرف ٹیسٹ چلاتا ہے؛
  3. پی وی اور پیویسی، جو آپ کو ٹیسٹ ڈیٹا ذخیرہ کرنے کی اجازت دیتا ہے۔

کے ساتھ تعارفی حالت پر توجہ دینا if مینی فیسٹ کے شروع میں - اس کے مطابق، درخواست کے ساتھ ہیلم چارٹ کی دیگر YAML فائلوں کو لپیٹنا ضروری ہے معکوس ڈیزائن تاکہ وہ جانچ کے دوران تعینات نہ ہوں۔ یہ ہے کہ:

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

تاہم، اگر ٹیسٹ کچھ بنیادی ڈھانچے کی ضرورت ہے (مثال کے طور پر، Redis، RabbitMQ، Mongo، PostgreSQL...) - ان کے YAMLs ہوسکتے ہیں کوئی بند کرو. انہیں آزمائشی ماحول میں بھی تعینات کریں... جیسا کہ آپ مناسب سمجھیں انہیں ایڈجسٹ کریں۔

آخری ٹچ

کیونکہ اسمبلی اور تعیناتی werf کا استعمال کرتے ہوئے ابھی کے لئے کام کرتا ہے۔ صرف بلڈ سرور پر (گٹ لیب رنر کے ساتھ)، اور ٹیسٹ کے ساتھ پوڈ ماسٹر پر لانچ کیا جاتا ہے، آپ کو ایک ڈائرکٹری بنانے کی ضرورت ہوگی /mnt/tests ماسٹر پر اور رنر کو دے دو، مثال کے طور پر، NFS کے ذریعے. وضاحت کے ساتھ ایک تفصیلی مثال میں مل سکتی ہے۔ K8s دستاویزات.

نتیجہ یہ ہوگا:

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

Gitlab-runner پر براہ راست NFS شیئر کرنے، اور پھر اسے pods میں نصب کرنے سے کوئی بھی منع نہیں کرتا ہے۔

نوٹ

آپ پوچھ رہے ہوں گے کہ اگر آپ شیل رنر پر براہ راست ٹیسٹ کے ساتھ اسکرپٹ چلا سکتے ہیں تو نوکری بنا کر ہر چیز کو کیوں پیچیدہ بنائیں؟ جواب بہت معمولی ہے...

کچھ ٹیسٹوں کے لیے بنیادی ڈھانچے تک رسائی کی ضرورت ہوتی ہے (MongoDB، RabbitMQ، PostgreSQL، وغیرہ) اس بات کی تصدیق کے لیے کہ وہ صحیح طریقے سے کام کرتے ہیں۔ ہم ٹیسٹنگ کو متحد بناتے ہیں - اس نقطہ نظر کے ساتھ، اس طرح کے اضافی اداروں کو شامل کرنا آسان ہو جاتا ہے۔ اس کے علاوہ، ہم حاصل کرتے ہیں معیار تعیناتی اپروچ (چاہے این ایف ایس استعمال کر رہے ہوں، ڈائرکٹریوں کا اضافی اضافہ)۔

نتیجہ

جب ہم تیار کردہ ترتیب کو لاگو کریں گے تو ہم کیا دیکھیں گے؟

انضمام کی درخواست اپنی تازہ ترین پائپ لائن میں چلائے جانے والے ٹیسٹوں کے خلاصے کے اعدادوشمار دکھائے گی:

GitLab CI میں JUnit Kubernetes کے ساتھ

ہر غلطی کو تفصیلات کے لیے یہاں کلک کیا جا سکتا ہے:

GitLab CI میں JUnit Kubernetes کے ساتھ

NB: توجہ دینے والا قارئین یہ محسوس کرے گا کہ ہم ایک نوڈ جے ایس ایپلی کیشن کی جانچ کر رہے ہیں، اور اسکرین شاٹس میں - NET... حیران نہ ہوں: یہ صرف اتنا ہے کہ مضمون کی تیاری کے دوران، پہلی ایپلیکیشن کی جانچ میں کوئی غلطی نہیں پائی گئی، لیکن وہ دوسرے میں پائے گئے۔

حاصل يہ ہوا

جیسا کہ آپ دیکھ سکتے ہیں، کچھ بھی پیچیدہ نہیں ہے!

اصولی طور پر، اگر آپ کے پاس پہلے سے ہی شیل کلیکٹر ہے اور یہ کام کرتا ہے، لیکن آپ کو کوبرنیٹس کی ضرورت نہیں ہے، تو اس کے ساتھ ٹیسٹنگ کو منسلک کرنا یہاں بیان کیے جانے سے کہیں زیادہ آسان کام ہوگا۔ اور میں GitLab CI دستاویزات آپ کو روبی، گو، گریڈل، ماون اور کچھ دیگر کی مثالیں ملیں گی۔

PS

ہمارے بلاگ پر بھی پڑھیں:

ماخذ: www.habr.com

نیا تبصرہ شامل کریں