[lxc-devel] [pylxd/master] Allow multipart form uploads for images.

rockstar on Github lxc-bot at linuxcontainers.org
Wed Nov 23 20:44:52 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 654 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161123/6b6b501d/attachment.bin>
-------------- next part --------------
From b558ef2ef02041ce6b46e0719bb9d5f97e2fd2e8 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer at canonical.com>
Date: Wed, 23 Nov 2016 13:41:51 -0700
Subject: [PATCH] Allow multipart form uploads for images.

In some cases, a user may want to upload a manifest with a raw image,
and pylxd previously didn't support that. This patch adds the needed
functionality.

As I did this, I found a bug in Image.create where fingerprint isn't
always reliably determined, so I have deprecated the ability to pass
a wait parameter to Image.create.

Fixes issue #172
---
 pylxd/models/image.py            | 56 +++++++++++++++++++++++++++++++++-------
 pylxd/tests/models/test_image.py |  3 ++-
 2 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/pylxd/models/image.py b/pylxd/models/image.py
index 2293dd9..18caed4 100644
--- a/pylxd/models/image.py
+++ b/pylxd/models/image.py
@@ -12,8 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 import contextlib
-import hashlib
 import tempfile
+import uuid
+import warnings
+
+import six
 
 from pylxd.models import _model as model
 from pylxd.models.operation import Operation
@@ -82,19 +85,54 @@ def all(cls, client):
         return images
 
     @classmethod
-    def create(cls, client, image_data, public=False, wait=False):
-        """Create an image."""
-        fingerprint = hashlib.sha256(image_data).hexdigest()
+    def create(
+            cls, client, image_data, metadata=None, public=False, wait=True):
+        """Create an image.
+
+        If metadata is provided, a multipart form data request is formed to
+        push metadata and image together in a single request. The metadata must
+        be a tar achive.
+
+        `wait` parameter is now ignored, as the image fingerprint cannot be
+        reliably determined consistently until after the image is indexed.
+        """
+
+        if wait is False:
+            warnings.warn(
+                'Image.create wait parameter ignored and will be removed in '
+                '2.3', DeprecationWarning)
 
         headers = {}
         if public:
             headers['X-LXD-Public'] = '1'
-        response = client.api.images.post(
-            data=image_data, headers=headers)
 
-        if wait:
-            Operation.wait_for_operation(client, response.json()['operation'])
-        return cls(client, fingerprint=fingerprint)
+        if metadata is not None:
+            boundary = str(uuid.uuid1())
+
+            data = b''
+            for name, contents in (
+                    ('metadata', metadata), ('rootfs', image_data)):
+                data += b'\r\n'.join([
+                    six.b('--{}'.format(boundary)),
+                    six.b(
+                        'Content-Disposition:form-data;'
+                        'name={};filename={}'.format(name, name)),
+                    b'Content-Type: application/octet-stream',
+                    b'',
+                    contents,
+                    b'',
+                ])
+            data += six.b('--{}--\r\n\r\n'.format(boundary))
+
+            headers['Content-Type'] = "multipart/form-data;boundary={}".format(
+                boundary)
+        else:
+            data = image_data
+
+        response = client.api.images.post(data=data, headers=headers)
+        operation = client.operations.wait_for_operation(
+            response.json()['operation'])
+        return cls(client, fingerprint=operation.metadata['fingerprint'])
 
     @classmethod
     def create_from_simplestreams(cls, client, server, alias,
diff --git a/pylxd/tests/models/test_image.py b/pylxd/tests/models/test_image.py
index 230b5da..9e2b39c 100644
--- a/pylxd/tests/models/test_image.py
+++ b/pylxd/tests/models/test_image.py
@@ -71,7 +71,8 @@ def test_all(self):
     def test_create(self):
         """An image is created."""
         fingerprint = hashlib.sha256(b'').hexdigest()
-        a_image = models.Image.create(self.client, b'', public=True, wait=True)
+        a_image = models.Image.create(
+            self.client, b'', metadata=b'', public=True, wait=True)
 
         self.assertIsInstance(a_image, models.Image)
         self.assertEqual(fingerprint, a_image.fingerprint)


More information about the lxc-devel mailing list