Fix for Image members not generating notifications

Image members CRUD doesn't generate notifications which
is impacting searchlight service by not having latest
changes to Image memberships.

If you create an image and later change its members,
the members are not updated via notifications.
You have to run the index sync again to get the updated
member list.

See: https://bugs.launchpad.net/searchlight/+bug/1490697

Membership information is critical for horizon filtering.
Typically, a person is allowed to view an image under the
following conditions:

1) The image is owned by the project I am currently logged into.
2) The image is public
3) The image is owned by another project which has added me
    as a member and I have accepted membership to it.

Without current membership information, 3) above is not possible.

See: https://bugs.launchpad.net/searchlight/+bug/1491085

Change-Id: Ia56e42d3d8da36cfa419d5c3c7d69c9ccf8974fd
Closes-Bug: #1441453
This commit is contained in:
Lakshmi N Sampath 2015-09-08 19:08:36 +05:30
parent f4d46d1c34
commit 4b0ce57c73
14 changed files with 334 additions and 100 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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