Merge "Add tags to image set"

This commit is contained in:
Jenkins 2015-10-07 00:17:44 +00:00 committed by Gerrit Code Review
commit 23e821a86b
5 changed files with 122 additions and 10 deletions

View File

@ -238,6 +238,7 @@ Set image properties
[--checksum <checksum>] [--checksum <checksum>]
[--stdin] [--stdin]
[--property <key=value> [...] ] [--property <key=value> [...] ]
[--tag <tag> [...] ]
[--architecture <architecture>] [--architecture <architecture>]
[--instance-id <instance-id>] [--instance-id <instance-id>]
[--kernel-id <kernel-id>] [--kernel-id <kernel-id>]
@ -344,6 +345,14 @@ Set image properties
Set a property on this image (repeat option to set multiple properties) Set a property on this image (repeat option to set multiple properties)
.. versionadded:: 2
.. option:: --tag <tag>
Set a tag on this image (repeat for multiple values)
.. versionadded:: 2
.. option:: --architecture <architecture> .. option:: --architecture <architecture>
Operating system architecture Operating system architecture

View File

@ -57,8 +57,7 @@ def _format_image(image):
properties[key] = image.get(key) properties[key] = image.get(key)
# format the tags if they are there # format the tags if they are there
if image.get('tags'): info['tags'] = utils.format_list(image.get('tags'))
info['tags'] = utils.format_list(image.get('tags'))
# add properties back into the dictionary as a top-level key # add properties back into the dictionary as a top-level key
if properties: if properties:
@ -540,7 +539,6 @@ class SetImage(show.ShowOne):
# --force - needs adding # --force - needs adding
# --checksum - maybe could be done client side # --checksum - maybe could be done client side
# --stdin - could be implemented # --stdin - could be implemented
# --tags - needs adding
parser.add_argument( parser.add_argument(
"image", "image",
metavar="<image>", metavar="<image>",
@ -610,6 +608,15 @@ class SetImage(show.ShowOne):
help="Set a property on this image " help="Set a property on this image "
"(repeat option to set multiple properties)", "(repeat option to set multiple properties)",
) )
parser.add_argument(
"--tag",
dest="tags",
metavar="<tag>",
default=[],
action='append',
help="Set a tag on this image "
"(repeat option to set multiple tags)",
)
parser.add_argument( parser.add_argument(
"--architecture", "--architecture",
metavar="<architecture>", metavar="<architecture>",
@ -669,7 +676,7 @@ class SetImage(show.ShowOne):
copy_attrs = ('architecture', 'container_format', 'disk_format', copy_attrs = ('architecture', 'container_format', 'disk_format',
'file', 'instance_id', 'kernel_id', 'locations', 'file', 'instance_id', 'kernel_id', 'locations',
'min_disk', 'min_ram', 'name', 'os_distro', 'os_version', 'min_disk', 'min_ram', 'name', 'os_distro', 'os_version',
'owner', 'prefix', 'progress', 'ramdisk_id') 'owner', 'prefix', 'progress', 'ramdisk_id', 'tags')
for attr in copy_attrs: for attr in copy_attrs:
if attr in parsed_args: if attr in parsed_args:
val = getattr(parsed_args, attr, None) val = getattr(parsed_args, attr, None)
@ -705,6 +712,10 @@ class SetImage(show.ShowOne):
image = utils.find_resource( image = utils.find_resource(
image_client.images, parsed_args.image) image_client.images, parsed_args.image)
if parsed_args.tags:
# Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
image = image_client.images.update(image.id, **kwargs) image = image_client.images.update(image.id, **kwargs)
info = {} info = {}
info.update(image) info.update(image)

View File

@ -410,13 +410,14 @@ class TestServerImageCreate(TestServer):
compute_fakes.server_name, compute_fakes.server_name,
) )
collist = ('id', 'name', 'owner', 'protected', 'visibility') collist = ('id', 'name', 'owner', 'protected', 'tags', 'visibility')
self.assertEqual(collist, columns) self.assertEqual(collist, columns)
datalist = ( datalist = (
image_fakes.image_id, image_fakes.image_id,
image_fakes.image_name, image_fakes.image_name,
image_fakes.image_owner, image_fakes.image_owner,
image_fakes.image_protected, image_fakes.image_protected,
image_fakes.image_tags,
image_fakes.image_visibility, image_fakes.image_visibility,
) )
self.assertEqual(datalist, data) self.assertEqual(datalist, data)
@ -441,13 +442,14 @@ class TestServerImageCreate(TestServer):
'img-nam', 'img-nam',
) )
collist = ('id', 'name', 'owner', 'protected', 'visibility') collist = ('id', 'name', 'owner', 'protected', 'tags', 'visibility')
self.assertEqual(collist, columns) self.assertEqual(collist, columns)
datalist = ( datalist = (
image_fakes.image_id, image_fakes.image_id,
image_fakes.image_name, image_fakes.image_name,
image_fakes.image_owner, image_fakes.image_owner,
image_fakes.image_protected, image_fakes.image_protected,
image_fakes.image_tags,
image_fakes.image_visibility, image_fakes.image_visibility,
) )
self.assertEqual(datalist, data) self.assertEqual(datalist, data)

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
# #
import copy
import mock import mock
from openstackclient.tests import fakes from openstackclient.tests import fakes
@ -25,6 +26,7 @@ image_name = 'graven'
image_owner = 'baal' image_owner = 'baal'
image_protected = False image_protected = False
image_visibility = 'public' image_visibility = 'public'
image_tags = []
IMAGE = { IMAGE = {
'id': image_id, 'id': image_id,
@ -32,11 +34,16 @@ IMAGE = {
'owner': image_owner, 'owner': image_owner,
'protected': image_protected, 'protected': image_protected,
'visibility': image_visibility, 'visibility': image_visibility,
'tags': image_tags
} }
IMAGE_columns = tuple(sorted(IMAGE)) IMAGE_columns = tuple(sorted(IMAGE))
IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE))) IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE)))
IMAGE_SHOW = copy.copy(IMAGE)
IMAGE_SHOW['tags'] = ''
IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW)))
member_status = 'pending' member_status = 'pending'
MEMBER = { MEMBER = {
'member_id': identity_fakes.project_id, 'member_id': identity_fakes.project_id,
@ -117,6 +124,14 @@ IMAGE_schema = {
"type": "string", "type": "string",
"description": "Status of the image (READ-ONLY)" "description": "Status of the image (READ-ONLY)"
}, },
"tags": {
"items": {
"type": "string",
"maxLength": 255
},
"type": "array",
"description": "List of strings related to the image"
},
"visibility": { "visibility": {
"enum": [ "enum": [
"public", "public",

View File

@ -96,7 +96,7 @@ class TestImageCreate(TestImage):
) )
self.assertEqual(image_fakes.IMAGE_columns, columns) self.assertEqual(image_fakes.IMAGE_columns, columns)
self.assertEqual(image_fakes.IMAGE_data, data) self.assertEqual(image_fakes.IMAGE_SHOW_data, data)
@mock.patch('glanceclient.common.utils.get_data_file', name='Open') @mock.patch('glanceclient.common.utils.get_data_file', name='Open')
def test_image_reserve_options(self, mock_open): def test_image_reserve_options(self, mock_open):
@ -151,7 +151,7 @@ class TestImageCreate(TestImage):
) )
self.assertEqual(image_fakes.IMAGE_columns, columns) self.assertEqual(image_fakes.IMAGE_columns, columns)
self.assertEqual(image_fakes.IMAGE_data, data) self.assertEqual(image_fakes.IMAGE_SHOW_data, data)
@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):
@ -208,7 +208,7 @@ class TestImageCreate(TestImage):
) )
self.assertEqual(image_fakes.IMAGE_columns, columns) self.assertEqual(image_fakes.IMAGE_columns, columns)
self.assertEqual(image_fakes.IMAGE_data, data) self.assertEqual(image_fakes.IMAGE_SHOW_data, data)
def test_image_create_dead_options(self): def test_image_create_dead_options(self):
@ -812,6 +812,81 @@ class TestImageSet(TestImage):
**kwargs **kwargs
) )
def test_image_set_tag(self):
arglist = [
'--tag', 'test-tag',
image_fakes.image_name,
]
verifylist = [
('tags', ['test-tag']),
('image', image_fakes.image_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
self.cmd.take_action(parsed_args)
kwargs = {
'tags': ['test-tag'],
}
# ImageManager.update(image, **kwargs)
self.images_mock.update.assert_called_with(
image_fakes.image_id,
**kwargs
)
def test_image_set_tag_merge(self):
old_image = copy.copy(image_fakes.IMAGE)
old_image['tags'] = ['old1', 'new2']
self.images_mock.get.return_value = self.model(**old_image)
arglist = [
'--tag', 'test-tag',
image_fakes.image_name,
]
verifylist = [
('tags', ['test-tag']),
('image', image_fakes.image_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
self.cmd.take_action(parsed_args)
kwargs = {
'tags': ['old1', 'new2', 'test-tag'],
}
# ImageManager.update(image, **kwargs)
a, k = self.images_mock.update.call_args
self.assertEqual(image_fakes.image_id, a[0])
self.assertTrue('tags' in k)
self.assertEqual(set(kwargs['tags']), set(k['tags']))
def test_image_set_tag_merge_dupe(self):
old_image = copy.copy(image_fakes.IMAGE)
old_image['tags'] = ['old1', 'new2']
self.images_mock.get.return_value = self.model(**old_image)
arglist = [
'--tag', 'old1',
image_fakes.image_name,
]
verifylist = [
('tags', ['old1']),
('image', image_fakes.image_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
self.cmd.take_action(parsed_args)
kwargs = {
'tags': ['new2', 'old1'],
}
# ImageManager.update(image, **kwargs)
a, k = self.images_mock.update.call_args
self.assertEqual(image_fakes.image_id, a[0])
self.assertTrue('tags' in k)
self.assertEqual(set(kwargs['tags']), set(k['tags']))
def test_image_set_dead_options(self): def test_image_set_dead_options(self):
arglist = [ arglist = [
@ -861,4 +936,4 @@ class TestImageShow(TestImage):
) )
self.assertEqual(image_fakes.IMAGE_columns, columns) self.assertEqual(image_fakes.IMAGE_columns, columns)
self.assertEqual(image_fakes.IMAGE_data, data) self.assertEqual(image_fakes.IMAGE_SHOW_data, data)