Repository: docker/docker-py

Issue:
Support for new ExposedPorts syntax
Update `client.port` function to parse the new `NetworkSettings.Ports` dict



Discussion:

Resolving patch:
diff --git a/docker/client.py b/docker/client.py
--- a/docker/client.py
+++ b/docker/client.py
@@ -540,13 +540,13 @@ def port(self, container, private_port):
         self._raise_for_status(res)
         json_ = res.json()
         s_port = str(private_port)
-        f_port = None
-        if s_port in json_['NetworkSettings']['PortMapping']['Udp']:
-            f_port = json_['NetworkSettings']['PortMapping']['Udp'][s_port]
-        elif s_port in json_['NetworkSettings']['PortMapping']['Tcp']:
-            f_port = json_['NetworkSettings']['PortMapping']['Tcp'][s_port]
+        h_ports = None
 
-        return f_port
+        h_ports = json_['NetworkSettings']['Ports'].get(s_port + '/udp')
+        if h_ports is None:
+            h_ports = json_['NetworkSettings']['Ports'].get(s_port + '/tcp')
+
+        return h_ports
 
     def pull(self, repository, tag=None, stream=False):
         registry, repo_name = auth.resolve_repository_name(repository)


Test patch:
diff --git a/tests/fake_api.py b/tests/fake_api.py
--- a/tests/fake_api.py
+++ b/tests/fake_api.py
@@ -157,6 +157,34 @@ def get_fake_inspect_image():
     return status_code, response
 
 
+def get_fake_port():
+    status_code = 200
+    response = {
+        'HostConfig': {
+            'Binds': None,
+            'ContainerIDFile': '',
+            'Links': None,
+            'LxcConf': None,
+            'PortBindings': {
+                '1111': None,
+                '1111/tcp': [{'HostIp': '127.0.0.1', 'HostPort': '4567'}],
+                '2222': None
+            },
+            'Privileged': False,
+            'PublishAllPorts': False
+        },
+        'NetworkSettings': {
+            'Bridge': 'docker0',
+            'PortMapping': None,
+            'Ports': {
+                '1111': None,
+                '1111/tcp': [{'HostIp': '127.0.0.1', 'HostPort': '4567'}],
+                '2222': None}
+        }
+    }
+    return status_code, response
+
+
 def get_fake_insert_image():
     status_code = 200
     response = {'StatusCode': 0}
@@ -282,6 +310,8 @@ def post_fake_tag_image():
     post_fake_stop_container,
     '{1}/{0}/containers/3cc2351ab11b/kill'.format(CURRENT_VERSION, prefix):
     post_fake_kill_container,
+    '{1}/{0}/containers/3cc2351ab11b/json'.format(CURRENT_VERSION, prefix):
+    get_fake_port,
     '{1}/{0}/containers/3cc2351ab11b/restart'.format(CURRENT_VERSION, prefix):
     post_fake_restart_container,
     '{1}/{0}/containers/3cc2351ab11b'.format(CURRENT_VERSION, prefix):
diff --git a/tests/integration_test.py b/tests/integration_test.py
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -403,6 +403,34 @@ def runTest(self):
         self.assertEqual(state['Running'], False, state)
 
 
+class TestPort(BaseTestCase):
+    def runTest(self):
+
+        port_bindings = {
+            1111: ('127.0.0.1', '4567'),
+            2222: ('192.168.0.100', '4568')
+        }
+
+        container = self.client.create_container(
+            'busybox', ['sleep', '60'], ports=port_bindings.keys()
+            )
+        id = container['Id']
+
+        self.client.start(container, port_bindings=port_bindings)
+
+        #Call the port function on each biding and compare expected vs actual
+        for port in port_bindings:
+            actual_bindings = self.client.port(container, port)
+            port_binding = actual_bindings.pop()
+
+            ip, host_port = port_binding['HostIp'], port_binding['HostPort']
+
+            self.assertEqual(ip, port_bindings[port][0])
+            self.assertEqual(host_port, port_bindings[port][1])
+
+        self.client.kill(id)
+
+
 class TestRestart(BaseTestCase):
     def runTest(self):
         container = self.client.create_container('busybox', ['sleep', '9999'])
diff --git a/tests/test.py b/tests/test.py
--- a/tests/test.py
+++ b/tests/test.py
@@ -34,10 +34,6 @@
     import mock
 
 
-# FIXME: missing tests for
-# port;
-
-
 def response(status_code=200, content='', headers=None, reason=None, elapsed=0,
              request=None):
     res = requests.Response()
@@ -587,6 +583,17 @@ def test_diff_with_dict_instead_of_id(self):
             timeout=docker.client.DEFAULT_TIMEOUT_SECONDS
         )
 
+    def test_port(self):
+        try:
+            self.client.port({'Id': fake_api.FAKE_CONTAINER_ID}, 1111)
+        except Exception as e:
+            self.fail('Command should not raise exception: {0}'.format(e))
+
+        fake_request.assert_called_with(
+            'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/json',
+            timeout=docker.client.DEFAULT_TIMEOUT_SECONDS
+        )
+
     def test_stop_container(self):
         try:
             self.client.stop(fake_api.FAKE_CONTAINER_ID, timeout=2)