Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / image / test.py @ 5a745d8a

History | View | Annotate | Download (10.9 kB)

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.perform_request' % khttp, return_value=FR())
247
    def test_add_member(self, PR):
248
        img0 = example_images_detailed[0]
249
        new_member = 'us3r-15-n3w'
250
        self.assertRaises(
251
            ClientError,
252
            self.client.add_member,
253
            img0['id'], new_member)
254
        FR.status_code = 204
255
        self.client.add_member(img0['id'], new_member)
256
        self.assertEqual(self.client.http_client.url, self.url)
257
        self.assertEqual(
258
            self.client.http_client.path,
259
            '/images/%s/members/%s' % (img0['id'], new_member))
260

    
261
    @patch('%s.perform_request' % khttp, return_value=FR())
262
    def test_remove_member(self, PR):
263
        img0 = example_images_detailed[0]
264
        old_member = 'us3r-15-0ld'
265
        self.assertRaises(
266
            ClientError,
267
            self.client.remove_member,
268
            img0['id'], old_member)
269
        FR.status_code = 204
270
        self.client.remove_member(img0['id'], old_member)
271
        self.assertEqual(self.client.http_client.url, self.url)
272
        self.assertEqual(
273
            self.client.http_client.path,
274
            '/images/%s/members/%s' % (img0['id'], old_member))
275

    
276
    @patch('%s.perform_request' % khttp, return_value=FR())
277
    def test_list_shared(self, PR):
278
        img0 = example_images_detailed[0]
279
        FR.json = dict(shared_images=example_images)
280
        r = self.client.list_shared(img0['id'])
281
        self.assertEqual(self.client.http_client.url, self.url)
282
        self.assertEqual(
283
            self.client.http_client.path,
284
            '/shared-images/%s' % img0['id'])
285
        for i in range(len(r)):
286
            self.assert_dicts_are_equal(r[i], example_images[i])
287

    
288
if __name__ == '__main__':
289
    from sys import argv
290
    from kamaki.clients.test import runTestCase
291
    runTestCase(Image, 'Plankton Client', argv[1:])