Skip to content

Commit f055fe6

Browse files
author
Jordan Pittier
committed
Add support for Glance 'update image members' feature
This patch adds 3 new options to the "image set" command: --accept, --reject and --pending. This updates the membership status for an image. Closes-Bug: 1620481 Change-Id: I13b8c067aad68ece9ff636fbdd83bcb3663c91b2
1 parent c416aec commit f055fe6

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

doc/source/command-objects/image.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ Set image properties
325325
[--ramdisk-id <ramdisk-id>]
326326
[--activate|--deactivate]
327327
[--project <project> [--project-domain <project-domain>]]
328+
[--accept | --reject | --pending]
328329
<image>
329330
330331
.. option:: --name <name>
@@ -490,6 +491,36 @@ Set image properties
490491
491492
.. versionadded:: 2
492493
494+
.. option:: --accept
495+
496+
Accept the image membership.
497+
498+
If `--project` is passed, this will update the membership status for the
499+
given project, otherwise `--project` will default to the project the user
500+
is authenticated to.
501+
502+
.. versionadded:: 2
503+
504+
.. option:: --reject
505+
506+
Reject the image membership.
507+
508+
If `--project` is passed, this will update the membership status for the
509+
given project, otherwise `--project` will default to the project the user
510+
is authenticated to.
511+
512+
.. versionadded:: 2
513+
514+
.. option:: --pending
515+
516+
Reset the image membership to 'pending'.
517+
518+
If `--project` is passed, this will update the membership status for the
519+
given project, otherwise `--project` will default to the project the user
520+
is authenticated to.
521+
522+
.. versionadded:: 2
523+
493524
.. _image_set-image:
494525
.. describe:: <image>
495526

openstackclient/image/v2/image.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,23 @@ def get_parser(self, prog_name):
777777
dest=deadopt.replace('-', '_'),
778778
help=argparse.SUPPRESS,
779779
)
780+
781+
membership_group = parser.add_mutually_exclusive_group()
782+
membership_group.add_argument(
783+
"--accept",
784+
action="store_true",
785+
help=_("Accept the image membership"),
786+
)
787+
membership_group.add_argument(
788+
"--reject",
789+
action="store_true",
790+
help=_("Reject the image membership"),
791+
)
792+
membership_group.add_argument(
793+
"--pending",
794+
action="store_true",
795+
help=_("Reset the image membership to 'pending'"),
796+
)
780797
return parser
781798

782799
def take_action(self, parsed_args):
@@ -828,12 +845,14 @@ def take_action(self, parsed_args):
828845
project_arg = parsed_args.owner
829846
LOG.warning(_('The --owner option is deprecated, '
830847
'please use --project instead.'))
848+
project_id = None
831849
if project_arg:
832-
kwargs['owner'] = common.find_project(
850+
project_id = common.find_project(
833851
identity_client,
834852
project_arg,
835853
parsed_args.project_domain,
836854
).id
855+
kwargs['owner'] = project_id
837856

838857
image = utils.find_resource(
839858
image_client.images, parsed_args.image)
@@ -846,6 +865,21 @@ def take_action(self, parsed_args):
846865
image_client.images.reactivate(image.id)
847866
activation_status = "activated"
848867

868+
membership_group_args = ('accept', 'reject', 'pending')
869+
membership_status = [status for status in membership_group_args
870+
if getattr(parsed_args, status)]
871+
if membership_status:
872+
# If a specific project is not passed, assume we want to update
873+
# our own membership
874+
if not project_id:
875+
project_id = self.app.client_manager.auth_ref.project_id
876+
# The mutually exclusive group of the arg parser ensure we have at
877+
# most one item in the membership_status list.
878+
if membership_status[0] != 'pending':
879+
membership_status[0] += 'ed' # Glance expects the past form
880+
image_client.image_members.update(
881+
image.id, project_id, membership_status[0])
882+
849883
if parsed_args.tags:
850884
# Tags should be extended, but duplicates removed
851885
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))

openstackclient/tests/functional/image/v2/test_image.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,25 @@ def test_image_unset(self):
7474
self.openstack('image unset --property a --property c ' + self.NAME)
7575
raw_output = self.openstack('image show ' + self.NAME + opts)
7676
self.assertEqual(self.NAME + "\n\n", raw_output)
77+
78+
def test_image_members(self):
79+
opts = self.get_opts(['project_id'])
80+
my_project_id = self.openstack('token issue' + opts).strip()
81+
self.openstack(
82+
'image add project {} {}'.format(self.NAME, my_project_id))
83+
84+
self.openstack(
85+
'image set --accept ' + self.NAME)
86+
shared_img_list = self.parse_listing(
87+
self.openstack('image list --shared', self.get_opts(['name']))
88+
)
89+
self.assertIn(self.NAME, [img['Name'] for img in shared_img_list])
90+
91+
self.openstack(
92+
'image set --reject ' + self.NAME)
93+
shared_img_list = self.parse_listing(
94+
self.openstack('image list --shared', self.get_opts(['name']))
95+
)
96+
97+
self.openstack(
98+
'image remove project {} {}'.format(self.NAME, my_project_id))

openstackclient/tests/unit/image/v2/test_image.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,39 @@ def test_image_set_no_options(self):
845845

846846
self.assertIsNone(result)
847847

848+
self.image_members_mock.update.assert_not_called()
849+
850+
def test_image_set_membership_option(self):
851+
membership = image_fakes.FakeImage.create_one_image_member(
852+
attrs={'image_id': image_fakes.image_id,
853+
'member_id': self.project.id}
854+
)
855+
self.image_members_mock.update.return_value = membership
856+
857+
for status in ('accept', 'reject', 'pending'):
858+
arglist = [
859+
'--%s' % status,
860+
image_fakes.image_id,
861+
]
862+
verifylist = [
863+
(status, True),
864+
('image', image_fakes.image_id)
865+
]
866+
867+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
868+
self.cmd.take_action(parsed_args)
869+
870+
self.image_members_mock.update.assert_called_once_with(
871+
image_fakes.image_id,
872+
self.app.client_manager.auth_ref.project_id,
873+
status if status == 'pending' else status + 'ed'
874+
)
875+
self.image_members_mock.update.reset_mock()
876+
877+
# Assert that the 'update image" route is also called, in addition to
878+
# the 'update membership' route.
879+
self.images_mock.update.assert_called_with(image_fakes.image_id)
880+
848881
def test_image_set_options(self):
849882
arglist = [
850883
'--name', 'new-name',
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- Add support to update image membership with the `--accept`,
4+
``--reject`` and ``--pending`` options of the ``image set command``.

0 commit comments

Comments
 (0)