cki_lib.inttests

Run integration tests via kind

Integration tests that require other services can be run via kind (Kubernetes in Docker).

Currently, a RabbitMQ service including certificates is implemented.

Creating an integration test

For an integration test, create a unit test in /inttests, and derive the TestCase from cki_lib.inttests.cluster.KubernetesCluster or one of the derived classes. As an example, for an integration test that needs a RabbitMQ server:

from cki_lib.inttests import cluster
from cki_lib.inttests.rabbitmq import RabbitMQServer

@cluster.skip_without_requirements()
class TestStompClient(RabbitMQServer):
    pass

During setupClass(), a Kubernetes cluster and any required services will be created and configured. The services will be removed in tearDownClass(), but the cluster will be kept running to speed up successive tests.

API

KubernetesCluster - Kubernetes cluster running in Docker

  • test case class: inttests.cluster.KubernetesCluster

The cluster is able to run locally and in GitLab CI as long as the kind and docker executables are available. This can be checked with the @skip_without_requirements decorator. For GitLab CI, jobs need to have the Docker-in-Docker service configured in .gitlab-ci.yml:

integration-tests:
  services:
    - docker:dind
  variables:
    DOCKER_HOST: 'tcp://docker:2375'

Fields provided by KubernetesCluster

  • cls.cluster_name: str: kind cluster name
  • cls.hostname: str: host name for all services usable locally and in GitLab CI
  • cls.api_client: client.ApiClient: Python kubernetes API client
  • cls.dynamic_client: dynamic.client.DynamicClient: Python kubernetes Dynamic client
  • cls.core_v1: client.CoreV1Api: Python kubernetes Core v1 API

Methods provided by KubernetesCluster

  • cls.k8s_namespace(namespace: str): context manager that creates and cleans up a Kubernetes namespace.
  • cls.k8s_apply(namespace: str, body: typing.Any): apply a Kubernetes resource update via server-side apply.
  • cls.k8s_delete_all(namespace: str, resources: Iterable[str]): delete all resources of the given types from a namespace.

RabbitMQServer - RabbitMQ server

  • test case class: inttests.rabbitmq.RabbitMQServer

The server is configured with AMQP and STOMP access with and without TLS. For AMQP and STOMP, authentication can be done via password and client certificates (from cert.common_name) methods.

Ports provided by RabbitMQServer

Port Description
5671 AMQP via TLS
5672 AMQP
15671 HTTP management API via TLS
15672 HTTP management API
61613 Stomp
61614 Stomp via TLS

Test environment variables provided by RabbitMQServer

Name Description
RABBITMQ_HOST host name
RABBITMQ_PORT 5671
RABBITMQ_MANAGEMENT_PORT 15671
RABBITMQ_VIRTUAL_HOST /
RABBITMQ_USER guest
RABBITMQ_PASSWORD guest
RABBITMQ_CAFILE CA certificate
RABBITMQ_CERTFILE client key and certificate
STOMP_HOST host name
STOMP_PORT 61614
STOMP_CERTFILE client key and certificate

HashiCorpVaultServer - HashiCorp Vault server

  • test case class: inttests.vault.HashiCorpVaultServer

The server is started in dev mode.

Ports provided by HashiCorpVaultServer

Port Description
8200 HTTP API

Test environment variables provided by HashiCorpVaultServer

Name Description
VAULT_ADDR server URL
VAULT_MOUNT_POINT mount point of the KV2 engine
VAULT_TOKEN secret token ID

MariaDBServer - MariaDB server

  • test case class: inttests.mariadb.MariaDBServer

The database db is created on startup.

Ports provided by MariaDBServer

Port Description
3306 SQL

Test environment variables provided by MariaDBServer

Name Description
MARIADB_HOST host name
MARIADB_PORT 3306
MARIADB_USER root
MARIADB_PASSWORD root
MARIADB_DATABASE db

MinioServer - Minio server

  • test case class: inttests.minio.MinioServer

Ports provided by MinioServer

Port Description
9000 S3

Test environment variables provided by MinioServer

Name Description
AWS_ENDPOINT_URL server URL
AWS_DEFAULT_REGION us-east-1
AWS_ACCESS_KEY_ID access-key
AWS_SECRET_ACCESS_KEY secret-key

RemoteResponsesServer - Remote Responses server

  • test case class: inttests.remote_responses.RemoteResponsesServer

Provides a remote URL request mocking endpoint with an API similar to responses.

Fields provided by RemoteResponsesServer

The cls.responses field contains a wrapper class with a subset of the responses API:

  • add(method, url, body=None, json=None, status=200, headers=None, match=[...])
  • replace(method, url, body=None, json=None, status=200, headers=None, match=[...])
  • upsert(method, url, body=None, json=None, status=200, headers=None, match=[...])
  • reset()
  • json_params_matcher(params, strict_match=True)
  • calls

The return values of add/replace/upsert are proxied BaseResponse instances and provide all expected fields:

  • call_count
  • calls

This allows to set up test beds with

# GET request
data = {'a': 'b'}
self.responses.add('GET', f'http://ignored/pooh', json=data)

# POST request with match filter
data = {'data': {'namespace': {'fullPath': 'foo', 'rootStorageStatistics': {
    'repositorySize': 1}, 'projects': {'nodes': []}, }}}
self.responses.add('POST', f'http://ignored/api/graphql', json=data, match=[
    self.responses.json_params_matcher({"operationName": "pooh"}, strict_match=False)])

The mocked responses are available from REMOTE_RESPONSES_URL_EXTERNAL when accessed from the test bed, or from REMOTE_RESPONSES_URL when accessed from other services inside the cluster.

The mocked responses are reset before each test. For sub tests, responses can be manually reset via self.responses.reset() as well.

Ports provided by RemoteResponsesServer

Port Description
7999 Responses

Test environment variables provided by RemoteResponsesServer

Name Description
REMOTE_RESPONSES_URL server URL reachable from other services deployed in the cluster
REMOTE_RESPONSES_URL_EXTERNAL server URL reachable from the test itself