Repository: typesafehub/conductr-cli

Issue:
Unable to load bundle from https 
ConductR RC1, CLI 0.15

Loading a bundle via https hangs. The cli must be terminated.

```bash
> conduct load https://github.com/typesafehub/project-doc/releases/download/1.0/project-doc-1.0-SNAPSHOT-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip
Retrieving bundle...
Retrieving configuration...
Loading bundle to ConductR...
```
If I wget the bundle to the local file system, the bundle loads successfully.

Discussion:

Patch:
diff --git a/conductr_cli/conduct_load.py b/conductr_cli/conduct_load.py
index 38bc3d8..982965d 100644
--- a/conductr_cli/conduct_load.py
+++ b/conductr_cli/conduct_load.py
@@ -19,11 +19,14 @@ def load(args):
     """`conduct load` command"""
 
     print('Retrieving bundle...')
-    bundle_file, bundle_headers = urlretrieve(get_url(args.bundle))
+    bundle_name, bundle_url = get_url(args.bundle)
+    bundle_file, bundle_headers = urlretrieve(bundle_url)
+
+    configuration_file, configuration_headers, configuration_name = (None, None, None)
     if args.configuration is not None:
         print('Retrieving configuration...')
-    configuration_file, configuration_headers = urlretrieve(get_url(args.configuration)) \
-        if args.configuration is not None else (None, None)
+        configuration_name, configuration_url = get_url(args.configuration)
+        configuration_file, configuration_headers = urlretrieve(configuration_url)
 
     bundle_conf = ConfigFactory.parse_string(bundle_utils.conf(bundle_file))
     overlay_bundle_conf = None if configuration_file is None else \
@@ -39,10 +42,10 @@ def load(args):
         ('roles', ' '.join(with_bundle_configurations(ConfigTree.get_list, 'roles'))),
         ('bundleName', with_bundle_configurations(ConfigTree.get_string, 'name')),
         ('system', with_bundle_configurations(ConfigTree.get_string, 'system')),
-        ('bundle', open(bundle_file, 'rb'))
+        ('bundle', (bundle_name, open(bundle_file, 'rb')))
     ]
     if configuration_file is not None:
-        files.append(('configuration', open(configuration_file, 'rb')))
+        files.append(('configuration', (configuration_name, open(configuration_file, 'rb'))))
 
     print('Loading bundle to ConductR...')
     response = requests.post(url, files=files)
@@ -74,4 +77,5 @@ def get_url(uri):
     parsed = urlparse(uri, scheme='file')
     op = Path(uri)
     np = str(op.cwd() / op if parsed.scheme == 'file' and op.root == '' else parsed.path)
-    return urlunparse(ParseResult(parsed.scheme, parsed.netloc, np, parsed.params, parsed.query, parsed.fragment))
+    url = urlunparse(ParseResult(parsed.scheme, parsed.netloc, np, parsed.params, parsed.query, parsed.fragment))
+    return (url.split('/')[-1], url)


Test patch:
diff --git a/conductr_cli/test/test_conduct_load.py b/conductr_cli/test/test_conduct_load.py
index b5503cb..93ee6f7 100644
--- a/conductr_cli/test/test_conduct_load.py
+++ b/conductr_cli/test/test_conduct_load.py
@@ -2,6 +2,7 @@ from unittest import TestCase
 from conductr_cli.test.cli_test_case import CliTestCase, create_temp_bundle, create_temp_bundle_with_contents, strip_margin
 from conductr_cli import conduct_load
 from urllib.error import URLError
+import os
 import shutil
 
 try:
@@ -54,7 +55,7 @@ class TestConductLoadCommand(TestCase, CliTestCase):
         ('roles', ' '.join(roles)),
         ('bundleName', bundleName),
         ('system', system),
-        ('bundle', 1)
+        ('bundle', ('bundle.zip', 1))
     ]
 
     output_template = """|Retrieving bundle...
@@ -177,7 +178,7 @@ class TestConductLoadCommand(TestCase, CliTestCase):
             [call(self.bundle_file, 'rb'), call(config_file, 'rb')]
         )
 
-        expected_files = self.default_files + [('configuration', 1)]
+        expected_files = self.default_files + [('configuration', ('bundle.zip', 1))]
         expected_files[4] = ('bundleName', 'overlaid-name')
         http_method.assert_called_with(self.default_url, files=expected_files)
 
@@ -361,3 +362,16 @@ class TestConductLoadCommand(TestCase, CliTestCase):
             strip_margin("""|ERROR: File not found: no_such.conf
                             |"""),
             self.output(stderr))
+
+
+class TestGetUrl(TestCase):
+
+    def test_url(self):
+        filename, url = conduct_load.get_url('https://site.com/bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip')
+        self.assertEqual('bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip', filename)
+        self.assertEqual('https://site.com/bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip', url)
+
+    def test_file(self):
+        filename, url = conduct_load.get_url('bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip')
+        self.assertEqual('bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip', filename)
+        self.assertEqual('file://' + os.getcwd() + '/bundle-1.0-e78ed07d4a895e14595a21aef1bf616b1b0e4d886f3265bc7b152acf93d259b5.zip', url)