Gentoo Archives: gentoo-commits

From: "Matt Thode (prometheanfire)" <prometheanfire@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in app-admin/glance/files: glance-CVE-2014-9623.patch
Date: Thu, 29 Jan 2015 05:39:43
Message-Id: 20150129053938.3A7ED108B1@oystercatcher.gentoo.org
1 prometheanfire 15/01/29 05:39:38
2
3 Added: glance-CVE-2014-9623.patch
4 Log:
5 bumping glance for CVE-2014-9623
6
7 (Portage version: 2.2.14/cvs/Linux x86_64, signed Manifest commit with key 0x33ED3FD25AFC78BA)
8
9 Revision Changes Path
10 1.1 app-admin/glance/files/glance-CVE-2014-9623.patch
11
12 file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/app-admin/glance/files/glance-CVE-2014-9623.patch?rev=1.1&view=markup
13 plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/app-admin/glance/files/glance-CVE-2014-9623.patch?rev=1.1&content-type=text/plain
14
15 Index: glance-CVE-2014-9623.patch
16 ===================================================================
17 From 7d5d8657fd70b20518610b3c6f8e41e16c72fa31 Mon Sep 17 00:00:00 2001
18 From: Zhi Yan Liu <zhiyanl@××××××.com>
19 Date: Tue, 30 Dec 2014 22:25:50 +0800
20 Subject: [PATCH] Cleanup chunks for deleted image that was 'saving'
21
22 Currently image data cannot be removed synchronously for an image that
23 is in saving state. And when, the upload operation for such an image is
24 completed the operator configured quota can be exceeded.
25
26 This patch fixes the issue of left over chunks for an image which was
27 deleted from saving status. However, by the limitation of the design we
28 cannot enforce a global quota check for the image in saving status.
29
30 This change introduces a inconsonance between http response codes of
31 v1 and v2 APIs. The status codes which we will now see after the upload
32 process completes on an image which was deleted mid way are:
33
34 v1: 412 Precondition Failed
35 v2: 410 Gone
36
37 SecurityImpact
38 UpgradeImpact
39 APIImpact
40
41 Closes-Bug: 1383973
42 Closes-Bug: 1398830
43 Closes-Bug: 1188532
44
45 Conflicts:
46 glance/api/v1/upload_utils.py
47 glance/api/v2/image_data.py
48 glance/tests/unit/test_domain_proxy.py
49 glance/tests/unit/v1/test_api.py
50
51 Change-Id: I47229b366c25367ec1bd48aec684e0880f3dfe60
52 Signed-off-by: Zhi Yan Liu <zhiyanl@××××××.com>
53 (cherry picked from commit 0dc8fbb3479a53c5bba8475d14f4c7206904c5ea)
54 ---
55 glance/api/authorization.py | 4 +-
56 glance/api/policy.py | 8 ++--
57 glance/api/v1/upload_utils.py | 19 +++++---
58 glance/api/v2/image_data.py | 8 +++-
59 glance/db/__init__.py | 7 +--
60 glance/domain/proxy.py | 4 +-
61 glance/location.py | 4 +-
62 glance/notifier.py | 4 +-
63 glance/quota/__init__.py | 4 +-
64 glance/tests/unit/test_domain_proxy.py | 14 +++---
65 glance/tests/unit/test_policy.py | 2 +-
66 glance/tests/unit/test_quota.py | 17 ++++---
67 glance/tests/unit/test_store_image.py | 2 +-
68 glance/tests/unit/v1/test_api.py | 57 +++++++++++-------------
69 glance/tests/unit/v2/test_image_data_resource.py | 24 +++++-----
70 15 files changed, 98 insertions(+), 80 deletions(-)
71
72 diff --git a/glance/api/authorization.py b/glance/api/authorization.py
73 index 149ff55..77f7ed3 100644
74 --- a/glance/api/authorization.py
75 +++ b/glance/api/authorization.py
76 @@ -158,10 +158,10 @@ class ImageMemberRepoProxy(glance.domain.proxy.Repo):
77 raise exception.Forbidden(message
78 % self.image.image_id)
79
80 - def save(self, image_member):
81 + def save(self, image_member, from_state=None):
82 if (self.context.is_admin or
83 self.context.owner == image_member.member_id):
84 - self.member_repo.save(image_member)
85 + self.member_repo.save(image_member, from_state=from_state)
86 else:
87 message = _("You cannot update image member %s")
88 raise exception.Forbidden(message % image_member.member_id)
89 diff --git a/glance/api/policy.py b/glance/api/policy.py
90 index 0bc8d56..e395876 100644
91 --- a/glance/api/policy.py
92 +++ b/glance/api/policy.py
93 @@ -182,9 +182,9 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
94 self.policy.enforce(self.context, 'get_images', {})
95 return super(ImageRepoProxy, self).list(*args, **kwargs)
96
97 - def save(self, image):
98 + def save(self, image, from_state=None):
99 self.policy.enforce(self.context, 'modify_image', {})
100 - return super(ImageRepoProxy, self).save(image)
101 + return super(ImageRepoProxy, self).save(image, from_state=from_state)
102
103 def add(self, image):
104 self.policy.enforce(self.context, 'add_image', {})
105 @@ -285,9 +285,9 @@ class ImageMemberRepoProxy(glance.domain.proxy.Repo):
106 self.policy.enforce(self.context, 'get_member', {})
107 return self.member_repo.get(member_id)
108
109 - def save(self, member):
110 + def save(self, member, from_state=None):
111 self.policy.enforce(self.context, 'modify_member', {})
112 - self.member_repo.save(member)
113 + self.member_repo.save(member, from_state=from_state)
114
115 def list(self, *args, **kwargs):
116 self.policy.enforce(self.context, 'get_members', {})
117 diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py
118 index 8a190fc..60c3d3d 100644
119 --- a/glance/api/v1/upload_utils.py
120 +++ b/glance/api/v1/upload_utils.py
121 @@ -153,12 +153,19 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier):
122 update_data = {'checksum': checksum,
123 'size': size}
124 try:
125 - image_meta = registry.update_image_metadata(req.context,
126 - image_id,
127 - update_data,
128 - from_state='saving')
129 -
130 - except exception.NotFound as e:
131 + try:
132 + state = 'saving'
133 + image_meta = registry.update_image_metadata(req.context,
134 + image_id,
135 + update_data,
136 + from_state=state)
137 + except exception.Duplicate:
138 + image = registry.get_image_metadata(req.context, image_id)
139 + if image['status'] == 'deleted':
140 + raise exception.NotFound()
141 + else:
142 + raise
143 + except exception.NotFound:
144 msg = _LI("Image %s could not be found after upload. The image may"
145 " have been deleted during the upload.") % image_id
146 LOG.info(msg)
147 diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py
148 index 430ffc5..cdfa34b 100644
149 --- a/glance/api/v2/image_data.py
150 +++ b/glance/api/v2/image_data.py
151 @@ -73,8 +73,8 @@ class ImageDataController(object):
152 try:
153 image_repo.save(image)
154 image.set_data(data, size)
155 - image_repo.save(image)
156 - except exception.NotFound as e:
157 + image_repo.save(image, from_state='saving')
158 + except (exception.NotFound, exception.Conflict) as e:
159 msg = (_("Image %(id)s could not be found after upload."
160 "The image may have been deleted during the upload: "
161 "%(error)s Cleaning up the chunks uploaded") %
162 @@ -152,6 +152,10 @@ class ImageDataController(object):
163 raise webob.exc.HTTPServiceUnavailable(explanation=msg,
164 request=req)
165
166 + except webob.exc.HTTPGone as e:
167 + with excutils.save_and_reraise_exception():
168 + LOG.error(_LE("Failed to upload image data due to HTTP error"))
169 +
170 except webob.exc.HTTPError as e:
171 with excutils.save_and_reraise_exception():
172 LOG.error(_LE("Failed to upload image data due to HTTP error"))
173 diff --git a/glance/db/__init__.py b/glance/db/__init__.py
174 index 05db7f8..483ba21 100644
175 --- a/glance/db/__init__.py
176 +++ b/glance/db/__init__.py
177 @@ -164,7 +164,7 @@ class ImageRepo(object):
178 image.created_at = new_values['created_at']
179 image.updated_at = new_values['updated_at']
180
181 - def save(self, image):
182 + def save(self, image, from_state=None):
183 image_values = self._format_image_to_db(image)
184 if image_values['size'] > CONF.image_size_cap:
185 raise exception.ImageSizeLimitExceeded
186 @@ -172,7 +172,8 @@ class ImageRepo(object):
187 new_values = self.db_api.image_update(self.context,
188 image.image_id,
189 image_values,
190 - purge_props=True)
191 + purge_props=True,
192 + from_state=from_state)
193 except (exception.NotFound, exception.Forbidden):
194 msg = _("No image found with ID %s") % image.image_id
195 raise exception.NotFound(msg)
196 @@ -265,7 +266,7 @@ class ImageMemberRepo(object):
197 msg = _("The specified member %s could not be found")
198 raise exception.NotFound(msg % image_member.id)
199
200 - def save(self, image_member):
201 + def save(self, image_member, from_state=None):
202 image_member_values = self._format_image_member_to_db(image_member)
203 try:
204 new_values = self.db_api.image_member_update(self.context,
205 diff --git a/glance/domain/proxy.py b/glance/domain/proxy.py
206 index 5a91d34..09c0fb2 100644
207 --- a/glance/domain/proxy.py
208 +++ b/glance/domain/proxy.py
209 @@ -94,9 +94,9 @@ class Repo(object):
210 result = self.base.add(base_item)
211 return self.helper.proxy(result)
212
213 - def save(self, item):
214 + def save(self, item, from_state=None):
215 base_item = self.helper.unproxy(item)
216 - result = self.base.save(base_item)
217 + result = self.base.save(base_item, from_state=from_state)
218 return self.helper.proxy(result)
219
220 def remove(self, item):
221 diff --git a/glance/location.py b/glance/location.py
222 index f83fa7a..b49546d 100644
223 --- a/glance/location.py
224 +++ b/glance/location.py
225 @@ -60,8 +60,8 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
226 self._set_acls(image)
227 return result
228
229 - def save(self, image):
230 - result = super(ImageRepoProxy, self).save(image)
231 + def save(self, image, from_state=None):
232 + result = super(ImageRepoProxy, self).save(image, from_state=from_state)
233 self._set_acls(image)
234 return result
235
236 diff --git a/glance/notifier.py b/glance/notifier.py
237 index 5ec0854..21223da 100644
238 --- a/glance/notifier.py
239 +++ b/glance/notifier.py
240 @@ -122,8 +122,8 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
241 item_proxy_class=ImageProxy,
242 item_proxy_kwargs=proxy_kwargs)
243
244 - def save(self, image):
245 - super(ImageRepoProxy, self).save(image)
246 + def save(self, image, from_state=None):
247 + super(ImageRepoProxy, self).save(image, from_state=from_state)
248 self.notifier.info('image.update',
249 format_image_notification(image))
250
251 diff --git a/glance/quota/__init__.py b/glance/quota/__init__.py
252 index 4051992..d628a8c 100644
253 --- a/glance/quota/__init__.py
254 +++ b/glance/quota/__init__.py
255 @@ -104,10 +104,10 @@ class ImageRepoProxy(glance.domain.proxy.Repo):
256 LOG.debug(six.text_type(exc))
257 raise exc
258
259 - def save(self, image):
260 + def save(self, image, from_state=None):
261 if image.added_new_properties():
262 self._enforce_image_property_quota(len(image.extra_properties))
263 - return super(ImageRepoProxy, self).save(image)
264 + return super(ImageRepoProxy, self).save(image, from_state=from_state)
265
266 def add(self, image):
267 self._enforce_image_property_quota(len(image.extra_properties))
268 diff --git a/glance/tests/unit/test_domain_proxy.py b/glance/tests/unit/test_domain_proxy.py
269 index 1bb6863..2b8f792 100644
270 --- a/glance/tests/unit/test_domain_proxy.py
271 +++ b/glance/tests/unit/test_domain_proxy.py
272 @@ -74,7 +74,7 @@ class TestProxyRepoPlain(test_utils.BaseTestCase):
273 self._test_method('add', 'snuff', 'enough')
274
275 def test_save(self):
276 - self._test_method('save', 'snuff', 'enough')
277 + self._test_method('save', 'snuff', 'enough', from_state=None)
278
279 def test_remove(self):
280 self._test_method('add', None, 'flying')
281 @@ -121,14 +121,14 @@ class TestProxyRepoWrapping(test_utils.BaseTestCase):
282 self.assertEqual(results[i].args, tuple())
283 self.assertEqual(results[i].kwargs, {'a': 1})
284
285 - def _test_method_with_proxied_argument(self, name, result):
286 + def _test_method_with_proxied_argument(self, name, result, **kwargs):
287 self.fake_repo.result = result
288 item = FakeProxy('snoop')
289 method = getattr(self.proxy_repo, name)
290 proxy_result = method(item)
291
292 - self.assertEqual(self.fake_repo.args, ('snoop',))
293 - self.assertEqual(self.fake_repo.kwargs, {})
294 + self.assertEqual(('snoop',), self.fake_repo.args)
295 + self.assertEqual(kwargs, self.fake_repo.kwargs)
296
297 if result is None:
298 self.assertIsNone(proxy_result)
299 @@ -145,10 +145,12 @@ class TestProxyRepoWrapping(test_utils.BaseTestCase):
300 self._test_method_with_proxied_argument('add', None)
301
302 def test_save(self):
303 - self._test_method_with_proxied_argument('save', 'dog')
304 + self._test_method_with_proxied_argument('save', 'dog',
305 + from_state=None)
306
307 def test_save_with_no_result(self):
308 - self._test_method_with_proxied_argument('save', None)
309 + self._test_method_with_proxied_argument('save', None,
310 + from_state=None)
311
312 def test_remove(self):
313 self._test_method_with_proxied_argument('remove', 'dog')
314 diff --git a/glance/tests/unit/test_policy.py b/glance/tests/unit/test_policy.py
315 index 5b5e870..44546a0 100644
316 --- a/glance/tests/unit/test_policy.py
317 +++ b/glance/tests/unit/test_policy.py
318 @@ -78,7 +78,7 @@ class MemberRepoStub(object):
319 def get(self, *args, **kwargs):
320 return 'member_repo_get'
321
322 - def save(self, image_member):
323 + def save(self, image_member, from_state=None):
324 image_member.output = 'member_repo_save'
325
326 def list(self, *args, **kwargs):
327 diff --git a/glance/tests/unit/test_quota.py b/glance/tests/unit/test_quota.py
328 index c12eda2..1c40fb4 100644
329 --- a/glance/tests/unit/test_quota.py
330 +++ b/glance/tests/unit/test_quota.py
331 @@ -367,7 +367,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
332 self.image.extra_properties = {'foo': 'bar'}
333 self.image_repo_proxy.save(self.image)
334
335 - self.image_repo_mock.save.assert_called_once_with(self.base_image)
336 + self.image_repo_mock.save.assert_called_once_with(self.base_image,
337 + from_state=None)
338
339 def test_save_image_too_many_image_properties(self):
340 self.config(image_property_quota=1)
341 @@ -383,7 +384,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
342 self.image.extra_properties = {'foo': 'bar'}
343 self.image_repo_proxy.save(self.image)
344
345 - self.image_repo_mock.save.assert_called_once_with(self.base_image)
346 + self.image_repo_mock.save.assert_called_once_with(self.base_image,
347 + from_state=None)
348
349 def test_add_image_with_image_property(self):
350 self.config(image_property_quota=1)
351 @@ -422,7 +424,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
352 self.config(image_property_quota=1)
353 self.image.extra_properties = {'foo': 'frob', 'spam': 'eggs'}
354 self.image_repo_proxy.save(self.image)
355 - self.image_repo_mock.save.assert_called_once_with(self.base_image)
356 + self.image_repo_mock.save.assert_called_once_with(self.base_image,
357 + from_state=None)
358 self.assertEqual('frob', self.base_image.extra_properties['foo'])
359 self.assertEqual('eggs', self.base_image.extra_properties['spam'])
360
361 @@ -431,7 +434,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
362 self.config(image_property_quota=1)
363 del self.image.extra_properties['foo']
364 self.image_repo_proxy.save(self.image)
365 - self.image_repo_mock.save.assert_called_once_with(self.base_image)
366 + self.image_repo_mock.save.assert_called_once_with(self.base_image,
367 + from_state=None)
368 self.assertNotIn('foo', self.base_image.extra_properties)
369 self.assertEqual('ham', self.base_image.extra_properties['spam'])
370
371 @@ -447,7 +451,7 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
372 del self.image.extra_properties['frob']
373 del self.image.extra_properties['lorem']
374 self.image_repo_proxy.save(self.image)
375 - call_args = mock.call(self.base_image)
376 + call_args = mock.call(self.base_image, from_state=None)
377 self.assertEqual(call_args, self.image_repo_mock.save.call_args)
378 self.assertEqual('bar', self.base_image.extra_properties['foo'])
379 self.assertEqual('ham', self.base_image.extra_properties['spam'])
380 @@ -466,7 +470,8 @@ class TestImagePropertyQuotas(test_utils.BaseTestCase):
381 self.config(image_property_quota=1)
382 del self.image.extra_properties['foo']
383 self.image_repo_proxy.save(self.image)
384 - self.image_repo_mock.save.assert_called_once_with(self.base_image)
385 + self.image_repo_mock.save.assert_called_once_with(self.base_image,
386 + from_state=None)
387 self.assertNotIn('foo', self.base_image.extra_properties)
388 self.assertEqual('ham', self.base_image.extra_properties['spam'])
389 self.assertEqual('baz', self.base_image.extra_properties['frob'])
390 diff --git a/glance/tests/unit/test_store_image.py b/glance/tests/unit/test_store_image.py
391 index 8b334ab..b656454 100644
392 --- a/glance/tests/unit/test_store_image.py
393 +++ b/glance/tests/unit/test_store_image.py
394 @@ -36,7 +36,7 @@ class ImageRepoStub(object):
395 def add(self, image):
396 return image
397
398 - def save(self, image):
399 + def save(self, image, from_state=None):
400 return image
401
402
403 diff --git a/glance/tests/unit/v1/test_api.py b/glance/tests/unit/v1/test_api.py
404 index 39e9a44..7dc0737 100644
405 --- a/glance/tests/unit/v1/test_api.py
406 +++ b/glance/tests/unit/v1/test_api.py
407 @@ -39,7 +39,6 @@ from glance.db.sqlalchemy import api as db_api
408 from glance.db.sqlalchemy import models as db_models
409 from glance.openstack.common import jsonutils
410 from glance.openstack.common import timeutils
411 -
412 import glance.registry.client.v1.api as registry
413 from glance.tests.unit import base
414 import glance.tests.unit.utils as unit_test_utils
415 @@ -1735,8 +1734,7 @@ class TestGlanceAPI(base.IsolatedUnitTest):
416
417 self.assertEqual(1, mock_store_add_to_backend.call_count)
418
419 - def test_delete_during_image_upload(self):
420 - req = unit_test_utils.get_fake_request()
421 + def _check_delete_during_image_upload(self, is_admin=False):
422
423 fixture_headers = {'x-image-meta-store': 'file',
424 'x-image-meta-disk-format': 'vhd',
425 @@ -1744,8 +1742,8 @@ class TestGlanceAPI(base.IsolatedUnitTest):
426 'x-image-meta-name': 'fake image #3',
427 'x-image-meta-property-key1': 'value1'}
428
429 - req = webob.Request.blank("/images")
430 - req.method = 'POST'
431 + req = unit_test_utils.get_fake_request(path="/images",
432 + is_admin=is_admin)
433 for k, v in six.iteritems(fixture_headers):
434 req.headers[k] = v
435
436 @@ -1770,31 +1768,18 @@ class TestGlanceAPI(base.IsolatedUnitTest):
437 mock_initiate_deletion)
438
439 orig_update_image_metadata = registry.update_image_metadata
440 - ctlr = glance.api.v1.controller.BaseController
441 - orig_get_image_meta_or_404 = ctlr.get_image_meta_or_404
442 -
443 - def mock_update_image_metadata(*args, **kwargs):
444
445 - if args[2].get('status', None) == 'deleted':
446 + data = "somedata"
447
448 - # One shot.
449 - def mock_get_image_meta_or_404(*args, **kwargs):
450 - ret = orig_get_image_meta_or_404(*args, **kwargs)
451 - ret['status'] = 'queued'
452 - self.stubs.Set(ctlr, 'get_image_meta_or_404',
453 - orig_get_image_meta_or_404)
454 - return ret
455 -
456 - self.stubs.Set(ctlr, 'get_image_meta_or_404',
457 - mock_get_image_meta_or_404)
458 + def mock_update_image_metadata(*args, **kwargs):
459
460 - req = webob.Request.blank("/images/%s" % image_id)
461 - req.method = 'PUT'
462 - req.headers['Content-Type'] = 'application/octet-stream'
463 - req.body = "somedata"
464 + if args[2].get('size', None) == len(data):
465 + path = "/images/%s" % image_id
466 + req = unit_test_utils.get_fake_request(path=path,
467 + method='DELETE',
468 + is_admin=is_admin)
469 res = req.get_response(self.api)
470 - self.assertEqual(res.status_int, 200)
471 - self.assertFalse(res.location)
472 + self.assertEqual(200, res.status_int)
473
474 self.stubs.Set(registry, 'update_image_metadata',
475 orig_update_image_metadata)
476 @@ -1804,20 +1789,30 @@ class TestGlanceAPI(base.IsolatedUnitTest):
477 self.stubs.Set(registry, 'update_image_metadata',
478 mock_update_image_metadata)
479
480 - req = webob.Request.blank("/images/%s" % image_id)
481 - req.method = 'DELETE'
482 + req = unit_test_utils.get_fake_request(path="/images/%s" % image_id,
483 + method='PUT')
484 + req.headers['Content-Type'] = 'application/octet-stream'
485 + req.body = data
486 res = req.get_response(self.api)
487 - self.assertEqual(res.status_int, 200)
488 + self.assertEqual(412, res.status_int)
489 + self.assertFalse(res.location)
490
491 self.assertTrue(called['initiate_deletion'])
492
493 - req = webob.Request.blank("/images/%s" % image_id)
494 - req.method = 'HEAD'
495 + req = unit_test_utils.get_fake_request(path="/images/%s" % image_id,
496 + method='HEAD',
497 + is_admin=True)
498 res = req.get_response(self.api)
499 self.assertEqual(res.status_int, 200)
500 self.assertEqual(res.headers['x-image-meta-deleted'], 'True')
501 self.assertEqual(res.headers['x-image-meta-status'], 'deleted')
502
503 + def test_delete_during_image_upload_by_normal_user(self):
504 + self._check_delete_during_image_upload(is_admin=False)
505 +
506 + def test_delete_during_image_upload_by_admin(self):
507 + self._check_delete_during_image_upload(is_admin=True)
508 +
509 def test_disable_purge_props(self):
510 """
511 Test the special x-glance-registry-purge-props header controls
512 diff --git a/glance/tests/unit/v2/test_image_data_resource.py b/glance/tests/unit/v2/test_image_data_resource.py
513 index cc8148a..a121e82 100644
514 --- a/glance/tests/unit/v2/test_image_data_resource.py
515 +++ b/glance/tests/unit/v2/test_image_data_resource.py
516 @@ -81,7 +81,7 @@ class FakeImageRepo(object):
517 else:
518 return self.result
519
520 - def save(self, image):
521 + def save(self, image, from_state=None):
522 self.saved_image = image
523
524
525 @@ -184,17 +184,21 @@ class TestImagesController(base.StoreClearingUnitTest):
526 request, unit_test_utils.UUID1, 'YYYY', 4)
527
528 def test_upload_non_existent_image_during_save_initiates_deletion(self):
529 - def fake_save(self):
530 + def fake_save_not_found(self):
531 raise exception.NotFound()
532
533 - request = unit_test_utils.get_fake_request()
534 - image = FakeImage('abcd', locations=['http://example.com/image'])
535 - self.image_repo.result = image
536 - self.image_repo.save = fake_save
537 - image.delete = mock.Mock()
538 - self.assertRaises(webob.exc.HTTPGone, self.controller.upload,
539 - request, str(uuid.uuid4()), 'ABC', 3)
540 - self.assertTrue(image.delete.called)
541 + def fake_save_conflict(self):
542 + raise exception.Conflict()
543 +
544 + for fun in [fake_save_not_found, fake_save_conflict]:
545 + request = unit_test_utils.get_fake_request()
546 + image = FakeImage('abcd', locations=['http://example.com/image'])
547 + self.image_repo.result = image
548 + self.image_repo.save = fun
549 + image.delete = mock.Mock()
550 + self.assertRaises(webob.exc.HTTPGone, self.controller.upload,
551 + request, str(uuid.uuid4()), 'ABC', 3)
552 + self.assertTrue(image.delete.called)
553
554 def test_upload_non_existent_image_raises_not_found_exception(self):
555 def fake_save(self):
556 --
557 2.0.5