diff --git a/glance/api/authorization.py b/glance/api/authorization.py index 7cea2c0da2..5af54118fa 100644 --- a/glance/api/authorization.py +++ b/glance/api/authorization.py @@ -112,13 +112,24 @@ class ImageRepoProxy(glance.domain.proxy.Repo): return [proxy_image(self.context, i) for i in images] -class ImageMemberRepoProxy(glance.domain.proxy.Repo): +class ImageMemberRepoProxy(glance.domain.proxy.MemberRepo): def __init__(self, member_repo, image, context): self.member_repo = member_repo self.image = image self.context = context - super(ImageMemberRepoProxy, self).__init__(member_repo) + proxy_kwargs = {'context': self.context} + super(ImageMemberRepoProxy, self).__init__( + image, + member_repo, + member_proxy_class=ImageMemberProxy, + member_proxy_kwargs=proxy_kwargs) + self._check_image_visibility() + + def _check_image_visibility(self): + if self.image.visibility == 'public': + message = _("Public images do not have members.") + raise exception.Forbidden(message) def get(self, member_id): if (self.context.is_admin or @@ -189,11 +200,16 @@ class ImageFactoryProxy(glance.domain.proxy.ImageFactory): return super(ImageFactoryProxy, self).new_image(owner=owner, **kwargs) -class ImageMemberFactoryProxy(object): +class ImageMemberFactoryProxy(glance.domain.proxy.ImageMembershipFactory): def __init__(self, image_member_factory, context): self.image_member_factory = image_member_factory self.context = context + kwargs = {'context': self.context} + super(ImageMemberFactoryProxy, self).__init__( + image_member_factory, + proxy_class=ImageMemberProxy, + proxy_kwargs=kwargs) def new_image_member(self, image, member_id): owner = image.owner @@ -315,10 +331,6 @@ class ImmutableImageProxy(object): message = _("You are not permitted to delete this image.") raise exception.Forbidden(message) - def get_member_repo(self): - member_repo = self.base.get_member_repo() - return ImageMemberRepoProxy(member_repo, self, self.context) - def get_data(self, *args, **kwargs): return self.base.get_data(*args, **kwargs) @@ -401,13 +413,13 @@ class ImageProxy(glance.domain.proxy.Image): self.context = context super(ImageProxy, self).__init__(image) - def get_member_repo(self, **kwargs): - if self.image.visibility == 'public': - message = _("Public images do not have members.") - raise exception.Forbidden(message) - else: - member_repo = self.image.get_member_repo(**kwargs) - return ImageMemberRepoProxy(member_repo, self, self.context) + +class ImageMemberProxy(glance.domain.proxy.ImageMember): + + def __init__(self, image_member, context): + self.image_member = image_member + self.context = context + super(ImageMemberProxy, self).__init__(image_member) class TaskProxy(glance.domain.proxy.Task): diff --git a/glance/api/policy.py b/glance/api/policy.py index 72e2c0ce8f..96f99c72ac 100644 --- a/glance/api/policy.py +++ b/glance/api/policy.py @@ -191,9 +191,14 @@ class ImageProxy(glance.domain.proxy.Image): self.policy.enforce(self.context, 'upload_image', self.target) return self.image.set_data(*args, **kwargs) - def get_member_repo(self, **kwargs): - member_repo = self.image.get_member_repo(**kwargs) - return ImageMemberRepoProxy(member_repo, self.context, self.policy) + +class ImageMemberProxy(glance.domain.proxy.ImageMember): + + def __init__(self, image_member, context, policy): + super(ImageMemberProxy, self).__init__(image_member) + self.image_member = image_member + self.context = context + self.policy = policy class ImageFactoryProxy(glance.domain.proxy.ImageFactory): @@ -218,15 +223,16 @@ class ImageMemberFactoryProxy(glance.domain.proxy.ImageMembershipFactory): def __init__(self, member_factory, context, policy): super(ImageMemberFactoryProxy, self).__init__( member_factory, - image_proxy_class=ImageProxy, - image_proxy_kwargs={'context': context, 'policy': policy}) + proxy_class=ImageMemberProxy, + proxy_kwargs={'context': context, 'policy': policy}) class ImageMemberRepoProxy(glance.domain.proxy.Repo): - def __init__(self, member_repo, context, policy): + def __init__(self, member_repo, image, context, policy): self.member_repo = member_repo - self.target = ImageTarget(self.member_repo.image) + self.image = image + self.target = ImageTarget(image) self.context = context self.policy = policy diff --git a/glance/api/v2/image_members.py b/glance/api/v2/image_members.py index 636c9f1f16..03f6429b1c 100644 --- a/glance/api/v2/image_members.py +++ b/glance/api/v2/image_members.py @@ -47,6 +47,18 @@ class ImageMembersController(object): self.gateway = glance.gateway.Gateway(self.db_api, self.store_api, self.notifier, self.policy) + def _get_member_repo(self, req, image): + try: + # For public images, a forbidden exception with message + # "Public images do not have members" is thrown. + return self.gateway.get_member_repo(image, req.context) + except exception.Forbidden as e: + msg = (_("Error fetching members of image %(image_id)s: " + "%(inner_msg)s") % {"image_id": image.image_id, + "inner_msg": e.msg}) + LOG.warning(msg) + raise webob.exc.HTTPForbidden(explanation=msg) + def _lookup_image(self, req, image_id): image_repo = self.gateway.get_repo(req.context) try: @@ -60,21 +72,8 @@ class ImageMembersController(object): LOG.warning(msg) raise webob.exc.HTTPForbidden(explanation=msg) - @staticmethod - def _get_member_repo(image): - try: - # For public images, a forbidden exception with message - # "Public images do not have members" is thrown. - return image.get_member_repo() - except exception.Forbidden as e: - msg = (_("Error fetching members of image %(image_id)s: " - "%(inner_msg)s") % {"image_id": image.image_id, - "inner_msg": e.msg}) - LOG.warning(msg) - raise webob.exc.HTTPForbidden(explanation=msg) - - def _lookup_member(self, image, member_id): - member_repo = self._get_member_repo(image) + def _lookup_member(self, req, image, member_id): + member_repo = self._get_member_repo(req, image) try: return member_repo.get(member_id) except (exception.NotFound): @@ -106,7 +105,7 @@ class ImageMembersController(object): """ image = self._lookup_image(req, image_id) - member_repo = self._get_member_repo(image) + member_repo = self._get_member_repo(req, image) image_member_factory = self.gateway.get_image_member_factory( req.context) try: @@ -148,8 +147,8 @@ class ImageMembersController(object): """ image = self._lookup_image(req, image_id) - member_repo = self._get_member_repo(image) - member = self._lookup_member(image, member_id) + member_repo = self._get_member_repo(req, image) + member = self._lookup_member(req, image, member_id) try: member.status = status member_repo.save(member) @@ -182,7 +181,7 @@ class ImageMembersController(object): ]} """ image = self._lookup_image(req, image_id) - member_repo = self._get_member_repo(image) + member_repo = self._get_member_repo(req, image) members = [] try: for member in member_repo.list(): @@ -209,7 +208,7 @@ class ImageMembersController(object): """ try: image = self._lookup_image(req, image_id) - return self._lookup_member(image, member_id) + return self._lookup_member(req, image, member_id) except webob.exc.HTTPForbidden as e: # Convert Forbidden to NotFound to prevent information # leakage. @@ -221,8 +220,8 @@ class ImageMembersController(object): Removes a membership from the image. """ image = self._lookup_image(req, image_id) - member_repo = self._get_member_repo(image) - member = self._lookup_member(image, member_id) + member_repo = self._get_member_repo(req, image) + member = self._lookup_member(req, image, member_id) try: member_repo.remove(member) return webob.Response(body='', status=204) diff --git a/glance/db/__init__.py b/glance/db/__init__.py index ed9a02af40..6c6e91d406 100644 --- a/glance/db/__init__.py +++ b/glance/db/__init__.py @@ -302,11 +302,6 @@ class ImageProxy(glance.domain.proxy.Image): self.image = image super(ImageProxy, self).__init__(image) - def get_member_repo(self): - member_repo = ImageMemberRepo(self.context, self.db_api, - self.image) - return member_repo - class ImageMemberRepo(object): diff --git a/glance/domain/proxy.py b/glance/domain/proxy.py index c9ce3e4bd7..9cc7bfef15 100644 --- a/glance/domain/proxy.py +++ b/glance/domain/proxy.py @@ -105,6 +105,37 @@ class Repo(object): return self.helper.proxy(result) +class MemberRepo(object): + def __init__(self, image, base, + member_proxy_class=None, member_proxy_kwargs=None): + self.image = image + self.base = base + self.member_proxy_helper = Helper(member_proxy_class, + member_proxy_kwargs) + + def get(self, member_id): + member = self.base.get(member_id) + return self.member_proxy_helper.proxy(member) + + def add(self, member): + self.base.add(self.member_proxy_helper.unproxy(member)) + + def list(self, *args, **kwargs): + members = self.base.list(*args, **kwargs) + return [self.member_proxy_helper.proxy(member) for member + in members] + + def remove(self, member): + base_item = self.member_proxy_helper.unproxy(member) + result = self.base.remove(base_item) + return self.member_proxy_helper.proxy(result) + + def save(self, member, from_state=None): + base_item = self.member_proxy_helper.unproxy(member) + result = self.base.save(base_item, from_state=from_state) + return self.member_proxy_helper.proxy(result) + + class ImageFactory(object): def __init__(self, base, proxy_class=None, proxy_kwargs=None): self.helper = Helper(proxy_class, proxy_kwargs) @@ -115,16 +146,14 @@ class ImageFactory(object): class ImageMembershipFactory(object): - def __init__(self, base, image_proxy_class=None, image_proxy_kwargs=None, - member_proxy_class=None, member_proxy_kwargs=None): + def __init__(self, base, proxy_class=None, proxy_kwargs=None): + self.helper = Helper(proxy_class, proxy_kwargs) self.base = base - self.image_helper = Helper(image_proxy_class, image_proxy_kwargs) - self.member_helper = Helper(member_proxy_class, member_proxy_kwargs) - def new_image_member(self, image, member_id): - base_image = self.image_helper.unproxy(image) - member = self.base.new_image_member(base_image, member_id) - return self.member_helper.proxy(member) + def new_image_member(self, image, member, **kwargs): + return self.helper.proxy(self.base.new_image_member(image, + member, + **kwargs)) class Image(object): @@ -168,8 +197,17 @@ class Image(object): def get_data(self, *args, **kwargs): return self.base.get_data(*args, **kwargs) - def get_member_repo(self): - return self.helper.proxy(self.base.get_member_repo()) + +class ImageMember(object): + def __init__(self, base): + self.base = base + + id = _proxy('base', 'id') + image_id = _proxy('base', 'image_id') + member_id = _proxy('base', 'member_id') + status = _proxy('base', 'status') + created_at = _proxy('base', 'created_at') + updated_at = _proxy('base', 'updated_at') class Task(object): diff --git a/glance/gateway.py b/glance/gateway.py index 6ecced5d52..c15d4d740f 100644 --- a/glance/gateway.py +++ b/glance/gateway.py @@ -89,6 +89,20 @@ class Gateway(object): return authorized_image_repo + def get_member_repo(self, image, context): + image_member_repo = glance.db.ImageMemberRepo( + context, self.db_api, image) + store_image_repo = glance.location.ImageMemberRepoProxy( + image_member_repo, image, context, self.store_api) + policy_member_repo = policy.ImageMemberRepoProxy( + store_image_repo, image, context, self.policy) + notifier_member_repo = glance.notifier.ImageMemberRepoProxy( + policy_member_repo, image, context, self.notifier) + authorized_member_repo = authorization.ImageMemberRepoProxy( + notifier_member_repo, image, context) + + return authorized_member_repo + def get_task_factory(self, context): task_factory = glance.domain.TaskFactory() policy_task_factory = policy.TaskFactoryProxy( diff --git a/glance/location.py b/glance/location.py index 0e29d18346..0b4fa45504 100644 --- a/glance/location.py +++ b/glance/location.py @@ -45,11 +45,16 @@ class ImageRepoProxy(glance.domain.proxy.Repo): item_proxy_class=ImageProxy, item_proxy_kwargs=proxy_kwargs) + self.db_api = glance.db.get_api() + def _set_acls(self, image): public = image.visibility == 'public' member_ids = [] if image.locations and not public: - member_repo = image.get_member_repo() + member_repo = _get_member_repo_for_store(image, + self.context, + self.db_api, + self.store_api) member_ids = [m.member_id for m in member_repo.list()] for location in image.locations: self.store_api.set_acls(location['url'], public=public, @@ -67,6 +72,15 @@ class ImageRepoProxy(glance.domain.proxy.Repo): return result +def _get_member_repo_for_store(image, context, db_api, store_api): + image_member_repo = glance.db.ImageMemberRepo( + context, db_api, image) + store_image_repo = glance.location.ImageMemberRepoProxy( + image_member_repo, image, context, store_api) + + return store_image_repo + + def _check_location_uri(context, store_api, store_utils, uri): """Check if an image location is valid. diff --git a/glance/notifier.py b/glance/notifier.py index 84a6b5440a..b81d529431 100644 --- a/glance/notifier.py +++ b/glance/notifier.py @@ -133,6 +133,21 @@ def format_image_notification(image): } +def format_image_member_notification(image_member): + """Given a glance.domain.ImageMember object, return a dictionary of relevant + notification information. + """ + return { + 'image_id': image_member.image_id, + 'member_id': image_member.member_id, + 'status': image_member.status, + 'created_at': timeutils.isotime(image_member.created_at), + 'updated_at': timeutils.isotime(image_member.updated_at), + 'deleted': False, + 'deleted_at': None, + } + + def format_task_notification(task): # NOTE(nikhil): input is not passed to the notifier payload as it may # contain sensitive info. @@ -436,6 +451,11 @@ class ImageProxy(NotificationProxy, domain_proxy.Image): self.send_notification('image.activate', self.repo) +class ImageMemberProxy(NotificationProxy, domain_proxy.ImageMember): + def get_super_class(self): + return domain_proxy.ImageMember + + class ImageFactoryProxy(NotificationFactoryProxy, domain_proxy.ImageFactory): def get_super_class(self): return domain_proxy.ImageFactory @@ -469,6 +489,43 @@ class ImageRepoProxy(NotificationRepoProxy, domain_proxy.Repo): }) +class ImageMemberRepoProxy(NotificationBase, domain_proxy.MemberRepo): + + def __init__(self, repo, image, context, notifier): + self.repo = repo + self.image = image + self.context = context + self.notifier = notifier + proxy_kwargs = {'context': self.context, 'notifier': self.notifier} + + proxy_class = self.get_proxy_class() + super_class = self.get_super_class() + super_class.__init__(self, image, repo, proxy_class, proxy_kwargs) + + def get_super_class(self): + return domain_proxy.MemberRepo + + def get_proxy_class(self): + return ImageMemberProxy + + def get_payload(self, obj): + return format_image_member_notification(obj) + + def save(self, member, from_state=None): + super(ImageMemberRepoProxy, self).save(member, from_state=from_state) + self.send_notification('image.member.update', member) + + def add(self, member): + super(ImageMemberRepoProxy, self).add(member) + self.send_notification('image.member.create', member) + + def remove(self, member): + super(ImageMemberRepoProxy, self).remove(member) + self.send_notification('image.member.delete', member, extra_payload={ + 'deleted': True, 'deleted_at': timeutils.isotime() + }) + + class TaskProxy(NotificationProxy, domain_proxy.Task): def get_super_class(self): return domain_proxy.Task diff --git a/glance/quota/__init__.py b/glance/quota/__init__.py index 1fc8079a7e..eb71f32bfb 100644 --- a/glance/quota/__init__.py +++ b/glance/quota/__init__.py @@ -169,8 +169,8 @@ class ImageMemberFactoryProxy(glance.domain.proxy.ImageMembershipFactory): 'store_utils': store_utils} super(ImageMemberFactoryProxy, self).__init__( member_factory, - image_proxy_class=ImageProxy, - image_proxy_kwargs=proxy_kwargs) + proxy_class=ImageMemberProxy, + proxy_kwargs=proxy_kwargs) def _enforce_image_member_quota(self, image): if CONF.image_member_quota < 0: @@ -368,3 +368,13 @@ class ImageProxy(glance.domain.proxy.Image): def added_new_properties(self): current_props = set(self.image.extra_properties.keys()) return bool(current_props.difference(self.orig_props)) + + +class ImageMemberProxy(glance.domain.proxy.ImageMember): + + def __init__(self, image_member, context, db_api, store_utils): + self.image_member = image_member + self.context = context + self.db_api = db_api + self.store_utils = store_utils + super(ImageMemberProxy, self).__init__(image_member) diff --git a/glance/tests/unit/test_domain_proxy.py b/glance/tests/unit/test_domain_proxy.py index 39b72becda..6979cab776 100644 --- a/glance/tests/unit/test_domain_proxy.py +++ b/glance/tests/unit/test_domain_proxy.py @@ -220,8 +220,7 @@ class TestImageMembershipFactory(test_utils.BaseTestCase): def test_proxy_wrapped_membership(self): proxy_factory = proxy.ImageMembershipFactory( - self.factory, member_proxy_class=FakeProxy, - member_proxy_kwargs={'a': 1}) + self.factory, proxy_class=FakeProxy, proxy_kwargs={'a': 1}) self.factory.result = 'tyrion' membership = proxy_factory.new_image_member('jaime', 'cersei') self.assertIsInstance(membership, FakeProxy) @@ -232,12 +231,12 @@ class TestImageMembershipFactory(test_utils.BaseTestCase): def test_proxy_wrapped_image(self): proxy_factory = proxy.ImageMembershipFactory( - self.factory, image_proxy_class=FakeProxy) + self.factory, proxy_class=FakeProxy) self.factory.result = 'tyrion' image = FakeProxy('jaime') membership = proxy_factory.new_image_member(image, 'cersei') - self.assertEqual('tyrion', membership) - self.assertEqual('jaime', self.factory.image) + self.assertIsInstance(membership, FakeProxy) + self.assertIsInstance(self.factory.image, FakeProxy) self.assertEqual('cersei', self.factory.member_id) def test_proxy_both_wrapped(self): @@ -246,9 +245,8 @@ class TestImageMembershipFactory(test_utils.BaseTestCase): proxy_factory = proxy.ImageMembershipFactory( self.factory, - member_proxy_class=FakeProxy, - member_proxy_kwargs={'b': 2}, - image_proxy_class=FakeProxy2) + proxy_class=FakeProxy, + proxy_kwargs={'b': 2}) self.factory.result = 'tyrion' image = FakeProxy2('jaime') @@ -256,7 +254,7 @@ class TestImageMembershipFactory(test_utils.BaseTestCase): self.assertIsInstance(membership, FakeProxy) self.assertEqual('tyrion', membership.base) self.assertEqual({'b': 2}, membership.kwargs) - self.assertEqual('jaime', self.factory.image) + self.assertIsInstance(self.factory.image, FakeProxy2) self.assertEqual('cersei', self.factory.member_id) @@ -264,29 +262,6 @@ class FakeImage(object): def __init__(self, result=None): self.result = result - def get_member_repo(self): - return self.result - - -class TestImage(test_utils.BaseTestCase): - def setUp(self): - super(TestImage, self).setUp() - self.image = FakeImage() - - def test_normal_member_repo(self): - proxy_image = proxy.Image(self.image) - self.image.result = 'mormont' - self.assertEqual('mormont', proxy_image.get_member_repo()) - - def test_proxied_member_repo(self): - proxy_image = proxy.Image(self.image, - member_repo_proxy_class=FakeProxy, - member_repo_proxy_kwargs={'a': 10}) - self.image.result = 'corn' - member_repo = proxy_image.get_member_repo() - self.assertIsInstance(member_repo, FakeProxy) - self.assertEqual('corn', member_repo.base) - class TestTaskFactory(test_utils.BaseTestCase): def setUp(self): diff --git a/glance/tests/unit/test_notifier.py b/glance/tests/unit/test_notifier.py index b0a32a4192..068a92ed34 100644 --- a/glance/tests/unit/test_notifier.py +++ b/glance/tests/unit/test_notifier.py @@ -66,6 +66,23 @@ class ImageRepoStub(object): return ['images_from_list'] +class ImageMemberRepoStub(object): + def remove(self, *args, **kwargs): + return 'image_member_from_remove' + + def save(self, *args, **kwargs): + return 'image_member_from_save' + + def add(self, *args, **kwargs): + return 'image_member_from_add' + + def get(self, *args, **kwargs): + return 'image_member_from_get' + + def list(self, *args, **kwargs): + return ['image_members_from_list'] + + class TaskStub(glance.domain.TaskStub): def run(self, executor): pass @@ -394,6 +411,91 @@ class TestImageNotifications(utils.BaseTestCase): self.assertIn('Failed', output_log['payload']) +class TestImageMemberNotifications(utils.BaseTestCase): + """Test Image Member Notifications work""" + + def setUp(self): + super(TestImageMemberNotifications, self).setUp() + self.context = glance.context.RequestContext(tenant=TENANT2, + user=USER1) + self.notifier = unit_test_utils.FakeNotifier() + + self.image = ImageStub( + image_id=UUID1, name='image-1', status='active', size=1024, + created_at=DATETIME, updated_at=DATETIME, owner=TENANT1, + visibility='public', container_format='ami', + tags=['one', 'two'], disk_format='ami', min_ram=128, + min_disk=10, checksum='ca425b88f047ce8ec45ee90e813ada91', + locations=['http://127.0.0.1']) + self.image_member = glance.domain.ImageMembership( + id=1, image_id=UUID1, member_id=TENANT1, created_at=DATETIME, + updated_at=DATETIME, status='accepted') + + self.image_member_repo_stub = ImageMemberRepoStub() + self.image_member_repo_proxy = glance.notifier.ImageMemberRepoProxy( + self.image_member_repo_stub, self.image, + self.context, self.notifier) + self.image_member_proxy = glance.notifier.ImageMemberProxy( + self.image_member, self.context, self.notifier) + + def _assert_image_member_with_notifier(self, output_log, deleted=False): + self.assertEqual(self.image_member.member_id, + output_log['payload']['member_id']) + self.assertEqual(self.image_member.image_id, + output_log['payload']['image_id']) + self.assertEqual(self.image_member.status, + output_log['payload']['status']) + self.assertEqual(timeutils.isotime(self.image_member.created_at), + output_log['payload']['created_at']) + self.assertEqual(timeutils.isotime(self.image_member.updated_at), + output_log['payload']['updated_at']) + + if deleted: + self.assertTrue(output_log['payload']['deleted']) + self.assertIsNotNone(output_log['payload']['deleted_at']) + else: + self.assertFalse(output_log['payload']['deleted']) + self.assertIsNone(output_log['payload']['deleted_at']) + + def test_image_member_add_notification(self): + self.image_member_repo_proxy.add(self.image_member_proxy) + output_logs = self.notifier.get_logs() + self.assertEqual(1, len(output_logs)) + output_log = output_logs[0] + self.assertEqual('INFO', output_log['notification_type']) + self.assertEqual('image.member.create', output_log['event_type']) + self._assert_image_member_with_notifier(output_log) + + def test_image_member_save_notification(self): + self.image_member_repo_proxy.save(self.image_member_proxy) + output_logs = self.notifier.get_logs() + self.assertEqual(1, len(output_logs)) + output_log = output_logs[0] + self.assertEqual('INFO', output_log['notification_type']) + self.assertEqual('image.member.update', output_log['event_type']) + self._assert_image_member_with_notifier(output_log) + + def test_image_member_delete_notification(self): + self.image_member_repo_proxy.remove(self.image_member_proxy) + output_logs = self.notifier.get_logs() + self.assertEqual(1, len(output_logs)) + output_log = output_logs[0] + self.assertEqual('INFO', output_log['notification_type']) + self.assertEqual('image.member.delete', output_log['event_type']) + self._assert_image_member_with_notifier(output_log, deleted=True) + + def test_image_member_get(self): + image_member = self.image_member_repo_proxy.get(TENANT1) + self.assertIsInstance(image_member, glance.notifier.ImageMemberProxy) + self.assertEqual('image_member_from_get', image_member.repo) + + def test_image_member_list(self): + image_members = self.image_member_repo_proxy.list() + self.assertIsInstance(image_members[0], + glance.notifier.ImageMemberProxy) + self.assertEqual('image_members_from_list', image_members[0].repo) + + class TestTaskNotifications(utils.BaseTestCase): """Test Task Notifications work""" diff --git a/glance/tests/unit/test_policy.py b/glance/tests/unit/test_policy.py index b260255cb9..f987b017a3 100644 --- a/glance/tests/unit/test_policy.py +++ b/glance/tests/unit/test_policy.py @@ -349,8 +349,10 @@ class TestMemberPolicy(test_utils.BaseTestCase): def setUp(self): self.policy = mock.Mock() self.policy.enforce = mock.Mock() + self.image_stub = ImageStub(UUID1) + image = glance.api.policy.ImageProxy(self.image_stub, {}, self.policy) self.member_repo = glance.api.policy.ImageMemberRepoProxy( - MemberRepoStub(), {}, self.policy) + MemberRepoStub(), image, {}, self.policy) self.target = self.member_repo.target super(TestMemberPolicy, self).setUp() diff --git a/glance/tests/unit/test_quota.py b/glance/tests/unit/test_quota.py index 1fdebee3ff..f7f9d13fd2 100644 --- a/glance/tests/unit/test_quota.py +++ b/glance/tests/unit/test_quota.py @@ -605,7 +605,7 @@ class TestImageMemberQuotas(test_utils.BaseTestCase): self.image_member_factory.new_image_member(self.image, 'fake_id') nim = self.base_image_member_factory.new_image_member - nim .assert_called_once_with(self.image.base, 'fake_id') + nim.assert_called_once_with(self.image, 'fake_id') def test_new_image_member_unlimited_members(self): self.config(image_member_quota=-1) @@ -613,7 +613,7 @@ class TestImageMemberQuotas(test_utils.BaseTestCase): self.image_member_factory.new_image_member(self.image, 'fake_id') nim = self.base_image_member_factory.new_image_member - nim.assert_called_once_with(self.image.base, 'fake_id') + nim.assert_called_once_with(self.image, 'fake_id') def test_new_image_member_too_many_members(self): self.config(image_member_quota=0) diff --git a/glance/tests/unit/test_store_image.py b/glance/tests/unit/test_store_image.py index 9aaa097148..d4b407998c 100644 --- a/glance/tests/unit/test_store_image.py +++ b/glance/tests/unit/test_store_image.py @@ -741,6 +741,18 @@ class TestStoreImageRepo(utils.BaseTestCase): self.image_repo = glance.location.ImageRepoProxy(self.image_repo_stub, {}, self.store_api, store_utils) + patcher = mock.patch("glance.location._get_member_repo_for_store", + self.get_fake_member_repo) + patcher.start() + self.addCleanup(patcher.stop) + self.fake_member_repo = FakeMemberRepo(self.image, [TENANT1, TENANT2]) + self.image_member_repo = glance.location.ImageMemberRepoProxy( + self.fake_member_repo, + self.image, + {}, self.store_api) + + def get_fake_member_repo(self, image, context, db_api, store_api): + return FakeMemberRepo(self.image, [TENANT1, TENANT2]) def test_add_updates_acls(self): self.image_stub.locations = [{'url': 'foo', 'metadata': {}, @@ -794,10 +806,9 @@ class TestStoreImageRepo(utils.BaseTestCase): self.image_stub.locations = [{'url': 'glug', 'metadata': {}, 'status': 'active'}] self.image_stub.visibility = 'private' - member_repo = self.image.get_member_repo() membership = glance.domain.ImageMembership( UUID1, TENANT3, None, None, status='accepted') - member_repo.add(membership) + self.image_member_repo.add(membership) self.assertIn('glug', self.store_api.acls) acls = self.store_api.acls['glug'] self.assertFalse(acls['public']) @@ -808,10 +819,9 @@ class TestStoreImageRepo(utils.BaseTestCase): self.image_stub.locations = [{'url': 'glug', 'metadata': {}, 'status': 'active'}] self.image_stub.visibility = 'private' - member_repo = self.image.get_member_repo() membership = glance.domain.ImageMembership( UUID1, TENANT1, None, None, status='accepted') - member_repo.remove(membership) + self.image_member_repo.remove(membership) self.assertIn('glug', self.store_api.acls) acls = self.store_api.acls['glug'] self.assertFalse(acls['public'])