From 2c6f367035bd09978d0fbf8c959d03210e421a9b Mon Sep 17 00:00:00 2001
From: James Nzomo <kazikubwa@gmail.com>
Date: Mon, 4 Jan 2016 16:09:29 +0300
Subject: [PATCH] Fix upload to pseudo-dir passed by <container> arg

This fix makes it possible to upload objects to pseudo-folders by
passing the upload paths via <container> arg regardless of whether the
container or folder path exist or not.

Change-Id: I575e58aa12adcf71cdaa70d025a0ea5c63f46903
Closes-Bug: #1478210
Partial-Bug: #1432734
Related-Bug: #1432734
---
 swiftclient/service.py   | 10 +++++++++-
 tests/unit/test_shell.py | 20 +++++++++++++++++++-
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/swiftclient/service.py b/swiftclient/service.py
index 8df13897..3d32fe75 100644
--- a/swiftclient/service.py
+++ b/swiftclient/service.py
@@ -1338,6 +1338,12 @@ class SwiftService(object):
         except ValueError:
             raise SwiftError('Segment size should be an integer value')
 
+        # Incase we have a psudeo-folder path for <container> arg, derive
+        # the container name from the top path to ensure new folder creation
+        # and prevent spawning zero-byte objects shadowing pseudo-folders
+        # by name.
+        container_name = container.split('/', 1)[0]
+
         # Try to create the container, just in case it doesn't exist. If this
         # fails, it might just be because the user doesn't have container PUT
         # permissions, so we'll ignore any error. If there's really a problem,
@@ -1349,7 +1355,9 @@ class SwiftService(object):
                 _header[POLICY]
         create_containers = [
             self.thread_manager.container_pool.submit(
-                self._create_container_job, container, headers=policy_header
+                self._create_container_job,
+                container_name,
+                headers=policy_header
             )
         ]
 
diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py
index f962c636..662fbcc7 100644
--- a/tests/unit/test_shell.py
+++ b/tests/unit/test_shell.py
@@ -463,7 +463,7 @@ class TestShell(testtools.TestCase):
         swiftclient.shell.main(argv)
         connection.return_value.put_container.assert_called_once_with(
             'container',
-            {'X-Storage-Policy': mock.ANY},
+            {'X-Storage-Policy': 'one'},
             response_dict={})
 
         connection.return_value.put_object.assert_called_with(
@@ -475,6 +475,24 @@ class TestShell(testtools.TestCase):
                      'X-Storage-Policy': 'one'},
             response_dict={})
 
+        # upload to pseudo-folder (via <container> param)
+        argv = ["", "upload", "container/pseudo-folder/nested", self.tmpfile,
+                "-H", "X-Storage-Policy:one"]
+        swiftclient.shell.main(argv)
+        connection.return_value.put_container.assert_called_with(
+            'container',
+            {'X-Storage-Policy': 'one'},
+            response_dict={})
+
+        connection.return_value.put_object.assert_called_with(
+            'container/pseudo-folder/nested',
+            self.tmpfile.lstrip('/'),
+            mock.ANY,
+            content_length=0,
+            headers={'x-object-meta-mtime': mock.ANY,
+                     'X-Storage-Policy': 'one'},
+            response_dict={})
+
         # Upload whole directory
         argv = ["", "upload", "container", "/tmp"]
         _tmpfile = self.tmpfile