Skip to content

Commit 7f1567f

Browse files
author
Gabriel Monroy
committed
Merge pull request #217 from opdemand/215-flavors-update
Fixed #215 -- allow flavors:update from CLI.
2 parents 12d6f07 + 2228d6c commit 7f1567f

4 files changed

Lines changed: 111 additions & 11 deletions

File tree

api/tests/flavor.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ def test_flavor(self):
4545
url = "/api/flavors/{flavor_id}".format(**locals())
4646
response = self.client.get(url)
4747
self.assertEqual(response.status_code, 200)
48-
new_params = {'instance_size': 't1.micro'}
49-
body = {'params': json.dumps(new_params)}
50-
response = self.client.patch(url, json.dumps(body), content_type='application/json')
51-
self.assertEqual(response.status_code, 200)
52-
self.assertEqual(json.loads(response.data['params']), new_params)
5348
response = self.client.delete(url)
5449
self.assertEqual(response.status_code, 204)
5550

@@ -82,6 +77,7 @@ def test_flavor_update(self):
8277
params = {
8378
'size': 'c1.xlarge',
8479
'image': 'ami-c98d1bf9',
80+
'zone': None
8581
}
8682
body = {'id': flavor_id, 'params': json.dumps(params)}
8783
response = self.client.patch(url, json.dumps(body), content_type='application/json')
@@ -95,7 +91,7 @@ def test_flavor_update(self):
9591
self.assertEqual(response.status_code, 200)
9692
self.assertEqual(response.data['uuid'], uuid)
9793
params = json.loads(response.data['params'])
98-
self.assertNotIn('region', params)
94+
self.assertIn('region', params)
9995
self.assertNotIn('zone', params)
10096
self.assertEqual(params['size'], 'c1.xlarge')
10197
self.assertEqual(params['image'], 'ami-c98d1bf9')

api/views.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ class FlavorViewSet(OwnerViewSet):
133133
serializer_class = serializers.FlavorSerializer
134134
lookup_field = 'id'
135135

136+
def update(self, request, *args, **kwargs):
137+
"""
138+
Override default update behavior to ensure that the params
139+
field is handled as a dict merge.
140+
"""
141+
params = self.get_object().params
142+
new_params = json.loads(request.DATA.get('params', '{}'))
143+
params.update(new_params)
144+
# remove param if we provided a null value
145+
[params.pop(k) for k, v in params.items() if v is None]
146+
request.DATA['params'] = json.dumps(params)
147+
return super(FlavorViewSet, self).update(request, *args, **kwargs)
148+
136149

137150
class FormationViewSet(OwnerViewSet):
138151
"""RESTful views for :class:`~api.models.Formation`."""

client/deis.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,27 @@ def flavors_list(self, args):
940940
else:
941941
raise ResponseError(response)
942942

943+
def flavors_update(self, args):
944+
"""
945+
Update an existing node flavor
946+
947+
Usage: deis flavors:update <id> [<params>] [--provider=<provider>]
948+
"""
949+
id_ = args.get('<id>')
950+
body = {'id': id_}
951+
params = args.get('<params>')
952+
if params:
953+
body['params'] = params
954+
provider = args.get('--provider')
955+
if provider:
956+
body['provider'] = provider
957+
response = self._dispatch(
958+
'patch', '/api/flavors/{}'.format(id_), json.dumps(body))
959+
if response.status_code == requests.codes.ok: # @UndefinedVariable
960+
print(json.dumps(response.json(), indent=2))
961+
else:
962+
raise ResponseError(response)
963+
943964
def formations(self, args):
944965
"""
945966
Valid commands for formations:

client/tests/test_flavors.py

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,93 @@ def tearDownClass(cls):
3030
teardown(cls.username, cls.password)
3131

3232
def test_create(self):
33+
# create a new flavor
3334
id_ = "test-flavor-{}".format(uuid4().hex[:4])
3435
child = pexpect.spawn("{} flavors:create {} --provider={} --params='{}'".format(
3536
DEIS, id_, 'ec2',
3637
'{"region":"ap-southeast-2","image":"ami-d5f66bef","zone":"any","size":"m1.medium"}'
3738
))
3839
child.expect(id_)
3940
child.expect(pexpect.EOF)
41+
# list the flavors and make sure it's in there
42+
child = pexpect.spawn("{} flavors".format(DEIS))
43+
child.expect(pexpect.EOF)
44+
flavors = re.findall('([\w|-]+): .*', child.before)
45+
self.assertIn(id_, flavors)
46+
# delete the new flavor
4047
child = pexpect.spawn("{} flavors:delete {}".format(DEIS, id_))
4148
child.expect(pexpect.EOF)
4249
self.assertNotIn('Error', child.before)
4350

44-
# def test_update(self):
45-
# pass
51+
def test_update(self):
52+
# create a new flavor
53+
id_ = "test-flavor-{}".format(uuid4().hex[:4])
54+
child = pexpect.spawn("{} flavors:create {} --provider={} --params={}".format(
55+
DEIS, id_, 'mock', '{}'))
56+
child.expect(id_)
57+
child.expect(pexpect.EOF)
58+
# update the provider
59+
child = pexpect.spawn("{} flavors:update {} --provider={}".format(DEIS, id_, 'ec2'))
60+
child.expect(pexpect.EOF)
61+
# update the params
62+
child = pexpect.spawn("{} flavors:update {} {}".format(
63+
DEIS, id_, "'{\"key1\": \"val1\"}'"))
64+
child.expect(pexpect.EOF)
65+
# test the flavor contents
66+
child = pexpect.spawn("{} flavors:info {}".format(DEIS, id_))
67+
child.expect(pexpect.EOF)
68+
results = json.loads(child.before)
69+
self.assertEqual('ec2', results['provider'])
70+
self.assertIn('key1', results['params'])
71+
self.assertIn('val1', results['params'])
72+
# update the params and provider
73+
child = pexpect.spawn("{} flavors:update {} {} --provider={}".format(
74+
DEIS, id_, "'{\"key2\": \"val2\"}'", 'mock'))
75+
child.expect(pexpect.EOF)
76+
# test the flavor contents
77+
child = pexpect.spawn("{} flavors:info {}".format(DEIS, id_))
78+
child.expect(pexpect.EOF)
79+
results = json.loads(child.before)
80+
self.assertIn('key1', results['params'])
81+
self.assertIn('val1', results['params'])
82+
self.assertIn('key2', results['params'])
83+
self.assertIn('val2', results['params'])
84+
self.assertEqual('mock', results['provider'])
85+
# update the params to remove a value
86+
child = pexpect.spawn("{} flavors:update {} {}".format(
87+
DEIS, id_, "'{\"key1\": null}'"))
88+
child.expect(pexpect.EOF)
89+
# test the flavor contents
90+
child = pexpect.spawn("{} flavors:info {}".format(DEIS, id_))
91+
child.expect(pexpect.EOF)
92+
results = json.loads(child.before)
93+
self.assertNotIn('key1', results['params'])
94+
self.assertNotIn('val1', results['params'])
95+
self.assertIn('key2', results['params'])
96+
self.assertIn('val2', results['params'])
97+
# delete the new flavor
98+
child = pexpect.spawn("{} flavors:delete {}".format(DEIS, id_))
99+
child.expect(pexpect.EOF)
100+
self.assertNotIn('Error', child.before)
46101

47-
# def test_delete(self):
48-
# pass
102+
def test_delete(self):
103+
# create a new flavor
104+
id_ = "test-flavor-{}".format(uuid4().hex[:4])
105+
child = pexpect.spawn("{} flavors:create {} --provider={} --params='{}'".format(
106+
DEIS, id_, 'ec2',
107+
'{"region":"ap-southeast-2","image":"ami-d5f66bef","zone":"any","size":"m1.medium"}'
108+
))
109+
child.expect(id_)
110+
child.expect(pexpect.EOF)
111+
# delete the new flavor
112+
child = pexpect.spawn("{} flavors:delete {}".format(DEIS, id_))
113+
child.expect(pexpect.EOF)
114+
self.assertNotIn('Error', child.before)
115+
# list the flavors and make sure it's not in there
116+
child = pexpect.spawn("{} flavors".format(DEIS))
117+
child.expect(pexpect.EOF)
118+
flavors = re.findall('([\w|-]+): .*', child.before)
119+
self.assertNotIn(id_, flavors)
49120

50121
def test_list(self):
51122
child = pexpect.spawn("{} flavors".format(DEIS))
@@ -68,7 +139,6 @@ def test_info(self):
68139
# test that we received JSON results
69140
# TODO: There's some error here, but only when run as part of the
70141
# entire test suite?
71-
print child.before
72142
results = json.loads(child.before)
73143
self.assertIn('created', results)
74144
self.assertIn('updated', results)

0 commit comments

Comments
 (0)