Skip to content

Commit

Permalink
Merge pull request #315 from pehala/httpbin_object
Browse files Browse the repository at this point in the history
Remove Httpbin templates
  • Loading branch information
pehala authored Jan 15, 2024
2 parents f763238 + 73d2e2d commit 71a6548
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 102 deletions.
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:
"""Dataclass for specifying selectors based on either expression or labels"""

# pylint: disable=invalid-name
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")
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",
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

0 comments on commit 71a6548

Please sign in to comment.