Enable client V2 to update/delete tags for a given image.
Added the CLI option image-tag-update to associate a tag to an image via API V2. Added the CLI option image-tag-delete to delete a tag associated with an image via API V2. Related to bp glance-client-v2 Change-Id: I76060e1982223770a6c2c0bd9376d568af0df456
This commit is contained in:
parent
62579fbb21
commit
b9c1df8dfc
@ -18,6 +18,7 @@ import warlock
|
||||
from glanceclient.common import http
|
||||
from glanceclient.v2 import images
|
||||
from glanceclient.v2 import image_members
|
||||
from glanceclient.v2 import image_tags
|
||||
from glanceclient.v2 import schemas
|
||||
|
||||
|
||||
@ -34,8 +35,10 @@ class Client(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.http_client = http.HTTPClient(*args, **kwargs)
|
||||
self.schemas = schemas.Controller(self.http_client)
|
||||
image_model = self._get_image_model()
|
||||
self.images = images.Controller(self.http_client,
|
||||
self._get_image_model())
|
||||
image_model)
|
||||
self.image_tags = image_tags.Controller(self.http_client, image_model)
|
||||
self.image_members = image_members.Controller(self.http_client,
|
||||
self._get_member_model())
|
||||
|
||||
|
40
glanceclient/v2/image_tags.py
Normal file
40
glanceclient/v2/image_tags.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class Controller(object):
|
||||
def __init__(self, http_client, model):
|
||||
self.http_client = http_client
|
||||
self.model = model
|
||||
|
||||
def update(self, image_id, tag_value):
|
||||
"""
|
||||
Update an image with the given tag.
|
||||
|
||||
:param image_id: image to be updated with the given tag.
|
||||
:param tag_value: value of the tag.
|
||||
"""
|
||||
url = '/v2/images/%s/tags/%s' % (image_id, tag_value)
|
||||
self.http_client.json_request('PUT', url)
|
||||
|
||||
def delete(self, image_id, tag_value):
|
||||
"""
|
||||
Delete the tag associated with the given image.
|
||||
|
||||
:param image_id: Image whose tag to be deleted.
|
||||
:param tag_value: tag value to be deleted.
|
||||
"""
|
||||
url = '/v2/images/%s/tags/%s' % (image_id, tag_value)
|
||||
self.http_client.json_request('DELETE', url)
|
@ -133,3 +133,31 @@ def do_image_download(gc, args):
|
||||
def do_image_delete(gc, args):
|
||||
"""Delete specified image."""
|
||||
gc.images.delete(args.id)
|
||||
|
||||
|
||||
@utils.arg('image_id', metavar='<IMAGE_ID>',
|
||||
help='Image to be updated with the given tag')
|
||||
@utils.arg('tag_value', metavar='<TAG_VALUE>',
|
||||
help='Value of the tag')
|
||||
def do_image_tag_update(gc, args):
|
||||
"""Update an image with the given tag."""
|
||||
if not (args.image_id and args.tag_value):
|
||||
utils.exit('Unable to update tag. Specify image_id and tag_value')
|
||||
else:
|
||||
gc.image_tags.update(args.image_id, args.tag_value)
|
||||
image = gc.images.get(args.image_id)
|
||||
image = [image]
|
||||
columns = ['ID', 'Tags']
|
||||
utils.print_list(image, columns)
|
||||
|
||||
|
||||
@utils.arg('image_id', metavar='<IMAGE_ID>',
|
||||
help='Image whose tag to be deleted')
|
||||
@utils.arg('tag_value', metavar='<TAG_VALUE>',
|
||||
help='Value of the tag')
|
||||
def do_image_tag_delete(gc, args):
|
||||
"""Delete the tag associated with the given image."""
|
||||
if not (args.image_id and args.tag_value):
|
||||
utils.exit('Unable to delete tag. Specify image_id and tag_value')
|
||||
else:
|
||||
gc.image_tags.delete(args.image_id, args.tag_value)
|
||||
|
@ -24,6 +24,12 @@ from glanceclient.v2 import shell as test_shell
|
||||
|
||||
|
||||
class LegacyShellV1Test(testtools.TestCase):
|
||||
def _mock_glance_client(self):
|
||||
my_mocked_gc = mock.Mock()
|
||||
my_mocked_gc.schemas.return_value = 'test'
|
||||
my_mocked_gc.get.return_value = {}
|
||||
return my_mocked_gc
|
||||
|
||||
def test_do_image_list(self):
|
||||
gc = client.Client('1', 'http://no.where')
|
||||
|
||||
@ -51,9 +57,7 @@ class LegacyShellV1Test(testtools.TestCase):
|
||||
actual = test_shell.do_image_show(gc, Fake())
|
||||
|
||||
def test_do_explain(self):
|
||||
my_mocked_gc = mock.Mock()
|
||||
my_mocked_gc.schemas.return_value = 'test'
|
||||
my_mocked_gc.get.return_value = {}
|
||||
my_mocked_gc = self._mock_glance_client()
|
||||
|
||||
class Fake():
|
||||
def __init__(self):
|
||||
@ -84,3 +88,54 @@ class LegacyShellV1Test(testtools.TestCase):
|
||||
with mock.patch.object(gc.images, 'delete') as mocked_delete:
|
||||
mocked_delete.return_value = 0
|
||||
test_shell.do_image_delete(gc, Fake())
|
||||
|
||||
def test_image_tag_update(self):
|
||||
class Fake():
|
||||
image_id = 'IMG-01'
|
||||
tag_value = 'tag01'
|
||||
|
||||
gc = self._mock_glance_client()
|
||||
|
||||
with mock.patch.object(gc.image_tags, 'update') as mocked_update:
|
||||
gc.images.get = mock.Mock(return_value={})
|
||||
mocked_update.return_value = None
|
||||
test_shell.do_image_tag_update(gc, Fake())
|
||||
mocked_update.assert_called_once_with('IMG-01', 'tag01')
|
||||
|
||||
def test_image_tag_update_with_few_arguments(self):
|
||||
class Fake():
|
||||
image_id = None
|
||||
tag_value = 'tag01'
|
||||
|
||||
gc = self._mock_glance_client()
|
||||
|
||||
with mock.patch.object(utils, 'exit') as mocked_utils_exit:
|
||||
err_msg = 'Unable to update tag. Specify image_id and tag_value'
|
||||
mocked_utils_exit.return_value = '%s' % err_msg
|
||||
test_shell.do_image_tag_update(gc, Fake())
|
||||
mocked_utils_exit.assert_called_once_with(err_msg)
|
||||
|
||||
def test_image_tag_delete(self):
|
||||
class Fake():
|
||||
image_id = 'IMG-01'
|
||||
tag_value = 'tag01'
|
||||
|
||||
gc = self._mock_glance_client()
|
||||
|
||||
with mock.patch.object(gc.image_tags, 'delete') as mocked_delete:
|
||||
mocked_delete.return_value = None
|
||||
test_shell.do_image_tag_delete(gc, Fake())
|
||||
mocked_delete.assert_called_once_with('IMG-01', 'tag01')
|
||||
|
||||
def test_image_tag_delete_with_few_arguments(self):
|
||||
class Fake():
|
||||
image_id = 'IMG-01'
|
||||
tag_value = None
|
||||
|
||||
gc = self._mock_glance_client()
|
||||
|
||||
with mock.patch.object(utils, 'exit') as mocked_utils_exit:
|
||||
err_msg = 'Unable to delete tag. Specify image_id and tag_value'
|
||||
mocked_utils_exit.return_value = '%s' % err_msg
|
||||
test_shell.do_image_tag_delete(gc, Fake())
|
||||
mocked_utils_exit.assert_called_once_with(err_msg)
|
||||
|
76
tests/v2/test_tags.py
Normal file
76
tests/v2/test_tags.py
Normal file
@ -0,0 +1,76 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import testtools
|
||||
import warlock
|
||||
|
||||
from glanceclient.v2 import image_tags
|
||||
from tests import utils
|
||||
|
||||
|
||||
IMAGE = '3a4560a1-e585-443e-9b39-553b46ec92d1'
|
||||
TAG = 'tag01'
|
||||
|
||||
|
||||
fixtures = {
|
||||
'/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE, tag_value=TAG): {
|
||||
'DELETE': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
'PUT': (
|
||||
{},
|
||||
{
|
||||
'image_id': IMAGE,
|
||||
'tag_value': TAG
|
||||
}
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
fake_schema = {'name': 'image', 'properties': {'image_id': {}, 'tags': {}}}
|
||||
FakeModel = warlock.model_factory(fake_schema)
|
||||
|
||||
|
||||
class TestController(testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(TestController, self).setUp()
|
||||
self.api = utils.FakeAPI(fixtures)
|
||||
self.controller = image_tags.Controller(self.api, FakeModel)
|
||||
|
||||
def test_update_image_tag(self):
|
||||
image_id = IMAGE
|
||||
tag_value = TAG
|
||||
self.controller.update(image_id, tag_value)
|
||||
expect = [
|
||||
('PUT',
|
||||
'/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE,
|
||||
tag_value=TAG),
|
||||
{},
|
||||
None)]
|
||||
self.assertEqual(self.api.calls, expect)
|
||||
|
||||
def test_delete_image_tag(self):
|
||||
image_id = IMAGE
|
||||
tag_value = TAG
|
||||
self.controller.delete(image_id, tag_value)
|
||||
expect = [
|
||||
('DELETE',
|
||||
'/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE,
|
||||
tag_value=TAG),
|
||||
{},
|
||||
None)]
|
||||
self.assertEqual(self.api.calls, expect)
|
Loading…
x
Reference in New Issue
Block a user