diff --git a/heat/engine/resources/swift.py b/heat/engine/resources/swift.py index 84d546f7f8..09adc6e638 100644 --- a/heat/engine/resources/swift.py +++ b/heat/engine/resources/swift.py @@ -14,6 +14,7 @@ import six from six.moves.urllib import parse as urlparse +from heat.common import exception from heat.common.i18n import _ from heat.common.i18n import _LW from heat.engine import attributes @@ -150,6 +151,13 @@ class SwiftContainer(resource.Resource): try: self.swift().delete_container(self.resource_id) except Exception as ex: + if self.client_plugin().is_conflict(ex): + container, objects = self.swift().get_container( + self.resource_id) + if objects: + msg = _("Deleting non-empty container (%s)" + ) % self.resource_id + raise exception.NotSupported(feature=msg) self.client_plugin().ignore_not_found(ex) def handle_check(self): diff --git a/heat/tests/test_swift.py b/heat/tests/test_swift.py index aa3f54240e..7cc68169d6 100644 --- a/heat/tests/test_swift.py +++ b/heat/tests/test_swift.py @@ -72,6 +72,7 @@ class swiftTest(common.HeatTestCase): self.m.CreateMock(sc.Connection) self.m.StubOutWithMock(sc.Connection, 'post_account') self.m.StubOutWithMock(sc.Connection, 'put_container') + self.m.StubOutWithMock(sc.Connection, 'get_container') self.m.StubOutWithMock(sc.Connection, 'delete_container') self.m.StubOutWithMock(sc.Connection, 'head_container') self.m.StubOutWithMock(sc.Connection, 'get_auth') @@ -255,6 +256,50 @@ class swiftTest(common.HeatTestCase): self.m.VerifyAll() + def test_delete_conflict_non_empty(self): + container_name = utils.PhysName('test_stack', 'test_resource') + sc.Connection.put_container( + container_name, + {}).AndReturn(None) + sc.Connection.delete_container(container_name).AndRaise( + sc.ClientException('Conflict', + http_status=409)) + sc.Connection.get_container( + container_name).AndReturn(({'name': container_name}, + [{'name': 'test'}])) + + self.m.ReplayAll() + t = template_format.parse(swift_template) + stack = utils.parse_stack(t) + rsrc = self.create_resource(t, stack, 'SwiftContainer') + deleter = scheduler.TaskRunner(rsrc.delete) + ex = self.assertRaises(exception.ResourceFailure, deleter) + self.assertIn('NotSupported: Deleting non-empty container', + six.text_type(ex)) + + self.m.VerifyAll() + + def test_delete_conflict_other(self): + container_name = utils.PhysName('test_stack', 'test_resource') + sc.Connection.put_container( + container_name, + {}).AndReturn(None) + sc.Connection.delete_container(container_name).AndRaise( + sc.ClientException('Conflict', + http_status=409)) + sc.Connection.get_container( + container_name).AndReturn(({'name': container_name}, [])) + + self.m.ReplayAll() + t = template_format.parse(swift_template) + stack = utils.parse_stack(t) + rsrc = self.create_resource(t, stack, 'SwiftContainer') + deleter = scheduler.TaskRunner(rsrc.delete) + ex = self.assertRaises(exception.ResourceFailure, deleter) + self.assertIn('Conflict', six.text_type(ex)) + + self.m.VerifyAll() + def _get_check_resource(self): t = template_format.parse(swift_template) stack = utils.parse_stack(t)