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

tests: test CRD versions #1317

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions apiclient/kube_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2024 SUSE LLC

from .api import KubeAPI

# This is just to make `tox -e pep8` stop complaining
# about things that it really shouldn't complain about.
_ = KubeAPI
70 changes: 70 additions & 0 deletions apiclient/kube_api/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2024 SUSE LLC
#
# pylint: disable=missing-function-docstring

from urllib.parse import urljoin

import kubernetes
import requests
import yaml


class KubeAPI:
"""
An abstraction of the Kubernetes API.

Example usage:

```
with KubeAPI(endpoint, tls_verify=false) as api:
api.authenticate(username, password, verify=false)
kube_client = api.get_client()

corev1 = kubernetes.client.CoreV1Api(kube_client)

namespaces = corev1.list_namespace()
```
"""

HARVESTER_API_VERSION = "harvesterhci.io/v1beta1"

def __init__(self, endpoint, tls_verify, token=None, session=None):
self.session = session or requests.Session()
self.session.verify = tls_verify
self.session.headers.update(Authorization=token or "")

self.endpoint = endpoint

def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, taceback):
pass

def _post(self, path, **kwargs):
url = self._get_url(path)
return self.session.post(url, **kwargs)

def _get_url(self, path):
return urljoin(self.endpoint, path).format(API_VERSION=self.HARVESTER_API_VERSION)

def get_client(self):
path = "/v1/management.cattle.io.clusters/local"
params = {"action": "generateKubeconfig"}

resp = self._post(path, params=params)
assert resp.status_code == 200, "Failed to generate kubeconfig"

kubeconfig = yaml.safe_load(resp.json()['config'])
return kubernetes.config.new_client_from_config_dict(kubeconfig)

def authenticate(self, user, passwd, **kwargs):
path = "v3-public/localProviders/local?action=login"
resp = self._post(path, json=dict(username=user, password=passwd), **kwargs)

assert resp.status_code == 201, "Failed to authenticate"

token = f"Bearer {resp.json()['token']}"
self.session.headers.update(Authorization=token)

return resp.json()
81 changes: 81 additions & 0 deletions harvester_e2e_tests/apis/test_crd_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (c) 2024 SUSE LLC
#
# pylint: disable=missing-function-docstring, redefined-outer-name

from urllib.parse import urljoin

import kubernetes
import pytest
import requests
import semver
import yaml


pytest_plugins = [
"harvester_e2e_tests.fixtures.kube_api_client",
"harvester_e2e_tests.fixtures.api_client"
]


@pytest.fixture(scope="session")
def server_version(api_client):
code, data = api_client.settings.get(name="server-version")
assert code == 200
assert data.get("value") is not None

yield semver.VersionInfo.parse(data.get("value").lstrip("v"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have apiclient.cluster_version to expose the cluster's version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apiclient.cluster_version returns a pkg_resources.Version object, but my tests are using the semver library to parse versioning information.
The semver library has several advantages. One being that it isn't in the process of being deprecated. Another advantage is that it has good documentation and deals with a lot of different operations on version information better. Third, the pkg_resources library is supposed to deal with python-packages and not generic version information. This is a difference in so far as python packages have specific requirements on the format of the version string that we don't adhere to with Harvester



@pytest.fixture(scope="module", params=[
("csi-snapshotter", "volumesnapshotclasses"),
("csi-snapshotter", "volumesnapshotcontents"),
("csi-snapshotter", "volumesnapshots"),
("kubevirt-operator", "crd-kubevirt"),
("whereabouts", "whereabouts.cni.cncf.io_ippools"),
("whereabouts", "whereabouts.cni.cncf.io_overlappingrangeipreservations")
])
def chart_and_file_name(request):
yield request.param


@pytest.fixture(scope="module")
def expected_crd(server_version, chart_and_file_name):
raw_url = "https://raw.githubusercontent.com/harvester/harvester/"
raw_url = urljoin(raw_url, f"v{server_version.major}.{server_version.minor}/")
raw_url = urljoin(raw_url, "deploy/charts/harvester/dependency_charts/")
raw_url = urljoin(raw_url, f"{chart_and_file_name[0]}/")
raw_url = urljoin(raw_url, "crds/")
raw_url = urljoin(raw_url, f"{chart_and_file_name[1]}.yaml")

resp = requests.get(raw_url, allow_redirects=True)
cont = resp.content.decode("utf-8")
data = yaml.safe_load(cont)
yield data


@pytest.fixture(scope="module")
def actual_crd(kube_api_client, expected_crd):
name = expected_crd['metadata']['name']
kube_client = kubernetes.client.ApiextensionsV1Api(kube_api_client)
yield kube_client.read_custom_resource_definition(name=name)


@pytest.mark.api
def test_api_version(expected_crd, actual_crd):
expected_versions = []
for ver in expected_crd['spec']['versions']:
expected_versions.append(ver['name'])

actual_versions = []
for ver in actual_crd.spec.versions:
actual_versions.append(ver.name)

assert expected_crd['metadata']['name'] == actual_crd.metadata.name

# Make sure all expected versions are there
for ver in expected_versions:
assert ver in actual_versions

# Make sure all installed versions are expected
for ver in actual_versions:
assert ver in expected_versions
20 changes: 20 additions & 0 deletions harvester_e2e_tests/fixtures/kube_api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2024 SUSE LLC
#
# pylint: disable=missing-function-docstring

import pytest

from kube_api import KubeAPI


@pytest.fixture(scope="session")
def kube_api_client(request):
endpoint = request.config.getoption("--endpoint")
username = request.config.getoption("--username")
password = request.config.getoption("--password")
tls_verify = request.config.getoption("--ssl_verify", False)

with KubeAPI(endpoint, tls_verify) as api:
api.authenticate(username, password, verify=tls_verify)

yield api.get_client()
2 changes: 2 additions & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pytest-json-report
pytest-dependency
jinja2
bcrypt
kubernetes
semver
requests
paramiko
pycryptodome
Expand Down