Browse Source

Only set a container affinity if there are volumes to copy over.

Signed-off-by: Daniel Nephin <[email protected]>
Daniel Nephin 9 years ago
parent
commit
4f7530c480
2 changed files with 66 additions and 20 deletions
  1. 16 12
      compose/service.py
  2. 50 8
      tests/unit/service_test.py

+ 16 - 12
compose/service.py

@@ -592,21 +592,20 @@ class Service(object):
                     ports.append(port)
             container_options['ports'] = ports
 
-        override_options['binds'] = merge_volume_bindings(
+        container_options['environment'] = merge_environment(
+            self.options.get('environment'),
+            override_options.get('environment'))
+
+        binds, affinity = merge_volume_bindings(
             container_options.get('volumes') or [],
             previous_container)
+        override_options['binds'] = binds
+        container_options['environment'].update(affinity)
 
         if 'volumes' in container_options:
             container_options['volumes'] = dict(
                 (v.internal, {}) for v in container_options['volumes'])
 
-        container_options['environment'] = merge_environment(
-            self.options.get('environment'),
-            override_options.get('environment'))
-
-        if previous_container:
-            container_options['environment']['affinity:container'] = ('=' + previous_container.id)
-
         container_options['image'] = self.image_name
 
         container_options['labels'] = build_container_labels(
@@ -877,18 +876,23 @@ def merge_volume_bindings(volumes, previous_container):
     """Return a list of volume bindings for a container. Container data volumes
     are replaced by those from the previous container.
     """
+    affinity = {}
+
     volume_bindings = dict(
         build_volume_binding(volume)
         for volume in volumes
         if volume.external)
 
     if previous_container:
-        data_volumes = get_container_data_volumes(previous_container, volumes)
-        warn_on_masked_volume(volumes, data_volumes, previous_container.service)
+        old_volumes = get_container_data_volumes(previous_container, volumes)
+        warn_on_masked_volume(volumes, old_volumes, previous_container.service)
         volume_bindings.update(
-            build_volume_binding(volume) for volume in data_volumes)
+            build_volume_binding(volume) for volume in old_volumes)
+
+        if old_volumes:
+            affinity = {'affinity:container': '=' + previous_container.id}
 
-    return list(volume_bindings.values())
+    return list(volume_bindings.values()), affinity
 
 
 def get_container_data_volumes(container, volumes_option):

+ 50 - 8
tests/unit/service_test.py

@@ -267,13 +267,52 @@ class ServiceTest(unittest.TestCase):
         self.assertEqual(
             opts['labels'][LABEL_CONFIG_HASH],
             'f8bfa1058ad1f4231372a0b1639f0dfdb574dafff4e8d7938049ae993f7cf1fc')
-        self.assertEqual(
-            opts['environment'],
-            {
-                'affinity:container': '=ababab',
-                'also': 'real',
-            }
+        assert opts['environment'] == {'also': 'real'}
+
+    def test_get_container_create_options_sets_affinity_with_binds(self):
+        service = Service(
+            'foo',
+            image='foo',
+            client=self.mock_client,
         )
+        self.mock_client.inspect_image.return_value = {'Id': 'abcd'}
+        prev_container = mock.Mock(
+            id='ababab',
+            image_config={'ContainerConfig': {'Volumes': ['/data']}})
+
+        def container_get(key):
+            return {
+                'Mounts': [
+                    {
+                        'Destination': '/data',
+                        'Source': '/some/path',
+                        'Name': 'abab1234',
+                    },
+                ]
+            }.get(key, None)
+
+        prev_container.get.side_effect = container_get
+
+        opts = service._get_container_create_options(
+            {},
+            1,
+            previous_container=prev_container)
+
+        assert opts['environment'] == {'affinity:container': '=ababab'}
+
+    def test_get_container_create_options_no_affinity_without_binds(self):
+        service = Service('foo', image='foo', client=self.mock_client)
+        self.mock_client.inspect_image.return_value = {'Id': 'abcd'}
+        prev_container = mock.Mock(
+            id='ababab',
+            image_config={'ContainerConfig': {}})
+        prev_container.get.return_value = None
+
+        opts = service._get_container_create_options(
+            {},
+            1,
+            previous_container=prev_container)
+        assert opts['environment'] == {}
 
     def test_get_container_not_found(self):
         self.mock_client.containers.return_value = []
@@ -650,6 +689,7 @@ class ServiceVolumesTest(unittest.TestCase):
             '/host/volume:/host/volume:ro',
             '/new/volume',
             '/existing/volume',
+            'named:/named/vol',
         ]]
 
         self.mock_client.inspect_image.return_value = {
@@ -710,7 +750,8 @@ class ServiceVolumesTest(unittest.TestCase):
             'ContainerConfig': {'Volumes': {}}
         }
 
-        intermediate_container = Container(self.mock_client, {
+        previous_container = Container(self.mock_client, {
+            'Id': 'cdefab',
             'Image': 'ababab',
             'Mounts': [{
                 'Source': '/var/lib/docker/aaaaaaaa',
@@ -727,8 +768,9 @@ class ServiceVolumesTest(unittest.TestCase):
             'existingvolume:/existing/volume:rw',
         ]
 
-        binds = merge_volume_bindings(options, intermediate_container)
+        binds, affinity = merge_volume_bindings(options, previous_container)
         assert sorted(binds) == sorted(expected)
+        assert affinity == {'affinity:container': '=cdefab'}
 
     def test_mount_same_host_path_to_two_volumes(self):
         service = Service(