Skip to content

Commit

Permalink
Merge pull request #38 from fiaas/list-and-find-across-namespaces
Browse files Browse the repository at this point in the history
Support list and find on Models across namespaces
  • Loading branch information
oyvindio authored Mar 8, 2018
2 parents b13a348 + dfc64d7 commit 4e0f3d5
Show file tree
Hide file tree
Showing 11 changed files with 71 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
1 change: 1 addition & 0 deletions k8s/models/configmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class ConfigMap(Model):
class Meta:
list_url = "/api/v1/configmaps"
url_template = "/api/v1/namespaces/{namespace}/configmaps/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class DeploymentStatus(Model):

class Deployment(Model):
class Meta:
list_url = "/apis/extensions/v1beta1/deployments"
url_template = "/apis/extensions/v1beta1/namespaces/{namespace}/deployments/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/ingress.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class IngressSpec(Model):

class Ingress(Model):
class Meta:
list_url = "/apis/extensions/v1beta1/ingresses"
url_template = "/apis/extensions/v1beta1/namespaces/{namespace}/ingresses/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class JobSpec(Model):

class Job(Model):
class Meta:
list_url = "/apis/batch/v1/jobs"
url_template = "/apis/batch/v1/namespaces/{namespace}/jobs"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/pod.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class PodTemplateSpec(Model):

class Pod(Model):
class Meta:
list_url = "/api/v1/pods"
url_template = "/api/v1/namespaces/{namespace}/pods/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/pod_disruption_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class PodDisruptionBudgetSpec(Model):

class PodDisruptionBudget(Model):
class Meta:
list_url = "/apis/policy/v1beta1/poddisruptionbudgets"
url_template = "/apis/policy/v1beta1/namespaces/{namespace}/poddisruptionbudgets/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/replication_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ReplicationControllerSpec(Model):

class ReplicationController(Model):
class Meta:
list_url = "/api/v1/replicationcontrollers"
url_template = "/api/v1/namespaces/{namespace}/replicationcontrollers/{name}"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/resourcequota.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ResourceQuotaStatus(Model):

class ResourceQuota(Model):
class Meta:
list_url = "/api/v1/resourcequotas"
url_template = "/api/v1/namespaces/{namespace}/resourcequotas"

metadata = Field(ObjectMeta)
Expand Down
1 change: 1 addition & 0 deletions k8s/models/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ServiceSpec(Model):

class Service(Model):
class Meta:
list_url = "/api/v1/services"
url_template = "/api/v1/namespaces/{namespace}/services/{name}"

metadata = Field(ObjectMeta)
Expand Down
50 changes: 49 additions & 1 deletion tests/k8s/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,53 @@ 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
)

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 +168,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 4e0f3d5

Please sign in to comment.