JUnit no IC do GitLab com o Kubernetes

Apesar de todos saberem que é importante e necessário testar seu software e muitos o fazem automaticamente há muito tempo, não há uma receita única para configurar um monte de produtos populares nesse nicho, como o (amado) GitLab e JUnit . Preencha esta lacuna!



Introdutório


Primeiro, descreverei o contexto:

  • Como todos os nossos aplicativos funcionam no Kubernetes, consideraremos a execução de testes na infraestrutura apropriada.
  • Para montagem e implantação, usamos o werf (no sentido de componentes da infraestrutura, isso também significa automaticamente que o Helm está envolvido).
  • Não vou entrar nos detalhes da criação direta de testes: no nosso caso, o cliente grava os testes por conta própria e só garantimos que eles sejam executados (e o relatório correspondente está disponível na solicitação de mesclagem).

Como será a sequência geral de ações?

  1. Montagem do aplicativo - omitiremos a descrição deste estágio.
  2. Implante o aplicativo em um espaço para nome separado do cluster Kubernetes e inicie o teste.
  3. Procure artefatos e analise um relatório JUnit do GitLab.
  4. Exclua o espaço para nome criado anteriormente.

Agora - para a implementação!

Personalização


Gitlab ci


Vamos começar com o fragmento .gitlab-ci.yaml descrevendo a implantação do aplicativo e executando os testes. A listagem acabou sendo bastante volumosa, portanto, é completamente complementada com comentários:

 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


Agora, no diretório .helm/templates , crie um YAML com Job - tests-job.yaml - para executar os testes e os recursos do Kubernetes necessários. Veja as explicações após a listagem:

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

Quais recursos são descritos nesta configuração? Ao implantar, crie um namespace exclusivo para o projeto (isso também é indicado em .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG} ) e tests-${CI_COMMIT_REF_SLUG} -o nele:

  1. ConfigMap com um script de teste;
  2. Trabalho com uma descrição do pod e a diretiva de command especificada, que apenas executa os testes;
  3. PV e PVC , que permite armazenar dados de teste.

Preste atenção à condição introdutória com if no início do manifesto - portanto, outros arquivos YAML do gráfico Helm com o aplicativo devem ser agrupados na construção reversa para que não sejam implantados durante o teste. Isto é:

 {{- if ne .Values.global.run_tests "yes" }} ---    {{- end }} 

No entanto, se os testes exigirem alguma infraestrutura (por exemplo, Redis, RabbitMQ, Mongo, PostgreSQL ...) - seus YAMLs poderão ser desativados. Implante-os em um ambiente de teste ... é claro, aprimorando como quiser.

Toque final


Porque Até agora, como a montagem e a implantação usando werf funcionam no servidor de construção (com gitlab-runner) e o pod com testes é executado no assistente, você precisa criar o diretório /mnt/tests no assistente e entregá-lo ao corredor, por exemplo, via NFS . Um exemplo detalhado com explicações pode ser encontrado na documentação do K8s .

O resultado será:

 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 

Ninguém proíbe fazer uma bola NFS diretamente no corredor do gitlab e montá-la em cápsulas.

Nota


Você pode perguntar, por que complicar tudo com a criação de Job, se você pode simplesmente executar o script de teste diretamente no shell runner? A resposta é bastante trivial ...

Alguns testes requerem acesso à infraestrutura (MongoDB, RabbitMQ, PostgreSQL, etc.) para verificar a correção de trabalhar com eles. Tornamos os testes unificados - com essa abordagem, fica fácil incluir essas entidades adicionais. Além disso, temos uma abordagem padrão na implantação (mesmo se estiver usando o NFS, montagem adicional de diretório).

Resultado


O que veremos quando aplicarmos a configuração preparada?

A solicitação de mesclagem mostrará estatísticas resumidas dos testes lançados em seu último pipeline:



Você pode clicar em cada erro aqui para obter detalhes:



Nota : Um leitor atento notará que estamos testando um aplicativo NodeJS e nas capturas de tela - .NET ... Não se surpreenda: assim como parte da preparação do artigo, não houve erros ao testar o primeiro aplicativo, mas foram encontrados em outro.

Conclusão


Aparentemente, nada complicado!

Em princípio, se você já possui um construtor de shell e ele funciona, e não precisa do Kubernetes, executar o teste com ele será uma tarefa ainda mais simples do que a descrita aqui. E na documentação do GitLab CI, você encontrará exemplos para Ruby, Go, Gradle, Maven e outros.

PS


Leia também em nosso blog:

Source: https://habr.com/ru/post/pt460897/


All Articles