1
0
Эх сурвалжийг харах

Fix smart recreate when 'image' is changed to something nonexistent

Signed-off-by: Aanand Prasad <[email protected]>
Aanand Prasad 10 жил өмнө
parent
commit
531265bc84

+ 20 - 3
compose/service.py

@@ -65,6 +65,10 @@ class NeedsBuildError(Exception):
         self.service = service
 
 
+class NoSuchImageError(Exception):
+    pass
+
+
 VolumeSpec = namedtuple('VolumeSpec', 'external internal mode')
 
 
@@ -225,8 +229,11 @@ class Service(object):
                             do_build=True,
                             insecure_registry=False):
 
-        if self.image():
+        try:
+            self.image()
             return
+        except NoSuchImageError:
+            pass
 
         if self.can_be_built():
             if do_build:
@@ -241,7 +248,7 @@ class Service(object):
             return self.client.inspect_image(self.image_name)
         except APIError as e:
             if e.response.status_code == 404 and e.explanation and 'No such image' in str(e.explanation):
-                return None
+                raise NoSuchImageError("Image '{}' not found".format(self.image_name))
             else:
                 raise
 
@@ -275,7 +282,17 @@ class Service(object):
         return ConvergencePlan('recreate', containers)
 
     def _containers_have_diverged(self, containers):
-        config_hash = self.config_hash()
+        config_hash = None
+
+        try:
+            config_hash = self.config_hash()
+        except NoSuchImageError as e:
+            log.debug(
+                'Service %s has diverged: %s',
+                self.name, six.text_type(e),
+            )
+            return True
+
         has_diverged = False
 
         for c in containers:

+ 7 - 0
tests/integration/state_test.py

@@ -215,6 +215,13 @@ class ServiceStateTest(DockerClientTestCase):
         web = self.create_service('web', command=["top", "-d", "1"])
         self.assertEqual(('recreate', [container]), web.convergence_plan(smart_recreate=True))
 
+    def test_trigger_recreate_with_nonexistent_image_tag(self):
+        web = self.create_service('web', image="busybox:latest")
+        container = web.create_container()
+
+        web = self.create_service('web', image="nonexistent-image")
+        self.assertEqual(('recreate', [container]), web.convergence_plan(smart_recreate=True))
+
     def test_trigger_recreate_with_image_change(self):
         repo = 'composetest_myimage'
         tag = 'latest'

+ 12 - 4
tests/unit/service_test.py

@@ -12,6 +12,7 @@ from compose.const import LABEL_SERVICE, LABEL_PROJECT, LABEL_ONE_OFF
 from compose.service import (
     ConfigError,
     NeedsBuildError,
+    NoSuchImageError,
     build_port_bindings,
     build_volume_binding,
     get_container_data_volumes,
@@ -245,7 +246,7 @@ class ServiceTest(unittest.TestCase):
             images.append({'Id': 'abc123'})
             return []
 
-        service.image = lambda: images[0] if images else None
+        service.image = lambda *args, **kwargs: mock_get_image(images)
         self.mock_client.pull = pull
 
         service.create_container(insecure_registry=True)
@@ -294,7 +295,7 @@ class ServiceTest(unittest.TestCase):
             images.append({'Id': 'abc123'})
             return []
 
-        service.image = lambda: images[0] if images else None
+        service.image = lambda *args, **kwargs: mock_get_image(images)
         self.mock_client.pull = pull
 
         service.create_container()
@@ -304,7 +305,7 @@ class ServiceTest(unittest.TestCase):
         service = Service('foo', client=self.mock_client, build='.')
 
         images = []
-        service.image = lambda *args, **kwargs: images[0] if images else None
+        service.image = lambda *args, **kwargs: mock_get_image(images)
         service.build = lambda: images.append({'Id': 'abc123'})
 
         service.create_container(do_build=True)
@@ -319,7 +320,7 @@ class ServiceTest(unittest.TestCase):
 
     def test_create_container_no_build_but_needs_build(self):
         service = Service('foo', client=self.mock_client, build='.')
-        service.image = lambda: None
+        service.image = lambda *args, **kwargs: mock_get_image([])
 
         with self.assertRaises(NeedsBuildError):
             service.create_container(do_build=False)
@@ -336,6 +337,13 @@ class ServiceTest(unittest.TestCase):
         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):
 
     def setUp(self):