Merge "Add owner validation for "openstack image create/set""
This commit is contained in:
		| @@ -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', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins