summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVenkatesh Sampath <venkatesh.sampath@outlook.com>2013-06-25 17:58:42 +0530
committerVenkatesh Sampath <venkatesh.sampath@outlook.com>2013-07-02 21:32:49 +0530
commitb9c1df8dfc6e6520b1a9ba407b4321bd199b134c (patch)
tree26ab8ee5c543a7fe97cb860870911bf703addc94
parent62579fbb217f4c1a4668e793ebaafaf668619206 (diff)
downloadpython-glanceclient-b9c1df8dfc6e6520b1a9ba407b4321bd199b134c.tar.gz
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
-rw-r--r--glanceclient/v2/client.py5
-rw-r--r--glanceclient/v2/image_tags.py40
-rw-r--r--glanceclient/v2/shell.py28
-rw-r--r--tests/v2/test_shell_v2.py61
-rw-r--r--tests/v2/test_tags.py76
5 files changed, 206 insertions, 4 deletions
diff --git a/glanceclient/v2/client.py b/glanceclient/v2/client.py
index 741a59e..99285c4 100644
--- a/glanceclient/v2/client.py
+++ b/glanceclient/v2/client.py
@@ -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())
diff --git a/glanceclient/v2/image_tags.py b/glanceclient/v2/image_tags.py
new file mode 100644
index 0000000..a943d6a
--- /dev/null
+++ b/glanceclient/v2/image_tags.py
@@ -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)
diff --git a/glanceclient/v2/shell.py b/glanceclient/v2/shell.py
index d7e0b3f..7427e73 100644
--- a/glanceclient/v2/shell.py
+++ b/glanceclient/v2/shell.py
@@ -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)
diff --git a/tests/v2/test_shell_v2.py b/tests/v2/test_shell_v2.py
index 1f3cc0b..354ca97 100644
--- a/tests/v2/test_shell_v2.py
+++ b/tests/v2/test_shell_v2.py
@@ -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)
diff --git a/tests/v2/test_tags.py b/tests/v2/test_tags.py
new file mode 100644
index 0000000..2d763d4
--- /dev/null
+++ b/tests/v2/test_tags.py
@@ -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)