Prevent duplicate binds in generated container config

Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
Joffrey F 2018-04-26 15:20:45 -07:00
parent 9f42fac2bb
commit aecc0de28f
2 changed files with 23 additions and 3 deletions

View File

@ -877,7 +877,6 @@ class Service(object):
container_volumes, self.options.get('tmpfs') or [], previous_container, container_volumes, self.options.get('tmpfs') or [], previous_container,
container_mounts container_mounts
) )
override_options['binds'] = binds
container_options['environment'].update(affinity) container_options['environment'].update(affinity)
container_options['volumes'] = dict((v.internal, {}) for v in container_volumes or {}) container_options['volumes'] = dict((v.internal, {}) for v in container_volumes or {})
@ -890,13 +889,13 @@ class Service(object):
if m.is_tmpfs: if m.is_tmpfs:
override_options['tmpfs'].append(m.target) override_options['tmpfs'].append(m.target)
else: else:
override_options['binds'].append(m.legacy_repr()) binds.append(m.legacy_repr())
container_options['volumes'][m.target] = {} container_options['volumes'][m.target] = {}
secret_volumes = self.get_secret_volumes() secret_volumes = self.get_secret_volumes()
if secret_volumes: if secret_volumes:
if version_lt(self.client.api_version, '1.30'): if version_lt(self.client.api_version, '1.30'):
override_options['binds'].extend(v.legacy_repr() for v in secret_volumes) binds.extend(v.legacy_repr() for v in secret_volumes)
container_options['volumes'].update( container_options['volumes'].update(
(v.target, {}) for v in secret_volumes (v.target, {}) for v in secret_volumes
) )
@ -904,6 +903,8 @@ class Service(object):
override_options['mounts'] = override_options.get('mounts') or [] override_options['mounts'] = override_options.get('mounts') or []
override_options['mounts'].extend([build_mount(v) for v in secret_volumes]) override_options['mounts'].extend([build_mount(v) for v in secret_volumes])
# Remove possible duplicates (see e.g. https://github.com/docker/compose/issues/5885)
override_options['binds'] = list(set(binds))
return container_options, override_options return container_options, override_options
def _get_container_host_config(self, override_options, one_off=False): def _get_container_host_config(self, override_options, one_off=False):

View File

@ -10,6 +10,7 @@ from docker.errors import NotFound
from .. import mock from .. import mock
from .. import unittest from .. import unittest
from compose.config.errors import DependencyError from compose.config.errors import DependencyError
from compose.config.types import MountSpec
from compose.config.types import ServicePort from compose.config.types import ServicePort
from compose.config.types import ServiceSecret from compose.config.types import ServiceSecret
from compose.config.types import VolumeFromSpec from compose.config.types import VolumeFromSpec
@ -955,6 +956,24 @@ class ServiceTest(unittest.TestCase):
assert service.create_container().id == 'new_cont_id' assert service.create_container().id == 'new_cont_id'
def test_build_volume_options_duplicate_binds(self):
self.mock_client.api_version = '1.29' # Trigger 3.2 format workaround
service = Service('foo', client=self.mock_client)
ctnr_opts, override_opts = service._build_container_volume_options(
previous_container=None,
container_options={
'volumes': [
MountSpec.parse({'source': 'vol', 'target': '/data', 'type': 'volume'}),
VolumeSpec.parse('vol:/data:rw'),
],
'environment': {},
},
override_options={},
)
assert 'binds' in override_opts
assert len(override_opts['binds']) == 1
assert override_opts['binds'][0] == 'vol:/data:rw'
class TestServiceNetwork(unittest.TestCase): class TestServiceNetwork(unittest.TestCase):
def setUp(self): def setUp(self):