Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 config/settings.local.yaml.tpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#default:
# tester: "someuser" # Optional: name of the user, who is running the tests, defaults to whoami/uid
# kuadrantctl: kuadrantctl
# kubectl-dns: "kubectl-dns"
# console:
# username: "CONSOLE_USERNAME" # OpenShift console username for UI test login
# password: "CONSOLE_PASSWORD" # OpenShift console password for UI tests login
Expand Down
1 change: 1 addition & 0 deletions config/settings.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
default:
dynaconf_merge: true
kuadrantctl: "kuadrantctl"
kubectl-dns: "kubectl-dns"
tools:
project: "tools"
cfssl: "cfssl"
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ markers = [
"defaults_overrides: Defaults and Overrides feature tests",
"observability: Tracing, metrics and logging tests",
"ui: Test uses browser automation via Playwright to test the console plugin UI",
"cli: Test is using CLI tools (kubectl-dns, kuadrantctl)",
]
filterwarnings = [
"ignore: WARNING the new order is not taken into account:UserWarning",
Expand Down
Empty file added testsuite/cli/__init__.py
Empty file.
File renamed without changes.
25 changes: 25 additions & 0 deletions testsuite/cli/kubectl_dns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Wrapper around kubectl-dns binary"""

import os
import subprocess


class KubectlDNS:
"""Wrapper on top of kubectl-dns binary"""

def __init__(self, binary) -> None:
super().__init__()
self.binary = binary

def run(self, *args, **kwargs):
"""Passes arguments to subprocess.run()"""
args = (self.binary, *args)
kwargs.setdefault("capture_output", True)
kwargs.setdefault("text", True)

if "env" in kwargs:
env = os.environ.copy()
env.update(kwargs["env"])
kwargs["env"] = env

return subprocess.run(args, **kwargs) # pylint: disable= subprocess-run-check
31 changes: 31 additions & 0 deletions testsuite/kubernetes/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from functools import cached_property
from urllib.parse import urlparse
import tempfile
import yaml

import openshift_client as oc
from openshift_client import Context, OpenShiftPythonException
Expand Down Expand Up @@ -45,6 +47,13 @@ def context(self):

return context

@property
def current_context_name(self) -> str:
"""Returns the current context name from the kubeconfig"""
if self._kubeconfig_path is None:
raise ValueError("Kubeconfig path is not set")
return self.do_action("config", "current-context").out().strip()

@property
def api_url(self):
"""Returns real API url"""
Expand Down Expand Up @@ -142,3 +151,25 @@ def apply_from_string(self, string, cls, cmd_args=None):
obj = selector.object(cls=cls)
obj.context = self.context
return obj

def create_merged_kubeconfig(self, cluster2: "KubernetesClient") -> str:
Copy link
Member

Choose a reason for hiding this comment

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

Due to coredns tests we will probably deal with more then two clusters, maybe refactor this method to take list of additional clusters? wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if we should have other tests running this command. I would leave it for when a new test is being developed

"""
Creates a merged kubeconfig from this instance and another KubernetesClient instance.
Returns the path to the temporary kubeconfig file.
"""
with self.context:
config1_yaml = oc.invoke("config", ["view", "--minify=true", "--flatten=true"]).out()
with cluster2.context:
config2_yaml = oc.invoke("config", ["view", "--minify=true", "--flatten=true"]).out()

config1 = yaml.safe_load(config1_yaml)
config2 = yaml.safe_load(config2_yaml)

merged_config = config1.copy()
merged_config["clusters"].extend(config2.get("clusters", []))
merged_config["contexts"].extend(config2.get("contexts", []))
merged_config["users"].extend(config2.get("users", []))

with tempfile.NamedTemporaryFile(mode="w", suffix=".kubeconfig", delete=False) as temp_file:
yaml.safe_dump(merged_config, temp_file)
return temp_file.name
2 changes: 1 addition & 1 deletion testsuite/tests/kuadrantctl/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from testsuite.gateway.gateway_api.gateway import KuadrantGateway
from testsuite.gateway.gateway_api.route import HTTPRoute
from testsuite.gateway import Gateway, GatewayListener, Hostname
from testsuite.kuadrantctl import KuadrantCTL
from testsuite.cli.kuadrantctl import KuadrantCTL
from testsuite.oas import OASWrapper


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Test kubectl-dns secret-generate command with basic coredns setup with 1 primary and 1 secondary clusters"""

import shutil

import dns.resolver
import pytest

from testsuite.cli.kubectl_dns import KubectlDNS
from testsuite.tests.multicluster.coredns.conftest import IP1, IP2

pytestmark = [pytest.mark.cli]


@pytest.fixture(scope="session")
def kubectl_dns(testconfig, skip_or_fail):
"""Return Kuadrantctl wrapper with merged kubeconfig"""
binary_path = testconfig["kubectl-dns"]
if not shutil.which(binary_path):
skip_or_fail("kubectl-dns binary not found")
return KubectlDNS(binary_path)


@pytest.fixture(scope="module")
def kubeconfig_secrets(request, testconfig, cluster, cluster2, kubectl_dns, blame):
"""Run generate-secret command on merged kubeconfig to generate kubeconfig secret for the secondary cluster"""
system_project = testconfig["service_protection"]["system_project"]
secret_name = blame("kubecfg")
Copy link
Member

Choose a reason for hiding this comment

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

Would use tempfile library for a temporary file. Also there is missing finalizer for removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Binary creates this secret directly on cluster. I have finalizer set right under this comment

request.addfinalizer(
lambda: cluster.do_action("delete", "secret", secret_name, "-n", system_project, "--ignore-not-found")
)

merged_kubeconfig = cluster.create_merged_kubeconfig(cluster2)
result = kubectl_dns.run(
"secret-generation",
"--name",
secret_name,
"--context",
cluster2.current_context_name,
"--namespace",
system_project,
"--service-account",
"coredns",
env={"KUBECONFIG": merged_kubeconfig},
)
assert result.returncode == 0, f"kubectl-dns couldn't generate kubeconfig secret: {result.stderr}"
return []


def test_kubectl_dns_secret_generation(hostname):
"""IPs from both, primary and secondary, clusters should return in DNS A record set"""
dns_ips = {ip.address for ip in dns.resolver.resolve(hostname.hostname)}
assert {IP1, IP2} == dns_ips, "CoreDNS should have returned both IP addresses in A record set"