From 89f2c5891815b6485fd805151a568f82449970bb Mon Sep 17 00:00:00 2001 From: shin- Date: Mon, 9 Dec 2013 17:45:12 +0100 Subject: [PATCH] Improved port binding conversion rules, fixed bugs, added unit tests --- README.md | 10 +++++- docker/client.py | 21 +++--------- docker/utils/__init__.py | 4 ++- docker/utils/utils.py | 34 +++++++++++++++++++ tests/test.py | 73 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ae4108fd..c7f4c432 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Bindings are then declared in the `Client.start` method. client.start(container_id, port_bindings={ 1111: 4567, - 2222: '' + 2222: None }) You can limit the host address on which the port will be exposed like such: @@ -150,6 +150,14 @@ You can limit the host address on which the port will be exposed like such: 1111: ('127.0.0.1', 4567) }) +or without host port assignment: + + client.start(container_id, port_bindings={ + 1111: ('127.0.0.1',) + }) + + + Using volumes ============= diff --git a/docker/client.py b/docker/client.py index 52b4cc14..d5e7108c 100644 --- a/docker/client.py +++ b/docker/client.py @@ -142,7 +142,7 @@ class Client(requests.Session): if len(port_definition) == 2: proto = port_definition[1] port = port_definition[0] - exposed_ports['{}{}'.format( + exposed_ports['{0}{1}'.format( port, '/' + proto if proto else '' )] = {} @@ -624,22 +624,9 @@ class Client(requests.Session): start_config['Binds'] = bind_pairs if port_bindings: - ports = {} - for k, v in six.iteritems(port_bindings): - key = str(k) - if '/' not in key: - key = key + '/tcp' - ports[key] = [{'HostIp': '', 'HostPort': ''}] - if isinstance(v, tuple): - if len(v) == 2: - ports[key][0]['HostPort'] = str(v[1]) - ports[key][0]['HostIp'] = v[0] - else: - ports[key][0]['HostPort'] = str(v[0]) - else: - ports[key][0]['HostPort'] = str(v) - - start_config['PortBindings'] = ports + start_config['PortBindings'] = utils.convert_port_bindings( + port_bindings + ) start_config['PublishAllPorts'] = publish_all_ports diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py index 82f94413..386a01af 100644 --- a/docker/utils/__init__.py +++ b/docker/utils/__init__.py @@ -1 +1,3 @@ -from .utils import compare_version, mkbuildcontext, ping, tar # flake8: noqa +from .utils import ( + compare_version, convert_port_bindings, mkbuildcontext, ping, tar +) # flake8: noqa diff --git a/docker/utils/utils.py b/docker/utils/utils.py index 1aa86aab..8fd9e947 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -60,3 +60,37 @@ def ping(url): return res.status >= 400 except Exception: return False + + +def _convert_port_binding(binding): + result = {'HostIp': '', 'HostPort': ''} + if isinstance(binding, tuple): + if len(binding) == 2: + result['HostPort'] = binding[1] + result['HostIp'] = binding[0] + elif isinstance(binding[0], six.string_types): + result['HostIp'] = binding[0] + else: + result['HostPort'] = binding[0] + else: + result['HostPort'] = binding + + if result['HostPort'] is None: + result['HostPort'] = '' + else: + result['HostPort'] = str(result['HostPort']) + + return result + + +def convert_port_bindings(port_bindings): + result = {} + for k, v in six.iteritems(port_bindings): + key = str(k) + if '/' not in key: + key = key + '/tcp' + if isinstance(v, list): + result[key] = [_convert_port_binding(binding) for binding in v] + else: + result[key] = [_convert_port_binding(v)] + return result diff --git a/tests/test.py b/tests/test.py index e058d287..7417842b 100644 --- a/tests/test.py +++ b/tests/test.py @@ -190,7 +190,7 @@ class DockerClientTest(unittest.TestCase): try: self.client.create_container('busybox', ['ls', mount_dest], - volumes={mount_dest: {}}) + volumes=[mount_dest]) except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) @@ -207,6 +207,30 @@ class DockerClientTest(unittest.TestCase): self.assertEqual(args[1]['headers'], {'Content-Type': 'application/json'}) + def test_create_container_with_ports(self): + try: + self.client.create_container('busybox', 'ls', + ports=[1111, (2222, 'udp'), (3333,)]) + except Exception as e: + self.fail('Command should not raise exception: {0}'.format(e)) + + args = fake_request.call_args + self.assertEqual(args[0][0], + 'unix://var/run/docker.sock/v1.6/containers/create') + self.assertEqual(json.loads(args[1]['data']), + json.loads(''' + {"Tty": false, "Image": "busybox", + "Cmd": ["ls"], "AttachStdin": false, + "Memory": 0, "ExposedPorts": { + "1111": {}, + "2222/udp": {}, + "3333": {} + }, + "AttachStderr": true, "Privileged": false, + "AttachStdout": true, "OpenStdin": false}''')) + self.assertEqual(args[1]['headers'], + {'Content-Type': 'application/json'}) + def test_create_container_privileged(self): try: self.client.create_container('busybox', 'true', privileged=True) @@ -324,6 +348,53 @@ class DockerClientTest(unittest.TestCase): docker.client.DEFAULT_TIMEOUT_SECONDS ) + def test_start_container_with_port_binds(self): + self.maxDiff = None + try: + self.client.start(fake_api.FAKE_CONTAINER_ID, port_bindings={ + 1111: None, + 2222: 2222, + '3333/udp': (3333,), + 4444: ('127.0.0.1',), + 5555: ('127.0.0.1', 5555), + 6666: [('127.0.0.1',), ('192.168.0.1',)] + }) + except Exception as e: + self.fail('Command should not raise exception: {0}'.format(e)) + + args = fake_request.call_args + self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.6/' + 'containers/3cc2351ab11b/start') + self.assertEqual(json.loads(args[1]['data']), { + "PublishAllPorts": False, + "PortBindings": { + "1111/tcp": [{"HostPort": "", "HostIp": ""}], + "2222/tcp": [{"HostPort": "2222", "HostIp": ""}], + "3333/udp": [{"HostPort": "3333", "HostIp": ""}], + "4444/tcp": [{ + "HostPort": "", + "HostIp": "127.0.0.1" + }], + "5555/tcp": [{ + "HostPort": "5555", + "HostIp": "127.0.0.1" + }], + "6666/tcp": [{ + "HostPort": "", + "HostIp": "127.0.0.1" + }, { + "HostPort": "", + "HostIp": "192.168.0.1" + }] + } + }) + self.assertEqual(args[1]['headers'], + {'Content-Type': 'application/json'}) + self.assertEqual( + args[1]['timeout'], + docker.client.DEFAULT_TIMEOUT_SECONDS + ) + def test_start_container_with_links(self): # one link try: