[lxc-devel] [pylxd/master] Add mode/uid/gid to the container put method

ajkavanagh on Github lxc-bot at linuxcontainers.org
Tue Feb 6 13:00:16 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 522 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180206/a91acdad/attachment.bin>
-------------- next part --------------
From cc434441d918b05c55289ec5de156ffdcee19583 Mon Sep 17 00:00:00 2001
From: Kees Bos <cornelis.bos at gmail.com>
Date: Thu, 22 Dec 2016 15:23:48 +0100
Subject: [PATCH 1/3] Handle mode/uid/gid in FilesManager.put()

---
 pylxd/models/container.py | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index f2c8f91..7f7281e 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -78,7 +78,20 @@ def __init__(self, client, container):
             self._client = client
             self._container = container
 
-        def put(self, filepath, data):
+        def put(self, filepath, data, mode=None, uid=None, gid=None):
+
+            if isinstance(mode, int):
+                mode = oct(mode)
+            elif not mode.startswith('0'):
+                mode = '0{0}'.format(mode)
+            headers = {}
+            if mode is not None:
+                headers['X-LXD-mode'] = mode
+            if uid is not None:
+                headers['X-LXD-uid'] = str(uid)
+            if gid is not None:
+                headers['X-LXD-gid'] = str(gid)
+
             response = self._client.api.containers[
                 self._container.name].files.post(
                 params={'path': filepath}, data=data)

From 23791a13c5e30226786ce0045f923f43e94ad5b7 Mon Sep 17 00:00:00 2001
From: Kees Bos <cornelis.bos at gmail.com>
Date: Fri, 27 Jan 2017 09:41:19 +0100
Subject: [PATCH 2/3] Fix FilesManager.put mode checking

---
 pylxd/models/container.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index 7f7281e..03f031b 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -80,12 +80,12 @@ def __init__(self, client, container):
 
         def put(self, filepath, data, mode=None, uid=None, gid=None):
 
-            if isinstance(mode, int):
-                mode = oct(mode)
-            elif not mode.startswith('0'):
-                mode = '0{0}'.format(mode)
             headers = {}
             if mode is not None:
+                if isinstance(mode, int):
+                    mode = oct(mode)
+                elif not mode.startswith('0'):
+                    mode = '0{0}'.format(mode)
                 headers['X-LXD-mode'] = mode
             if uid is not None:
                 headers['X-LXD-uid'] = str(uid)

From 4a9292c0bcd10048f705e5dbdb5c3f4e07f23e38 Mon Sep 17 00:00:00 2001
From: Alex Kavanagh <alex at ajkavanagh.co.uk>
Date: Tue, 6 Feb 2018 12:56:40 +0000
Subject: [PATCH 3/3] Fix up file mode to work on Py3 and add tests/docs

Fix up the file mode str-ing to work with Python 3.
Add tests to check that headers are being set according to policy.
Add docstring to the `put` method to describe how to use the feature.
---
 pylxd/models/container.py            | 44 ++++++++++++++++++++++--------
 pylxd/tests/models/test_container.py | 53 +++++++++++++++++++++++++++++++++---
 2 files changed, 82 insertions(+), 15 deletions(-)

diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index 03f031b..bc7f984 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -79,22 +79,45 @@ def __init__(self, client, container):
             self._container = container
 
         def put(self, filepath, data, mode=None, uid=None, gid=None):
-
+            """Push a file to the container.
+
+            This pushes a single file to the containers file system named by
+            the `filepath`.
+
+            :param filepath: The path in the container to to store the data in.
+            :type filepath: str
+            :param data: The data to store in the file.
+            :type data: bytes or str
+            :param mode: The unit mode to store the file with.  The default of
+                None stores the file with the current mask of 0700, which is
+                the lxd default.
+            :type mode: oct | int | str
+            :param uid: The uid to use inside the container. Default of None
+                results in 0 (root).
+            :type uid: int
+            :param gid: The gid to use inside the container.  Default of None
+                results in 0 (root).
+            :type gid: int
+            :returns: True if the file store succeeded otherwise False.
+            :rtype: Bool
+            """
             headers = {}
             if mode is not None:
                 if isinstance(mode, int):
-                    mode = oct(mode)
-                elif not mode.startswith('0'):
-                    mode = '0{0}'.format(mode)
+                    mode = format(mode, 'o')
+                if not isinstance(mode, six.string_types):
+                    raise ValueError("'mode' parameter must be int or string")
+                if not mode.startswith('0'):
+                    mode = '0{}'.format(mode)
                 headers['X-LXD-mode'] = mode
             if uid is not None:
                 headers['X-LXD-uid'] = str(uid)
             if gid is not None:
                 headers['X-LXD-gid'] = str(gid)
-
-            response = self._client.api.containers[
-                self._container.name].files.post(
-                params={'path': filepath}, data=data)
+            response = (self._client.api.containers[self._container.name]
+                        .files.post(params={'path': filepath},
+                                    data=data,
+                                    headers=headers or None))
             return response.status_code == 200
 
         def delete_available(self):
@@ -114,9 +137,8 @@ def delete(self, filepath):
                     'File Deletion is not available for this host')
 
         def get(self, filepath):
-            response = self._client.api.containers[
-                self._container.name].files.get(
-                params={'path': filepath})
+            response = (self._client.api.containers[self._container.name]
+                        .files.get(params={'path': filepath}))
             return response.content
 
     @classmethod
diff --git a/pylxd/tests/models/test_container.py b/pylxd/tests/models/test_container.py
index f32ceff..72b3bff 100644
--- a/pylxd/tests/models/test_container.py
+++ b/pylxd/tests/models/test_container.py
@@ -436,7 +436,9 @@ def test_put_delete(self):
         data = 'The quick brown fox'
 
         res = self.container.files.put('/tmp/putted', data)
-        self.assertEqual(True, res, msg='Failed to put file, result: {}'.format(res)) # NOQA
+        self.assertEqual(True, res,
+                         msg=('Failed to put file, result: {}'
+                              .format(res)))  # NOQA
 
         # we are mocked, so delete should initially not be available
         self.assertEqual(False, self.container.files.delete_available())
@@ -462,7 +464,48 @@ def test_put_delete(self):
         self.assertEqual(True, self.container.files.delete_available())
 
         res = self.container.files.delete('/tmp/putted')
-        self.assertEqual(True, res, msg='Failed to delete file, result: {}'.format(res)) # NOQA
+        self.assertEqual(True, res,
+                         msg=('Failed to delete file, result: {}'
+                              .format(res)))  # NOQA
+
+    def test_put_mode_uid_gid(self):
+        """Should be able to set the mode, uid and gid of a file"""
+        # fix up the default POST rule to allow us to see the posted vars
+        _capture = {}
+
+        def capture(request, context):
+            _capture['headers'] = getattr(request._request, 'headers')
+            context.status_code = 200
+
+        rule = {
+            'text': capture,
+            'method': 'POST',
+            'url': (r'^http://pylxd.test/1.0/containers/an-container/files'
+                    '\?path=%2Ftmp%2Fputted$'),  # NOQA
+        }
+        self.add_rule(rule)
+
+        data = 'The quick brown fox'
+        # start with an octal mode
+        res = self.container.files.put('/tmp/putted', data, mode=0o123,
+                                       uid=1, gid=2)
+        self.assertEqual(True, res,
+                         msg=('Failed to put file, result: {}'
+                              .format(res)))  # NOQA
+        headers = _capture['headers']
+        self.assertEqual(headers['X-LXD-mode'], '0123')
+        self.assertEqual(headers['X-LXD-uid'], '1')
+        self.assertEqual(headers['X-LXD-gid'], '2')
+        # use a str mode this type
+        res = self.container.files.put('/tmp/putted', data, mode='456')
+        self.assertEqual(True, res,
+                         msg=('Failed to put file, result: {}'
+                              .format(res)))  # NOQA
+        headers = _capture['headers']
+        self.assertEqual(headers['X-LXD-mode'], '0456')
+        # check that assertion is raised
+        with self.assertRaises(ValueError):
+            res = self.container.files.put('/tmp/putted', data, mode=object)
 
     def test_get(self):
         """A file is retrieved from the container."""
@@ -477,7 +520,8 @@ def not_found(request, context):
         rule = {
             'text': not_found,
             'method': 'GET',
-            'url': r'^http://pylxd.test/1.0/containers/an-container/files\?path=%2Ftmp%2Fgetted$',  # NOQA
+            'url': (r'^http://pylxd.test/1.0/containers/an-container/files'
+                    '\?path=%2Ftmp%2Fgetted$'),  # NOQA
         }
         self.add_rule(rule)
 
@@ -492,7 +536,8 @@ def not_found(request, context):
         rule = {
             'text': not_found,
             'method': 'GET',
-            'url': r'^http://pylxd.test/1.0/containers/an-container/files\?path=%2Ftmp%2Fgetted$',  # NOQA
+            'url': (r'^http://pylxd.test/1.0/containers/an-container/files'
+                    '\?path=%2Ftmp%2Fgetted$'),  # NOQA
         }
         self.add_rule(rule)
 


More information about the lxc-devel mailing list