Repository: typesafehub/conductr-cli

Issue:
Distinct authorities causing spurious warnings on service cmd
@edwardcallahan Discovered the following warnings when performing `conduct services` having `http://:8080` and `http://milo.typesafe.com` as endpoint services:

```bash
ubuntu@10:~$ conduct services
SERVICE                   BUNDLE ID        BUNDLE NAME  STATUS 
http://:8080              d248907          visualizer   Running
http://milo.typesafe.com  353a913-bbb42aa  project-doc  Running

WARNING: Multiple endpoints found for the following services: 
WARNING: Service resolution for these services is undefined.
```

Those warnings shouldn't be appearing here as different authorities are used.

Discussion:

Patch:
diff --git a/conductr_cli/conduct_services.py b/conductr_cli/conduct_services.py
index cacec60..2b07544 100644
--- a/conductr_cli/conduct_services.py
+++ b/conductr_cli/conduct_services.py
@@ -34,10 +34,11 @@ def services(args):
     service_endpoints = {}
     for service in data:
         url = urlparse(service['service'])
-        try:
-            service_endpoints[url.path] |= {service['service']}
-        except KeyError:
-            service_endpoints[url.path] = {service['service']}
+        if not (url.path == '' or url.path == '/'):
+            try:
+                service_endpoints[url.path] |= {service['service']}
+            except KeyError:
+                service_endpoints[url.path] = {service['service']}
     duplicate_endpoints = [service for (service, endpoint) in service_endpoints.items() if len(endpoint) > 1] if len(service_endpoints) > 0 else []
 
     data.insert(0, {'service': 'SERVICE', 'bundle_id': 'BUNDLE ID', 'bundle_name': 'BUNDLE NAME', 'status': 'STATUS'})


Test patch:
diff --git a/conductr_cli/test/data/two_bundles_no_path.json b/conductr_cli/test/data/two_bundles_no_path.json
new file mode 100644
index 0000000..5199179
--- /dev/null
+++ b/conductr_cli/test/data/two_bundles_no_path.json
@@ -0,0 +1,144 @@
+[
+  {
+    "attributes": {
+      "bundleName": "multi-comp-multi-endp-1.0.0",
+      "diskSpace": 100,
+      "memory": 200,
+      "nrOfCpus": 1.0,
+      "roles": []
+    },
+    "bundleConfig": {
+      "endpoints": {
+        "comp1-endp1": {
+          "protocol": "http",
+          "services": ["http://:8010/comp1-endp1"]
+        },
+        "comp1-endp2": {
+          "protocol": "http",
+          "services": ["http://:8011/comp1-endp2"]
+        },
+        "comp2-endp1": {
+          "protocol": "http",
+          "services": ["http://:9010/comp2-endp1"]
+        },
+        "comp2-endp2": {
+          "protocol": "http",
+          "services": ["http://:9011"]
+        }
+      }
+    },
+    "bundleDigest": "f804d644a01a5ab9f679f76939f5c7e28301e1aecc83627877065cef26de12db",
+    "bundleExecutions": [
+      {
+        "endpoints": {
+          "comp1-endp1": {
+            "bindPort": 8000,
+            "hostPort": 8000
+          },
+          "comp1-endp2": {
+            "bindPort": 8001,
+            "hostPort": 8001
+          },
+          "comp2-endp1": {
+            "bindPort": 9000,
+            "hostPort": 9000
+          },
+          "comp2-endp2": {
+            "bindPort": 9001,
+            "hostPort": 9001
+          }
+        },
+        "host": "172.17.0.4",
+        "isStarted": true
+      }
+    ],
+    "bundleId": "f804d644a01a5ab9f679f76939f5c7e2",
+    "bundleInstallations": [
+      {
+        "bundleFile": "file:///tmp/f804d644a01a5ab9f679f76939f5c7e28301e1aecc83627877065cef26de12db.zip",
+        "uniqueAddress": {
+          "address": "akka.tcp://conductr@172.17.0.4:9004",
+          "uid": -29020887
+        }
+      },
+      {
+        "bundleFile": "file:///tmp/f804d644a01a5ab9f679f76939f5c7e28301e1aecc83627877065cef26de12db.zip",
+        "uniqueAddress": {
+          "address": "akka.tcp://conductr@172.17.0.3:9004",
+          "uid": 247035768
+        }
+      }
+    ]
+  },
+  {
+    "attributes": {
+      "bundleName": "multi2-comp-multi-endp-1.0.0",
+      "diskSpace": 100,
+      "memory": 200,
+      "nrOfCpus": 1.0,
+      "roles": []
+    },
+    "bundleConfig": {
+      "endpoints": {
+        "comp2-endp1": {
+          "protocol": "http",
+          "services": ["http://:9010/comp2-endp1"]
+        },
+        "comp2-endp2": {
+          "protocol": "http",
+          "services": ["http://:6011"]
+        },
+        "comp3-endp1": {
+          "protocol": "http",
+          "services": ["http://:7010/comp3-endp1"]
+        },
+        "comp3-endp2": {
+          "protocol": "http",
+          "services": ["http://:7011/comp3-endp2"]
+        }
+      }
+    },
+    "bundleDigest": "6e4560ef252cd57322f595627c881c48b2f8bf98c3b6d08073c0b9b5db1e5068",
+    "bundleExecutions": [
+      {
+        "endpoints": {
+          "comp2-endp1": {
+            "bindPort": 6000,
+            "hostPort": 6000
+          },
+          "comp2-endp2": {
+            "bindPort": 6001,
+            "hostPort": 6001
+          },
+          "comp3-endp1": {
+            "bindPort": 7000,
+            "hostPort": 7000
+          },
+          "comp3-endp2": {
+            "bindPort": 7001,
+            "hostPort": 7001
+          }
+        },
+        "host": "172.17.0.4",
+        "isStarted": true
+      }
+    ],
+    "bundleId": "6e4560ef252cd57322f595627c881c48",
+    "bundleInstallations": [
+      {
+        "bundleFile": "file:///tmp/6e4560ef252cd57322f595627c881c48b2f8bf98c3b6d08073c0b9b5db1e5068.zip",
+        "uniqueAddress": {
+          "address": "akka.tcp://conductr@172.17.0.4:9004",
+          "uid": -29020887
+        }
+      },
+      {
+        "bundleFile": "file:///tmp/6e4560ef252cd57322f595627c881c48b2f8bf98c3b6d08073c0b9b5db1e5068.zip",
+        "uniqueAddress": {
+          "address": "akka.tcp://conductr@172.17.0.3:9004",
+          "uid": 247035768
+        }
+      }
+    ]
+  }
+]
diff --git a/conductr_cli/test/test_conduct_services.py b/conductr_cli/test/test_conduct_services.py
index 8fc4fc8..590794e 100644
--- a/conductr_cli/test/test_conduct_services.py
+++ b/conductr_cli/test/test_conduct_services.py
@@ -52,6 +52,27 @@ class TestConductServicesCommand(TestCase, CliTestCase):
                             |"""),
             self.output(stdout))
 
+    def test_two_bundles_mult_components_endpoints_no_path(self):
+        http_method = self.respond_with_file_contents('data/two_bundles_no_path.json')
+        stdout = MagicMock()
+
+        with patch('requests.get', http_method), patch('sys.stdout', stdout):
+            conduct_services.services(MagicMock(**self.default_args))
+
+        http_method.assert_called_with(self.default_url)
+        self.assertEqual(
+            strip_margin("""|SERVICE                   BUNDLE ID  BUNDLE NAME                   STATUS
+                            |http://:6011              6e4560e    multi2-comp-multi-endp-1.0.0  Running
+                            |http://:7010/comp3-endp1  6e4560e    multi2-comp-multi-endp-1.0.0  Running
+                            |http://:7011/comp3-endp2  6e4560e    multi2-comp-multi-endp-1.0.0  Running
+                            |http://:8010/comp1-endp1  f804d64    multi-comp-multi-endp-1.0.0   Running
+                            |http://:8011/comp1-endp2  f804d64    multi-comp-multi-endp-1.0.0   Running
+                            |http://:9010/comp2-endp1  f804d64    multi-comp-multi-endp-1.0.0   Running
+                            |http://:9010/comp2-endp1  6e4560e    multi2-comp-multi-endp-1.0.0  Running
+                            |http://:9011              f804d64    multi-comp-multi-endp-1.0.0   Running
+                            |"""),
+            self.output(stdout))
+
     def test_one_bundle_starting(self):
         http_method = self.respond_with_file_contents('data/one_bundle_starting.json')
         stdout = MagicMock()