blob: 356fc2abd39224db3e69614dfe255547ce9a2094 [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 Salkeld2bd63a42015-01-07 11:11:29 +1000118
119class NestedAttributesTest(test.HeatIntegrationTest):
120 """Prove that we can use the template resource references."""
121
122 main_templ = '''
123heat_template_version: 2014-10-16
124resources:
125 secret2:
126 type: My::NestedSecret
127outputs:
128 old_way:
129 value: { get_attr: [secret2, nested_str]}
130 test_attr1:
131 value: { get_attr: [secret2, resource.secret1, value]}
132 test_attr2:
133 value: { get_attr: [secret2, resource.secret1.value]}
134 test_ref:
135 value: { get_resource: secret2 }
136'''
137
138 env_templ = '''
139resource_registry:
140 "My::NestedSecret": nested.yaml
141'''
142
143 def setUp(self):
144 super(NestedAttributesTest, self).setUp()
145 self.client = self.orchestration_client
146
147 def test_stack_ref(self):
148 nested_templ = '''
149heat_template_version: 2014-10-16
150resources:
151 secret1:
152 type: OS::Heat::RandomString
153'''
154 stack_identifier = self.stack_create(
155 template=self.main_templ,
156 files={'nested.yaml': nested_templ},
157 environment=self.env_templ)
158 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
159 stack = self.client.stacks.get(stack_identifier)
160 test_ref = self._stack_output(stack, 'test_ref')
161 self.assertIn('arn:openstack:heat:', test_ref)
162
163 def test_transparent_ref(self):
164 """With the addition of OS::stack_id we can now use the nested resource
165 more transparently.
166 """
167 nested_templ = '''
168heat_template_version: 2014-10-16
169resources:
170 secret1:
171 type: OS::Heat::RandomString
172outputs:
173 OS::stack_id:
174 value: {get_resource: secret1}
175 nested_str:
176 value: {get_attr: [secret1, value]}
177'''
178 stack_identifier = self.stack_create(
179 template=self.main_templ,
180 files={'nested.yaml': nested_templ},
181 environment=self.env_templ)
182 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
183 stack = self.client.stacks.get(stack_identifier)
184 test_ref = self._stack_output(stack, 'test_ref')
185 test_attr = self._stack_output(stack, 'old_way')
186
187 self.assertNotIn('arn:openstack:heat', test_ref)
188 self.assertEqual(test_attr, test_ref)
189
190 def test_nested_attributes(self):
191 nested_templ = '''
192heat_template_version: 2014-10-16
193resources:
194 secret1:
195 type: OS::Heat::RandomString
196outputs:
197 nested_str:
198 value: {get_attr: [secret1, value]}
199'''
200 stack_identifier = self.stack_create(
201 template=self.main_templ,
202 files={'nested.yaml': nested_templ},
203 environment=self.env_templ)
204 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
205 stack = self.client.stacks.get(stack_identifier)
206 old_way = self._stack_output(stack, 'old_way')
207 test_attr1 = self._stack_output(stack, 'test_attr1')
208 test_attr2 = self._stack_output(stack, 'test_attr2')
209
210 self.assertEqual(old_way, test_attr1)
211 self.assertEqual(old_way, test_attr2)
212
213
214class TemplateResourceUpdateTest(test.HeatIntegrationTest):
215 """Prove that we can do template resource updates."""
216
217 main_template = '''
218HeatTemplateFormatVersion: '2012-12-12'
219Resources:
220 the_nested:
221 Type: the.yaml
222 Properties:
223 one: my_name
224
225Outputs:
226 identifier:
227 Value: {Ref: the_nested}
228 value:
229 Value: {'Fn::GetAtt': [the_nested, the_str]}
230'''
231
232 main_template_2 = '''
233HeatTemplateFormatVersion: '2012-12-12'
234Resources:
235 the_nested:
236 Type: the.yaml
237 Properties:
238 one: updated_name
239
240Outputs:
241 identifier:
242 Value: {Ref: the_nested}
243 value:
244 Value: {'Fn::GetAtt': [the_nested, the_str]}
245'''
246
247 initial_tmpl = '''
248HeatTemplateFormatVersion: '2012-12-12'
249Parameters:
250 one:
251 Default: foo
252 Type: String
253Resources:
254 NestedResource:
255 Type: OS::Heat::RandomString
256 Properties:
257 salt: {Ref: one}
258Outputs:
259 the_str:
260 Value: {'Fn::GetAtt': [NestedResource, value]}
261'''
262 prop_change_tmpl = '''
263HeatTemplateFormatVersion: '2012-12-12'
264Parameters:
265 one:
266 Default: yikes
267 Type: String
268 two:
269 Default: foo
270 Type: String
271Resources:
272 NestedResource:
273 Type: OS::Heat::RandomString
274 Properties:
275 salt: {Ref: one}
276Outputs:
277 the_str:
278 Value: {'Fn::GetAtt': [NestedResource, value]}
279'''
280 attr_change_tmpl = '''
281HeatTemplateFormatVersion: '2012-12-12'
282Parameters:
283 one:
284 Default: foo
285 Type: String
286Resources:
287 NestedResource:
288 Type: OS::Heat::RandomString
289 Properties:
290 salt: {Ref: one}
291Outputs:
292 the_str:
293 Value: {'Fn::GetAtt': [NestedResource, value]}
294 something_else:
295 Value: just_a_string
296'''
297 content_change_tmpl = '''
298HeatTemplateFormatVersion: '2012-12-12'
299Parameters:
300 one:
301 Default: foo
302 Type: String
303Resources:
304 NestedResource:
305 Type: OS::Heat::RandomString
306 Properties:
307 salt: yum
308Outputs:
309 the_str:
310 Value: {'Fn::GetAtt': [NestedResource, value]}
311'''
312
313 EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
314 scenarios = [
315 ('no_changes', dict(template=main_template,
316 provider=initial_tmpl,
317 expect=NOCHANGE)),
318 ('main_tmpl_change', dict(template=main_template_2,
319 provider=initial_tmpl,
320 expect=UPDATE)),
321 ('provider_change', dict(template=main_template,
322 provider=content_change_tmpl,
323 expect=UPDATE)),
324 ('provider_props_change', dict(template=main_template,
325 provider=prop_change_tmpl,
326 expect=NOCHANGE)),
327 ('provider_attr_change', dict(template=main_template,
328 provider=attr_change_tmpl,
329 expect=NOCHANGE)),
330 ]
331
332 def setUp(self):
333 super(TemplateResourceUpdateTest, self).setUp()
334 self.client = self.orchestration_client
335
336 def test_template_resource_update_template_schema(self):
337 stack_identifier = self.stack_create(
338 template=self.main_template,
339 files={'the.yaml': self.initial_tmpl})
340 stack = self.client.stacks.get(stack_identifier)
341 initial_id = self._stack_output(stack, 'identifier')
342 initial_val = self._stack_output(stack, 'value')
343
344 self.update_stack(stack_identifier,
345 self.template,
346 files={'the.yaml': self.provider})
347 stack = self.client.stacks.get(stack_identifier)
348 self.assertEqual(initial_id,
349 self._stack_output(stack, 'identifier'))
350 if self.expect == self.NOCHANGE:
351 self.assertEqual(initial_val,
352 self._stack_output(stack, 'value'))
353 else:
354 self.assertNotEqual(initial_val,
355 self._stack_output(stack, 'value'))
356
357
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000358class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
359 """Prove that we can do updates on a nested stack to fix a stack."""
360 main_template = '''
361HeatTemplateFormatVersion: '2012-12-12'
362Resources:
363 keypair:
364 Type: OS::Nova::KeyPair
365 Properties:
366 name: replace-this
367 save_private_key: false
368 server:
369 Type: server_fail.yaml
370 DependsOn: keypair
371'''
372 nested_templ = '''
373HeatTemplateFormatVersion: '2012-12-12'
374Resources:
375 RealRandom:
376 Type: OS::Heat::RandomString
377'''
378
379 def setUp(self):
380 super(TemplateResourceUpdateFailedTest, self).setUp()
381 self.client = self.orchestration_client
Sergey Krayneva265c132015-02-13 03:51:03 -0500382 self.assign_keypair()
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000383
384 def test_update_on_failed_create(self):
Vikas Jainac6b02f2015-02-11 11:31:46 -0800385 # create a stack with "server" dependent on "keypair", but
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000386 # keypair fails, so "server" is not created properly.
387 # We then fix the template and it should succeed.
388 broken_templ = self.main_template.replace('replace-this',
389 self.keypair_name)
390 stack_identifier = self.stack_create(
391 template=broken_templ,
392 files={'server_fail.yaml': self.nested_templ},
393 expected_status='CREATE_FAILED')
394
395 fixed_templ = self.main_template.replace('replace-this',
396 test.rand_name())
397 self.update_stack(stack_identifier,
398 fixed_templ,
399 files={'server_fail.yaml': self.nested_templ})
400
401
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000402class TemplateResourceAdoptTest(test.HeatIntegrationTest):
403 """Prove that we can do template resource adopt/abandon."""
404
405 main_template = '''
406HeatTemplateFormatVersion: '2012-12-12'
407Resources:
408 the_nested:
409 Type: the.yaml
410 Properties:
411 one: my_name
412Outputs:
413 identifier:
414 Value: {Ref: the_nested}
415 value:
416 Value: {'Fn::GetAtt': [the_nested, the_str]}
417'''
418
419 nested_templ = '''
420HeatTemplateFormatVersion: '2012-12-12'
421Parameters:
422 one:
423 Default: foo
424 Type: String
425Resources:
426 RealRandom:
427 Type: OS::Heat::RandomString
428 Properties:
429 salt: {Ref: one}
430Outputs:
431 the_str:
432 Value: {'Fn::GetAtt': [RealRandom, value]}
433'''
434
435 def setUp(self):
436 super(TemplateResourceAdoptTest, self).setUp()
437 self.client = self.orchestration_client
438
439 def _yaml_to_json(self, yaml_templ):
440 return yaml.load(yaml_templ)
441
442 def test_abandon(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400443 stack_identifier = self.stack_create(
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000444 template=self.main_template,
445 files={'the.yaml': self.nested_templ},
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400446 enable_cleanup=False
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000447 )
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000448
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530449 info = self.stack_abandon(stack_id=stack_identifier)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000450 self.assertEqual(self._yaml_to_json(self.main_template),
451 info['template'])
452 self.assertEqual(self._yaml_to_json(self.nested_templ),
453 info['resources']['the_nested']['template'])
454
455 def test_adopt(self):
456 data = {
457 'resources': {
458 'the_nested': {
459 "type": "the.yaml",
460 "resources": {
461 "RealRandom": {
462 "type": "OS::Heat::RandomString",
463 'resource_data': {'value': 'goopie'},
464 'resource_id': 'froggy'
465 }
466 }
467 }
468 },
469 "environment": {"parameters": {}},
470 "template": yaml.load(self.main_template)
471 }
472
473 stack_identifier = self.stack_adopt(
474 adopt_data=json.dumps(data),
475 files={'the.yaml': self.nested_templ})
476
477 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
478 stack = self.client.stacks.get(stack_identifier)
479 self.assertEqual('goopie', self._stack_output(stack, 'value'))
Angus Salkeld87601722015-01-05 14:57:27 +1000480
481
482class TemplateResourceCheckTest(test.HeatIntegrationTest):
483 """Prove that we can do template resource check."""
484
485 main_template = '''
486HeatTemplateFormatVersion: '2012-12-12'
487Resources:
488 the_nested:
489 Type: the.yaml
490 Properties:
491 one: my_name
492Outputs:
493 identifier:
494 Value: {Ref: the_nested}
495 value:
496 Value: {'Fn::GetAtt': [the_nested, the_str]}
497'''
498
499 nested_templ = '''
500HeatTemplateFormatVersion: '2012-12-12'
501Parameters:
502 one:
503 Default: foo
504 Type: String
505Resources:
506 RealRandom:
507 Type: OS::Heat::RandomString
508 Properties:
509 salt: {Ref: one}
510Outputs:
511 the_str:
512 Value: {'Fn::GetAtt': [RealRandom, value]}
513'''
514
515 def setUp(self):
516 super(TemplateResourceCheckTest, self).setUp()
517 self.client = self.orchestration_client
518
519 def test_check(self):
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400520 stack_identifier = self.stack_create(
Angus Salkeld87601722015-01-05 14:57:27 +1000521 template=self.main_template,
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400522 files={'the.yaml': self.nested_templ}
Angus Salkeld87601722015-01-05 14:57:27 +1000523 )
Angus Salkeld87601722015-01-05 14:57:27 +1000524
525 self.client.actions.check(stack_id=stack_identifier)
526 self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')