[lxc-devel] [pylxd/master] Add `NotFound` and `exists`

rockstar on Github lxc-bot at linuxcontainers.org
Thu Nov 24 18:34:09 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 774 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161124/09236aae/attachment.bin>
-------------- next part --------------
From 1fe83e913c9a054fc5e413be485ab7644a7596b3 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer at canonical.com>
Date: Thu, 24 Nov 2016 11:25:15 -0700
Subject: [PATCH 1/2] Add NotFound exception in the case of 404

---
 pylxd/client.py     | 2 ++
 pylxd/exceptions.py | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/pylxd/client.py b/pylxd/client.py
index 606f11f..b4470d0 100644
--- a/pylxd/client.py
+++ b/pylxd/client.py
@@ -64,6 +64,8 @@ def _assert_response(
         user.
         """
         if response.status_code not in allowed_status_codes:
+            if response.status_code == 404:
+                raise exceptions.NotFound(response)
             raise exceptions.LXDAPIException(response)
 
         # In the case of streaming, we can't validate the json the way we
diff --git a/pylxd/exceptions.py b/pylxd/exceptions.py
index d5b6b5e..a8b27f6 100644
--- a/pylxd/exceptions.py
+++ b/pylxd/exceptions.py
@@ -26,5 +26,9 @@ def __str__(self):
         return self.response.content.decode('utf-8')
 
 
+class NotFound(LXDAPIException):
+    """An exception raised when an object is not found."""
+
+
 class ClientConnectionFailed(Exception):
     """An exception raised when the Client connection fails."""

From 4514e8f551d57c63b86662445d8180b0935f3d7d Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer at canonical.com>
Date: Thu, 24 Nov 2016 11:26:25 -0700
Subject: [PATCH 2/2] Add `exists` to `Container`, `Image`, and `Profile`

---
 pylxd/models/_model.py               |  2 ++
 pylxd/models/container.py            |  9 +++++++++
 pylxd/models/image.py                | 16 ++++++++++++++++
 pylxd/models/profile.py              |  9 +++++++++
 pylxd/tests/models/test_container.py | 24 ++++++++++++++++++++++++
 pylxd/tests/models/test_image.py     | 29 +++++++++++++++++++++++++++++
 pylxd/tests/models/test_profile.py   | 22 ++++++++++++++++++++++
 7 files changed, 111 insertions(+)

diff --git a/pylxd/models/_model.py b/pylxd/models/_model.py
index 7307a0d..24900ae 100644
--- a/pylxd/models/_model.py
+++ b/pylxd/models/_model.py
@@ -15,6 +15,7 @@
 
 import six
 
+from pylxd import exceptions
 from pylxd.models.operation import Operation
 
 
@@ -96,6 +97,7 @@ class Model(object):
     the instance is marked as dirty. `save` will save the changes
     to the server.
     """
+    NotFound = exceptions.NotFound
     __slots__ = ['client', '__dirty__']
 
     def __init__(self, client, **kwargs):
diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index 27552f8..dbedc6c 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -91,6 +91,15 @@ def get(self, filepath):
             return response.content
 
     @classmethod
+    def exists(cls, client, name):
+        """Determine whether a container exists."""
+        try:
+            client.containers.get(name)
+            return True
+        except cls.NotFound:
+            return False
+
+    @classmethod
     def get(cls, client, name):
         """Get a container by name."""
         response = client.api.containers[name].get()
diff --git a/pylxd/models/image.py b/pylxd/models/image.py
index ac1c4fa..6c54e9f 100644
--- a/pylxd/models/image.py
+++ b/pylxd/models/image.py
@@ -58,6 +58,22 @@ def api(self):
         return self.client.api.images[self.fingerprint]
 
     @classmethod
+    def exists(cls, client, fingerprint, alias=False):
+        """Determine whether an image exists.
+
+        If `alias` is True, look up the image by its alias,
+        rather than its fingerprint.
+        """
+        try:
+            if alias:
+                client.images.get_by_alias(fingerprint)
+            else:
+                client.images.get(fingerprint)
+            return True
+        except cls.NotFound:
+            return False
+
+    @classmethod
     def get(cls, client, fingerprint):
         """Get an image."""
         response = client.api.images[fingerprint].get()
diff --git a/pylxd/models/profile.py b/pylxd/models/profile.py
index c5d564c..13f01e7 100644
--- a/pylxd/models/profile.py
+++ b/pylxd/models/profile.py
@@ -23,6 +23,15 @@ class Profile(model.Model):
     devices = model.Attribute()
 
     @classmethod
+    def exists(cls, client, name):
+        """Determine whether a profile exists."""
+        try:
+            client.profiles.get(name)
+            return True
+        except cls.NotFound:
+            return False
+
+    @classmethod
     def get(cls, client, name):
         """Get a profile."""
         response = client.api.profiles[name].get()
diff --git a/pylxd/tests/models/test_container.py b/pylxd/tests/models/test_container.py
index a32eb84..4fd15ac 100644
--- a/pylxd/tests/models/test_container.py
+++ b/pylxd/tests/models/test_container.py
@@ -72,6 +72,30 @@ def test_create(self):
 
         self.assertEqual(config['name'], an_new_container.name)
 
+    def test_exists(self):
+        """A container exists."""
+        name = 'an-container'
+
+        self.assertTrue(models.Container.exists(self.client, name))
+
+    def test_not_exists(self):
+        """A container exists."""
+        def not_found(request, context):
+            context.status_code = 404
+            return json.dumps({
+                'type': 'error',
+                'error': 'Not found',
+                'error_code': 404})
+        self.add_rule({
+            'text': not_found,
+            'method': 'GET',
+            'url': r'^http://pylxd.test/1.0/containers/an-missing-container$',  # NOQA
+        })
+
+        name = 'an-missing-container'
+
+        self.assertFalse(models.Container.exists(self.client, name))
+
     def test_fetch(self):
         """A sync updates the properties of a container."""
         an_container = models.Container(
diff --git a/pylxd/tests/models/test_image.py b/pylxd/tests/models/test_image.py
index 40cf853..7040a2d 100644
--- a/pylxd/tests/models/test_image.py
+++ b/pylxd/tests/models/test_image.py
@@ -62,6 +62,35 @@ def test_get_by_alias(self):
 
         self.assertEqual(fingerprint, a_image.fingerprint)
 
+    def test_exists(self):
+        """An image is fetched."""
+        fingerprint = hashlib.sha256(b'').hexdigest()
+
+        self.assertTrue(models.Image.exists(self.client, fingerprint))
+
+    def test_exists_by_alias(self):
+        """An image is fetched."""
+        self.assertTrue(models.Image.exists(
+            self.client, 'an-alias', alias=True))
+
+    def test_not_exists(self):
+        """LXDAPIException is raised when the image isn't found."""
+        def not_found(request, context):
+            context.status_code = 404
+            return json.dumps({
+                'type': 'error',
+                'error': 'Not found',
+                'error_code': 404})
+        self.add_rule({
+            'text': not_found,
+            'method': 'GET',
+            'url': r'^http://pylxd.test/1.0/images/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855$',  # NOQA
+        })
+
+        fingerprint = hashlib.sha256(b'').hexdigest()
+
+        self.assertFalse(models.Image.exists(self.client, fingerprint))
+
     def test_all(self):
         """A list of all images is returned."""
         images = models.Image.all(self.client)
diff --git a/pylxd/tests/models/test_profile.py b/pylxd/tests/models/test_profile.py
index 6a357aa..58b01bd 100644
--- a/pylxd/tests/models/test_profile.py
+++ b/pylxd/tests/models/test_profile.py
@@ -50,6 +50,28 @@ def error(request, context):
             exceptions.LXDAPIException,
             models.Profile.get, self.client, 'an-profile')
 
+    def test_exists(self):
+        name = 'an-profile'
+
+        self.assertTrue(models.Profile.exists(self.client, name))
+
+    def test_not_exists(self):
+        def not_found(request, context):
+            context.status_code = 404
+            return json.dumps({
+                'type': 'error',
+                'error': 'Not found',
+                'error_code': 404})
+        self.add_rule({
+            'text': not_found,
+            'method': 'GET',
+            'url': r'^http://pylxd.test/1.0/profiles/an-profile$',
+        })
+
+        name = 'an-profile'
+
+        self.assertFalse(models.Profile.exists(self.client, name))
+
     def test_all(self):
         """A list of all profiles is returned."""
         profiles = models.Profile.all(self.client)


More information about the lxc-devel mailing list