1 # Copyright 2012-2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
34 from mock import patch, call
35 from unittest import TestCase
36 from itertools import product
38 from kamaki.clients import ClientError
42 "status": "available",
44 "disk_format": "diskdump",
45 "container_format": "bare",
46 "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
49 "status": "available",
51 "disk_format": "diskdump",
52 "container_format": "bare",
53 "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
56 "status": "available",
58 "disk_format": "diskdump",
59 "container_format": "bare",
60 "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
63 example_images_detailed = [
65 "status": "available",
67 "checksum": "1a126aad07475b43cc1959b446344211be13974",
68 "created_at": "2013-01-28 22:44:54",
69 "disk_format": "diskdump",
70 "updated_at": "2013-01-28 22:44:55",
72 "partition_table": "msdos",
75 "exclude_task_assignhostname": "yes",
77 "root_partition": "1",
78 "description": "Archlinux base install 2012.12.01"},
79 "location": "pithos://us3r-I6E-1d/images/archlinux.12.2012",
80 "container_format": "bare",
81 "owner": "user163@mail.example.com",
84 "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
87 "status": "available",
89 "checksum": "b202b8c7030cb22f896c6664ac",
90 "created_at": "2013-02-13 10:07:42",
91 "disk_format": "diskdump",
92 "updated_at": "2013-02-13 10:07:44",
94 "partition_table": "msdos",
96 "description": "Ubuntu 12.04.1 LTS",
98 "root_partition": "1",
100 "location": "pithos://us3r-@r3n@-1d/images/mls-201302131203.diskdump",
101 "container_format": "bare",
102 "owner": "user3@mail.example.com",
105 "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
108 "status": "available",
110 "checksum": "06d3099815d1f6fada91e80107638b882",
111 "created_at": "2013-02-13 12:35:21",
112 "disk_format": "diskdump",
113 "updated_at": "2013-02-13 12:35:23",
115 "partition_table": "msdos",
117 "description": "Ubuntu 12.04.2 LTS",
119 "root_partition": "1",
121 "location": "pithos://us3r-E-1d/images/Gardenia-201302131431.diskdump",
122 "container_format": "bare",
123 "owner": "user3@mail.example.com",
126 "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
131 json = example_images
140 khttp = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
141 image_pkg = 'kamaki.clients.image.ImageClient'
144 class Image(TestCase):
146 def assert_dicts_are_equal(self, d1, d2):
147 for k, v in d1.items():
148 self.assertTrue(k in d2)
149 if isinstance(v, dict):
150 self.assert_dicts_are_equal(v, d2[k])
152 self.assertEqual(unicode(v), unicode(d2[k]))
155 self.url = 'http://image.example.com'
156 self.token = 'an1m@g370k3n=='
157 from kamaki.clients.image import ImageClient
158 self.client = ImageClient(self.url, self.token)
159 from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
160 self.C = KamakiHTTPConnection
163 FR.json = example_images
166 @patch('%s.get' % image_pkg, return_value=FR())
167 def test_list_public(self, get):
168 a_filter = dict(size_max=42)
169 for args in product((False, True), ({}, a_filter), ('', '-')):
170 (detail, filters, order) = args
171 r = self.client.list_public(*args)
172 filters['sort_dir'] = 'desc' if order.startswith('-') else 'asc'
173 self.assertEqual(get.mock_calls[-1], call(
174 '/images/%s' % ('detail' if detail else ''),
175 async_params=filters, success=200))
176 for i in range(len(r)):
177 self.assert_dicts_are_equal(r[i], example_images[i])
179 @patch('%s.head' % image_pkg, return_value=FR())
180 def test_get_meta(self, head):
181 img0 = example_images[0]
184 for k, v in example_images_detailed[0].items():
185 img0_headers['x-image-meta-%s' % k] = v
186 FR.headers = img0_headers
187 r = self.client.get_meta(img0['id'])
188 head.assert_called_once_with('/images/%s' % img0['id'], success=200)
189 self.assertEqual(r['id'], img0['id'])
190 self.assert_dicts_are_equal(r, example_images_detailed[0])
192 @patch('%s.set_header' % image_pkg, return_value=FR())
193 @patch('%s.post' % image_pkg, return_value=FR())
194 def test_register(self, post, SH):
195 img0 = example_images_detailed[0]
196 img0_location = img0['location']
197 img0_name = 'A new img0 name'
198 prfx = 'x-image-meta-'
199 proprfx = 'x-image-meta-property-'
201 'id', 'store', 'dist_format', 'container_format',
202 'size', 'checksum', 'is_public', 'owner']
204 ('v_id', None), ('v_store', None),
205 ('v_dist_format', None), ('v_container_format', None),
206 ('v_size', None), ('v_checksum', None),
207 ('v_is_public', None), ('v_owner', None)):
209 async_headers = dict()
211 for i, k in enumerate(keys):
214 async_headers['%s%s' % (prfx, k)] = args[i]
215 props['%s%s' % (proprfx, args[i])] = k
216 async_headers.update(props)
217 self.client.register(
218 img0_name, img0_location,
219 params=params, properties=props)
222 call('/images/', async_headers=async_headers, success=200))
223 self.assertEqual(SH.mock_calls[-2:], [
224 call('X-Image-Meta-Name', img0_name),
225 call('X-Image-Meta-Location', img0_location)])
227 @patch('%s.put' % image_pkg, return_value=FR())
228 def test_set_members(self, put):
229 img0 = example_images_detailed[0]
230 members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
231 self.client.set_members(img0['id'], members)
232 put.assert_called_once_with(
233 '/images/%s/members' % img0['id'],
234 json=dict(memberships=[dict(member_id=m) for m in members]),
237 @patch('%s.put' % image_pkg, return_value=FR())
238 def test_list_members(self, get):
239 imgid = example_images_detailed[0]['id']
240 members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
241 FR.json = dict(members=members)
242 r = self.client.list_members(imgid)
243 get.assert_called_once_with('/images/%s/members' % imgid, success=200)
244 self.assertEqual(r, members)
246 @patch('%s.put' % image_pkg, return_value=FR())
247 def test_add_member(self, put):
248 imgid = example_images_detailed[0]['id']
249 new_member = 'us3r-15-n3w'
250 self.client.add_member(imgid, new_member)
251 put.assert_called_once_with(
252 '/images/%s/members/%s' % (imgid, new_member),
255 @patch('%s.delete' % image_pkg, return_value=FR())
256 def test_remove_member(self, delete):
257 imgid = example_images_detailed[0]['id']
258 old_member = 'us3r-15-0ld'
259 self.client.remove_member(imgid, old_member)
260 delete.assert_called_once_with(
261 '/images/%s/members/%s' % (imgid, old_member),
264 @patch('%s.perform_request' % khttp, return_value=FR())
265 def test_list_shared(self, PR):
266 img0 = example_images_detailed[0]
267 FR.json = dict(shared_images=example_images)
268 r = self.client.list_shared(img0['id'])
269 self.assertEqual(self.client.http_client.url, self.url)
271 self.client.http_client.path,
272 '/shared-images/%s' % img0['id'])
273 for i in range(len(r)):
274 self.assert_dicts_are_equal(r[i], example_images[i])
276 if __name__ == '__main__':
278 from kamaki.clients.test import runTestCase
279 runTestCase(Image, 'Plankton Client', argv[1:])