blob: 0d5734b3ee8bcb092ff095f1f5ff3ff46a97f2dd [file] [log] [blame]
Angus Salkeld2bd63a42015-01-07 11:11:29 +10001# 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
13import json
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020014
Angus Salkeld2bd63a42015-01-07 11:11:29 +100015import yaml
16
17from heat_integrationtests.common import test
18
19
Angus Salkeld2bd63a42015-01-07 11:11:29 +100020class TemplateResourceTest(test.HeatIntegrationTest):
21 """Prove that we can use the registry in a nested provider."""
Angus Salkeld95403d82015-02-12 14:06:01 +100022
23 template = '''
24heat_template_version: 2013-05-23
25resources:
26 secret1:
27 type: OS::Heat::RandomString
28outputs:
29 secret-out:
30 value: { get_attr: [secret1, value] }
31'''
32 nested_templ = '''
33heat_template_version: 2013-05-23
34resources:
35 secret2:
36 type: OS::Heat::RandomString
37outputs:
38 value:
39 value: { get_attr: [secret2, value] }
40'''
41
42 env_templ = '''
43resource_registry:
44 "OS::Heat::RandomString": nested.yaml
45'''
46
Angus Salkeld2bd63a42015-01-07 11:11:29 +100047 def setUp(self):
48 super(TemplateResourceTest, self).setUp()
49 self.client = self.orchestration_client
50
51 def test_nested_env(self):
52 main_templ = '''
53heat_template_version: 2013-05-23
54resources:
55 secret1:
56 type: My::NestedSecret
57outputs:
58 secret-out:
59 value: { get_attr: [secret1, value] }
60'''
61
62 nested_templ = '''
63heat_template_version: 2013-05-23
64resources:
65 secret2:
66 type: My::Secret
67outputs:
68 value:
69 value: { get_attr: [secret2, value] }
70'''
71
72 env_templ = '''
73resource_registry:
74 "My::Secret": "OS::Heat::RandomString"
75 "My::NestedSecret": nested.yaml
76'''
77
78 stack_identifier = self.stack_create(
79 template=main_templ,
80 files={'nested.yaml': nested_templ},
81 environment=env_templ)
Angus Salkeld2e61f9f2015-04-07 09:25:50 +100082 nested_ident = self.assert_resource_is_a_stack(stack_identifier,
83 'secret1')
84 # prove that resource.parent_resource is populated.
85 sec2 = self.client.resources.get(nested_ident, 'secret2')
86 self.assertEqual('secret1', sec2.parent_resource)
Angus Salkeld2bd63a42015-01-07 11:11:29 +100087
88 def test_no_infinite_recursion(self):
89 """Prove that we can override a python resource.
90
91 And use that resource within the template resource.
92 """
Angus Salkeld2bd63a42015-01-07 11:11:29 +100093 stack_identifier = self.stack_create(
Angus Salkeld95403d82015-02-12 14:06:01 +100094 template=self.template,
95 files={'nested.yaml': self.nested_templ},
96 environment=self.env_templ)
Angus Salkeld2bd63a42015-01-07 11:11:29 +100097 self.assert_resource_is_a_stack(stack_identifier, 'secret1')
98
Angus Salkeld95403d82015-02-12 14:06:01 +100099 def test_nested_stack_delete_then_delete_parent_stack(self):
100 """Check the robustness of stack deletion.
101
102 This tests that if you manually delete a nested
103 stack, the parent stack is still deletable.
104 """
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400105 # disable cleanup so we can call _stack_delete() directly.
106 stack_identifier = self.stack_create(
Angus Salkeld95403d82015-02-12 14:06:01 +1000107 template=self.template,
108 files={'nested.yaml': self.nested_templ},
109 environment=self.env_templ,
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400110 enable_cleanup=False)
Angus Salkeld95403d82015-02-12 14:06:01 +1000111
112 nested_ident = self.assert_resource_is_a_stack(stack_identifier,
113 'secret1')
114
115 self._stack_delete(nested_ident)
116 self._stack_delete(stack_identifier)
117
Angus Salkeld19b9e1d2015-05-08 11:02:38 +1000118 def test_change_in_file_path(self):
119 stack_identifier = self.stack_create(
120 template=self.template,
121 files={'nested.yaml': self.nested_templ},
122 environment=self.env_templ)
123 stack = self.client.stacks.get(stack_identifier)
124 secret_out1 = self._stack_output(stack, 'secret-out')
125
126 nested_templ_2 = '''
127heat_template_version: 2013-05-23
128resources:
129 secret2:
130 type: OS::Heat::RandomString
131outputs:
132 value:
133 value: freddy
134'''
135 env_templ_2 = '''
136resource_registry:
137 "OS::Heat::RandomString": new/nested.yaml
138'''
139 self.update_stack(stack_identifier,
140 template=self.template,
141 files={'new/nested.yaml': nested_templ_2},
142 environment=env_templ_2)
143 stack = self.client.stacks.get(stack_identifier)
144 secret_out2 = self._stack_output(stack, 'secret-out')
145 self.assertNotEqual(secret_out1, secret_out2)
146 self.assertEqual('freddy', secret_out2)
147
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000148
149class NestedAttributesTest(test.HeatIntegrationTest):
150 """Prove that we can use the template resource references."""
151
152 main_templ = '''
153heat_template_version: 2014-10-16
154resources:
155 secret2:
156 type: My::NestedSecret
157outputs:
158 old_way:
159 value: { get_attr: [secret2, nested_str]}
160 test_attr1:
161 value: { get_attr: [secret2, resource.secret1, value]}
162 test_attr2:
163 value: { get_attr: [secret2, resource.secret1.value]}
164 test_ref:
165 value: { get_resource: secret2 }
166'''
167
168 env_templ = '''
169resource_registry:
170 "My::NestedSecret": nested.yaml
171'''
172
173 def setUp(self):
174 super(NestedAttributesTest, self).setUp()
175 self.client = self.orchestration_client
176
177 def test_stack_ref(self):
178 nested_templ = '''
179heat_template_version: 2014-10-16
180resources:
181 secret1:
182 type: OS::Heat::RandomString
183'''
184 stack_identifier = self.stack_create(
185 template=self.main_templ,
186 files={'nested.yaml': nested_templ},
187 environment=self.env_templ)
188 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
189 stack = self.client.stacks.get(stack_identifier)
190 test_ref = self._stack_output(stack, 'test_ref')
191 self.assertIn('arn:openstack:heat:', test_ref)
192
193 def test_transparent_ref(self):
194 """With the addition of OS::stack_id we can now use the nested resource
195 more transparently.
196 """
197 nested_templ = '''
198heat_template_version: 2014-10-16
199resources:
200 secret1:
201 type: OS::Heat::RandomString
202outputs:
203 OS::stack_id:
204 value: {get_resource: secret1}
205 nested_str:
206 value: {get_attr: [secret1, value]}
207'''
208 stack_identifier = self.stack_create(
209 template=self.main_templ,
210 files={'nested.yaml': nested_templ},
211 environment=self.env_templ)
212 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
213 stack = self.client.stacks.get(stack_identifier)
214 test_ref = self._stack_output(stack, 'test_ref')
215 test_attr = self._stack_output(stack, 'old_way')
216
217 self.assertNotIn('arn:openstack:heat', test_ref)
218 self.assertEqual(test_attr, test_ref)
219
220 def test_nested_attributes(self):
221 nested_templ = '''
222heat_template_version: 2014-10-16
223resources:
224 secret1:
225 type: OS::Heat::RandomString
226outputs:
227 nested_str:
228 value: {get_attr: [secret1, value]}
229'''
230 stack_identifier = self.stack_create(
231 template=self.main_templ,
232 files={'nested.yaml': nested_templ},
233 environment=self.env_templ)
234 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
235 stack = self.client.stacks.get(stack_identifier)
236 old_way = self._stack_output(stack, 'old_way')
237 test_attr1 = self._stack_output(stack, 'test_attr1')
238 test_attr2 = self._stack_output(stack, 'test_attr2')
239
240 self.assertEqual(old_way, test_attr1)
241 self.assertEqual(old_way, test_attr2)
242
243
244class TemplateResourceUpdateTest(test.HeatIntegrationTest):
245 """Prove that we can do template resource updates."""
246
247 main_template = '''
248HeatTemplateFormatVersion: '2012-12-12'
249Resources:
250 the_nested:
251 Type: the.yaml
252 Properties:
253 one: my_name
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530254 two: your_name
255Outputs:
256 identifier:
257 Value: {Ref: the_nested}
258 value:
259 Value: {'Fn::GetAtt': [the_nested, the_str]}
260'''
261
262 main_template_change_prop = '''
263HeatTemplateFormatVersion: '2012-12-12'
264Resources:
265 the_nested:
266 Type: the.yaml
267 Properties:
268 one: updated_name
269 two: your_name
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000270
271Outputs:
272 identifier:
273 Value: {Ref: the_nested}
274 value:
275 Value: {'Fn::GetAtt': [the_nested, the_str]}
276'''
277
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530278 main_template_add_prop = '''
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000279HeatTemplateFormatVersion: '2012-12-12'
280Resources:
281 the_nested:
282 Type: the.yaml
283 Properties:
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530284 one: my_name
285 two: your_name
286 three: third_name
287
288Outputs:
289 identifier:
290 Value: {Ref: the_nested}
291 value:
292 Value: {'Fn::GetAtt': [the_nested, the_str]}
293'''
294
295 main_template_remove_prop = '''
296HeatTemplateFormatVersion: '2012-12-12'
297Resources:
298 the_nested:
299 Type: the.yaml
300 Properties:
301 one: my_name
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000302
303Outputs:
304 identifier:
305 Value: {Ref: the_nested}
306 value:
307 Value: {'Fn::GetAtt': [the_nested, the_str]}
308'''
309
310 initial_tmpl = '''
311HeatTemplateFormatVersion: '2012-12-12'
312Parameters:
313 one:
314 Default: foo
315 Type: String
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530316 two:
317 Default: bar
318 Type: String
319
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000320Resources:
321 NestedResource:
322 Type: OS::Heat::RandomString
323 Properties:
324 salt: {Ref: one}
325Outputs:
326 the_str:
327 Value: {'Fn::GetAtt': [NestedResource, value]}
328'''
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530329
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000330 prop_change_tmpl = '''
331HeatTemplateFormatVersion: '2012-12-12'
332Parameters:
333 one:
334 Default: yikes
335 Type: String
336 two:
337 Default: foo
338 Type: String
339Resources:
340 NestedResource:
341 Type: OS::Heat::RandomString
342 Properties:
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530343 salt: {Ref: two}
344Outputs:
345 the_str:
346 Value: {'Fn::GetAtt': [NestedResource, value]}
347'''
348
349 prop_add_tmpl = '''
350HeatTemplateFormatVersion: '2012-12-12'
351Parameters:
352 one:
353 Default: yikes
354 Type: String
355 two:
356 Default: foo
357 Type: String
358 three:
359 Default: bar
360 Type: String
361
362Resources:
363 NestedResource:
364 Type: OS::Heat::RandomString
365 Properties:
366 salt: {Ref: three}
367Outputs:
368 the_str:
369 Value: {'Fn::GetAtt': [NestedResource, value]}
370'''
371
372 prop_remove_tmpl = '''
373HeatTemplateFormatVersion: '2012-12-12'
374Parameters:
375 one:
376 Default: yikes
377 Type: String
378
379Resources:
380 NestedResource:
381 Type: OS::Heat::RandomString
382 Properties:
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000383 salt: {Ref: one}
384Outputs:
385 the_str:
386 Value: {'Fn::GetAtt': [NestedResource, value]}
387'''
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530388
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000389 attr_change_tmpl = '''
390HeatTemplateFormatVersion: '2012-12-12'
391Parameters:
392 one:
393 Default: foo
394 Type: String
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530395 two:
396 Default: bar
397 Type: String
398
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000399Resources:
400 NestedResource:
401 Type: OS::Heat::RandomString
402 Properties:
403 salt: {Ref: one}
404Outputs:
405 the_str:
406 Value: {'Fn::GetAtt': [NestedResource, value]}
407 something_else:
408 Value: just_a_string
409'''
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530410
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000411 content_change_tmpl = '''
412HeatTemplateFormatVersion: '2012-12-12'
413Parameters:
414 one:
415 Default: foo
416 Type: String
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530417 two:
418 Default: bar
419 Type: String
420
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000421Resources:
422 NestedResource:
423 Type: OS::Heat::RandomString
424 Properties:
425 salt: yum
426Outputs:
427 the_str:
428 Value: {'Fn::GetAtt': [NestedResource, value]}
429'''
430
431 EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
432 scenarios = [
433 ('no_changes', dict(template=main_template,
434 provider=initial_tmpl,
435 expect=NOCHANGE)),
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530436 ('main_tmpl_change', dict(template=main_template_change_prop,
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000437 provider=initial_tmpl,
438 expect=UPDATE)),
439 ('provider_change', dict(template=main_template,
440 provider=content_change_tmpl,
441 expect=UPDATE)),
442 ('provider_props_change', dict(template=main_template,
443 provider=prop_change_tmpl,
Rabi Mishrab1293ae2015-05-13 16:39:04 +0530444 expect=UPDATE)),
445 ('provider_props_add', dict(template=main_template_add_prop,
446 provider=prop_add_tmpl,
447 expect=UPDATE)),
448 ('provider_props_remove', dict(template=main_template_remove_prop,
449 provider=prop_remove_tmpl,
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000450 expect=NOCHANGE)),
451 ('provider_attr_change', dict(template=main_template,
452 provider=attr_change_tmpl,
453 expect=NOCHANGE)),
454 ]
455
456 def setUp(self):
457 super(TemplateResourceUpdateTest, self).setUp()
458 self.client = self.orchestration_client
459
460 def test_template_resource_update_template_schema(self):
461 stack_identifier = self.stack_create(
462 template=self.main_template,
463 files={'the.yaml': self.initial_tmpl})
464 stack = self.client.stacks.get(stack_identifier)
465 initial_id = self._stack_output(stack, 'identifier')
466 initial_val = self._stack_output(stack, 'value')
467
468 self.update_stack(stack_identifier,
469 self.template,
470 files={'the.yaml': self.provider})
471 stack = self.client.stacks.get(stack_identifier)
472 self.assertEqual(initial_id,
473 self._stack_output(stack, 'identifier'))
474 if self.expect == self.NOCHANGE:
475 self.assertEqual(initial_val,
476 self._stack_output(stack, 'value'))
477 else:
478 self.assertNotEqual(initial_val,
479 self._stack_output(stack, 'value'))
480
481
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000482class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
483 """Prove that we can do updates on a nested stack to fix a stack."""
484 main_template = '''
485HeatTemplateFormatVersion: '2012-12-12'
486Resources:
487 keypair:
488 Type: OS::Nova::KeyPair
489 Properties:
490 name: replace-this
491 save_private_key: false
492 server:
493 Type: server_fail.yaml
494 DependsOn: keypair
495'''
496 nested_templ = '''
497HeatTemplateFormatVersion: '2012-12-12'
498Resources:
499 RealRandom:
500 Type: OS::Heat::RandomString
501'''
502
503 def setUp(self):
504 super(TemplateResourceUpdateFailedTest, self).setUp()
505 self.client = self.orchestration_client
Sergey Krayneva265c132015-02-13 03:51:03 -0500506 self.assign_keypair()
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000507
508 def test_update_on_failed_create(self):
Vikas Jainac6b02f2015-02-11 11:31:46 -0800509 # create a stack with "server" dependent on "keypair", but
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000510 # keypair fails, so "server" is not created properly.
511 # We then fix the template and it should succeed.
512 broken_templ = self.main_template.replace('replace-this',
513 self.keypair_name)
514 stack_identifier = self.stack_create(
515 template=broken_templ,
516 files={'server_fail.yaml': self.nested_templ},
517 expected_status='CREATE_FAILED')
518
519 fixed_templ = self.main_template.replace('replace-this',
520 test.rand_name())
521 self.update_stack(stack_identifier,
522 fixed_templ,
523 files={'server_fail.yaml': self.nested_templ})
524
525
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000526class TemplateResourceAdoptTest(test.HeatIntegrationTest):
527 """Prove that we can do template resource adopt/abandon."""
528
529 main_template = '''
530HeatTemplateFormatVersion: '2012-12-12'
531Resources:
532 the_nested:
533 Type: the.yaml
534 Properties:
535 one: my_name
536Outputs:
537 identifier:
538 Value: {Ref: the_nested}
539 value:
540 Value: {'Fn::GetAtt': [the_nested, the_str]}
541'''
542
543 nested_templ = '''
544HeatTemplateFormatVersion: '2012-12-12'
545Parameters:
546 one:
547 Default: foo
548 Type: String
549Resources:
550 RealRandom:
551 Type: OS::Heat::RandomString
552 Properties:
553 salt: {Ref: one}
554Outputs:
555 the_str:
556 Value: {'Fn::GetAtt': [RealRandom, value]}
557'''
558
559 def setUp(self):
560 super(TemplateResourceAdoptTest, self).setUp()
561 self.client = self.orchestration_client
562
563 def _yaml_to_json(self, yaml_templ):
564 return yaml.load(yaml_templ)
565
566 def test_abandon(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400567 stack_identifier = self.stack_create(
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000568 template=self.main_template,
569 files={'the.yaml': self.nested_templ},
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400570 enable_cleanup=False
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000571 )
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000572
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530573 info = self.stack_abandon(stack_id=stack_identifier)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000574 self.assertEqual(self._yaml_to_json(self.main_template),
575 info['template'])
576 self.assertEqual(self._yaml_to_json(self.nested_templ),
577 info['resources']['the_nested']['template'])
578
579 def test_adopt(self):
580 data = {
581 'resources': {
582 'the_nested': {
583 "type": "the.yaml",
584 "resources": {
585 "RealRandom": {
586 "type": "OS::Heat::RandomString",
587 'resource_data': {'value': 'goopie'},
588 'resource_id': 'froggy'
589 }
590 }
591 }
592 },
593 "environment": {"parameters": {}},
594 "template": yaml.load(self.main_template)
595 }
596
597 stack_identifier = self.stack_adopt(
598 adopt_data=json.dumps(data),
599 files={'the.yaml': self.nested_templ})
600
601 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
602 stack = self.client.stacks.get(stack_identifier)
603 self.assertEqual('goopie', self._stack_output(stack, 'value'))
Angus Salkeld87601722015-01-05 14:57:27 +1000604
605
606class TemplateResourceCheckTest(test.HeatIntegrationTest):
607 """Prove that we can do template resource check."""
608
609 main_template = '''
610HeatTemplateFormatVersion: '2012-12-12'
611Resources:
612 the_nested:
613 Type: the.yaml
614 Properties:
615 one: my_name
616Outputs:
617 identifier:
618 Value: {Ref: the_nested}
619 value:
620 Value: {'Fn::GetAtt': [the_nested, the_str]}
621'''
622
623 nested_templ = '''
624HeatTemplateFormatVersion: '2012-12-12'
625Parameters:
626 one:
627 Default: foo
628 Type: String
629Resources:
630 RealRandom:
631 Type: OS::Heat::RandomString
632 Properties:
633 salt: {Ref: one}
634Outputs:
635 the_str:
636 Value: {'Fn::GetAtt': [RealRandom, value]}
637'''
638
639 def setUp(self):
640 super(TemplateResourceCheckTest, self).setUp()
641 self.client = self.orchestration_client
642
643 def test_check(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400644 stack_identifier = self.stack_create(
Angus Salkeld87601722015-01-05 14:57:27 +1000645 template=self.main_template,
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400646 files={'the.yaml': self.nested_templ}
Angus Salkeld87601722015-01-05 14:57:27 +1000647 )
Angus Salkeld87601722015-01-05 14:57:27 +1000648
649 self.client.actions.check(stack_id=stack_identifier)
650 self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
Angus Salkeld793b0fc2015-06-24 08:52:08 +1000651
652
653class TemplateResourceErrorMessageTest(test.HeatIntegrationTest):
654 """Prove that nested stack errors don't suck."""
655 template = '''
656HeatTemplateFormatVersion: '2012-12-12'
657Resources:
658 victim:
659 Type: fail.yaml
660'''
661 nested_templ = '''
662HeatTemplateFormatVersion: '2012-12-12'
663Resources:
664 oops:
665 Type: OS::Heat::TestResource
666 Properties:
667 fail: true
668 wait_secs: 2
669'''
670
671 def setUp(self):
672 super(TemplateResourceErrorMessageTest, self).setUp()
673 self.client = self.orchestration_client
674
675 def test_fail(self):
676 stack_identifier = self.stack_create(
677 template=self.template,
678 files={'fail.yaml': self.nested_templ},
679 expected_status='CREATE_FAILED')
680 stack = self.client.stacks.get(stack_identifier)
681
682 exp_path = 'resources.victim.resources.oops'
683 exp_msg = 'Test Resource failed oops'
684 exp = 'Resource CREATE failed: ValueError: %s: %s' % (exp_path,
685 exp_msg)
686 self.assertEqual(exp, stack.stack_status_reason)