mirror of https://github.com/docker/docs.git
rebasing port range changes
Signed-off-by: Yuval Kohavi <yuval.kohavi@gmail.com>
This commit is contained in:
parent
22b7ee42de
commit
297941e460
|
@ -10,6 +10,7 @@ from operator import attrgetter
|
||||||
import six
|
import six
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
from docker.utils import create_host_config, LogConfig
|
from docker.utils import create_host_config, LogConfig
|
||||||
|
from docker.utils.ports import build_port_bindings, split_port
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from .config import DOCKER_CONFIG_KEYS, merge_environment
|
from .config import DOCKER_CONFIG_KEYS, merge_environment
|
||||||
|
@ -599,13 +600,13 @@ class Service(object):
|
||||||
if 'ports' in container_options or 'expose' in self.options:
|
if 'ports' in container_options or 'expose' in self.options:
|
||||||
ports = []
|
ports = []
|
||||||
all_ports = container_options.get('ports', []) + self.options.get('expose', [])
|
all_ports = container_options.get('ports', []) + self.options.get('expose', [])
|
||||||
for port in all_ports:
|
for port_range in all_ports:
|
||||||
port = str(port)
|
internal_range, _ = split_port(port_range)
|
||||||
if ':' in port:
|
for port in internal_range:
|
||||||
port = port.split(':')[-1]
|
port = str(port)
|
||||||
if '/' in port:
|
if '/' in port:
|
||||||
port = tuple(port.split('/'))
|
port = tuple(port.split('/'))
|
||||||
ports.append(port)
|
ports.append(port)
|
||||||
container_options['ports'] = ports
|
container_options['ports'] = ports
|
||||||
|
|
||||||
override_options['binds'] = merge_volume_bindings(
|
override_options['binds'] = merge_volume_bindings(
|
||||||
|
@ -859,38 +860,6 @@ def parse_volume_spec(volume_config):
|
||||||
|
|
||||||
return VolumeSpec(external, internal, mode)
|
return VolumeSpec(external, internal, mode)
|
||||||
|
|
||||||
|
|
||||||
# Ports
|
|
||||||
|
|
||||||
|
|
||||||
def build_port_bindings(ports):
|
|
||||||
port_bindings = {}
|
|
||||||
for port in ports:
|
|
||||||
internal_port, external = split_port(port)
|
|
||||||
if internal_port in port_bindings:
|
|
||||||
port_bindings[internal_port].append(external)
|
|
||||||
else:
|
|
||||||
port_bindings[internal_port] = [external]
|
|
||||||
return port_bindings
|
|
||||||
|
|
||||||
|
|
||||||
def split_port(port):
|
|
||||||
parts = str(port).split(':')
|
|
||||||
if not 1 <= len(parts) <= 3:
|
|
||||||
raise ConfigError('Invalid port "%s", should be '
|
|
||||||
'[[remote_ip:]remote_port:]port[/protocol]' % port)
|
|
||||||
|
|
||||||
if len(parts) == 1:
|
|
||||||
internal_port, = parts
|
|
||||||
return internal_port, None
|
|
||||||
if len(parts) == 2:
|
|
||||||
external_port, internal_port = parts
|
|
||||||
return internal_port, external_port
|
|
||||||
|
|
||||||
external_ip, external_port, internal_port = parts
|
|
||||||
return internal_port, (external_ip, external_port or None)
|
|
||||||
|
|
||||||
|
|
||||||
# Labels
|
# Labels
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ simple:
|
||||||
ports:
|
ports:
|
||||||
- '3000'
|
- '3000'
|
||||||
- '49152:3001'
|
- '49152:3001'
|
||||||
|
- '49153-49154:3002-3003'
|
||||||
|
|
|
@ -334,6 +334,7 @@ class CLITestCase(DockerClientTestCase):
|
||||||
# get port information
|
# get port information
|
||||||
port_random = container.get_local_port(3000)
|
port_random = container.get_local_port(3000)
|
||||||
port_assigned = container.get_local_port(3001)
|
port_assigned = container.get_local_port(3001)
|
||||||
|
port_range = container.get_local_port(3002), container.get_local_port(3003)
|
||||||
|
|
||||||
# close all one off containers we just created
|
# close all one off containers we just created
|
||||||
container.stop()
|
container.stop()
|
||||||
|
@ -342,6 +343,8 @@ class CLITestCase(DockerClientTestCase):
|
||||||
self.assertNotEqual(port_random, None)
|
self.assertNotEqual(port_random, None)
|
||||||
self.assertIn("0.0.0.0", port_random)
|
self.assertIn("0.0.0.0", port_random)
|
||||||
self.assertEqual(port_assigned, "0.0.0.0:49152")
|
self.assertEqual(port_assigned, "0.0.0.0:49152")
|
||||||
|
self.assertEqual(port_range[0], "0.0.0.0:49153")
|
||||||
|
self.assertEqual(port_range[1], "0.0.0.0:49154")
|
||||||
|
|
||||||
def test_rm(self):
|
def test_rm(self):
|
||||||
service = self.project.get_service('simple')
|
service = self.project.get_service('simple')
|
||||||
|
@ -456,7 +459,7 @@ class CLITestCase(DockerClientTestCase):
|
||||||
|
|
||||||
self.assertEqual(get_port(3000), container.get_local_port(3000))
|
self.assertEqual(get_port(3000), container.get_local_port(3000))
|
||||||
self.assertEqual(get_port(3001), "0.0.0.0:49152")
|
self.assertEqual(get_port(3001), "0.0.0.0:49152")
|
||||||
self.assertEqual(get_port(3002), "")
|
self.assertEqual(get_port(3002), "0.0.0.0:49153")
|
||||||
|
|
||||||
def test_port_with_scale(self):
|
def test_port_with_scale(self):
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ from .. import unittest
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
from docker.utils import LogConfig
|
|
||||||
|
|
||||||
from compose.service import Service
|
from compose.service import Service
|
||||||
from compose.container import Container
|
from compose.container import Container
|
||||||
|
@ -13,14 +12,11 @@ from compose.const import LABEL_SERVICE, LABEL_PROJECT, LABEL_ONE_OFF
|
||||||
from compose.service import (
|
from compose.service import (
|
||||||
ConfigError,
|
ConfigError,
|
||||||
NeedsBuildError,
|
NeedsBuildError,
|
||||||
NoSuchImageError,
|
|
||||||
build_port_bindings,
|
|
||||||
build_volume_binding,
|
build_volume_binding,
|
||||||
get_container_data_volumes,
|
get_container_data_volumes,
|
||||||
merge_volume_bindings,
|
merge_volume_bindings,
|
||||||
parse_repository_tag,
|
parse_repository_tag,
|
||||||
parse_volume_spec,
|
parse_volume_spec,
|
||||||
split_port,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,48 +104,6 @@ class ServiceTest(unittest.TestCase):
|
||||||
self.assertEqual(service._get_volumes_from(), [container_id])
|
self.assertEqual(service._get_volumes_from(), [container_id])
|
||||||
from_service.create_container.assert_called_once_with()
|
from_service.create_container.assert_called_once_with()
|
||||||
|
|
||||||
def test_split_port_with_host_ip(self):
|
|
||||||
internal_port, external_port = split_port("127.0.0.1:1000:2000")
|
|
||||||
self.assertEqual(internal_port, "2000")
|
|
||||||
self.assertEqual(external_port, ("127.0.0.1", "1000"))
|
|
||||||
|
|
||||||
def test_split_port_with_protocol(self):
|
|
||||||
internal_port, external_port = split_port("127.0.0.1:1000:2000/udp")
|
|
||||||
self.assertEqual(internal_port, "2000/udp")
|
|
||||||
self.assertEqual(external_port, ("127.0.0.1", "1000"))
|
|
||||||
|
|
||||||
def test_split_port_with_host_ip_no_port(self):
|
|
||||||
internal_port, external_port = split_port("127.0.0.1::2000")
|
|
||||||
self.assertEqual(internal_port, "2000")
|
|
||||||
self.assertEqual(external_port, ("127.0.0.1", None))
|
|
||||||
|
|
||||||
def test_split_port_with_host_port(self):
|
|
||||||
internal_port, external_port = split_port("1000:2000")
|
|
||||||
self.assertEqual(internal_port, "2000")
|
|
||||||
self.assertEqual(external_port, "1000")
|
|
||||||
|
|
||||||
def test_split_port_no_host_port(self):
|
|
||||||
internal_port, external_port = split_port("2000")
|
|
||||||
self.assertEqual(internal_port, "2000")
|
|
||||||
self.assertEqual(external_port, None)
|
|
||||||
|
|
||||||
def test_split_port_invalid(self):
|
|
||||||
with self.assertRaises(ConfigError):
|
|
||||||
split_port("0.0.0.0:1000:2000:tcp")
|
|
||||||
|
|
||||||
def test_build_port_bindings_with_one_port(self):
|
|
||||||
port_bindings = build_port_bindings(["127.0.0.1:1000:1000"])
|
|
||||||
self.assertEqual(port_bindings["1000"], [("127.0.0.1", "1000")])
|
|
||||||
|
|
||||||
def test_build_port_bindings_with_matching_internal_ports(self):
|
|
||||||
port_bindings = build_port_bindings(["127.0.0.1:1000:1000", "127.0.0.1:2000:1000"])
|
|
||||||
self.assertEqual(port_bindings["1000"], [("127.0.0.1", "1000"), ("127.0.0.1", "2000")])
|
|
||||||
|
|
||||||
def test_build_port_bindings_with_nonmatching_internal_ports(self):
|
|
||||||
port_bindings = build_port_bindings(["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"])
|
|
||||||
self.assertEqual(port_bindings["1000"], [("127.0.0.1", "1000")])
|
|
||||||
self.assertEqual(port_bindings["2000"], [("127.0.0.1", "2000")])
|
|
||||||
|
|
||||||
def test_split_domainname_none(self):
|
def test_split_domainname_none(self):
|
||||||
service = Service('foo', image='foo', hostname='name', client=self.mock_client)
|
service = Service('foo', image='foo', hostname='name', client=self.mock_client)
|
||||||
self.mock_client.containers.return_value = []
|
self.mock_client.containers.return_value = []
|
||||||
|
@ -157,23 +111,6 @@ class ServiceTest(unittest.TestCase):
|
||||||
self.assertEqual(opts['hostname'], 'name', 'hostname')
|
self.assertEqual(opts['hostname'], 'name', 'hostname')
|
||||||
self.assertFalse('domainname' in opts, 'domainname')
|
self.assertFalse('domainname' in opts, 'domainname')
|
||||||
|
|
||||||
def test_memory_swap_limit(self):
|
|
||||||
service = Service(name='foo', image='foo', hostname='name', client=self.mock_client, mem_limit=1000000000, memswap_limit=2000000000)
|
|
||||||
self.mock_client.containers.return_value = []
|
|
||||||
opts = service._get_container_create_options({'some': 'overrides'}, 1)
|
|
||||||
self.assertEqual(opts['memswap_limit'], 2000000000)
|
|
||||||
self.assertEqual(opts['mem_limit'], 1000000000)
|
|
||||||
|
|
||||||
def test_log_opt(self):
|
|
||||||
log_opt = {'address': 'tcp://192.168.0.42:123'}
|
|
||||||
service = Service(name='foo', image='foo', hostname='name', client=self.mock_client, log_driver='syslog', log_opt=log_opt)
|
|
||||||
self.mock_client.containers.return_value = []
|
|
||||||
opts = service._get_container_create_options({'some': 'overrides'}, 1)
|
|
||||||
|
|
||||||
self.assertIsInstance(opts['host_config']['LogConfig'], LogConfig)
|
|
||||||
self.assertEqual(opts['host_config']['LogConfig'].type, 'syslog')
|
|
||||||
self.assertEqual(opts['host_config']['LogConfig'].config, log_opt)
|
|
||||||
|
|
||||||
def test_split_domainname_fqdn(self):
|
def test_split_domainname_fqdn(self):
|
||||||
service = Service(
|
service = Service(
|
||||||
'foo',
|
'foo',
|
||||||
|
@ -229,10 +166,11 @@ class ServiceTest(unittest.TestCase):
|
||||||
@mock.patch('compose.service.log', autospec=True)
|
@mock.patch('compose.service.log', autospec=True)
|
||||||
def test_pull_image(self, mock_log):
|
def test_pull_image(self, mock_log):
|
||||||
service = Service('foo', client=self.mock_client, image='someimage:sometag')
|
service = Service('foo', client=self.mock_client, image='someimage:sometag')
|
||||||
service.pull()
|
service.pull(insecure_registry=True)
|
||||||
self.mock_client.pull.assert_called_once_with(
|
self.mock_client.pull.assert_called_once_with(
|
||||||
'someimage',
|
'someimage',
|
||||||
tag='sometag',
|
tag='sometag',
|
||||||
|
insecure_registry=True,
|
||||||
stream=True)
|
stream=True)
|
||||||
mock_log.info.assert_called_once_with('Pulling foo (someimage:sometag)...')
|
mock_log.info.assert_called_once_with('Pulling foo (someimage:sometag)...')
|
||||||
|
|
||||||
|
@ -242,8 +180,26 @@ class ServiceTest(unittest.TestCase):
|
||||||
self.mock_client.pull.assert_called_once_with(
|
self.mock_client.pull.assert_called_once_with(
|
||||||
'ababab',
|
'ababab',
|
||||||
tag='latest',
|
tag='latest',
|
||||||
|
insecure_registry=False,
|
||||||
stream=True)
|
stream=True)
|
||||||
|
|
||||||
|
def test_create_container_from_insecure_registry(self):
|
||||||
|
service = Service('foo', client=self.mock_client, image='someimage:sometag')
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def pull(repo, tag=None, insecure_registry=False, **kwargs):
|
||||||
|
self.assertEqual('someimage', repo)
|
||||||
|
self.assertEqual('sometag', tag)
|
||||||
|
self.assertTrue(insecure_registry)
|
||||||
|
images.append({'Id': 'abc123'})
|
||||||
|
return []
|
||||||
|
|
||||||
|
service.image = lambda: images[0] if images else None
|
||||||
|
self.mock_client.pull = pull
|
||||||
|
|
||||||
|
service.create_container(insecure_registry=True)
|
||||||
|
self.assertEqual(1, len(images))
|
||||||
|
|
||||||
@mock.patch('compose.service.Container', autospec=True)
|
@mock.patch('compose.service.Container', autospec=True)
|
||||||
def test_recreate_container(self, _):
|
def test_recreate_container(self, _):
|
||||||
mock_container = mock.create_autospec(Container)
|
mock_container = mock.create_autospec(Container)
|
||||||
|
@ -287,7 +243,7 @@ class ServiceTest(unittest.TestCase):
|
||||||
images.append({'Id': 'abc123'})
|
images.append({'Id': 'abc123'})
|
||||||
return []
|
return []
|
||||||
|
|
||||||
service.image = lambda *args, **kwargs: mock_get_image(images)
|
service.image = lambda: images[0] if images else None
|
||||||
self.mock_client.pull = pull
|
self.mock_client.pull = pull
|
||||||
|
|
||||||
service.create_container()
|
service.create_container()
|
||||||
|
@ -297,7 +253,7 @@ class ServiceTest(unittest.TestCase):
|
||||||
service = Service('foo', client=self.mock_client, build='.')
|
service = Service('foo', client=self.mock_client, build='.')
|
||||||
|
|
||||||
images = []
|
images = []
|
||||||
service.image = lambda *args, **kwargs: mock_get_image(images)
|
service.image = lambda *args, **kwargs: images[0] if images else None
|
||||||
service.build = lambda: images.append({'Id': 'abc123'})
|
service.build = lambda: images.append({'Id': 'abc123'})
|
||||||
|
|
||||||
service.create_container(do_build=True)
|
service.create_container(do_build=True)
|
||||||
|
@ -312,7 +268,7 @@ class ServiceTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_create_container_no_build_but_needs_build(self):
|
def test_create_container_no_build_but_needs_build(self):
|
||||||
service = Service('foo', client=self.mock_client, build='.')
|
service = Service('foo', client=self.mock_client, build='.')
|
||||||
service.image = lambda *args, **kwargs: mock_get_image([])
|
service.image = lambda: None
|
||||||
|
|
||||||
with self.assertRaises(NeedsBuildError):
|
with self.assertRaises(NeedsBuildError):
|
||||||
service.create_container(do_build=False)
|
service.create_container(do_build=False)
|
||||||
|
@ -329,13 +285,6 @@ class ServiceTest(unittest.TestCase):
|
||||||
self.assertFalse(self.mock_client.build.call_args[1]['pull'])
|
self.assertFalse(self.mock_client.build.call_args[1]['pull'])
|
||||||
|
|
||||||
|
|
||||||
def mock_get_image(images):
|
|
||||||
if images:
|
|
||||||
return images[0]
|
|
||||||
else:
|
|
||||||
raise NoSuchImageError()
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceVolumesTest(unittest.TestCase):
|
class ServiceVolumesTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -353,13 +302,14 @@ class ServiceVolumesTest(unittest.TestCase):
|
||||||
spec = parse_volume_spec('external:interval:ro')
|
spec = parse_volume_spec('external:interval:ro')
|
||||||
self.assertEqual(spec, ('external', 'interval', 'ro'))
|
self.assertEqual(spec, ('external', 'interval', 'ro'))
|
||||||
|
|
||||||
spec = parse_volume_spec('external:interval:z')
|
|
||||||
self.assertEqual(spec, ('external', 'interval', 'z'))
|
|
||||||
|
|
||||||
def test_parse_volume_spec_too_many_parts(self):
|
def test_parse_volume_spec_too_many_parts(self):
|
||||||
with self.assertRaises(ConfigError):
|
with self.assertRaises(ConfigError):
|
||||||
parse_volume_spec('one:two:three:four')
|
parse_volume_spec('one:two:three:four')
|
||||||
|
|
||||||
|
def test_parse_volume_bad_mode(self):
|
||||||
|
with self.assertRaises(ConfigError):
|
||||||
|
parse_volume_spec('one:two:notrw')
|
||||||
|
|
||||||
def test_build_volume_binding(self):
|
def test_build_volume_binding(self):
|
||||||
binding = build_volume_binding(parse_volume_spec('/outside:/inside'))
|
binding = build_volume_binding(parse_volume_spec('/outside:/inside'))
|
||||||
self.assertEqual(binding, ('/inside', '/outside:/inside:rw'))
|
self.assertEqual(binding, ('/inside', '/outside:/inside:rw'))
|
||||||
|
@ -488,26 +438,3 @@ class ServiceVolumesTest(unittest.TestCase):
|
||||||
create_options['host_config']['Binds'],
|
create_options['host_config']['Binds'],
|
||||||
['/mnt/sda1/host/path:/data:rw'],
|
['/mnt/sda1/host/path:/data:rw'],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_with_special_volume_mode(self):
|
|
||||||
self.mock_client.inspect_image.return_value = {'Id': 'imageid'}
|
|
||||||
|
|
||||||
create_calls = []
|
|
||||||
|
|
||||||
def create_container(*args, **kwargs):
|
|
||||||
create_calls.append((args, kwargs))
|
|
||||||
return {'Id': 'containerid'}
|
|
||||||
|
|
||||||
self.mock_client.create_container = create_container
|
|
||||||
|
|
||||||
volumes = ['/tmp:/foo:z']
|
|
||||||
|
|
||||||
Service(
|
|
||||||
'web',
|
|
||||||
client=self.mock_client,
|
|
||||||
image='busybox',
|
|
||||||
volumes=volumes,
|
|
||||||
).create_container()
|
|
||||||
|
|
||||||
self.assertEqual(len(create_calls), 1)
|
|
||||||
self.assertEqual(create_calls[0][1]['host_config']['Binds'], volumes)
|
|
||||||
|
|
Loading…
Reference in New Issue