Skip to content

Commit

Permalink
Add Kuadrantctl tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pehala committed Jun 25, 2024
1 parent 0b02c4c commit 834e126
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/settings.local.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# api_url: "https://api.openshift.com" # Optional: OpenShift API URL, if None it will OpenShift that you are logged in
# token: "KUADRANT_RULEZ" # Optional: OpenShift Token, if None it will OpenShift that you are logged in
# kubeconfig_path: "~/.kube/config" # Optional: Kubeconfig to use, if None the default one is used
# kuadrantctl: kuadrantctl
# tools:
# project: "tools" # Optional: OpenShift project, where external tools are located
# keycloak:
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
cluster: {}
kuadrantctl: "kuadrantctl"
tools:
project: "tools"
cfssl: "cfssl"
Expand Down
12 changes: 12 additions & 0 deletions testsuite/gateway/gateway_api/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,15 @@ def add_backend(self, backend: "Backend", prefix="/"):
@modify
def remove_all_backend(self):
self.model.spec.rules.clear()

def wait_for_ready(self):
"""Waits until HTTPRoute is reconcilled by GatewayProvider"""

def _ready(obj):
for condition_set in obj.model.status.parents:
if condition_set.controllerName == "istio.io/gateway-controller":
return (all(x.status == "True" for x in condition_set.conditions),)
return False

success = self.wait_until(_ready, timelimit=10)
assert success, f"{self.kind()} did got get ready in time"
52 changes: 52 additions & 0 deletions testsuite/kuadrantctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# pylint: disable=line-too-long
"""
Help as of 0.2.3
Kuadrant configuration command line utility
Usage:
kuadrantctl [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
generate Commands related to kubernetes object generation
gatewayapi Generate Gataway API resources
httproute Generate Gateway API HTTPRoute from OpenAPI 3.0.X
kuadrant Generate Kuadrant resources
authpolicy Generate Kuadrant AuthPolicy from OpenAPI 3.0.X
ratelimitpolicy Generate Kuadrant Rate Limit Policy from OpenAPI 3.0.X
help Help about any command
version Print the version number of kuadrantctl
Flags:
-h, --help help for httproute
--oas string Path to OpenAPI spec file (in JSON or YAML format), URL, or '-' to read from standard input (required)
-o, --output-format string Output format: 'yaml' or 'json'. (default "yaml")
Global Flags:
-v, --verbose verbose output
Use "kuadrantctl [command] --help" for more information about a command.
"""

import subprocess


class KuadrantCTL:
"""The doc string"""

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

def run(self, *args, **kwargs):
"""Passes arguments to Subprocess.run, see that for more details"""
args = (self.binary, *args)
kwargs.setdefault("capture_output", True)
kwargs.setdefault("check", True)
kwargs.setdefault("text", True)
# We do supply value for check :)
return subprocess.run(args, **kwargs) # pylint: disable= subprocess-run-check
20 changes: 20 additions & 0 deletions testsuite/oas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""OAS processing"""

import json
from collections import UserDict

import yaml


def as_json(oas):
"""Returns OAS as JSON"""
return json.dumps(oas.data)


def as_yaml(oas):
"""Returns OAS as JSON"""
return yaml.dump(oas.data)


class OASWrapper(UserDict):
"""Simple wrapper for OAS"""
Empty file.
35 changes: 35 additions & 0 deletions testsuite/resources/oas/base_httpbin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
openapi: 3.1.0
info:
title: Httpbin
version: 0.0.51
paths:
"/get":
get:
operationId: get_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
type: string
"/anything":
get:
operationId: get_anything
responses:
'200':
description: Successful Response
content:
application/json:
schema:
type: string
put:
operationId: put_anything
responses:
'200':
description: Successful Response
content:
application/json:
schema:
type: string
Empty file.
Empty file.
12 changes: 12 additions & 0 deletions testsuite/tests/kuadrantctl/cli/test_basic_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Tests basic commands"""

import pytest


# https://github.com/Kuadrant/kuadrantctl/issues/90
@pytest.mark.parametrize("command", ["help", "version"])
def test_commands(kuadrantctl, command):
"""Test that basic commands exists and returns anything"""
result = kuadrantctl.run(command)
assert not result.stderr, f"Command '{command}' returned an error: {result.stderr}"
assert result.stdout, f"Command '{command}' returned empty output"
86 changes: 86 additions & 0 deletions testsuite/tests/kuadrantctl/cli/test_simple_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Tests that you can generate simple AuthPolicy, focused on the cmdline options more than on extension functionality"""

import tempfile

import pytest
from openshift_client import apply

from testsuite.httpx.auth import HttpxOidcClientAuth
from testsuite.oas import as_json, as_yaml
from testsuite.policy.authorization.auth_policy import AuthPolicy


@pytest.fixture(scope="module")
def auth(keycloak):
"""Returns authentication object for HTTPX"""
return HttpxOidcClientAuth(keycloak.get_token, "authorization")


@pytest.fixture(scope="module")
def base_oas(base_oas, keycloak):
"""Add OIDC configuration"""
base_oas["components"] = {
"securitySchemes": {
"oidc": {
"type": "openIdConnect",
"openIdConnectUrl": keycloak.well_known["issuer"],
# https://github.com/Kuadrant/kuadrantctl/issues/94
# "openIdConnectUrl": keycloak.well_known["issuer"] + "/.well-known/openid-configuration",
}
}
}
return base_oas


@pytest.fixture(scope="module")
def oas_kuadrant(base_oas, blame, gateway, hostname, backend):
"""Add X-Kuadrant specific fields"""
base_oas["info"]["x-kuadrant"] = {
"route": {
"name": blame("route"),
"hostnames": [hostname.hostname],
"parentRefs": [gateway.reference],
}
}

anything = base_oas["paths"]["/anything"]
anything["x-kuadrant"] = {
"backendRefs": [backend.reference],
}
anything["get"]["security"] = [{"oidc": []}]
return base_oas


@pytest.mark.parametrize("encoder", [pytest.param(as_json, id="JSON"), pytest.param(as_yaml, id="YAML")])
@pytest.mark.parametrize("stdin", [pytest.param(True, id="STDIN"), pytest.param(False, id="File")])
def test_generate_authpolicy(request, kuadrantctl, oas, encoder, openshift, client, stdin, auth):
"""Generates Policy from OAS and tests that it works as expected"""
encoded = encoder(oas)

if stdin:
result = kuadrantctl.run("generate", "kuadrant", "authpolicy", "--oas", "-", input=encoded)
else:
with tempfile.NamedTemporaryFile("w") as file:
file.write(encoded)
file.flush()
result = kuadrantctl.run("generate", "kuadrant", "authpolicy", "--oas", file.name)

with openshift.context:
selector = apply(result.stdout)
request.addfinalizer(selector.delete)
policy = selector.object(cls=AuthPolicy)
policy.committed = True

policy.wait_for_ready()

response = client.get("/anything")
assert response.status_code == 401

response = client.get("/anything", auth=auth)
assert response.status_code == 200

response = client.get("/anything", headers={"Authorization": "Bearer xyz"})
assert response.status_code == 401

response = client.put("/anything")
assert response.status_code == 200
78 changes: 78 additions & 0 deletions testsuite/tests/kuadrantctl/cli/test_simple_route.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Tests that you can generate simple HTTPRoute, focused on the cmdline options more than on extension functionality"""

import tempfile

import pytest
from openshift_client import apply

from testsuite.gateway.gateway_api.route import HTTPRoute
from testsuite.oas import as_json, as_yaml


@pytest.fixture(scope="module")
def route():
"""Make sure Route is not created automatically"""
return None


@pytest.fixture(scope="module")
def oas_kuadrant(base_oas, blame, gateway, hostname, backend):
"""Add Route and Backend specifications to OAS"""
base_oas["info"]["x-kuadrant"] = {
"route": {
"name": blame("route"),
"hostnames": [hostname.hostname],
"parentRefs": [gateway.reference],
}
}
for path in base_oas["paths"].values():
path["x-kuadrant"] = {
"backendRefs": [backend.reference],
}
return base_oas


@pytest.mark.parametrize("encoder", [pytest.param(as_json, id="JSON"), pytest.param(as_yaml, id="YAML")])
@pytest.mark.parametrize("stdin", [pytest.param(True, id="STDIN"), pytest.param(False, id="File")])
def test_generate_route(request, kuadrantctl, oas, encoder, openshift, client, stdin):
"""Tests that Route can be generated and that is works as expected"""
encoded = encoder(oas)

if stdin:
result = kuadrantctl.run("generate", "gatewayapi", "httproute", "--oas", "-", input=encoded)
else:
with tempfile.NamedTemporaryFile("w") as file:
file.write(encoded)
file.flush()
result = kuadrantctl.run("generate", "gatewayapi", "httproute", "--oas", file.name)

with openshift.context:
# https://github.com/Kuadrant/kuadrantctl/issues/91
selector = apply(result.stdout, cmd_args="--validate=false")
# selector = apply(result.stdout)
request.addfinalizer(selector.delete)
route = selector.object(cls=HTTPRoute)
route.committed = True

route.wait_for_ready()

response = client.get("/get")
assert response.status_code == 200

response = client.get("/anything")
assert response.status_code == 200

response = client.put("/anything")
assert response.status_code == 200

# Incorrect methods
response = client.post("/anything")
assert response.status_code == 404

# Incorrect path
response = client.get("/anything/test")
assert response.status_code == 404

# Incorrect endpoint
response = client.post("/post")
assert response.status_code == 404
63 changes: 63 additions & 0 deletions testsuite/tests/kuadrantctl/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Conftest for kuadrantctl tests"""

import shutil
from importlib import resources

import pytest
import yaml
from openshift_client import apply

from testsuite.gateway.gateway_api.route import HTTPRoute
from testsuite.kuadrantctl import KuadrantCTL
from testsuite.oas import OASWrapper, as_yaml


@pytest.fixture(scope="session")
def kuadrantctl(testconfig, skip_or_fail):
"""Return Kuadrantctl wrapper"""
binary_path = testconfig["kuadrantctl"]
if not shutil.which(binary_path):
skip_or_fail("Kuadrantctl binary not found")
return KuadrantCTL(binary_path)


@pytest.fixture(scope="module")
def base_oas():
"""
Pure OAS, base should be loaded from the resources and securityScheme should be added manually to suit the test
"""
return OASWrapper(
yaml.safe_load(resources.files("testsuite.resources.oas").joinpath("base_httpbin.yaml").read_text())
)


@pytest.fixture(scope="module")
def oas_kuadrant(base_oas):
"""Used for adding X-kuadrant extension values to the OAS"""
return base_oas


@pytest.fixture(scope="module")
def oas(oas_kuadrant):
"""Shortcut to final version of OAS, should not be overridden"""
return oas_kuadrant


@pytest.fixture(scope="module")
def route(request, kuadrantctl, oas, openshift):
"""Generates Route from OAS"""
result = kuadrantctl.run("generate", "gatewayapi", "httproute", "--oas", "-", input=as_yaml(oas))
with openshift.context:
selector = apply(result.stdout, cmd_args="--validate=false")
request.addfinalizer(selector.delete)
route = selector.object(cls=HTTPRoute)
route.committed = True
return route


@pytest.fixture(scope="module")
def client(hostname, route): # pylint: disable=unused-argument
"""Returns httpx client to be used for requests, it also commits AuthConfig"""
client = hostname.client()
yield client
client.close()

0 comments on commit 834e126

Please sign in to comment.