blob: 4407138501d182a2fd862b224fac50a892144361 [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 = {
Thomas Herve92902b82017-06-22 22:40:50 +020020 'heat_template_version': 'pike',
Rakesh H Sa3325d62015-04-04 19:42:29 +053021 '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,
Rabi Mishrabe7a59e2016-06-15 08:41:29 +053029 'wait_secs': 1,
Angus Salkeld73dcbc62015-08-31 09:02:58 +100030 '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 = {
Thomas Herve92902b82017-06-22 22:40:50 +020039 'heat_template_version': 'pike',
Rakesh H Sa3325d62015-04-04 19:42:29 +053040 '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 test_create_rollback(self):
78 values = {'fail': True, 'value': 'test_create_rollback'}
79 template = _change_rsrc_properties(test_template_one_resource,
80 ['test1'], values)
81
82 self.stack_create(
83 template=template,
84 expected_status='ROLLBACK_COMPLETE',
85 disable_rollback=False)
86
87
Rabi Mishra477efc92015-07-31 13:01:45 +053088class UpdateStackTest(functional_base.FunctionalTestsBase):
Rakesh H Sa3325d62015-04-04 19:42:29 +053089
90 provider_template = {
91 'heat_template_version': '2013-05-23',
92 'description': 'foo',
93 'resources': {
94 'test1': {
95 'type': 'My::TestResource'
96 }
97 }
98 }
99
100 provider_group_template = '''
101heat_template_version: 2013-05-23
Steven Hardy23284b62015-10-01 19:03:42 +0100102parameters:
103 count:
104 type: number
105 default: 2
Rakesh H Sa3325d62015-04-04 19:42:29 +0530106resources:
107 test_group:
108 type: OS::Heat::ResourceGroup
109 properties:
Steven Hardy23284b62015-10-01 19:03:42 +0100110 count: {get_param: count}
Rakesh H Sa3325d62015-04-04 19:42:29 +0530111 resource_def:
112 type: My::TestResource
113'''
114
115 update_userdata_template = '''
116heat_template_version: 2014-10-16
117parameters:
118 flavor:
119 type: string
120 user_data:
121 type: string
122 image:
123 type: string
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530124 network:
125 type: string
Rakesh H Sa3325d62015-04-04 19:42:29 +0530126
127resources:
128 server:
129 type: OS::Nova::Server
130 properties:
131 image: {get_param: image}
132 flavor: {get_param: flavor}
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530133 networks: [{network: {get_param: network} }]
Rakesh H Sa3325d62015-04-04 19:42:29 +0530134 user_data_format: SOFTWARE_CONFIG
135 user_data: {get_param: user_data}
136'''
137
Steven Hardy23284b62015-10-01 19:03:42 +0100138 fail_param_template = '''
139heat_template_version: 2014-10-16
140parameters:
141 do_fail:
142 type: boolean
143 default: False
144resources:
145 aresource:
146 type: OS::Heat::TestResource
147 properties:
148 value: Test
149 fail: {get_param: do_fail}
Jaime Guerrero93a2da82016-05-23 21:31:43 +0000150 wait_secs: 1
Steven Hardy23284b62015-10-01 19:03:42 +0100151'''
152
Rakesh H Sa3325d62015-04-04 19:42:29 +0530153 def test_stack_update_nochange(self):
154 template = _change_rsrc_properties(test_template_one_resource,
155 ['test1'],
156 {'value': 'test_no_change'})
157 stack_identifier = self.stack_create(
158 template=template)
159 expected_resources = {'test1': 'OS::Heat::TestResource'}
160 self.assertEqual(expected_resources,
161 self.list_resources(stack_identifier))
162
163 # Update with no changes, resources should be unchanged
164 self.update_stack(stack_identifier, template)
165 self.assertEqual(expected_resources,
166 self.list_resources(stack_identifier))
167
168 def test_stack_in_place_update(self):
169 template = _change_rsrc_properties(test_template_one_resource,
170 ['test1'],
171 {'value': 'test_in_place'})
172 stack_identifier = self.stack_create(
173 template=template)
174 expected_resources = {'test1': 'OS::Heat::TestResource'}
175 self.assertEqual(expected_resources,
176 self.list_resources(stack_identifier))
177 resource = self.client.resources.list(stack_identifier)
178 initial_phy_id = resource[0].physical_resource_id
179
180 tmpl_update = _change_rsrc_properties(
181 test_template_one_resource, ['test1'],
182 {'value': 'test_in_place_update'})
183 # Update the Value
184 self.update_stack(stack_identifier, tmpl_update)
185 resource = self.client.resources.list(stack_identifier)
186 # By default update_in_place
187 self.assertEqual(initial_phy_id,
188 resource[0].physical_resource_id)
189
190 def test_stack_update_replace(self):
191 template = _change_rsrc_properties(test_template_one_resource,
192 ['test1'],
193 {'value': 'test_replace'})
194 stack_identifier = self.stack_create(
195 template=template)
196 expected_resources = {'test1': 'OS::Heat::TestResource'}
197 self.assertEqual(expected_resources,
198 self.list_resources(stack_identifier))
199 resource = self.client.resources.list(stack_identifier)
200 initial_phy_id = resource[0].physical_resource_id
201
202 # Update the value and also set update_replace prop
203 tmpl_update = _change_rsrc_properties(
204 test_template_one_resource, ['test1'],
205 {'value': 'test_in_place_update', 'update_replace': True})
206 self.update_stack(stack_identifier, tmpl_update)
207 resource = self.client.resources.list(stack_identifier)
208 # update Replace
209 self.assertNotEqual(initial_phy_id,
210 resource[0].physical_resource_id)
211
212 def test_stack_update_add_remove(self):
213 template = _change_rsrc_properties(test_template_one_resource,
214 ['test1'],
215 {'value': 'test_add_remove'})
216 stack_identifier = self.stack_create(
217 template=template)
218 initial_resources = {'test1': 'OS::Heat::TestResource'}
219 self.assertEqual(initial_resources,
220 self.list_resources(stack_identifier))
221
222 tmpl_update = _change_rsrc_properties(
223 test_template_two_resource, ['test1', 'test2'],
224 {'value': 'test_add_remove_update'})
225 # Add one resource via a stack update
226 self.update_stack(stack_identifier, tmpl_update)
227 updated_resources = {'test1': 'OS::Heat::TestResource',
228 'test2': 'OS::Heat::TestResource'}
229 self.assertEqual(updated_resources,
230 self.list_resources(stack_identifier))
231
232 # Then remove it by updating with the original template
233 self.update_stack(stack_identifier, template)
234 self.assertEqual(initial_resources,
235 self.list_resources(stack_identifier))
236
237 def test_stack_update_rollback(self):
238 template = _change_rsrc_properties(test_template_one_resource,
239 ['test1'],
240 {'value': 'test_update_rollback'})
241 stack_identifier = self.stack_create(
242 template=template)
243 initial_resources = {'test1': 'OS::Heat::TestResource'}
244 self.assertEqual(initial_resources,
245 self.list_resources(stack_identifier))
246
247 tmpl_update = _change_rsrc_properties(
248 test_template_two_resource, ['test1', 'test2'],
249 {'value': 'test_update_rollback', 'fail': True})
250 # stack update, also set failure
251 self.update_stack(stack_identifier, tmpl_update,
252 expected_status='ROLLBACK_COMPLETE',
253 disable_rollback=False)
254 # since stack update failed only the original resource is present
255 updated_resources = {'test1': 'OS::Heat::TestResource'}
256 self.assertEqual(updated_resources,
257 self.list_resources(stack_identifier))
258
Sergey Kraynev89082a32015-09-04 04:42:33 -0400259 def test_stack_update_from_failed(self):
260 # Prove it's possible to update from an UPDATE_FAILED state
261 template = _change_rsrc_properties(test_template_one_resource,
262 ['test1'],
263 {'value': 'test_update_failed'})
264 stack_identifier = self.stack_create(
265 template=template)
266 initial_resources = {'test1': 'OS::Heat::TestResource'}
267 self.assertEqual(initial_resources,
268 self.list_resources(stack_identifier))
269
270 tmpl_update = _change_rsrc_properties(
271 test_template_one_resource, ['test1'], {'fail': True})
272 # Update with bad template, we should fail
273 self.update_stack(stack_identifier, tmpl_update,
274 expected_status='UPDATE_FAILED')
275 # but then passing a good template should succeed
276 self.update_stack(stack_identifier, test_template_two_resource)
277 updated_resources = {'test1': 'OS::Heat::TestResource',
278 'test2': 'OS::Heat::TestResource'}
279 self.assertEqual(updated_resources,
280 self.list_resources(stack_identifier))
281
Rakesh H Sa3325d62015-04-04 19:42:29 +0530282 def test_stack_update_provider(self):
283 template = _change_rsrc_properties(
284 test_template_one_resource, ['test1'],
285 {'value': 'test_provider_template'})
286 files = {'provider.template': json.dumps(template)}
287 env = {'resource_registry':
288 {'My::TestResource': 'provider.template'}}
289 stack_identifier = self.stack_create(
290 template=self.provider_template,
291 files=files,
292 environment=env
293 )
294
295 initial_resources = {'test1': 'My::TestResource'}
296 self.assertEqual(initial_resources,
297 self.list_resources(stack_identifier))
298
299 # Prove the resource is backed by a nested stack, save the ID
300 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
301 'test1')
302 nested_id = nested_identifier.split('/')[-1]
303
304 # Then check the expected resources are in the nested stack
305 nested_resources = {'test1': 'OS::Heat::TestResource'}
306 self.assertEqual(nested_resources,
307 self.list_resources(nested_identifier))
308 tmpl_update = _change_rsrc_properties(
309 test_template_two_resource, ['test1', 'test2'],
310 {'value': 'test_provider_template'})
311 # Add one resource via a stack update by changing the nested stack
312 files['provider.template'] = json.dumps(tmpl_update)
313 self.update_stack(stack_identifier, self.provider_template,
314 environment=env, files=files)
315
316 # Parent resources should be unchanged and the nested stack
317 # should have been updated in-place without replacement
318 self.assertEqual(initial_resources,
319 self.list_resources(stack_identifier))
320 rsrc = self.client.resources.get(stack_identifier, 'test1')
321 self.assertEqual(rsrc.physical_resource_id, nested_id)
322
323 # Then check the expected resources are in the nested stack
324 nested_resources = {'test1': 'OS::Heat::TestResource',
325 'test2': 'OS::Heat::TestResource'}
326 self.assertEqual(nested_resources,
327 self.list_resources(nested_identifier))
328
Steven Hardy8a3c1ec2015-10-21 18:56:01 +0100329 def test_stack_update_alias_type(self):
330 env = {'resource_registry':
331 {'My::TestResource': 'OS::Heat::RandomString',
332 'My::TestResource2': 'OS::Heat::RandomString'}}
333 stack_identifier = self.stack_create(
334 template=self.provider_template,
335 environment=env
336 )
337 p_res = self.client.resources.get(stack_identifier, 'test1')
338 self.assertEqual('My::TestResource', p_res.resource_type)
339
340 initial_resources = {'test1': 'My::TestResource'}
341 self.assertEqual(initial_resources,
342 self.list_resources(stack_identifier))
343 res = self.client.resources.get(stack_identifier, 'test1')
344 # Modify the type of the resource alias to My::TestResource2
345 tmpl_update = copy.deepcopy(self.provider_template)
346 tmpl_update['resources']['test1']['type'] = 'My::TestResource2'
347 self.update_stack(stack_identifier, tmpl_update, environment=env)
348 res_a = self.client.resources.get(stack_identifier, 'test1')
349 self.assertEqual(res.physical_resource_id, res_a.physical_resource_id)
350 self.assertEqual(res.attributes['value'], res_a.attributes['value'])
351
352 def test_stack_update_alias_changes(self):
353 env = {'resource_registry':
354 {'My::TestResource': 'OS::Heat::RandomString'}}
355 stack_identifier = self.stack_create(
356 template=self.provider_template,
357 environment=env
358 )
359 p_res = self.client.resources.get(stack_identifier, 'test1')
360 self.assertEqual('My::TestResource', p_res.resource_type)
361
362 initial_resources = {'test1': 'My::TestResource'}
363 self.assertEqual(initial_resources,
364 self.list_resources(stack_identifier))
365 res = self.client.resources.get(stack_identifier, 'test1')
366 # Modify the resource alias to point to a different type
367 env = {'resource_registry':
368 {'My::TestResource': 'OS::Heat::TestResource'}}
369 self.update_stack(stack_identifier, template=self.provider_template,
370 environment=env)
371 res_a = self.client.resources.get(stack_identifier, 'test1')
372 self.assertNotEqual(res.physical_resource_id,
373 res_a.physical_resource_id)
374
375 def test_stack_update_provider_type(self):
376 template = _change_rsrc_properties(
377 test_template_one_resource, ['test1'],
378 {'value': 'test_provider_template'})
379 files = {'provider.template': json.dumps(template)}
380 env = {'resource_registry':
381 {'My::TestResource': 'provider.template',
382 'My::TestResource2': 'provider.template'}}
383 stack_identifier = self.stack_create(
384 template=self.provider_template,
385 files=files,
386 environment=env
387 )
388 p_res = self.client.resources.get(stack_identifier, 'test1')
389 self.assertEqual('My::TestResource', p_res.resource_type)
390
391 initial_resources = {'test1': 'My::TestResource'}
392 self.assertEqual(initial_resources,
393 self.list_resources(stack_identifier))
394
395 # Prove the resource is backed by a nested stack, save the ID
396 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
397 'test1')
398 nested_id = nested_identifier.split('/')[-1]
399
400 # Then check the expected resources are in the nested stack
401 nested_resources = {'test1': 'OS::Heat::TestResource'}
402 self.assertEqual(nested_resources,
403 self.list_resources(nested_identifier))
404 n_res = self.client.resources.get(nested_identifier, 'test1')
405
406 # Modify the type of the provider resource to My::TestResource2
407 tmpl_update = copy.deepcopy(self.provider_template)
408 tmpl_update['resources']['test1']['type'] = 'My::TestResource2'
409 self.update_stack(stack_identifier, tmpl_update,
410 environment=env, files=files)
411 p_res = self.client.resources.get(stack_identifier, 'test1')
412 self.assertEqual('My::TestResource2', p_res.resource_type)
413
414 # Parent resources should be unchanged and the nested stack
415 # should have been updated in-place without replacement
416 self.assertEqual({u'test1': u'My::TestResource2'},
417 self.list_resources(stack_identifier))
418 rsrc = self.client.resources.get(stack_identifier, 'test1')
419 self.assertEqual(rsrc.physical_resource_id, nested_id)
420
421 # Then check the expected resources are in the nested stack
422 self.assertEqual(nested_resources,
423 self.list_resources(nested_identifier))
424 n_res2 = self.client.resources.get(nested_identifier, 'test1')
425 self.assertEqual(n_res.physical_resource_id,
426 n_res2.physical_resource_id)
427
Rakesh H Sa3325d62015-04-04 19:42:29 +0530428 def test_stack_update_provider_group(self):
Peter Razumovskyf0ac9582015-09-24 16:49:03 +0300429 """Test two-level nested update."""
430
Rakesh H Sa3325d62015-04-04 19:42:29 +0530431 # Create a ResourceGroup (which creates a nested stack),
432 # containing provider resources (which create a nested
Steven Hardy23284b62015-10-01 19:03:42 +0100433 # stack), thus exercising an update which traverses
Rakesh H Sa3325d62015-04-04 19:42:29 +0530434 # two levels of nesting.
435 template = _change_rsrc_properties(
436 test_template_one_resource, ['test1'],
437 {'value': 'test_provider_group_template'})
438 files = {'provider.template': json.dumps(template)}
439 env = {'resource_registry':
440 {'My::TestResource': 'provider.template'}}
441
442 stack_identifier = self.stack_create(
443 template=self.provider_group_template,
444 files=files,
445 environment=env
446 )
447
448 initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
449 self.assertEqual(initial_resources,
450 self.list_resources(stack_identifier))
451
452 # Prove the resource is backed by a nested stack, save the ID
453 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
454 'test_group')
455
456 # Then check the expected resources are in the nested stack
457 nested_resources = {'0': 'My::TestResource',
458 '1': 'My::TestResource'}
459 self.assertEqual(nested_resources,
460 self.list_resources(nested_identifier))
461
462 for n_rsrc in nested_resources:
463 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
464 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
465 provider_identifier = '%s/%s' % (provider_stack.stack_name,
466 provider_stack.id)
467 provider_resources = {u'test1': u'OS::Heat::TestResource'}
468 self.assertEqual(provider_resources,
469 self.list_resources(provider_identifier))
470
471 tmpl_update = _change_rsrc_properties(
472 test_template_two_resource, ['test1', 'test2'],
473 {'value': 'test_provider_group_template'})
474 # Add one resource via a stack update by changing the nested stack
475 files['provider.template'] = json.dumps(tmpl_update)
476 self.update_stack(stack_identifier, self.provider_group_template,
477 environment=env, files=files)
478
479 # Parent resources should be unchanged and the nested stack
480 # should have been updated in-place without replacement
481 self.assertEqual(initial_resources,
482 self.list_resources(stack_identifier))
483
484 # Resource group stack should also be unchanged (but updated)
485 nested_stack = self.client.stacks.get(nested_identifier)
486 self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
487 self.assertEqual(nested_resources,
488 self.list_resources(nested_identifier))
489
490 for n_rsrc in nested_resources:
491 rsrc = self.client.resources.get(nested_identifier, n_rsrc)
492 provider_stack = self.client.stacks.get(rsrc.physical_resource_id)
493 provider_identifier = '%s/%s' % (provider_stack.stack_name,
494 provider_stack.id)
495 provider_resources = {'test1': 'OS::Heat::TestResource',
496 'test2': 'OS::Heat::TestResource'}
497 self.assertEqual(provider_resources,
498 self.list_resources(provider_identifier))
499
500 def test_stack_update_with_replacing_userdata(self):
Peter Razumovskyf0ac9582015-09-24 16:49:03 +0300501 """Test case for updating userdata of instance.
Rakesh H Sa3325d62015-04-04 19:42:29 +0530502
Peter Razumovskyf0ac9582015-09-24 16:49:03 +0300503 Confirm that we can update userdata of instance during updating stack
504 by the user of member role.
505
506 Make sure that a resource that inherits from StackUser can be deleted
Rakesh H Sa3325d62015-04-04 19:42:29 +0530507 during updating stack.
508 """
509 if not self.conf.minimal_image_ref:
510 raise self.skipException("No minimal image configured to test")
511 if not self.conf.minimal_instance_type:
512 raise self.skipException("No flavor configured to test")
513
514 parms = {'flavor': self.conf.minimal_instance_type,
515 'image': self.conf.minimal_image_ref,
Rabi Mishraec4b03b2015-05-23 02:20:47 +0530516 'network': self.conf.fixed_network_name,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530517 'user_data': ''}
Rakesh H Sa3325d62015-04-04 19:42:29 +0530518
519 stack_identifier = self.stack_create(
Rakesh H Sa3325d62015-04-04 19:42:29 +0530520 template=self.update_userdata_template,
521 parameters=parms
522 )
523
524 parms_updated = parms
525 parms_updated['user_data'] = 'two'
526 self.update_stack(
527 stack_identifier,
528 template=self.update_userdata_template,
529 parameters=parms_updated)
Steven Hardy23284b62015-10-01 19:03:42 +0100530
531 def test_stack_update_provider_group_patch(self):
532 '''Test two-level nested update with PATCH'''
533 template = _change_rsrc_properties(
534 test_template_one_resource, ['test1'],
535 {'value': 'test_provider_group_template'})
536 files = {'provider.template': json.dumps(template)}
537 env = {'resource_registry':
538 {'My::TestResource': 'provider.template'}}
539
540 stack_identifier = self.stack_create(
541 template=self.provider_group_template,
542 files=files,
543 environment=env
544 )
545
546 initial_resources = {'test_group': 'OS::Heat::ResourceGroup'}
547 self.assertEqual(initial_resources,
548 self.list_resources(stack_identifier))
549
550 # Prove the resource is backed by a nested stack, save the ID
551 nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
552 'test_group')
553
554 # Then check the expected resources are in the nested stack
555 nested_resources = {'0': 'My::TestResource',
556 '1': 'My::TestResource'}
557 self.assertEqual(nested_resources,
558 self.list_resources(nested_identifier))
559
560 # increase the count, pass only the paramter, no env or template
561 params = {'count': 3}
562 self.update_stack(stack_identifier, parameters=params, existing=True)
563
564 # Parent resources should be unchanged and the nested stack
565 # should have been updated in-place without replacement
566 self.assertEqual(initial_resources,
567 self.list_resources(stack_identifier))
568
569 # Resource group stack should also be unchanged (but updated)
570 nested_stack = self.client.stacks.get(nested_identifier)
571 self.assertEqual('UPDATE_COMPLETE', nested_stack.stack_status)
572 # Add a resource, as we should have added one
573 nested_resources['2'] = 'My::TestResource'
574 self.assertEqual(nested_resources,
575 self.list_resources(nested_identifier))
576
577 def test_stack_update_from_failed_patch(self):
578 '''Test PATCH update from a failed state.'''
579
580 # Start with empty template
581 stack_identifier = self.stack_create(
582 template='heat_template_version: 2014-10-16')
583
584 # Update with a good template, but bad parameter
585 self.update_stack(stack_identifier,
586 template=self.fail_param_template,
587 parameters={'do_fail': True},
588 expected_status='UPDATE_FAILED')
589
590 # PATCH update, only providing the parameter
591 self.update_stack(stack_identifier,
592 parameters={'do_fail': False},
593 existing=True)
594 self.assertEqual({u'aresource': u'OS::Heat::TestResource'},
595 self.list_resources(stack_identifier))
Thomas Herveb0cccac2015-11-13 09:35:55 +0100596
597 def test_stack_update_with_new_env(self):
598 """Update handles new resource types in the environment.
599
600 If a resource type appears during an update and the update fails,
601 retrying the update is able to find the type properly in the
602 environment.
603 """
604 stack_identifier = self.stack_create(
605 template=test_template_one_resource)
606
607 # Update with a new resource and make the update fails
608 template = _change_rsrc_properties(test_template_one_resource,
609 ['test1'], {'fail': True})
610 template['resources']['test2'] = {'type': 'My::TestResource'}
611 template['resources']['test1']['depends_on'] = 'test2'
612 env = {'resource_registry':
613 {'My::TestResource': 'OS::Heat::TestResource'}}
614 self.update_stack(stack_identifier,
615 template=template,
616 environment=env,
617 expected_status='UPDATE_FAILED')
618
619 # Fixing the template should fix the stack
620 template = _change_rsrc_properties(template,
621 ['test1'], {'fail': False})
622 self.update_stack(stack_identifier,
623 template=template,
624 environment=env)
625 self.assertEqual({'test1': 'OS::Heat::TestResource',
626 'test2': 'My::TestResource'},
627 self.list_resources(stack_identifier))
Thomas Herveab243752017-04-20 15:11:08 +0200628
629 def test_stack_update_with_new_version(self):
630 """Update handles new template version in failure.
631
632 If a stack update fails while changing the template version, update is
633 able to handle the new version fine.
634 """
635 stack_identifier = self.stack_create(
636 template=test_template_one_resource)
637
638 # Update with a new function and make the update fails
639 template = _change_rsrc_properties(test_template_two_resource,
640 ['test1'], {'fail': True})
641
642 template['heat_template_version'] = '2015-10-15'
643 template['resources']['test2']['properties']['value'] = {
644 'list_join': [',', ['a'], ['b']]}
645 self.update_stack(stack_identifier,
646 template=template,
647 expected_status='UPDATE_FAILED')
648
649 template = _change_rsrc_properties(template,
650 ['test2'], {'value': 'Test2'})
651 self.update_stack(stack_identifier,
652 template=template,
653 expected_status='UPDATE_FAILED')
654 self._stack_delete(stack_identifier)
655
656 def test_stack_update_with_old_version(self):
657 """Update handles old template version in failure.
658
659 If a stack update fails while changing the template version, update is
660 able to handle the old version fine.
661 """
662 template = _change_rsrc_properties(
663 test_template_one_resource,
664 ['test1'], {'value': {'list_join': [',', ['a'], ['b']]}})
665 template['heat_template_version'] = '2015-10-15'
666 stack_identifier = self.stack_create(
667 template=template)
668
669 # Update with a new function and make the update fails
670 template = _change_rsrc_properties(test_template_one_resource,
671 ['test1'], {'fail': True})
672 self.update_stack(stack_identifier,
673 template=template,
674 expected_status='UPDATE_FAILED')
675 self._stack_delete(stack_identifier)
Thomas Herve92902b82017-06-22 22:40:50 +0200676
677 def test_stack_update_with_conditions(self):
678 """Update manages new conditions added.
679
680 When a new resource is added during updates, the stacks handles the new
681 conditions correctly, and doesn't fail to load them while the update is
682 still in progress.
683 """
684 stack_identifier = self.stack_create(
685 template=test_template_one_resource)
686
687 updated_template = copy.deepcopy(test_template_two_resource)
688 updated_template['conditions'] = {'cond1': True}
689 updated_template['resources']['test3'] = {
690 'type': 'OS::Heat::TestResource',
691 'properties': {
692 'value': {'if': ['cond1', 'val3', 'val4']}
693 }
694 }
695 test2_props = updated_template['resources']['test2']['properties']
696 test2_props['action_wait_secs'] = {'create': 30}
697
698 self.update_stack(stack_identifier,
699 template=updated_template,
700 expected_status='UPDATE_IN_PROGRESS')
701
702 self.assertIn('test3', self.list_resources(stack_identifier))