Finetest Image.remove_member
[kamaki] / kamaki / clients / image / test.py
1 # Copyright 2012-2013 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
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.
15 #
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.
28 #
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.
33
34 from mock import patch, call
35 from unittest import TestCase
36 from itertools import product
37
38 from kamaki.clients import ClientError
39
40 example_images = [
41     {
42         "status": "available",
43         "name": "Archlinux",
44         "disk_format": "diskdump",
45         "container_format": "bare",
46         "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
47         "size": 752782848},
48     {
49         "status": "available",
50         "name": "maelstrom",
51         "disk_format": "diskdump",
52         "container_format": "bare",
53         "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
54         "size": 2583195644},
55     {
56         "status": "available",
57         "name": "Gardenia",
58         "disk_format": "diskdump",
59         "container_format": "bare",
60         "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
61         "size": 2589802496}]
62
63 example_images_detailed = [
64     {
65         "status": "available",
66         "name": "Archlinux",
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",
71         "properties": {
72             "partition_table": "msdos",
73             "osfamily": "linux",
74             "users": "root",
75             "exclude_task_assignhostname": "yes",
76             "os": "archlinux",
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",
82         "is_public": True,
83         "deleted_at": "",
84         "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
85         "size": 752782848},
86     {
87         "status": "available",
88         "name": "maelstrom",
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",
93         "properties": {
94             "partition_table": "msdos",
95             "osfamily": "linux",
96             "description": "Ubuntu 12.04.1 LTS",
97             "os": "ubuntu",
98             "root_partition": "1",
99             "users": "user"},
100         "location": "pithos://us3r-@r3n@-1d/images/mls-201302131203.diskdump",
101         "container_format": "bare",
102         "owner": "user3@mail.example.com",
103         "is_public": True,
104         "deleted_at": "",
105         "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
106         "size": 2583195648},
107     {
108         "status": "available",
109         "name": "Gardenia",
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",
114         "properties": {
115             "partition_table": "msdos",
116             "osfamily": "linux",
117             "description": "Ubuntu 12.04.2 LTS",
118             "os": "ubuntu",
119             "root_partition": "1",
120             "users": "user"},
121         "location": "pithos://us3r-E-1d/images/Gardenia-201302131431.diskdump",
122         "container_format": "bare",
123         "owner": "user3@mail.example.com",
124         "is_public": True,
125         "deleted_at": "",
126         "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
127         "size": 2589802496}]
128
129
130 class FR(object):
131     json = example_images
132     headers = {}
133     content = json
134     status = None
135     status_code = 200
136
137     def release(self):
138         pass
139
140 khttp = 'kamaki.clients.connection.kamakicon.KamakiHTTPConnection'
141 image_pkg = 'kamaki.clients.image.ImageClient'
142
143
144 class Image(TestCase):
145
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])
151             else:
152                 self.assertEqual(unicode(v), unicode(d2[k]))
153
154     def setUp(self):
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
161
162     def tearDown(self):
163         FR.json = example_images
164         FR.status_code = 200
165
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])
178
179     @patch('%s.head' % image_pkg, return_value=FR())
180     def test_get_meta(self, head):
181         img0 = example_images[0]
182         FR.json = img0
183         img0_headers = {}
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])
191
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-'
200         keys = [
201             'id', 'store', 'dist_format', 'container_format',
202             'size', 'checksum', 'is_public', 'owner']
203         for args in product(
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)):
208             params = dict()
209             async_headers = dict()
210             props = dict()
211             for i, k in enumerate(keys):
212                 if args[i]:
213                     params[k] = args[i]
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)
220         self.assertEqual(
221             post.mock_calls[-1],
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)])
226
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]),
235             success=204)
236
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)
245
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),
253             success=204)
254
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),
262             success=204)
263
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)
270         self.assertEqual(
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])
275
276 if __name__ == '__main__':
277     from sys import argv
278     from kamaki.clients.test import runTestCase
279     runTestCase(Image, 'Plankton Client', argv[1:])