Skip to content

Commit

Permalink
Add Ingress Type (#172)
Browse files Browse the repository at this point in the history
* add ingress

* remove comment

* update version

* fix object and referencing of ingress, fix typos
  • Loading branch information
danieldiamond committed Mar 17, 2020
1 parent ac5ffd2 commit 07606c7
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 1 deletion.
61 changes: 60 additions & 1 deletion kubetest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def load_service(self, path: str, set_namespace: bool = True) -> objects.Service
service.namespace = self.namespace
return service

def load_persistentvolumeclaim(self, path, set_namespace=True):
def load_persistentvolumeclaim(self, path, set_namespace=True) -> objects.PersistentVolumeClaim:
"""Load a manifest YAML into a PersistentVolumeClaim object.
By default, this will augment the PersistentVolumeClaim object with
Expand All @@ -253,6 +253,28 @@ def load_persistentvolumeclaim(self, path, set_namespace=True):
persistentvolumeclaim.namespace = self.namespace
return persistentvolumeclaim

def load_ingress(self, path, set_namespace=True) -> objects.Ingress:
"""Load a manifest YAML into a Ingress object.
By default, this will augment the Ingress object with
the generated test case namespace. This behavior can be
disabled with the ``set_namespace`` flag.
Args:
path (str): The path to the Ingress manifest.
set_namespace (bool): Enable/disable the automatic
augmentation of the Ingress namespace.
Returns:
objects.Ingress: The ingress for the specified
manifest.
"""
log.info('loading ingress from path: %s', path)
ingress = objects.Ingress.load(path)
if set_namespace:
ingress.namespace = self.namespace
return ingress

def load_statefulset(self, path: str, set_namespace: bool = True) -> objects.StatefulSet:
"""Load a manifest YAML into a StatefulSet object.
Expand Down Expand Up @@ -690,6 +712,43 @@ def get_persistentvolumeclaims(self, namespace=None, fields=None, labels=None):

return persistentvolumeclaims

def get_ingresses(self, namespace=None, fields=None, labels=None):
"""Get Ingresses from the cluster.
Args:
namespace (str): The namespace to get the Ingress from. If not
specified, it will use the auto-generated test case namespace
by default.
fields (dict[str, str]): A dictionary of fields used to restrict
the returned collection of Ingress to only those which match
these field selectors. By default, no restricting is done.
labels (dict[str, str]): A dictionary of labels used to restrict
the returned collection of Ingress to only those which match
these label selectors. By default, no restricting is done.
Returns:
dict[str, objects.Ingress]: A dictionary where the key is
the Ingress name and the value is the Ingress
itself.
"""
if namespace is None:
namespace = self.namespace

selectors = utils.selector_kwargs(fields, labels)

ingress_list = client.CoreV1Api().\
list_namespaced_ingress(
namespace=namespace,
**selectors,
)

ingresses = {}
for obj in ingress_list.items:
ingress = objects.Ingress(obj)
ingresses[ingress.name] = ingress

return ingresses

def get_statefulsets(
self,
namespace: str = None,
Expand Down
2 changes: 2 additions & 0 deletions kubetest/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ObjectManager:
'service',
'configmap',
'persistentvolumeclaim',
'ingress',
'daemonset',
'statefulset',
'deployment',
Expand Down Expand Up @@ -104,6 +105,7 @@ def get_objects_in_apply_order(self) -> Generator[objects.ApiObject, None, None]
- Service
- ConfigMap
- PersistentVolumeClaim
- Ingress
- DaemonSet
- StatefulSet
- Deployment
Expand Down
1 change: 1 addition & 0 deletions kubetest/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .deployment import Deployment
from .endpoints import Endpoints
from .event import Event
from .ingress import Ingress
from .namespace import Namespace
from .node import Node
from .persistentvolumeclaim import PersistentVolumeClaim
Expand Down
106 changes: 106 additions & 0 deletions kubetest/objects/ingress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Kubetest wrapper for the Kubernetes ``Ingress`` API Object."""

import logging

from kubernetes import client

from .api_object import ApiObject

log = logging.getLogger('kubetest')


class Ingress(ApiObject):
"""Kubetest wrapper around a Kubernetes `Ingress`_ API Object.
The actual ``kubernetes.client.ExtensionsV1beta1Api`` instance that this
wraps can be accessed via the ``obj`` instance member.
This wrapper provides some convenient functionality around the
API Object and provides some state management for the `Ingress`_.
.. _Ingress:
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#ingress-v1beta1-extensions
"""

obj_type = client.ExtensionsV1beta1Api

api_clients = {
'preferred': client.ExtensionsV1beta1Api,
'extensions/v1beta1': client.ExtensionsV1beta1Api,
}

def __str__(self):
return str(self.obj)

def __repr__(self):
return self.__str__()

def create(self, namespace=None):
"""Create the Ingress under the given namespace.
Args:
namespace (str): The namespace to create the Ingress under.
If the Ingress was loaded via the kubetest client, the
namespace will already be set, so it is not needed here.
Otherwise, the namespace will need to be provided.
"""
if namespace is None:
namespace = self.namespace

log.info(
'creating ingress "%s" in namespace "%s"',
self.name,
self.namespace
)
log.debug('ingress: %s', self.obj)

self.obj = self.api_client.create_namespaced_ingress(
namespace=namespace,
body=self.obj,
)

def delete(self, options):
"""Delete the Ingress.
This method expects the Ingress to have been loaded or otherwise
assigned a namespace already. If it has not, the namespace will need
to be set manually.
Args:
options (client.V1DeleteOptions): Options for Ingress deletion.
Returns:
client.V1Status: The status of the delete operation.
"""
if options is None:
options = client.V1DeleteOptions()

log.info('deleting ingress "%s"', self.name)
log.debug('delete options: %s', options)
log.debug('ingress: %s', self.obj)

return self.api_client.delete_namespaced_ingress(
name=self.name,
namespace=self.namespace,
body=options,
)

def refresh(self):
"""Refresh the underlying Kubernetes Ingress resource."""
self.obj = self.api_client.read_namespaced_ingress(
name=self.name,
namespace=self.namespace,
)

def is_ready(self):
"""Check if the Ingress is in the ready state.
Returns:
bool: True if in the ready state; False otherwise.
"""
try:
self.refresh()
except: # noqa
return False
else:
return True
26 changes: 26 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,29 @@ def simple_persistentvolumeclaim():
)
)
)


@pytest.fixture()
def simple_ingress():
"""Return the Kubernetes config matching the simple-ingress.yaml
manifest."""
return client.ExtensionsV1beta1Ingress(
api_version='extensions/v1beta1',
kind='Ingress',
metadata=client.V1ObjectMeta(
name='my-ingress'
),
spec=client.ExtensionsV1beta1IngressSpec(
rules=[client.ExtensionsV1beta1IngressRule(
http=client.ExtensionsV1beta1HTTPIngressRuleValue(
paths=[client.ExtensionsV1beta1HTTPIngressPath(
backend=client.ExtensionsV1beta1IngressBackend(
service_name='my-service',
service_port=80
),
path='/'
)]
)
)],
)
)
13 changes: 13 additions & 0 deletions tests/data/simple-ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host:
http:
paths:
- path: /
backend:
serviceName: my-service
servicePort: 80
18 changes: 18 additions & 0 deletions tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,24 @@ def test_simple_service_wrong_type(self, manifest_dir):
os.path.join(manifest_dir, 'simple-service.yaml')
)

def test_simple_ingress_ok(self, manifest_dir, simple_ingress):
"""Test loading the simple service successfully."""
obj = manifest.load_type(
client.ExtensionsV1beta1Ingress,
os.path.join(manifest_dir, 'simple-ingress.yaml')
)
assert obj == simple_ingress

def test_simple_ingress_wrong_type(self, manifest_dir):
"""Test loading the simple ingress to the wrong type."""
with pytest.raises(ValueError):
# The V1Container requires a name -- since the manifest has no name,
# it will cause V1Container construction to fail with ValueError.
manifest.load_type(
client.V1Container,
os.path.join(manifest_dir, 'simple-ingress.yaml')
)

def test_simple_persistentvolumeclaim_ok(
self,
manifest_dir,
Expand Down

0 comments on commit 07606c7

Please sign in to comment.