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