Skip to content

Commit

Permalink
Merge pull request #29 from fiaas/add-namespace-support-in-watch_list
Browse files Browse the repository at this point in the history
Add namespace parameter to watch_list
  • Loading branch information
oyvindio authored Jan 24, 2018
2 parents 0fc2c92 + 3d18044 commit 2b1b980
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .prospector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ignore-paths:

pep8:
options:
max-line-length: 140
max-line-length: 120

mccabe:
max-complexity: 10
Expand Down
17 changes: 13 additions & 4 deletions k8s/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __new__(mcs, cls, bases, attrs):
meta = {
"url_template": getattr(attr_meta, "url_template", ""),
"watch_list_url": getattr(attr_meta, "watch_list_url", ""),
"watch_list_url_template": getattr(attr_meta, "watch_list_url_template", ""),
"fields": [],
"field_names": []
}
Expand Down Expand Up @@ -78,11 +79,19 @@ def list(cls, namespace="default"):
return [cls.from_dict(item) for item in resp.json()[u"items"]]

@classmethod
def watch_list(cls):
def watch_list(cls, namespace=None):
"""Return a generator that yields WatchEvents of cls"""
url = cls._meta.watch_list_url
if not url:
raise NotImplementedError("Cannot watch_list, no watch_list_url defined on class {}".format(cls))
if namespace:
if cls._meta.watch_list_url_template:
url = cls._meta.watch_list_url_template.format(namespace=namespace)
else:
raise NotImplementedError(
"Cannot watch_list with namespace, no watch_list_url_template defined on class {}".format(cls))
else:
url = cls._meta.watch_list_url
if not url:
raise NotImplementedError("Cannot watch_list, no watch_list_url defined on class {}".format(cls))

resp = cls._client.get(url, stream=True, timeout=None)
for line in resp.iter_lines(chunk_size=None):
if line:
Expand Down
6 changes: 4 additions & 2 deletions k8s/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ def __init__(self, model, capacity=DEFAULT_CAPACITY):
self._seen = cachetools.LRUCache(capacity)
self._model = model

def watch(self):
def watch(self, namespace=None):
"""Watch for events
:param str namespace: the namespace to watch for events in. The default (None) results in watching for events
in all namespaces.
:return: a generator that yields :py:class:`~.WatchEvent` objects not seen before
"""
while True:
for event in self._model.watch_list():
for event in self._model.watch_list(namespace=namespace):
o = event.object
key = (o.metadata.name, o.metadata.namespace)
if self._seen.get(key) == o.metadata.resourceVersion and event.type != WatchEvent.DELETED:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def main():
"release": ["gitpython", "twine"],
"docs": ["Sphinx>=1.6.3"]
},

tests_require=TESTS_REQ,
# Metadata
description="Python client library for the Kubernetes API",
long_description=_generate_description(),
Expand Down
17 changes: 14 additions & 3 deletions tests/k8s/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,22 @@ def test_watch_list_should_raise_exception_when_watch_list_url_is_not_set_on_met
with pytest.raises(NotImplementedError):
list(WatchListExampleUnsupported.watch_list())

def test_watch_list_with_namespace_should_raise_exception_when_watch_list_url_template_is_not_set_on_metaclass(self, session):
with pytest.raises(NotImplementedError):
list(WatchListExampleUnsupported.watch_list(namespace="explicitly-set"))

def test_watch_list(self, session):
list(WatchListExample.watch_list())
session.request.assert_called_once_with(
"GET", _absolute_url("/watch/example"), json=None, timeout=None, stream=True
)

def test_watch_list_with_namespace(self, session):
list(WatchListExample.watch_list(namespace="explicitly-set"))
session.request.assert_called_once_with(
"GET", _absolute_url("/watch/explicitly-set/example"), json=None, timeout=None, stream=True
)

@pytest.mark.parametrize("key", SENSITIVE_HEADERS)
def test_redacts_sensitive_headers(self, key):
message = []
Expand All @@ -111,14 +121,15 @@ def _absolute_url(url):

class WatchListExample(Model):
class Meta:
url_template = '/example'
watch_list_url = '/watch/example'
url_template = "/example"
watch_list_url = "/watch/example"
watch_list_url_template = "/watch/{namespace}/example"

value = Field(int)


class WatchListExampleUnsupported(Model):
class Meta:
url_template = '/example'
url_template = "/example"

value = Field(int)
14 changes: 14 additions & 0 deletions tests/k8s/test_watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def test_multiple_events(self, api_watch_list):
with pytest.raises(StopIteration):
next(gen)

api_watch_list.assert_called_with(namespace=None)

def test_handle_reconnect(self, api_watch_list):
events = [_event(0, ADDED, 1)]
api_watch_list.side_effect = [events, events]
Expand Down Expand Up @@ -81,6 +83,17 @@ def test_complicated(self, api_watch_list):
with pytest.raises(StopIteration):
next(gen)

def test_namespace(self, api_watch_list):
namespace = "the-namespace"
api_watch_list.side_effect = []

gen = Watcher(WatchListExample).watch(namespace=namespace)

with pytest.raises(StopIteration):
next(gen)

api_watch_list.assert_called_with(namespace=namespace)


def _event(id, event_type, rv, namespace="default"):
metadict = {"name": "name{}".format(id), "namespace": namespace, "resourceVersion": rv}
Expand All @@ -102,6 +115,7 @@ class WatchListExample(Model):
class Meta:
url_template = '/example'
watch_list_url = '/watch/example'
watch_list_url_template = '/watch/{namespace}/example'

apiVersion = Field(six.text_type, "example.com/v1")
kind = Field(six.text_type, "Example")
Expand Down

0 comments on commit 2b1b980

Please sign in to comment.