Skip to content

Commit

Permalink
Adds support to run lightweight kubernetes testcontainer using k3s (#313
Browse files Browse the repository at this point in the history
)

* Adds support to run lightweight kubernetes testcontainer using k3s
  • Loading branch information
ash1425 authored Nov 28, 2023
1 parent 6668ca4 commit 6ee98a3
Show file tree
Hide file tree
Showing 14 changed files with 379 additions and 104 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
- rabbitmq
- redis
- selenium
- k3s
runs-on: ${{ matrix.runtime.machine }}
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 @@ -35,6 +35,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
19 changes: 19 additions & 0 deletions k3s/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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",
"pyyaml"
],
python_requires=">=3.7",
)
65 changes: 65 additions & 0 deletions k3s/testcontainers/k3s/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#
# 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 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 @@ -20,6 +20,7 @@
-e file:rabbitmq
-e file:redis
-e file:selenium
-e file:k3s
cryptography<37
flake8<3.8.0 # 3.8.0 adds a dependency on importlib-metadata which conflicts with other packages.
pg8000
Expand Down
55 changes: 40 additions & 15 deletions requirements/macos-latest-3.10.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# testcontainers-clickhouse
# testcontainers-elasticsearch
# testcontainers-gcp
# testcontainers-k3s
# testcontainers-kafka
# testcontainers-keycloak
# testcontainers-localstack
Expand All @@ -40,6 +41,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,16 +93,17 @@ azure-storage-blob==12.19.0
# via testcontainers-azurite
babel==2.13.1
# via sphinx
boto3==1.29.1
boto3==1.33.1
# via testcontainers-localstack
botocore==1.32.1
botocore==1.33.1
# via
# boto3
# s3transfer
cachetools==5.3.2
# via google-auth
certifi==2023.7.22
certifi==2023.11.17
# via
# kubernetes
# minio
# opensearch-py
# requests
Expand Down Expand Up @@ -137,7 +141,7 @@ ecdsa==0.18.0
# via python-jose
entrypoints==0.3
# via flake8
exceptiongroup==1.1.3
exceptiongroup==1.2.0
# via
# pytest
# trio
Expand All @@ -149,7 +153,9 @@ google-api-core[grpc]==2.14.0
# google-api-core
# google-cloud-pubsub
google-auth==2.23.4
# via google-api-core
# via
# google-api-core
# kubernetes
google-cloud-pubsub==2.18.4
# via testcontainers-gcp
googleapis-common-protos[grpc]==1.61.0
Expand All @@ -161,20 +167,20 @@ greenlet==3.0.1
# via sqlalchemy
grpc-google-iam-v1==0.12.7
# via google-cloud-pubsub
grpcio==1.59.2
grpcio==1.59.3
# via
# google-api-core
# google-cloud-pubsub
# googleapis-common-protos
# grpc-google-iam-v1
# grpcio-status
grpcio-status==1.59.2
grpcio-status==1.59.3
# via
# google-api-core
# google-cloud-pubsub
h11==0.14.0
# via wsproto
idna==3.4
idna==3.6
# via
# requests
# trio
Expand All @@ -201,6 +207,8 @@ kafka-python==2.0.2
# via testcontainers-kafka
keyring==24.3.0
# via twine
kubernetes==28.1.0
# via testcontainers-k3s
markdown-it-py==3.0.0
# via rich
markupsafe==2.1.3
Expand All @@ -213,11 +221,15 @@ minio==7.2.0
# via testcontainers-minio
more-itertools==10.1.0
# via jaraco-classes
neo4j==5.14.1
neo4j==5.15.0
# via testcontainers-neo4j
nh3==0.2.14
# via readme-renderer
opensearch-py==2.4.1
oauthlib==3.2.2
# via
# kubernetes
# requests-oauthlib
opensearch-py==2.4.2
# via testcontainers-opensearch
outcome==1.3.0.post0
# via trio
Expand Down Expand Up @@ -248,7 +260,7 @@ protobuf==4.25.1
# proto-plus
psycopg2-binary==2.9.9
# via testcontainers-postgres
pyasn1==0.5.0
pyasn1==0.5.1
# via
# pyasn1-modules
# python-jose
Expand All @@ -263,7 +275,7 @@ pycryptodome==3.19.0
# via minio
pyflakes==2.1.1
# via flake8
pygments==2.16.1
pygments==2.17.2
# via
# readme-renderer
# rich
Expand All @@ -289,6 +301,7 @@ python-arango==7.8.1
python-dateutil==2.8.2
# via
# botocore
# kubernetes
# opensearch-py
# pg8000
python-jose==3.3.0
Expand All @@ -299,6 +312,10 @@ pytz==2023.3.post1
# via
# clickhouse-driver
# neo4j
pyyaml==6.0.1
# via
# kubernetes
# testcontainers-k3s
readme-renderer==42.0
# via twine
redis==5.0.1
Expand All @@ -308,12 +325,16 @@ requests==2.31.0
# azure-core
# docker
# google-api-core
# kubernetes
# opensearch-py
# python-arango
# python-keycloak
# requests-oauthlib
# requests-toolbelt
# sphinx
# twine
requests-oauthlib==1.3.1
# via kubernetes
requests-toolbelt==1.0.0
# via
# python-arango
Expand All @@ -327,7 +348,7 @@ rsa==4.9
# via
# google-auth
# python-jose
s3transfer==0.7.0
s3transfer==0.8.0
# via boto3
scramp==1.4.4
# via pg8000
Expand All @@ -338,6 +359,7 @@ six==1.16.0
# azure-core
# ecdsa
# isodate
# kubernetes
# opensearch-py
# python-dateutil
sniffio==1.3.0
Expand Down Expand Up @@ -395,6 +417,7 @@ urllib3[socks]==1.26.18
# via
# botocore
# docker
# kubernetes
# minio
# opensearch-py
# python-arango
Expand All @@ -403,8 +426,10 @@ urllib3[socks]==1.26.18
# testcontainers-core
# twine
websocket-client==1.6.4
# via docker
wheel==0.41.3
# via
# docker
# kubernetes
wheel==0.42.0
# via -r requirements.in
wrapt==1.16.0
# via testcontainers-core
Expand Down
Loading

0 comments on commit 6ee98a3

Please sign in to comment.