Better error on deleting non-empty Swift container

Swift client method delete_container() does not allow to delete
non-empty containers, in which case user gets a confusing 409 error
exessively truncated by Swift with no actual reason information.

This patch catches 409 errors from Swift client on container delete,
checks for objects in the container and raises a suitable error if
container is not empty, so that the DELETE_FAILED reason is clearly
seen.

Change-Id: Ibca3851d11b509413b739a693b1ddd244479d37b
Closes-Bug: #1406263
This commit is contained in:
Pavlo Shchelokovskyy 2014-12-29 15:37:41 +00:00
parent d5de387c86
commit ae0ccc4b51
2 changed files with 53 additions and 0 deletions

View File

@ -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):

View File

@ -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)