blob: f7de55ce8fa5dea402dc76dd0652f9a9d7f96bfd [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
254
255Outputs:
256 identifier:
257 Value: {Ref: the_nested}
258 value:
259 Value: {'Fn::GetAtt': [the_nested, the_str]}
260'''
261
262 main_template_2 = '''
263HeatTemplateFormatVersion: '2012-12-12'
264Resources:
265 the_nested:
266 Type: the.yaml
267 Properties:
268 one: updated_name
269
270Outputs:
271 identifier:
272 Value: {Ref: the_nested}
273 value:
274 Value: {'Fn::GetAtt': [the_nested, the_str]}
275'''
276
277 initial_tmpl = '''
278HeatTemplateFormatVersion: '2012-12-12'
279Parameters:
280 one:
281 Default: foo
282 Type: String
283Resources:
284 NestedResource:
285 Type: OS::Heat::RandomString
286 Properties:
287 salt: {Ref: one}
288Outputs:
289 the_str:
290 Value: {'Fn::GetAtt': [NestedResource, value]}
291'''
292 prop_change_tmpl = '''
293HeatTemplateFormatVersion: '2012-12-12'
294Parameters:
295 one:
296 Default: yikes
297 Type: String
298 two:
299 Default: foo
300 Type: String
301Resources:
302 NestedResource:
303 Type: OS::Heat::RandomString
304 Properties:
305 salt: {Ref: one}
306Outputs:
307 the_str:
308 Value: {'Fn::GetAtt': [NestedResource, value]}
309'''
310 attr_change_tmpl = '''
311HeatTemplateFormatVersion: '2012-12-12'
312Parameters:
313 one:
314 Default: foo
315 Type: String
316Resources:
317 NestedResource:
318 Type: OS::Heat::RandomString
319 Properties:
320 salt: {Ref: one}
321Outputs:
322 the_str:
323 Value: {'Fn::GetAtt': [NestedResource, value]}
324 something_else:
325 Value: just_a_string
326'''
327 content_change_tmpl = '''
328HeatTemplateFormatVersion: '2012-12-12'
329Parameters:
330 one:
331 Default: foo
332 Type: String
333Resources:
334 NestedResource:
335 Type: OS::Heat::RandomString
336 Properties:
337 salt: yum
338Outputs:
339 the_str:
340 Value: {'Fn::GetAtt': [NestedResource, value]}
341'''
342
343 EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
344 scenarios = [
345 ('no_changes', dict(template=main_template,
346 provider=initial_tmpl,
347 expect=NOCHANGE)),
348 ('main_tmpl_change', dict(template=main_template_2,
349 provider=initial_tmpl,
350 expect=UPDATE)),
351 ('provider_change', dict(template=main_template,
352 provider=content_change_tmpl,
353 expect=UPDATE)),
354 ('provider_props_change', dict(template=main_template,
355 provider=prop_change_tmpl,
356 expect=NOCHANGE)),
357 ('provider_attr_change', dict(template=main_template,
358 provider=attr_change_tmpl,
359 expect=NOCHANGE)),
360 ]
361
362 def setUp(self):
363 super(TemplateResourceUpdateTest, self).setUp()
364 self.client = self.orchestration_client
365
366 def test_template_resource_update_template_schema(self):
367 stack_identifier = self.stack_create(
368 template=self.main_template,
369 files={'the.yaml': self.initial_tmpl})
370 stack = self.client.stacks.get(stack_identifier)
371 initial_id = self._stack_output(stack, 'identifier')
372 initial_val = self._stack_output(stack, 'value')
373
374 self.update_stack(stack_identifier,
375 self.template,
376 files={'the.yaml': self.provider})
377 stack = self.client.stacks.get(stack_identifier)
378 self.assertEqual(initial_id,
379 self._stack_output(stack, 'identifier'))
380 if self.expect == self.NOCHANGE:
381 self.assertEqual(initial_val,
382 self._stack_output(stack, 'value'))
383 else:
384 self.assertNotEqual(initial_val,
385 self._stack_output(stack, 'value'))
386
387
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000388class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
389 """Prove that we can do updates on a nested stack to fix a stack."""
390 main_template = '''
391HeatTemplateFormatVersion: '2012-12-12'
392Resources:
393 keypair:
394 Type: OS::Nova::KeyPair
395 Properties:
396 name: replace-this
397 save_private_key: false
398 server:
399 Type: server_fail.yaml
400 DependsOn: keypair
401'''
402 nested_templ = '''
403HeatTemplateFormatVersion: '2012-12-12'
404Resources:
405 RealRandom:
406 Type: OS::Heat::RandomString
407'''
408
409 def setUp(self):
410 super(TemplateResourceUpdateFailedTest, self).setUp()
411 self.client = self.orchestration_client
Sergey Krayneva265c132015-02-13 03:51:03 -0500412 self.assign_keypair()
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000413
414 def test_update_on_failed_create(self):
Vikas Jainac6b02f2015-02-11 11:31:46 -0800415 # create a stack with "server" dependent on "keypair", but
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000416 # keypair fails, so "server" is not created properly.
417 # We then fix the template and it should succeed.
418 broken_templ = self.main_template.replace('replace-this',
419 self.keypair_name)
420 stack_identifier = self.stack_create(
421 template=broken_templ,
422 files={'server_fail.yaml': self.nested_templ},
423 expected_status='CREATE_FAILED')
424
425 fixed_templ = self.main_template.replace('replace-this',
426 test.rand_name())
427 self.update_stack(stack_identifier,
428 fixed_templ,
429 files={'server_fail.yaml': self.nested_templ})
430
431
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000432class TemplateResourceAdoptTest(test.HeatIntegrationTest):
433 """Prove that we can do template resource adopt/abandon."""
434
435 main_template = '''
436HeatTemplateFormatVersion: '2012-12-12'
437Resources:
438 the_nested:
439 Type: the.yaml
440 Properties:
441 one: my_name
442Outputs:
443 identifier:
444 Value: {Ref: the_nested}
445 value:
446 Value: {'Fn::GetAtt': [the_nested, the_str]}
447'''
448
449 nested_templ = '''
450HeatTemplateFormatVersion: '2012-12-12'
451Parameters:
452 one:
453 Default: foo
454 Type: String
455Resources:
456 RealRandom:
457 Type: OS::Heat::RandomString
458 Properties:
459 salt: {Ref: one}
460Outputs:
461 the_str:
462 Value: {'Fn::GetAtt': [RealRandom, value]}
463'''
464
465 def setUp(self):
466 super(TemplateResourceAdoptTest, self).setUp()
467 self.client = self.orchestration_client
468
469 def _yaml_to_json(self, yaml_templ):
470 return yaml.load(yaml_templ)
471
472 def test_abandon(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400473 stack_identifier = self.stack_create(
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000474 template=self.main_template,
475 files={'the.yaml': self.nested_templ},
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400476 enable_cleanup=False
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000477 )
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000478
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530479 info = self.stack_abandon(stack_id=stack_identifier)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000480 self.assertEqual(self._yaml_to_json(self.main_template),
481 info['template'])
482 self.assertEqual(self._yaml_to_json(self.nested_templ),
483 info['resources']['the_nested']['template'])
484
485 def test_adopt(self):
486 data = {
487 'resources': {
488 'the_nested': {
489 "type": "the.yaml",
490 "resources": {
491 "RealRandom": {
492 "type": "OS::Heat::RandomString",
493 'resource_data': {'value': 'goopie'},
494 'resource_id': 'froggy'
495 }
496 }
497 }
498 },
499 "environment": {"parameters": {}},
500 "template": yaml.load(self.main_template)
501 }
502
503 stack_identifier = self.stack_adopt(
504 adopt_data=json.dumps(data),
505 files={'the.yaml': self.nested_templ})
506
507 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
508 stack = self.client.stacks.get(stack_identifier)
509 self.assertEqual('goopie', self._stack_output(stack, 'value'))
Angus Salkeld87601722015-01-05 14:57:27 +1000510
511
512class TemplateResourceCheckTest(test.HeatIntegrationTest):
513 """Prove that we can do template resource check."""
514
515 main_template = '''
516HeatTemplateFormatVersion: '2012-12-12'
517Resources:
518 the_nested:
519 Type: the.yaml
520 Properties:
521 one: my_name
522Outputs:
523 identifier:
524 Value: {Ref: the_nested}
525 value:
526 Value: {'Fn::GetAtt': [the_nested, the_str]}
527'''
528
529 nested_templ = '''
530HeatTemplateFormatVersion: '2012-12-12'
531Parameters:
532 one:
533 Default: foo
534 Type: String
535Resources:
536 RealRandom:
537 Type: OS::Heat::RandomString
538 Properties:
539 salt: {Ref: one}
540Outputs:
541 the_str:
542 Value: {'Fn::GetAtt': [RealRandom, value]}
543'''
544
545 def setUp(self):
546 super(TemplateResourceCheckTest, self).setUp()
547 self.client = self.orchestration_client
548
549 def test_check(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400550 stack_identifier = self.stack_create(
Angus Salkeld87601722015-01-05 14:57:27 +1000551 template=self.main_template,
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400552 files={'the.yaml': self.nested_templ}
Angus Salkeld87601722015-01-05 14:57:27 +1000553 )
Angus Salkeld87601722015-01-05 14:57:27 +1000554
555 self.client.actions.check(stack_id=stack_identifier)
556 self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')