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

Remove Httpbin templates #315

Merged
merged 4 commits into from
Jan 15, 2024
Merged
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
27 changes: 27 additions & 0 deletions testsuite/openshift/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""OpenShift common objects"""
import functools
from dataclasses import dataclass, field
from typing import Optional, Literal

from openshift import APIObject, timeout

Expand Down Expand Up @@ -52,3 +54,28 @@ def _wrap(self, *args, **kwargs):
func(self, *args, **kwargs)

return _wrap


@dataclass
class MatchExpression:
"""
Data class intended for defining K8 Label Selector expressions.
Used by selector.matchExpressions API key identity.
"""

operator: Literal["In", "NotIn", "Exists", "DoesNotExist"]
values: list[str]
key: str = "group"


@dataclass
class Selector:
averevki marked this conversation as resolved.
Show resolved Hide resolved
"""Dataclass for specifying selectors based on either expression or labels"""

# pylint: disable=invalid-name
matchExpressions: Optional[list[MatchExpression]] = field(default=None, kw_only=True)
jsmolar marked this conversation as resolved.
Show resolved Hide resolved
matchLabels: Optional[dict[str, str]] = field(default=None, kw_only=True)

def __post_init__(self):
if not (self.matchLabels is None) ^ (self.matchExpressions is None):
raise AttributeError("`matchLabels` xor `matchExpressions` argument must be used")
3 changes: 1 addition & 2 deletions testsuite/openshift/api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
import base64
from functools import cached_property

from testsuite.policy.authorization import Selector
from testsuite.openshift.client import OpenShiftClient
from testsuite.openshift import OpenShiftObject, modify
from testsuite.openshift import OpenShiftObject, modify, Selector


class APIKey(OpenShiftObject):
Expand Down
50 changes: 50 additions & 0 deletions testsuite/openshift/deployment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Deployment related objects"""
import openshift as oc

from testsuite.openshift import OpenShiftObject, Selector
from testsuite.utils import asdict


class Deployment(OpenShiftObject):
"""Kubernetes Deployment object"""

@classmethod
def create_instance(
cls, openshift, name, container_name, image, ports: dict[str, int], selector: Selector, labels: dict[str, str]
):
"""
Creates new instance of Deployment
Supports only single container Deployments everything else should be edited directly
"""
model: dict = {
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": {
"name": name,
"labels": labels,
},
"spec": {
"selector": asdict(selector),
"template": {
"metadata": {"labels": {"deployment": name, **labels}},
"spec": {
"containers": [
{
"image": image,
"name": container_name,
"imagePullPolicy": "IfNotPresent",
"ports": [{"name": name, "containerPort": port} for name, port in ports.items()],
}
]
},
},
},
}

return cls(model, context=openshift.context)

def wait_for_ready(self, timeout=90):
"""Waits until Deployment is marked as ready"""
with oc.timeout(timeout):
success, _, _ = self.self_selector().until_all(success_func=lambda obj: "readyReplicas" in obj.model.status)
assert success, f"Deployment {self.name()} did not get ready in time"
51 changes: 32 additions & 19 deletions testsuite/openshift/httpbin.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Module for Httpbin backend classes"""
"""Httpbin related classes"""
from functools import cached_property
from importlib import resources

from testsuite.lifecycle import LifecycleObject
from testsuite.gateway import Referencable
from testsuite.openshift import Selector
from testsuite.openshift.client import OpenShiftClient
from testsuite.openshift.deployment import Deployment
from testsuite.openshift.service import Service, ServicePort


class Httpbin(LifecycleObject, Referencable):
"""Httpbin deployed in OpenShift through template"""
"""Httpbin deployed in OpenShift"""

def __init__(self, openshift: OpenShiftClient, name, label, replicas=1) -> None:
super().__init__()
Expand All @@ -17,7 +19,8 @@ def __init__(self, openshift: OpenShiftClient, name, label, replicas=1) -> None:
self.label = label
self.replicas = replicas

self.httpbin_objects = None
self.deployment = None
self.service = None

@property
def reference(self):
Expand All @@ -29,27 +32,37 @@ def url(self):
return f"{self.name}.{self.openshift.project}.svc.cluster.local"

def commit(self):
self.httpbin_objects = self.openshift.new_app(
resources.files("testsuite.resources").joinpath("httpbin.yaml"),
{"NAME": self.name, "LABEL": self.label, "REPLICAS": self.replicas},
match_labels = {"app": self.label, "deployment": self.name}
self.deployment = Deployment.create_instance(
self.openshift,
self.name,
container_name="httpbin",
image="quay.io/jsmadis/httpbin:latest",
jsmolar marked this conversation as resolved.
Show resolved Hide resolved
ports={"api": 8080},
selector=Selector(matchLabels=match_labels),
labels={"app": self.label},
)
self.deployment.commit()
self.deployment.wait_for_ready()

with self.openshift.context:
assert self.openshift.is_ready(self.httpbin_objects.narrow("deployment")), "Httpbin wasn't ready in time"
self.service = Service.create_instance(
self.openshift,
self.name,
selector=match_labels,
ports=[ServicePort(name="http", port=8080, targetPort="api")],
)
self.service.commit()

def delete(self):
with self.openshift.context:
if self.httpbin_objects:
self.httpbin_objects.delete()
self.httpbin_objects = None

@cached_property
def service(self):
"""Service associated with httpbin"""
with self.openshift.context:
return self.httpbin_objects.narrow("service").object()
if self.service:
self.service.delete()
self.service = None
if self.deployment:
self.deployment.delete()
self.deployment = None

@cached_property
def port(self):
"""Service port that httpbin listens on"""
return self.service.model.spec.ports[0].get("port")
return self.service.get_port("http").port
48 changes: 48 additions & 0 deletions testsuite/openshift/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Service related objects"""
from dataclasses import dataclass, asdict

from testsuite.openshift import OpenShiftObject


@dataclass
class ServicePort:
"""Kubernetes Service Port object"""

name: str
port: int
targetPort: int | str # pylint: disable=invalid-name


class Service(OpenShiftObject):
"""Kubernetes Service object"""

@classmethod
def create_instance(
cls,
openshift,
name,
selector: dict[str, str],
ports: list[ServicePort],
labels: dict[str, str] = None,
):
"""Creates new Service"""
model: dict = {
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": name,
"labels": labels,
},
"spec": {
"ports": [asdict(port) for port in ports],
"selector": selector,
},
}
return cls(model, context=openshift.context)

def get_port(self, name):
"""Returns port definition for a port with said name"""
for port in self.model.spec.ports:
if port["name"] == name:
return port
raise KeyError(f"No port with name {name} exists")
24 changes: 1 addition & 23 deletions testsuite/policy/authorization/__init__.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
"""Authorization related objects"""
import abc
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Literal, Optional
from testsuite.utils import asdict, JSONValues


# pylint: disable=invalid-name
@dataclass
class MatchExpression:
"""
Data class intended for defining K8 Label Selector expressions.
Used by selector.matchExpressions API key identity.
"""

operator: Literal["In", "NotIn", "Exists", "DoesNotExist"]
values: list[str]
key: str = "group"


@dataclass
class Selector:
"""Dataclass for specifying selectors based on either expression or labels"""

matchExpressions: Optional[list[MatchExpression]] = field(default=None, kw_only=True)
matchLabels: Optional[dict[str, str]] = field(default=None, kw_only=True)

def __post_init__(self):
if not (self.matchLabels is None) ^ (self.matchExpressions is None):
raise AttributeError("`matchLabels` xor `matchExpressions` argument must be used")


@dataclass
Expand Down
3 changes: 1 addition & 2 deletions testsuite/policy/authorization/sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from typing import Literal, Iterable, TYPE_CHECKING, Union

from testsuite.policy.authorization import (
Selector,
Credentials,
Rule,
Pattern,
Expand All @@ -15,7 +14,7 @@
Cache,
)
from testsuite.utils import asdict
from testsuite.openshift import modify
from testsuite.openshift import modify, Selector

if TYPE_CHECKING:
from .auth_config import AuthConfig
Expand Down
53 changes: 0 additions & 53 deletions testsuite/resources/httpbin.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""
import pytest

from testsuite.policy.authorization import MatchExpression, Selector
from testsuite.openshift import Selector, MatchExpression


@pytest.fixture(scope="module")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from testsuite.httpx.auth import HeaderApiKeyAuth
from testsuite.policy.authorization import Selector
from testsuite.openshift import Selector


@pytest.fixture(scope="function")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pytest

from testsuite.certificates import Certificate, CertInfo
from testsuite.policy.authorization import Selector
from testsuite.openshift import Selector
from testsuite.gateway import Exposer
from testsuite.gateway.envoy.tls import TLSEnvoy
from testsuite.gateway.gateway_api.hostname import OpenShiftExposer
Expand Down