Skip to content

Commit

Permalink
Support list and find on Models across namespaces
Browse files Browse the repository at this point in the history
Add list_url metaclass field to hold the url for the list all namespaces endpoint
  • Loading branch information
oyvindio committed Mar 6, 2018
1 parent b13a348 commit fc426d9
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 3 deletions.
15 changes: 13 additions & 2 deletions k8s/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __new__(mcs, cls, bases, attrs):
bases += (ApiMixIn,)
meta = {
"url_template": getattr(attr_meta, "url_template", ""),
"list_url": getattr(attr_meta, "list_url", ""),
"watch_list_url": getattr(attr_meta, "watch_list_url", ""),
"watch_list_url_template": getattr(attr_meta, "watch_list_url_template", ""),
"fields": [],
Expand Down Expand Up @@ -64,7 +65,12 @@ def _build_url(cls, **kwargs):

@classmethod
def find(cls, name, namespace="default", labels=None):
url = cls._build_url(name="", namespace=namespace)
if namespace is None:
if not cls._meta.list_url:
raise NotImplementedError("Cannot find without namespace, no list_url defined on class {}".format(cls))
url = cls._meta.list_url
else:
url = cls._build_url(name="", namespace=namespace)
if labels:
selector = ",".join("{}={}".format(k, v) for k, v in labels.items())
else:
Expand All @@ -74,7 +80,12 @@ def find(cls, name, namespace="default", labels=None):

@classmethod
def list(cls, namespace="default"):
url = cls._build_url(name="", namespace=namespace)
if namespace is None:
if not cls._meta.list_url:
raise NotImplementedError("Cannot list without namespace, no list_url defined on class {}".format(cls))
url = cls._meta.list_url
else:
url = cls._build_url(name="", namespace=namespace)
resp = cls._client.get(url)
return [cls.from_dict(item) for item in resp.json()[u"items"]]

Expand Down
58 changes: 57 additions & 1 deletion tests/k8s/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,61 @@ def test_watch_list_with_namespace(self, session):
"GET", _absolute_url("/watch/explicitly-set/example"), json=None, timeout=None, stream=True
)

def test_list_without_namespace_should_raise_exception_when_list_url_is_not_set_on_metaclass(self, session):
with pytest.raises(NotImplementedError):
WatchListExampleUnsupported.list(namespace=None)

def test_list_default_namespace(self, session):
WatchListExample.list()
session.request.assert_called_once_with(
"GET", _absolute_url("/apis/namespaces/default/example"), json=None, timeout=10
)

def test_list_explicit_namespace(self, session):
WatchListExample.list(namespace="explicitly-set")
session.request.assert_called_once_with(
"GET", _absolute_url("/apis/namespaces/explicitly-set/example"), json=None, timeout=10
)

def test_list_without_namespace(self, session):
WatchListExample.list(namespace=None)
session.request.assert_called_once_with(
"GET", _absolute_url("/example/list"), json=None, timeout=10
)

@pytest.mark.parametrize("key", SENSITIVE_HEADERS)
def test_redacts_sensitive_headers(self, key):
message = []
sensitive_value = "super sensitive data that should not be exposed"
Client._add_headers(message, {key: sensitive_value}, "")
text = "".join(message)
assert sensitive_value not in text

def test_find_without_namespace_should_raise_exception_when_list_url_is_not_set_on_metaclass(self, session):
with pytest.raises(NotImplementedError):
list(WatchListExampleUnsupported.find("foo", namespace=None))

def test_find_default_namespace(self, session):
WatchListExample.find("foo")
session.request.assert_called_once_with(
"GET", _absolute_url("/apis/namespaces/default/example"), json=None, timeout=10,
params={"labelSelector": "app=foo"}
)

def test_find_explicit_namespace(self, session):
WatchListExample.find("foo", namespace="explicitly-set")
session.request.assert_called_once_with(
"GET", _absolute_url("/apis/namespaces/explicitly-set/example"), json=None, timeout=10,
params={"labelSelector": "app=foo"}
)

def test_find_without_namespace(self, session):
WatchListExample.find("foo", namespace=None)
session.request.assert_called_once_with(
"GET", _absolute_url("/example/list"), json=None, timeout=10,
params={"labelSelector": "app=foo"}
)

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

class WatchListExample(Model):
class Meta:
url_template = "/example"
list_url = "/example/list"
url_template = "/apis/namespaces/{namespace}/example"
watch_list_url = "/watch/example"
watch_list_url_template = "/watch/{namespace}/example"

Expand Down

0 comments on commit fc426d9

Please sign in to comment.