Skip to content

Adds support to run lightweight kubernetes testcontainer using k3s #313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
- rabbitmq
- redis
- selenium
- k3s
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ testcontainers-python facilitates the use of Docker containers for functional an
rabbitmq/README
redis/README
selenium/README
k3s/README

Getting Started
---------------
Expand Down
1 change: 1 addition & 0 deletions k3s/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. autoclass:: testcontainers.k3s.K3SContainer
18 changes: 18 additions & 0 deletions k3s/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from setuptools import setup, find_namespace_packages

description = "K3S component of testcontainers-python."

setup(
name="testcontainers-k3s",
version="0.0.1rc1",
packages=find_namespace_packages(),
description=description,
long_description=description,
long_description_content_type="text/x-rst",
url="https://github.com/testcontainers/testcontainers-python",
install_requires=[
"testcontainers-core",
"kubernetes"
],
python_requires=">=3.7",
)
67 changes: 67 additions & 0 deletions k3s/testcontainers/k3s/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from testcontainers.core.config import MAX_TRIES
from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_for_logs


class K3SContainer(DockerContainer):
"""
K3S container.

Example:

.. doctest::

>>> import json
>>> import urllib
>>> import yaml
>>> from testcontainers.k3s import K3SContainer
>>> from kubernetes import client, config

>>> with K3SContainer() as k3s:
... config.load_kube_config_from_dict(yaml.safe_load(k3s.config_yaml()))
... pod = client.CoreV1Api().list_pod_for_all_namespaces(limit=1)
... assert len(pod.items) > 0, "Unable to get running nodes from k3s cluster"
"""

KUBE_SECURE_PORT = 6443
RANCHER_WEBHOOK_PORT = 8443

def __init__(self, image="rancher/k3s:latest", **kwargs) -> None:
super(K3SContainer, self).__init__(image, **kwargs)
self.with_exposed_ports(self.KUBE_SECURE_PORT, self.RANCHER_WEBHOOK_PORT)
self.with_env("K3S_URL", f'https://{self.get_container_host_ip()}:{self.KUBE_SECURE_PORT}')
self.with_command("server --disable traefik --tls-san=" + self.get_container_host_ip())
self.with_kwargs(privileged=True, tmpfs={"/run": "", "/var/run": ""})
self.with_volume_mapping("/sys/fs/cgroup", "/sys/fs/cgroup", "rw")

def _connect(self) -> None:
wait_for_logs(self, predicate="Node controller sync successful", timeout=MAX_TRIES)

def start(self) -> "K3SContainer":
super().start()
self._connect()
return self

def config_yaml(self) -> str:
"""This function returns the kubernetes config yaml which can be used
to initialise k8s client
"""
execution = self.get_wrapped_container().exec_run(['cat', '/etc/rancher/k3s/k3s.yaml'])
config_yaml = execution.output.decode('utf-8') \
.replace(f'https://127.0.0.1:{self.KUBE_SECURE_PORT}',
f'https://{self.get_container_host_ip()}:'
f'{self.get_exposed_port(self.KUBE_SECURE_PORT)}')
return config_yaml
12 changes: 12 additions & 0 deletions k3s/tests/test_k3s.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# The versions below were the current supported versions at time of writing (2022-08-11)
import yaml
from kubernetes import client, config

from testcontainers.k3s import K3SContainer


def test_docker_run_k3s():
with K3SContainer() as k3s:
config.load_kube_config_from_dict(yaml.safe_load(k3s.config_yaml()))
pod = client.CoreV1Api().list_pod_for_all_namespaces(limit=1)
assert len(pod.items) > 0, "Unable to get running nodes from k3s cluster"
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
-e file:rabbitmq
-e file:redis
-e file:selenium
-e file:k3s
codecov>=2.1.0
cryptography<37
flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages.
Expand Down
79 changes: 47 additions & 32 deletions requirements/3.10.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# testcontainers-compose
# testcontainers-elasticsearch
# testcontainers-gcp
# testcontainers-k3s
# testcontainers-kafka
# testcontainers-keycloak
# testcontainers-localstack
Expand All @@ -43,6 +44,8 @@
# via -r requirements.in
-e file:google
# via -r requirements.in
-e file:k3s
# via -r requirements.in
-e file:kafka
# via -r requirements.in
-e file:keycloak
Expand Down Expand Up @@ -90,12 +93,10 @@ attrs==22.2.0
# pytest
# trio
azure-core==1.26.3
# via
# azure-storage-blob
# msrest
azure-storage-blob==12.14.1
# via azure-storage-blob
azure-storage-blob==12.15.0
# via testcontainers-azurite
babel==2.11.0
babel==2.12.1
# via sphinx
bcrypt==4.0.1
# via paramiko
Expand All @@ -105,22 +106,22 @@ cachetools==5.3.0
# via google-auth
certifi==2022.12.7
# via
# kubernetes
# minio
# msrest
# opensearch-py
# requests
# selenium
cffi==1.15.1
# via
# cryptography
# pynacl
charset-normalizer==3.0.1
charset-normalizer==3.1.0
# via requests
clickhouse-driver==0.2.5
# via testcontainers-clickhouse
codecov==2.1.12
# via -r requirements.in
coverage[toml]==7.1.0
coverage[toml]==7.2.1
# via
# codecov
# pytest-cov
Expand All @@ -132,6 +133,8 @@ cryptography==36.0.2
# secretstorage
cx-oracle==8.3.0
# via testcontainers-oracle
deprecation==2.1.0
# via python-keycloak
distro==1.8.0
# via docker-compose
dnspython==2.3.0
Expand Down Expand Up @@ -162,8 +165,10 @@ flake8==3.7.9
# via -r requirements.in
google-api-core[grpc]==2.11.0
# via google-cloud-pubsub
google-auth==2.16.0
# via google-api-core
google-auth==2.16.2
# via
# google-api-core
# kubernetes
google-cloud-pubsub==1.7.2
# via testcontainers-gcp
googleapis-common-protos[grpc]==1.58.0
Expand All @@ -175,7 +180,7 @@ greenlet==2.0.2
# via sqlalchemy
grpc-google-iam-v1==0.12.6
# via google-cloud-pubsub
grpcio==1.51.1
grpcio==1.51.3
# via
# google-api-core
# googleapis-common-protos
Expand All @@ -198,7 +203,7 @@ importlib-metadata==6.0.0
iniconfig==2.0.0
# via pytest
isodate==0.6.1
# via msrest
# via azure-storage-blob
jaraco-classes==3.2.3
# via keyring
jeepney==0.8.0
Expand All @@ -213,7 +218,9 @@ kafka-python==2.0.2
# via testcontainers-kafka
keyring==23.13.1
# via twine
markdown-it-py==2.1.0
kubernetes==26.1.0
# via testcontainers-k3s
markdown-it-py==2.2.0
# via rich
markupsafe==2.1.2
# via jinja2
Expand All @@ -223,20 +230,19 @@ mdurl==0.1.2
# via markdown-it-py
minio==7.1.13
# via testcontainers-minio
more-itertools==9.0.0
more-itertools==9.1.0
# via jaraco-classes
msrest==0.7.1
# via azure-storage-blob
neo4j==5.5.0
neo4j==5.6.0
# via testcontainers-neo4j
oauthlib==3.2.2
# via requests-oauthlib
opensearch-py==2.1.1
opensearch-py==2.2.0
# via testcontainers-opensearch
outcome==1.2.0
# via trio
packaging==23.0
# via
# deprecation
# docker
# pytest
# sphinx
Expand Down Expand Up @@ -291,31 +297,35 @@ pyrsistent==0.19.3
# via jsonschema
pysocks==1.7.1
# via urllib3
pytest==7.2.1
pytest==7.2.2
# via
# -r requirements.in
# pytest-cov
pytest-cov==4.0.0
# via -r requirements.in
python-arango==7.5.6
python-arango==7.5.7
# via testcontainers-arangodb
python-dateutil==2.8.2
# via pg8000
# via
# kubernetes
# opensearch-py
# pg8000
python-dotenv==0.21.1
# via docker-compose
python-jose==3.3.0
# via python-keycloak
python-keycloak==2.12.0
python-keycloak==2.13.2
# via testcontainers-keycloak
pytz==2022.7.1
# via
# babel
# clickhouse-driver
# neo4j
pytz-deprecation-shim==0.1.0.post0
# via tzlocal
pyyaml==5.4.1
# via docker-compose
# via
# docker-compose
# kubernetes
readme-renderer==37.3
# via twine
redis==4.5.1
Expand All @@ -327,7 +337,7 @@ requests==2.28.2
# docker
# docker-compose
# google-api-core
# msrest
# kubernetes
# opensearch-py
# python-arango
# python-keycloak
Expand All @@ -336,15 +346,15 @@ requests==2.28.2
# sphinx
# twine
requests-oauthlib==1.3.1
# via msrest
requests-toolbelt==0.9.1
# via kubernetes
requests-toolbelt==0.10.1
# via
# python-arango
# python-keycloak
# twine
rfc3986==2.0.0
# via twine
rich==13.3.1
rich==13.3.2
# via twine
rsa==4.9
# via
Expand All @@ -354,7 +364,7 @@ scramp==1.4.4
# via pg8000
secretstorage==3.3.3
# via keyring
selenium==4.8.0
selenium==4.8.2
# via testcontainers-selenium
six==1.16.0
# via
Expand All @@ -365,6 +375,8 @@ six==1.16.0
# google-auth
# isodate
# jsonschema
# kubernetes
# opensearch-py
# python-dateutil
# websocket-client
sniffio==1.3.0
Expand All @@ -387,7 +399,7 @@ sphinxcontrib-qthelp==1.0.3
# via sphinx
sphinxcontrib-serializinghtml==1.1.5
# via sphinx
sqlalchemy==2.0.3
sqlalchemy==2.0.5.post1
# via
# testcontainers-mssql
# testcontainers-mysql
Expand All @@ -410,6 +422,7 @@ twine==4.0.2
typing-extensions==4.5.0
# via
# azure-core
# azure-storage-blob
# sqlalchemy
tzdata==2022.7
# via pytz-deprecation-shim
Expand All @@ -418,6 +431,7 @@ tzlocal==4.2
urllib3[socks]==1.26.14
# via
# docker
# kubernetes
# minio
# opensearch-py
# python-arango
Expand All @@ -431,13 +445,14 @@ websocket-client==0.59.0
# via
# docker
# docker-compose
# kubernetes
wheel==0.38.4
# via -r requirements.in
wrapt==1.14.1
wrapt==1.15.0
# via testcontainers-core
wsproto==1.2.0
# via trio-websocket
zipp==3.13.0
zipp==3.15.0
# via importlib-metadata

# The following packages are considered to be unsafe in a requirements file:
Expand Down
Loading