blob: 771404ab5db072936248e0be3894801a55299b74 [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,
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 = {
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 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))