Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / image / test.py @ e99f9efb

History | View | Annotate | Download (11.4 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
example_image_headers = {
39
    'x-image-meta-id': '3edd4d15-41b4-4a39-9601-015ef56b3bb3',
40
    'x-image-meta-checksum': 'df23837c30889252c0aed80b6f770a53a86',
41
    'x-image-meta-container-format': 'bare',
42
    'x-image-meta-location': 'pithos://a13528163db/con/obj_13.0',
43
    'x-image-meta-disk-format': 'diskdump',
44
    'x-image-meta-is-public': 'True',
45
    'x-image-meta-status': 'available',
46
    'x-image-meta-deleted-at': '',
47
    'x-image-meta-updated-at': '2013-04-11 15:22:39',
48
    'x-image-meta-created-at': '2013-04-11 15:22:37',
49
    'x-image-meta-owner': 'a13529bb3c3db',
50
    'x-image-meta-size': '1073741824',
51
    'x-image-meta-name': 'img_1365686546.0',
52
    'extraheaders': 'should be ignored'
53
}
54
example_images = [
55
    {
56
        "status": "available",
57
        "name": "Archlinux",
58
        "disk_format": "diskdump",
59
        "container_format": "bare",
60
        "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
61
        "size": 752782848},
62
    {
63
        "status": "available",
64
        "name": "maelstrom",
65
        "disk_format": "diskdump",
66
        "container_format": "bare",
67
        "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
68
        "size": 2583195644},
69
    {
70
        "status": "available",
71
        "name": "Gardenia",
72
        "disk_format": "diskdump",
73
        "container_format": "bare",
74
        "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
75
        "size": 2589802496}]
76
imgid = "b4713f20-3a41-4eaf-81ae-88698c18b3e8"
77
example_images_detailed = [
78
    {
79
        "status": "available",
80
        "name": "Archlinux",
81
        "checksum": "1a126aad07475b43cc1959b446344211be13974",
82
        "created_at": "2013-01-28 22:44:54",
83
        "disk_format": "diskdump",
84
        "updated_at": "2013-01-28 22:44:55",
85
        "properties": {
86
            "partition_table": "msdos",
87
            "osfamily": "linux",
88
            "users": "root",
89
            "exclude_task_assignhostname": "yes",
90
            "os": "archlinux",
91
            "root_partition": "1",
92
            "description": "Archlinux base install 2012.12.01"},
93
        "location": "pithos://us3r-I6E-1d/images/archlinux.12.2012",
94
        "container_format": "bare",
95
        "owner": "user163@mail.example.com",
96
        "is_public": True,
97
        "deleted_at": "",
98
        "id": "b4713f20-3a41-4eaf-81ae-88698c18b3e8",
99
        "size": 752782848},
100
    {
101
        "status": "available",
102
        "name": "maelstrom",
103
        "checksum": "b202b8c7030cb22f896c6664ac",
104
        "created_at": "2013-02-13 10:07:42",
105
        "disk_format": "diskdump",
106
        "updated_at": "2013-02-13 10:07:44",
107
        "properties": {
108
            "partition_table": "msdos",
109
            "osfamily": "linux",
110
            "description": "Ubuntu 12.04.1 LTS",
111
            "os": "ubuntu",
112
            "root_partition": "1",
113
            "users": "user"},
114
        "location": "pithos://us3r-@r3n@-1d/images/mls-201302131203.diskdump",
115
        "container_format": "bare",
116
        "owner": "user3@mail.example.com",
117
        "is_public": True,
118
        "deleted_at": "",
119
        "id": "0fb03e45-7d5a-4515-bd4e-e6bbf6457f06",
120
        "size": 2583195648},
121
    {
122
        "status": "available",
123
        "name": "Gardenia",
124
        "checksum": "06d3099815d1f6fada91e80107638b882",
125
        "created_at": "2013-02-13 12:35:21",
126
        "disk_format": "diskdump",
127
        "updated_at": "2013-02-13 12:35:23",
128
        "properties": {
129
            "partition_table": "msdos",
130
            "osfamily": "linux",
131
            "description": "Ubuntu 12.04.2 LTS",
132
            "os": "ubuntu",
133
            "root_partition": "1",
134
            "users": "user"},
135
        "location": "pithos://us3r-E-1d/images/Gardenia-201302131431.diskdump",
136
        "container_format": "bare",
137
        "owner": "user3@mail.example.com",
138
        "is_public": True,
139
        "deleted_at": "",
140
        "id": "5963020b-ab74-4e11-bc59-90c494bbdedb",
141
        "size": 2589802496}]
142

    
143

    
144
class FR(object):
145
    json = example_images
146
    headers = {}
147
    content = json
148
    status = None
149
    status_code = 200
150

    
151
image_pkg = 'kamaki.clients.image.ImageClient'
152

    
153

    
154
class ImageClient(TestCase):
155

    
156
    def assert_dicts_are_equal(self, d1, d2):
157
        for k, v in d1.items():
158
            self.assertTrue(k in d2)
159
            if isinstance(v, dict):
160
                self.assert_dicts_are_equal(v, d2[k])
161
            else:
162
                self.assertEqual(unicode(v), unicode(d2[k]))
163

    
164
    def setUp(self):
165
        self.url = 'http://image.example.com'
166
        self.token = 'an1m@g370k3n=='
167
        from kamaki.clients import image
168
        self.client = image.ImageClient(self.url, self.token)
169

    
170
    def tearDown(self):
171
        FR.json = example_images
172
        FR.status_code = 200
173

    
174
    @patch('%s.get' % image_pkg, return_value=FR())
175
    def test_list_public(self, get):
176
        a_filter = dict(size_max=42)
177
        for args in product((False, True), ({}, a_filter), ('', '-')):
178
            (detail, filters, order) = args
179
            r = self.client.list_public(*args)
180
            filters['sort_dir'] = 'desc' if order.startswith('-') else 'asc'
181
            self.assertEqual(get.mock_calls[-1], call(
182
                '/images/%s' % ('detail' if detail else ''),
183
                async_params=filters, success=200))
184
            for i in range(len(r)):
185
                self.assert_dicts_are_equal(r[i], example_images[i])
186

    
187
    @patch('%s.head' % image_pkg, return_value=FR())
188
    def test_get_meta(self, head):
189
        img0 = example_images[0]
190
        FR.json = img0
191
        img0_headers = {}
192
        for k, v in example_images_detailed[0].items():
193
            img0_headers['x-image-meta-%s' % k] = v
194
        FR.headers = img0_headers
195
        r = self.client.get_meta(img0['id'])
196
        head.assert_called_once_with('/images/%s' % img0['id'], success=200)
197
        self.assertEqual(r['id'], img0['id'])
198
        self.assert_dicts_are_equal(r, example_images_detailed[0])
199

    
200
    @patch('%s.set_header' % image_pkg, return_value=FR())
201
    @patch('%s.post' % image_pkg, return_value=FR())
202
    def test_register(self, post, SH):
203
        img0 = example_images_detailed[0]
204
        FR.headers = example_image_headers
205
        img0_location = img0['location']
206
        img0_name = 'A new img0 name'
207
        prfx = 'x-image-meta-'
208
        proprfx = 'x-image-meta-property-'
209
        keys = [
210
            'id', 'store', 'dist_format', 'container_format',
211
            'size', 'checksum', 'is_public', 'owner']
212
        for args in product(
213
                ('v_id', None), ('v_store', None),
214
                ('v_dist_format', None), ('v_container_format', None),
215
                ('v_size', None), ('v_checksum', None),
216
                ('v_is_public', None), ('v_owner', None)):
217
            params = dict()
218
            async_headers = dict()
219
            props = dict()
220
            for i, k in enumerate(keys):
221
                if args[i]:
222
                    params[k] = args[i]
223
                    async_headers['%s%s' % (prfx, k)] = args[i]
224
                    props['%s%s' % (proprfx, args[i])] = k
225
            async_headers.update(props)
226
        r = self.client.register(
227
            img0_name, img0_location, params=params, properties=props)
228
        expectedict = dict(example_image_headers)
229
        expectedict.pop('extraheaders')
230
        from kamaki.clients.image import _format_image_headers
231
        self.assert_dicts_are_equal(_format_image_headers(expectedict), r)
232
        self.assertEqual(
233
            post.mock_calls[-1],
234
            call('/images/', async_headers=async_headers, success=200))
235
        self.assertEqual(SH.mock_calls[-2:], [
236
            call('X-Image-Meta-Name', img0_name),
237
            call('X-Image-Meta-Location', img0_location)])
238
        img1_location = ('some_uuid', 'some_container', 'some/path')
239
        r = self.client.register(
240
            img0_name, img1_location, params=params, properties=props)
241
        img1_location = 'pithos://%s' % '/'.join(img1_location)
242
        self.assertEqual(SH.mock_calls[-2:], [
243
            call('X-Image-Meta-Name', img0_name),
244
            call('X-Image-Meta-Location', img1_location)])
245

    
246
    @patch('%s.delete' % image_pkg)
247
    def test_unregister(self, delete):
248
        img_id = 'an1m4g3'
249
        self.client.unregister(img_id)
250
        delete.assert_called_once_with('/images/%s' % img_id, success=204)
251

    
252
    @patch('%s.put' % image_pkg, return_value=FR())
253
    def test_set_members(self, put):
254
        members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
255
        self.client.set_members(imgid, members)
256
        put.assert_called_once_with(
257
            '/images/%s/members' % imgid,
258
            json=dict(memberships=[dict(member_id=m) for m in members]),
259
            success=204)
260

    
261
    @patch('%s.get' % image_pkg, return_value=FR())
262
    def test_list_members(self, get):
263
        members = ['use3r-1d-0', 'us2r-1d-1', 'us3r-1d-2']
264
        FR.json = dict(members=members)
265
        r = self.client.list_members(imgid)
266
        get.assert_called_once_with('/images/%s/members' % imgid, success=200)
267
        self.assertEqual(r, members)
268

    
269
    @patch('%s.put' % image_pkg, return_value=FR())
270
    def test_add_member(self, put):
271
        new_member = 'us3r-15-n3w'
272
        self.client.add_member(imgid, new_member)
273
        put.assert_called_once_with(
274
            '/images/%s/members/%s' % (imgid, new_member),
275
            success=204)
276

    
277
    @patch('%s.delete' % image_pkg, return_value=FR())
278
    def test_remove_member(self, delete):
279
        old_member = 'us3r-15-0ld'
280
        self.client.remove_member(imgid, old_member)
281
        delete.assert_called_once_with(
282
            '/images/%s/members/%s' % (imgid, old_member),
283
            success=204)
284

    
285
    @patch('%s.get' % image_pkg, return_value=FR())
286
    def test_list_shared(self, get):
287
        FR.json = dict(shared_images=example_images)
288
        r = self.client.list_shared(imgid)
289
        get.assert_called_once_with('/shared-images/%s' % imgid, success=200)
290
        for i in range(len(r)):
291
            self.assert_dicts_are_equal(r[i], example_images[i])
292

    
293
if __name__ == '__main__':
294
    from sys import argv
295
    from kamaki.clients.test import runTestCase
296
    runTestCase(ImageClient, 'Plankton Client', argv[1:])