blob: b2aad34ef86cbb63fc555f90b5a1ba2240fa4c0c [file] [log] [blame]
Rakesh H Sa3325d62015-04-04 19:42:29 +05301# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13
14import copy
Rakesh H Sa3325d62015-04-04 19:42:29 +053015import json
16
Rabi Mishra477efc92015-07-31 13:01:45 +053017from heat_integrationtests.functional import functional_base
18
Rakesh H Sa3325d62015-04-04 19:42:29 +053019test_template_one_resource = {
20 'heat_template_version': '2013-05-23',
21 'description': 'Test template to create one instance.',
22 'resources': {
23 'test1': {
24 'type': 'OS::Heat::TestResource',
25 'properties': {
26 'value': 'Test1',
27 'fail': False,
28 'update_replace': False,
Angus Salkeld73dcbc62015-08-31 09:02:58 +100029 'wait_secs': 0,
30 'action_wait_secs': {'create': 1},
31 'client_name': 'nova',
32 'entity_name': 'servers',
Rakesh H Sa3325d62015-04-04 19:42:29 +053033 }
34 }
35 }
36}
37
38test_template_two_resource = {
39 'heat_template_version': '2013-05-23',
40 'description': 'Test template to create two instance.',
41 'resources': {
42 'test1': {
43 'type': 'OS::Heat::TestResource',
44 'properties': {
45 'value': 'Test1',
46 'fail': False,
47 'update_replace': False,
Angus Salkeld73dcbc62015-08-31 09:02:58 +100048 'wait_secs': 0,
49 'action_wait_secs': {'update': 1}
Rakesh H Sa3325d62015-04-04 19:42:29 +053050 }
51 },
52 'test2': {
53 'type': 'OS::Heat::TestResource',
54 'properties': {
55 'value': 'Test1',
56 'fail': False,
57 'update_replace': False,
58 'wait_secs': 0
59 }
60 }
61 }
62}
63
64
65def _change_rsrc_properties(template, rsrcs, values):
66 modified_template = copy.deepcopy(template)
67 for rsrc_name in rsrcs:
68 rsrc_prop = modified_template['resources'][
69 rsrc_name]['properties']
70 for prop in rsrc_prop:
71 if prop in values:
72 rsrc_prop[prop] = values[prop]
73 return modified_template
74
75
Rabi Mishra477efc92015-07-31 13:01:45 +053076class CreateStackTest(functional_base.FunctionalTestsBase):
Rakesh H Sa3325d62015-04-04 19:42:29 +053077 def setUp(self):
78 super(CreateStackTest, self).setUp()
Rakesh H Sa3325d62015-04-04 19:42:29 +053079
80 def test_create_rollback(self):
81 values = {'fail': True, 'value': 'test_create_rollback'}
82 template = _change_rsrc_properties(test_template_one_resource,
83 ['test1'], values)
84
85 self.stack_create(
86 template=template,
87 expected_status='ROLLBACK_COMPLETE',
88 disable_rollback=False)
89
90
Rabi Mishra477efc92015-07-31 13:01:45 +053091class UpdateStackTest(functional_base.FunctionalTestsBase):
Rakesh H Sa3325d62015-04-04 19:42:29 +053092
93 provider_template = {
94 'heat_template_version': '2013-05-23',
95 'description': 'foo',
96 'resources': {
97 'test1': {
98 'type': 'My::TestResource'
99 }
100 }
101 }
102
103 provider_group_template = '''
104heat_template_version: 2013-05-23
105resources:
106 test_group:
107 type: OS::Heat::ResourceGroup
108 properties:
109 count: 2
110 resource_def:
111 type: My::TestResource
112'''
113
114 update_userdata_template = '''
115heat_template_version: 2014-10-16
116parameters:
117 flavor:
118 type: string
119 user_data:
120 type: string
121 image:
122 type: string
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530123 network:
124 type: string
Rakesh H Sa3325d62015-04-04 19:42:29 +0530125
126resources:
127 server:
128 type: OS::Nova::Server
129 properties:
130 image: {get_param: image}
131 flavor: {get_param: flavor}
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530132 networks: [{network: {get_param: network} }]
Rakesh H Sa3325d62015-04-04 19:42:29 +0530133 user_data_format: SOFTWARE_CONFIG
134 user_data: {get_param: user_data}
135'''
136
137 def setUp(self):
138 super(UpdateStackTest, self).setUp()
Rakesh H Sa3325d62015-04-04 19:42:29 +0530139
140 def test_stack_update_nochange(self):
141 template = _change_rsrc_properties(test_template_one_resource,
142 ['test1'],
143 {'value': 'test_no_change'})
144 stack_identifier = self.stack_create(
145 template=template)
146 expected_resources = {'test1': 'OS::Heat::TestResource'}
147 self.assertEqual(expected_resources,
148 self.list_resources(stack_identifier))
149
150 # Update with no changes, resources should be unchanged
151 self.update_stack(stack_identifier, template)
152 self.assertEqual(expected_resources,
153 self.list_resources(stack_identifier))
154
155 def test_stack_in_place_update(self):
156 template = _change_rsrc_properties(test_template_one_resource,
157 ['test1'],
158 {'value': 'test_in_place'})
159 stack_identifier = self.stack_create(
160 template=template)
161 expected_resources = {'test1': 'OS::Heat::TestResource'}
162 self.assertEqual(expected_resources,
163 self.list_resources(stack_identifier))
164 resource = self.client.resources.list(stack_identifier)
165 initial_phy_id = resource[0].physical_resource_id
166
167 tmpl_update = _change_rsrc_properties(
168 test_template_one_resource, ['test1'],
169 {'value': 'test_in_place_update'})
170 # Update the Value
171 self.update_stack(stack_identifier, tmpl_update)
172 resource = self.client.resources.list(stack_identifier)
173 # By default update_in_place
174 self.assertEqual(initial_phy_id,
175 resource[0].physical_resource_id)
176
177 def test_stack_update_replace(self):
178 template = _change_rsrc_properties(test_template_one_resource,
179 ['test1'],
180 {'value': 'test_replace'})
181 stack_identifier = self.stack_create(
182 template=template)
183 expected_resources = {'test1': 'OS::Heat::TestResource'}
184 self.assertEqual(expected_resources,
185 self.list_resources(stack_identifier))
186 resource = self.client.resources.list(stack_identifier)
187 initial_phy_id = resource[0].physical_resource_id
188
189 # Update the value and also set update_replace prop
190 tmpl_update = _change_rsrc_properties(
191 test_template_one_resource, ['test1'],
192 {'value': 'test_in_place_update', 'update_replace': True})
193 self.update_stack(stack_identifier, tmpl_update)
194 resource = self.client.resources.list(stack_identifier)
195 # update Replace
196 self.assertNotEqual(initial_phy_id,
197 resource[0].physical_resource_id)
198
199 def test_stack_update_add_remove(self):
200 template = _change_rsrc_properties(test_template_one_resource,
201 ['test1'],
202 {'value': 'test_add_remove'})
203 stack_identifier = self.stack_create(
204 template=template)
205 initial_resources = {'test1': 'OS::Heat::TestResource'}
206 self.assertEqual(initial_resources,
207 self.list_resources(stack_identifier))
208
209 tmpl_update = _change_rsrc_properties(
210 test_template_two_resource, ['test1', 'test2'],
211 {'value': 'test_add_remove_update'})
212 # Add one resource via a stack update
213 self.update_stack(stack_identifier, tmpl_update)
214 updated_resources = {'test1': 'OS::Heat::TestResource',
215 'test2': 'OS::Heat::TestResource'}
216 self.assertEqual(updated_resources,
217 self.list_resources(stack_identifier))
218
219 # Then remove it by updating with the original template
220 self.update_stack(stack_identifier, template)
221 self.assertEqual(initial_resources,
222 self.list_resources(stack_identifier))
223
224 def test_stack_update_rollback(self):
225 template = _change_rsrc_properties(test_template_one_resource,
226 ['test1'],
227 {'value': 'test_update_rollback'})
228 stack_identifier = self.stack_create(
229 template=template)
230 initial_resources = {'test1': 'OS::Heat::TestResource'}
231 self.assertEqual(initial_resources,
232 self.list_resources(stack_identifier))
233
234 tmpl_update = _change_rsrc_properties(
235 test_template_two_resource, ['test1', 'test2'],
236 {'value': 'test_update_rollback', 'fail': True})
237 # stack update, also set failure
238 self.update_stack(stack_identifier, tmpl_update,
239 expected_status='ROLLBACK_COMPLETE',
240 disable_rollback=False)
241 # since stack update failed only the original resource is present
242 updated_resources = {'test1': 'OS::Heat::TestResource'}
243 self.assertEqual(updated_resources,
244 self.list_resources(stack_identifier))
245
246 def test_stack_update_provider(self):
247 template = _change_rsrc_properties(
248 test_template_one_resource, ['test1'],
249 {'value': 'test_provider_template'})
250 files = {'provider.template': json.dumps(template)}
251 env = {'resource_registry':
252 {'My::TestResource': 'provider.template'}}
253 stack_identifier = self.stack_create(
254 template=self.provider_template,
255 files=files,
256 environment=env
257 )
258
259 initial_resources = {'test1': 'My::TestResource'}
260 self.assertEqual(initial_resources,
261 self.list_resources(stack_identifier))
262
263 # Prove the resource is backed by a nested stack, save the ID
264 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
265 'test1')
266 nested_id = nested_identifier.split('/')[-1]
267
268 # Then check the expected resources are in the nested stack
269 nested_resources = {'test1': 'OS::Heat::TestResource'}
270 self.assertEqual(nested_resources,
271 self.list_resources(nested_identifier))
272 tmpl_update = _change_rsrc_properties(
273 test_template_two_resource, ['test1', 'test2'],
274 {'value': 'test_provider_template'})
275 # Add one resource via a stack update by changing the nested stack
276 files['provider.template'] = json.dumps(tmpl_update)
277 self.update_stack(stack_identifier, self.provider_template,
278 environment=env, files=files)
279
280 # Parent resources should be unchanged and the nested stack
281 # should have been updated in-place without replacement
282 self.assertEqual(initial_resources,
283 self.list_resources(stack_identifier))
284 rsrc = self.client.resources.get(stack_identifier, 'test1')
285 self.assertEqual(rsrc.physical_resource_id, nested_id)
286
287 # Then check the expected resources are in the nested stack
288 nested_resources = {'test1': 'OS::Heat::TestResource',
289 'test2': 'OS::Heat::TestResource'}
290 self.assertEqual(nested_resources,
291 self.list_resources(nested_identifier))
292
293 def test_stack_update_provider_group(self):
294 '''Test two-level nested update.'''
295 # Create a ResourceGroup (which creates a nested stack),
296 # containing provider resources (which create a nested
297 # stack), thus excercising an update which traverses
298 # two levels of nesting.
299 template = _change_rsrc_properties(
300 test_template_one_resource, ['test1'],
301 {'value': 'test_provider_group_template'})
302 files = {'provider.template': json.dumps(template)}
303 env = {'resource_registry':
304 {'My::TestResource': 'provider.template'}}
305
306 stack_identifier = self.stack_create(
307 template=self.provider_group_template,
308 files=files,
309 environment=env
310 )
311
312 initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
313 self.assertEqual(initial_resources,
314 self.list_resources(stack_identifier))
315
316 # Prove the resource is backed by a nested stack, save the ID
317 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
318 'test_group')
319
320 # Then check the expected resources are in the nested stack
321 nested_resources = {'0': 'My::TestResource',
322 '1': 'My::TestResource'}
323 self.assertEqual(nested_resources,
324 self.list_resources(nested_identifier))
325
326 for n_rsrc in nested_resources:
327 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
328 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
329 provider_identifier = '%s/%s' % (provider_stack.stack_name,
330 provider_stack.id)
331 provider_resources = {u'test1': u'OS::Heat::TestResource'}
332 self.assertEqual(provider_resources,
333 self.list_resources(provider_identifier))
334
335 tmpl_update = _change_rsrc_properties(
336 test_template_two_resource, ['test1', 'test2'],
337 {'value': 'test_provider_group_template'})
338 # Add one resource via a stack update by changing the nested stack
339 files['provider.template'] = json.dumps(tmpl_update)
340 self.update_stack(stack_identifier, self.provider_group_template,
341 environment=env, files=files)
342
343 # Parent resources should be unchanged and the nested stack
344 # should have been updated in-place without replacement
345 self.assertEqual(initial_resources,
346 self.list_resources(stack_identifier))
347
348 # Resource group stack should also be unchanged (but updated)
349 nested_stack = self.client.stacks.get(nested_identifier)
350 self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
351 self.assertEqual(nested_resources,
352 self.list_resources(nested_identifier))
353
354 for n_rsrc in nested_resources:
355 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
356 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
357 provider_identifier = '%s/%s' % (provider_stack.stack_name,
358 provider_stack.id)
359 provider_resources = {'test1': 'OS::Heat::TestResource',
360 'test2': 'OS::Heat::TestResource'}
361 self.assertEqual(provider_resources,
362 self.list_resources(provider_identifier))
363
364 def test_stack_update_with_replacing_userdata(self):
365 """Confirm that we can update userdata of instance during updating
366 stack by the user of member role.
367
368 Make sure that a resource that inherites from StackUser can be deleted
369 during updating stack.
370 """
371 if not self.conf.minimal_image_ref:
372 raise self.skipException("No minimal image configured to test")
373 if not self.conf.minimal_instance_type:
374 raise self.skipException("No flavor configured to test")
375
376 parms = {'flavor': self.conf.minimal_instance_type,
377 'image': self.conf.minimal_image_ref,
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530378 'network': self.conf.fixed_network_name,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530379 'user_data': ''}
380 name = self._stack_rand_name()
381
382 stack_identifier = self.stack_create(
383 stack_name=name,
384 template=self.update_userdata_template,
385 parameters=parms
386 )
387
388 parms_updated = parms
389 parms_updated['user_data'] = 'two'
390 self.update_stack(
391 stack_identifier,
392 template=self.update_userdata_template,
393 parameters=parms_updated)