Skip to content
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

Enable compliance tests to use plugins for cluster provisioning #753

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a326e99
Enable compliance tests to use plugins for cluster provisioning
tonifinger Sep 18, 2024
ab0803b
Fix python style
tonifinger Sep 18, 2024
199fdf2
Fix path to junit file
tonifinger Sep 20, 2024
f994cde
Inital integration of the plugin into the 'scs-test-runner.py'
tonifinger Oct 7, 2024
4a51c0d
Adjust 'scs-test-runner.py' to handle kaas tests
tonifinger Oct 10, 2024
928ea83
Add ability to deploy clusters for different K8s versions
tonifinger Oct 14, 2024
acc451d
Update `scs-test-runner.py`
tonifinger Oct 18, 2024
e4d6c3c
still draft: a few steps towards my vision
mbuechse Oct 23, 2024
1dd220c
add missing file
mbuechse Oct 23, 2024
7c063d8
Fixup!
tonifinger Oct 23, 2024
5b11641
Fixup plugin kubeconfig file generation
tonifinger Oct 24, 2024
7fb5f32
Draft: split up clusterspec file
tonifinger Nov 4, 2024
9a2809a
Merge to back to usage of one single clustersspec
tonifinger Nov 4, 2024
bfa6ba4
Rearange configuration files
tonifinger Nov 5, 2024
1cfee94
Fixup configuration files
tonifinger Nov 5, 2024
9f7fe4c
Fixup configuration files
tonifinger Nov 5, 2024
3e5357a
revert sonobuoy executor handling form this PR
tonifinger Nov 5, 2024
99c450f
Apply cluster configuration file handling
tonifinger Nov 5, 2024
5adb9f6
Update Tests/kaas/plugin/run_plugin.py
tonifinger Nov 5, 2024
2127323
Update Tests/config.toml
tonifinger Nov 5, 2024
5392c5e
Fixup
tonifinger Nov 5, 2024
73edf99
Restructure abstract method handling in plugin
tonifinger Nov 6, 2024
90d958e
Add example plugin implementation to interface.py
tonifinger Nov 6, 2024
fc78a7c
Fixup: change kube_plugin_config directory name
tonifinger Nov 6, 2024
76134d6
Fixup: missing adjustment to config.toml
tonifinger Nov 6, 2024
cfa113c
Remove abstract base clase handling and directly
tonifinger Nov 7, 2024
d43a38d
Update 'kind_plugin.py' kubeconfig filepath handling
tonifinger Nov 7, 2024
8f4970b
Sort python imports
tonifinger Nov 7, 2024
5a8f8ca
Fixup: use '__name__' for logging handler
tonifinger Nov 7, 2024
7d6a0cb
Update Tests/kaas/plugin/run_plugin.py
tonifinger Nov 7, 2024
c4b2611
Update plugin kubeconfig handling
tonifinger Nov 7, 2024
78fd694
Update plugin_static.py
tonifinger Nov 7, 2024
2301ba3
Fixup remove obsolete f-string
tonifinger Nov 7, 2024
fa0247e
Fixup
tonifinger Nov 8, 2024
932ed7a
Fixup: remove default value in description
tonifinger Nov 8, 2024
a96047d
Fixup: remove version parameter form delete
tonifinger Nov 11, 2024
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
42 changes: 41 additions & 1 deletion Tests/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,51 @@ subjects = [
workers = 4


[presets.all-kaas]
scopes = [
"scs-compatible-kaas",
]
subjects = [
"cspA-current",
"cspA-current-1",
"cspA-current-2",
]
workers = 4


[scopes.scs-compatible-iaas]
spec = "./scs-compatible-iaas.yaml"


[scopes.scs-compatible-kaas]
spec = "./scs-compatible-kaas.yaml"


# default subject (not a real subject, but used to declare a default mapping)
# (this is the only mapping declaration that supports using Python string interpolation)
[subjects._.mapping]
os_cloud = "{subject}"
os_cloud = "{subject}"
subject_root = "{subject}"


[subjects._.kubernetes_setup]
clusterspec = "kaas/clusterspec.yaml"


[subjects.cspA-current.kubernetes_setup]
kube_plugin = "kind"
kube_plugin_config = "../playbooks/k8s_configs/kind_config.yaml"
clusterspec_cluster = "current-k8s-release"
tonifinger marked this conversation as resolved.
Show resolved Hide resolved


[subjects.cspA-current-1.kubernetes_setup]
kube_plugin = "kind"
kube_plugin_config = "../playbooks/k8s_configs/kind_config.yaml"
clusterspec_cluster = "current-k8s-release-1"


[subjects.cspA-current-2.kubernetes_setup]
kube_plugin = "kind"
kube_plugin_config = "../playbooks/k8s_configs/kind_config.yaml"
clusterspec_cluster = "current-k8s-release-2"

11 changes: 11 additions & 0 deletions Tests/kaas/clusterspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# this file specifies all clusters that have to be provisioned for the tests to run
clusters:
current-k8s-release:
branch: "1.31"
kubeconfig: kubeconfig.yaml
current-k8s-release-1:
branch: "1.30"
kubeconfig: kubeconfig.yaml
current-k8s-release-2:
branch: "1.29"
kubeconfig: kubeconfig.yaml
39 changes: 39 additions & 0 deletions Tests/kaas/plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Plugin for provisioning k8s clusters and performing conformance tests on these clusters

## Development environment

### requirements

* [docker](https://docs.docker.com/engine/install/)
* [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
* [sonobuoy](https://sonobuoy.io/docs/v0.57.1/#installation)

### setup for development

1. Generate python 3.10 env

```bash
sudo apt-get install python3.10-dev
virtualenv -p /usr/bin/python3.10 venv
echo "*" >> venv/.gitignore
source venv/bin/activate
(venv) curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10
(venv) python3.10 -m pip install --upgrade pip
(venv) python3.10 -m pip --version

```

2. Install dependencies:

```bash
(venv) pip install pip-tools
(venv) pip-compile requirements.in
(venv) pip-sync requirements.txt
```

3. Set environment variables and launch the process:

```bash
(venv) export CLUSTER_PROVIDER="kind"
(venv) python run.py
```
55 changes: 55 additions & 0 deletions Tests/kaas/plugin/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from abc import abstractmethod


class KubernetesClusterPlugin():
"""
An abstract base class for custom Kubernetes cluster provider plugins.
It represents an interface class from which the api provider-specific
plugins must be derived as child classes

To implement fill the methods `create_cluster` and `delete_cluster` with
api provider-specific functionalities for creating and deleting clusters.

- Implement `create_cluster` and `delete_cluster` methods
- Create `__init__(self, config_file=None)` method to handle api specific
configurations.

Example:
.. code:: python

from interface import KubernetesClusterPlugin
from apiX_library import cluster_api_class as ClusterAPI

class PluginX(KubernetesClusterPlugin):

def __init__(self, config_file=None):
self.config = config_file

def create_cluster(self):
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
self.cluster = ClusterAPI(name=cluster_name, image=cluster_image)
self.cluster.create(self.config)
kubeconfig_filepath = str(self.cluster.kubeconfig_path.resolve())
return self.kubeconfig_filepath

def delete_cluster(self):
self.cluster = ClusterAPI(cluster_name)
self.cluster.delete()
..
"""

@abstractmethod
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
def create_cluster(self, cluster_name="scs-cluster", version=None, kubeconfig_filepath=None) -> (str):
"""
This method is to be called to create a k8s cluster
:param: cluster_name:
:param: version:
:param: kubeconfig_filepath:
:return: kubeconfig_filepath
"""

@abstractmethod
def delete_cluster(self, cluster_name=None):
"""
This method is to be called in order to unprovision a cluster
:param: cluster_name:
"""
50 changes: 50 additions & 0 deletions Tests/kaas/plugin/plugin_kind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import shutil
import os
import os.path
from interface import KubernetesClusterPlugin
from pytest_kind import KindCluster
import logging
logger = logging.getLogger("plugin_kind")
tonifinger marked this conversation as resolved.
Show resolved Hide resolved


class PluginKind(KubernetesClusterPlugin):
"""
Plugin to handle the provisioning of kubernetes cluster for
conformance testing purpose with the use of Kind
"""
def __init__(self, config_file=None):
logger.info(f"Init provider plug-in of type {self.__class__.__name__}")
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
self.config = config_file
logger.debug(self.config)
self.working_directory = os.getcwd()
logger.debug(f"Working from {self.working_directory}")

def create_cluster(self, cluster_name="scs-cluster", version=None, kubeconfig_filepath=None):
"""
This method is to be called to create a k8s cluster
:param: kubernetes_version:
:return: kubeconfig_filepath
"""
cluster_version = version
if cluster_version == '1.29':
cluster_version = 'v1.29.8'
elif cluster_version == '1.30':
cluster_version = 'v1.30.4'
elif cluster_version == '1.31' or cluster_version == 'default':
cluster_version = 'v1.31.1'
cluster_image = f"kindest/node:{cluster_version}"
self.cluster = KindCluster(name=cluster_name, image=cluster_image)
if self.config is None:
self.cluster.create()
else:
self.cluster.create(self.config)
self.kubeconfig = str(self.cluster.kubeconfig_path.resolve())
if kubeconfig_filepath:
shutil.move(self.kubeconfig, kubeconfig_filepath)
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
else:
kubeconfig_filepath = str(self.kubeconfig)
return kubeconfig_filepath

def delete_cluster(self, cluster_name=None):
self.cluster = KindCluster(cluster_name)
self.cluster.delete()
14 changes: 14 additions & 0 deletions Tests/kaas/plugin/plugin_static.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from interface import KubernetesClusterPlugin


class PluginStatic(KubernetesClusterPlugin):
"""
Plugin to handle the provisioning of kubernetes
using a kubeconfig file
"""

def create_cluster(self):
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
self.kubeconfig = self.kubeconfig

def delete_cluster(self):
pass
3 changes: 3 additions & 0 deletions Tests/kaas/plugin/requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest-kind
kubernetes
junitparser
62 changes: 62 additions & 0 deletions Tests/kaas/plugin/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile requirements.in
#
cachetools==5.5.0
# via google-auth
certifi==2024.8.30
# via
# kubernetes
# requests
charset-normalizer==3.3.2
# via requests
google-auth==2.34.0
# via kubernetes
idna==3.8
# via requests
junitparser==3.2.0
# via -r requirements.in
kubernetes==30.1.0
# via -r requirements.in
oauthlib==3.2.2
# via
# kubernetes
# requests-oauthlib
pyasn1==0.6.0
# via
# pyasn1-modules
# rsa
pyasn1-modules==0.4.0
# via google-auth
pykube-ng==23.6.0
# via pytest-kind
pytest-kind==22.11.1
# via -r requirements.in
python-dateutil==2.9.0.post0
# via kubernetes
pyyaml==6.0.2
# via
# kubernetes
# pykube-ng
requests==2.32.3
# via
# kubernetes
# pykube-ng
# requests-oauthlib
requests-oauthlib==2.0.0
# via kubernetes
rsa==4.9
# via google-auth
six==1.16.0
# via
# kubernetes
# python-dateutil
urllib3==2.2.2
# via
# kubernetes
# pykube-ng
# requests
websocket-client==1.8.0
# via kubernetes
67 changes: 67 additions & 0 deletions Tests/kaas/plugin/run_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
import logging
import os.path
import click
import yaml


from plugin_kind import PluginKind
from plugin_static import PluginStatic


PLUGIN_LOOKUP = {
"kind": PluginKind,
"static": PluginStatic,
}


def init_plugin(plugin_kind, config=None):
plugin_maker = PLUGIN_LOOKUP.get(plugin_kind)
if plugin_maker is None:
raise ValueError(f"unknown plugin '{plugin_kind}'")
return plugin_maker(config)


def run_plugin_create(plugin_kind, plugin_config, clusterspec_cluster, clusterspec):
plugin = init_plugin(plugin_kind, plugin_config)
plugin.create_cluster(clusterspec_cluster, clusterspec[clusterspec_cluster]['branch'], os.path.abspath(clusterspec[clusterspec_cluster]['kubeconfig']))
tonifinger marked this conversation as resolved.
Show resolved Hide resolved


def run_plugin_delete(plugin_kind, plugin_config, clusterspec_cluster, clusterspec):
tonifinger marked this conversation as resolved.
Show resolved Hide resolved
plugin = init_plugin(plugin_kind, plugin_config)
plugin.delete_cluster(clusterspec_cluster)


def load_spec(clusterspec_path):
with open(clusterspec_path, "rb") as fileobj:
return yaml.load(fileobj, Loader=yaml.SafeLoader)


@click.group()
def cli():
pass


@cli.command()
@click.argument('plugin_kind', type=click.Choice(list(PLUGIN_LOOKUP), case_sensitive=False))
@click.argument('plugin_config', type=click.Path(exists=True, dir_okay=False))
@click.argument('clusterspec_path', type=click.Path(exists=True, dir_okay=False))
@click.argument('clusterspec_cluster', type=str, default="default")
def create(plugin_kind, plugin_config, clusterspec_path, clusterspec_cluster):
clusterspec = load_spec(clusterspec_path)['clusters']
run_plugin_create(plugin_kind, plugin_config, clusterspec_cluster, clusterspec)
tonifinger marked this conversation as resolved.
Show resolved Hide resolved


@cli.command()
@click.argument('plugin_kind', type=click.Choice(list(PLUGIN_LOOKUP), case_sensitive=False))
@click.argument('plugin_config', type=click.Path(exists=True, dir_okay=False))
@click.argument('clusterspec_path', type=click.Path(exists=True, dir_okay=False))
@click.argument('clusterspec_cluster', type=str, default="default")
def delete(plugin_kind, plugin_config, clusterspec_path, clusterspec_cluster):
clusterspec = load_spec(clusterspec_path)['clusters']
run_plugin_delete(plugin_kind, plugin_config, clusterspec_cluster, clusterspec)


if __name__ == '__main__':
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
cli()
Loading