Add owner validation for "openstack image create/set"
Owner validation is necessary if a new image owner will be created/set. Change-Id: I621774e02866bfa98a31b613deff5d7b6a962737 Closes-Bug: #1517134
This commit is contained in:
parent
b611045639
commit
0a444fc949
@ -33,6 +33,7 @@ Create/upload an image
|
|||||||
[--public | --private]
|
[--public | --private]
|
||||||
[--property <key=value> [...] ]
|
[--property <key=value> [...] ]
|
||||||
[--tag <tag> [...] ]
|
[--tag <tag> [...] ]
|
||||||
|
[--project-domain <project-domain>]
|
||||||
<image-name>
|
<image-name>
|
||||||
|
|
||||||
.. option:: --id <id>
|
.. option:: --id <id>
|
||||||
@ -127,6 +128,11 @@ Create/upload an image
|
|||||||
|
|
||||||
.. versionadded:: 2
|
.. versionadded:: 2
|
||||||
|
|
||||||
|
.. option:: --project-domain <project-domain>
|
||||||
|
|
||||||
|
Domain the project belongs to (name or ID).
|
||||||
|
This can be used in case collisions between project names exist.
|
||||||
|
|
||||||
.. describe:: <image-name>
|
.. describe:: <image-name>
|
||||||
|
|
||||||
New image name
|
New image name
|
||||||
@ -244,6 +250,7 @@ Set image properties
|
|||||||
[--os-version <os-version>]
|
[--os-version <os-version>]
|
||||||
[--ramdisk-id <ramdisk-id>]
|
[--ramdisk-id <ramdisk-id>]
|
||||||
[--activate|--deactivate]
|
[--activate|--deactivate]
|
||||||
|
[--project-domain <project-domain>]
|
||||||
<image>
|
<image>
|
||||||
|
|
||||||
.. option:: --name <name>
|
.. option:: --name <name>
|
||||||
@ -400,6 +407,11 @@ Set image properties
|
|||||||
|
|
||||||
.. versionadded:: 2
|
.. versionadded:: 2
|
||||||
|
|
||||||
|
.. option:: --project-domain <project-domain>
|
||||||
|
|
||||||
|
Domain the project belongs to (name or ID).
|
||||||
|
This can be used in case collisions between project names exist.
|
||||||
|
|
||||||
.. describe:: <image>
|
.. describe:: <image>
|
||||||
|
|
||||||
Image to modify (name or ID)
|
Image to modify (name or ID)
|
||||||
|
@ -220,6 +220,7 @@ class CreateImage(show.ShowOne):
|
|||||||
help="Set a tag on this image "
|
help="Set a tag on this image "
|
||||||
"(repeat option to set multiple tags)",
|
"(repeat option to set multiple tags)",
|
||||||
)
|
)
|
||||||
|
common.add_project_domain_option_to_parser(parser)
|
||||||
for deadopt in self.deadopts:
|
for deadopt in self.deadopts:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--%s" % deadopt,
|
"--%s" % deadopt,
|
||||||
@ -231,6 +232,7 @@ class CreateImage(show.ShowOne):
|
|||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)", parsed_args)
|
self.log.debug("take_action(%s)", parsed_args)
|
||||||
|
identity_client = self.app.client_manager.identity
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
for deadopt in self.deadopts:
|
for deadopt in self.deadopts:
|
||||||
@ -285,6 +287,13 @@ class CreateImage(show.ShowOne):
|
|||||||
self.log.warning("Failed to get an image file.")
|
self.log.warning("Failed to get an image file.")
|
||||||
return {}, {}
|
return {}, {}
|
||||||
|
|
||||||
|
if parsed_args.owner:
|
||||||
|
kwargs['owner'] = common.find_project(
|
||||||
|
identity_client,
|
||||||
|
parsed_args.owner,
|
||||||
|
parsed_args.project_domain,
|
||||||
|
).id
|
||||||
|
|
||||||
# If a volume is specified.
|
# If a volume is specified.
|
||||||
if parsed_args.volume:
|
if parsed_args.volume:
|
||||||
volume_client = self.app.client_manager.volume
|
volume_client = self.app.client_manager.volume
|
||||||
@ -704,6 +713,7 @@ class SetImage(command.Command):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Activate the image",
|
help="Activate the image",
|
||||||
)
|
)
|
||||||
|
common.add_project_domain_option_to_parser(parser)
|
||||||
for deadopt in self.deadopts:
|
for deadopt in self.deadopts:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--%s" % deadopt,
|
"--%s" % deadopt,
|
||||||
@ -715,6 +725,7 @@ class SetImage(command.Command):
|
|||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)", parsed_args)
|
self.log.debug("take_action(%s)", parsed_args)
|
||||||
|
identity_client = self.app.client_manager.identity
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
for deadopt in self.deadopts:
|
for deadopt in self.deadopts:
|
||||||
@ -779,6 +790,13 @@ class SetImage(command.Command):
|
|||||||
# Tags should be extended, but duplicates removed
|
# Tags should be extended, but duplicates removed
|
||||||
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
|
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
|
||||||
|
|
||||||
|
if parsed_args.owner:
|
||||||
|
kwargs['owner'] = common.find_project(
|
||||||
|
identity_client,
|
||||||
|
parsed_args.owner,
|
||||||
|
parsed_args.project_domain,
|
||||||
|
).id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image = image_client.images.update(image.id, **kwargs)
|
image = image_client.images.update(image.id, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -57,6 +57,19 @@ class TestImageCreate(TestImage):
|
|||||||
|
|
||||||
self.new_image = image_fakes.FakeImage.create_one_image()
|
self.new_image = image_fakes.FakeImage.create_one_image()
|
||||||
self.images_mock.create.return_value = self.new_image
|
self.images_mock.create.return_value = self.new_image
|
||||||
|
|
||||||
|
self.project_mock.get.return_value = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
copy.deepcopy(identity_fakes.PROJECT),
|
||||||
|
loaded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.domain_mock.get.return_value = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
copy.deepcopy(identity_fakes.DOMAIN),
|
||||||
|
loaded=True,
|
||||||
|
)
|
||||||
|
|
||||||
# This is the return value for utils.find_resource()
|
# This is the return value for utils.find_resource()
|
||||||
self.images_mock.get.return_value = copy.deepcopy(
|
self.images_mock.get.return_value = copy.deepcopy(
|
||||||
image_fakes.FakeImage.get_image_info(self.new_image))
|
image_fakes.FakeImage.get_image_info(self.new_image))
|
||||||
@ -123,6 +136,7 @@ class TestImageCreate(TestImage):
|
|||||||
if self.new_image.protected else '--unprotected'),
|
if self.new_image.protected else '--unprotected'),
|
||||||
('--private'
|
('--private'
|
||||||
if self.new_image.visibility == 'private' else '--public'),
|
if self.new_image.visibility == 'private' else '--public'),
|
||||||
|
'--project-domain', identity_fakes.domain_id,
|
||||||
self.new_image.name,
|
self.new_image.name,
|
||||||
]
|
]
|
||||||
verifylist = [
|
verifylist = [
|
||||||
@ -135,6 +149,7 @@ class TestImageCreate(TestImage):
|
|||||||
('unprotected', not self.new_image.protected),
|
('unprotected', not self.new_image.protected),
|
||||||
('public', self.new_image.visibility == 'public'),
|
('public', self.new_image.visibility == 'public'),
|
||||||
('private', self.new_image.visibility == 'private'),
|
('private', self.new_image.visibility == 'private'),
|
||||||
|
('project_domain', identity_fakes.domain_id),
|
||||||
('name', self.new_image.name),
|
('name', self.new_image.name),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -149,7 +164,7 @@ class TestImageCreate(TestImage):
|
|||||||
disk_format='fs',
|
disk_format='fs',
|
||||||
min_disk=10,
|
min_disk=10,
|
||||||
min_ram=4,
|
min_ram=4,
|
||||||
owner=self.new_image.owner,
|
owner=identity_fakes.project_id,
|
||||||
protected=self.new_image.protected,
|
protected=self.new_image.protected,
|
||||||
visibility=self.new_image.visibility,
|
visibility=self.new_image.visibility,
|
||||||
)
|
)
|
||||||
@ -168,6 +183,40 @@ class TestImageCreate(TestImage):
|
|||||||
image_fakes.FakeImage.get_image_data(self.new_image),
|
image_fakes.FakeImage.get_image_data(self.new_image),
|
||||||
data)
|
data)
|
||||||
|
|
||||||
|
def test_image_create_with_unexist_owner(self):
|
||||||
|
self.project_mock.get.side_effect = exceptions.NotFound(None)
|
||||||
|
self.project_mock.find.side_effect = exceptions.NotFound(None)
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--container-format', 'ovf',
|
||||||
|
'--disk-format', 'fs',
|
||||||
|
'--min-disk', '10',
|
||||||
|
'--min-ram', '4',
|
||||||
|
'--owner', 'unexist_owner',
|
||||||
|
'--protected',
|
||||||
|
'--private',
|
||||||
|
image_fakes.image_name,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('container_format', 'ovf'),
|
||||||
|
('disk_format', 'fs'),
|
||||||
|
('min_disk', 10),
|
||||||
|
('min_ram', 4),
|
||||||
|
('owner', 'unexist_owner'),
|
||||||
|
('protected', True),
|
||||||
|
('unprotected', False),
|
||||||
|
('public', False),
|
||||||
|
('private', True),
|
||||||
|
('name', image_fakes.image_name),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.cmd.take_action,
|
||||||
|
parsed_args,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('glanceclient.common.utils.get_data_file', name='Open')
|
@mock.patch('glanceclient.common.utils.get_data_file', name='Open')
|
||||||
def test_image_create_file(self, mock_open):
|
def test_image_create_file(self, mock_open):
|
||||||
mock_file = mock.MagicMock(name='File')
|
mock_file = mock.MagicMock(name='File')
|
||||||
@ -686,6 +735,18 @@ class TestImageSet(TestImage):
|
|||||||
schemas.SchemaBasedModel,
|
schemas.SchemaBasedModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.project_mock.get.return_value = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
copy.deepcopy(identity_fakes.PROJECT),
|
||||||
|
loaded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.domain_mock.get.return_value = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
copy.deepcopy(identity_fakes.DOMAIN),
|
||||||
|
loaded=True,
|
||||||
|
)
|
||||||
|
|
||||||
self.images_mock.get.return_value = self.model(**image_fakes.IMAGE)
|
self.images_mock.get.return_value = self.model(**image_fakes.IMAGE)
|
||||||
self.images_mock.update.return_value = self.model(**image_fakes.IMAGE)
|
self.images_mock.update.return_value = self.model(**image_fakes.IMAGE)
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
@ -694,20 +755,22 @@ class TestImageSet(TestImage):
|
|||||||
def test_image_set_options(self):
|
def test_image_set_options(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--name', 'new-name',
|
'--name', 'new-name',
|
||||||
'--owner', 'new-owner',
|
'--owner', identity_fakes.project_name,
|
||||||
'--min-disk', '2',
|
'--min-disk', '2',
|
||||||
'--min-ram', '4',
|
'--min-ram', '4',
|
||||||
'--container-format', 'ovf',
|
'--container-format', 'ovf',
|
||||||
'--disk-format', 'vmdk',
|
'--disk-format', 'vmdk',
|
||||||
|
'--project-domain', identity_fakes.domain_id,
|
||||||
image_fakes.image_id,
|
image_fakes.image_id,
|
||||||
]
|
]
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('name', 'new-name'),
|
('name', 'new-name'),
|
||||||
('owner', 'new-owner'),
|
('owner', identity_fakes.project_name),
|
||||||
('min_disk', 2),
|
('min_disk', 2),
|
||||||
('min_ram', 4),
|
('min_ram', 4),
|
||||||
('container_format', 'ovf'),
|
('container_format', 'ovf'),
|
||||||
('disk_format', 'vmdk'),
|
('disk_format', 'vmdk'),
|
||||||
|
('project_domain', identity_fakes.domain_id),
|
||||||
('image', image_fakes.image_id),
|
('image', image_fakes.image_id),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
@ -717,7 +780,7 @@ class TestImageSet(TestImage):
|
|||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'name': 'new-name',
|
'name': 'new-name',
|
||||||
'owner': 'new-owner',
|
'owner': identity_fakes.project_id,
|
||||||
'min_disk': 2,
|
'min_disk': 2,
|
||||||
'min_ram': 4,
|
'min_ram': 4,
|
||||||
'container_format': 'ovf',
|
'container_format': 'ovf',
|
||||||
@ -727,6 +790,25 @@ class TestImageSet(TestImage):
|
|||||||
self.images_mock.update.assert_called_with(
|
self.images_mock.update.assert_called_with(
|
||||||
image_fakes.image_id, **kwargs)
|
image_fakes.image_id, **kwargs)
|
||||||
|
|
||||||
|
def test_image_set_with_unexist_owner(self):
|
||||||
|
self.project_mock.get.side_effect = exceptions.NotFound(None)
|
||||||
|
self.project_mock.find.side_effect = exceptions.NotFound(None)
|
||||||
|
|
||||||
|
arglist = [
|
||||||
|
'--owner', 'unexist_owner',
|
||||||
|
image_fakes.image_id,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('owner', 'unexist_owner'),
|
||||||
|
('image', image_fakes.image_id),
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CommandError,
|
||||||
|
self.cmd.take_action, parsed_args)
|
||||||
|
|
||||||
def test_image_set_bools1(self):
|
def test_image_set_bools1(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--protected',
|
'--protected',
|
||||||
|
Loading…
Reference in New Issue
Block a user