blob: 80dcdceb7ef3bc7bd342d65e6999eb040ed6297c [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
14import logging
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020015
Angus Salkeld2bd63a42015-01-07 11:11:29 +100016import yaml
17
18from heat_integrationtests.common import test
19
20
21LOG = logging.getLogger(__name__)
22
23
24class TemplateResourceTest(test.HeatIntegrationTest):
25 """Prove that we can use the registry in a nested provider."""
26 def setUp(self):
27 super(TemplateResourceTest, self).setUp()
28 self.client = self.orchestration_client
29
30 def test_nested_env(self):
31 main_templ = '''
32heat_template_version: 2013-05-23
33resources:
34 secret1:
35 type: My::NestedSecret
36outputs:
37 secret-out:
38 value: { get_attr: [secret1, value] }
39'''
40
41 nested_templ = '''
42heat_template_version: 2013-05-23
43resources:
44 secret2:
45 type: My::Secret
46outputs:
47 value:
48 value: { get_attr: [secret2, value] }
49'''
50
51 env_templ = '''
52resource_registry:
53 "My::Secret": "OS::Heat::RandomString"
54 "My::NestedSecret": nested.yaml
55'''
56
57 stack_identifier = self.stack_create(
58 template=main_templ,
59 files={'nested.yaml': nested_templ},
60 environment=env_templ)
61 self.assert_resource_is_a_stack(stack_identifier, 'secret1')
62
63 def test_no_infinite_recursion(self):
64 """Prove that we can override a python resource.
65
66 And use that resource within the template resource.
67 """
68
69 main_templ = '''
70heat_template_version: 2013-05-23
71resources:
72 secret1:
73 type: OS::Heat::RandomString
74outputs:
75 secret-out:
76 value: { get_attr: [secret1, value] }
77'''
78
79 nested_templ = '''
80heat_template_version: 2013-05-23
81resources:
82 secret2:
83 type: OS::Heat::RandomString
84outputs:
85 value:
86 value: { get_attr: [secret2, value] }
87'''
88
89 env_templ = '''
90resource_registry:
91 "OS::Heat::RandomString": nested.yaml
92'''
93
94 stack_identifier = self.stack_create(
95 template=main_templ,
96 files={'nested.yaml': nested_templ},
97 environment=env_templ)
98 self.assert_resource_is_a_stack(stack_identifier, 'secret1')
99
100
101class NestedAttributesTest(test.HeatIntegrationTest):
102 """Prove that we can use the template resource references."""
103
104 main_templ = '''
105heat_template_version: 2014-10-16
106resources:
107 secret2:
108 type: My::NestedSecret
109outputs:
110 old_way:
111 value: { get_attr: [secret2, nested_str]}
112 test_attr1:
113 value: { get_attr: [secret2, resource.secret1, value]}
114 test_attr2:
115 value: { get_attr: [secret2, resource.secret1.value]}
116 test_ref:
117 value: { get_resource: secret2 }
118'''
119
120 env_templ = '''
121resource_registry:
122 "My::NestedSecret": nested.yaml
123'''
124
125 def setUp(self):
126 super(NestedAttributesTest, self).setUp()
127 self.client = self.orchestration_client
128
129 def test_stack_ref(self):
130 nested_templ = '''
131heat_template_version: 2014-10-16
132resources:
133 secret1:
134 type: OS::Heat::RandomString
135'''
136 stack_identifier = self.stack_create(
137 template=self.main_templ,
138 files={'nested.yaml': nested_templ},
139 environment=self.env_templ)
140 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
141 stack = self.client.stacks.get(stack_identifier)
142 test_ref = self._stack_output(stack, 'test_ref')
143 self.assertIn('arn:openstack:heat:', test_ref)
144
145 def test_transparent_ref(self):
146 """With the addition of OS::stack_id we can now use the nested resource
147 more transparently.
148 """
149 nested_templ = '''
150heat_template_version: 2014-10-16
151resources:
152 secret1:
153 type: OS::Heat::RandomString
154outputs:
155 OS::stack_id:
156 value: {get_resource: secret1}
157 nested_str:
158 value: {get_attr: [secret1, value]}
159'''
160 stack_identifier = self.stack_create(
161 template=self.main_templ,
162 files={'nested.yaml': nested_templ},
163 environment=self.env_templ)
164 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
165 stack = self.client.stacks.get(stack_identifier)
166 test_ref = self._stack_output(stack, 'test_ref')
167 test_attr = self._stack_output(stack, 'old_way')
168
169 self.assertNotIn('arn:openstack:heat', test_ref)
170 self.assertEqual(test_attr, test_ref)
171
172 def test_nested_attributes(self):
173 nested_templ = '''
174heat_template_version: 2014-10-16
175resources:
176 secret1:
177 type: OS::Heat::RandomString
178outputs:
179 nested_str:
180 value: {get_attr: [secret1, value]}
181'''
182 stack_identifier = self.stack_create(
183 template=self.main_templ,
184 files={'nested.yaml': nested_templ},
185 environment=self.env_templ)
186 self.assert_resource_is_a_stack(stack_identifier, 'secret2')
187 stack = self.client.stacks.get(stack_identifier)
188 old_way = self._stack_output(stack, 'old_way')
189 test_attr1 = self._stack_output(stack, 'test_attr1')
190 test_attr2 = self._stack_output(stack, 'test_attr2')
191
192 self.assertEqual(old_way, test_attr1)
193 self.assertEqual(old_way, test_attr2)
194
195
196class TemplateResourceUpdateTest(test.HeatIntegrationTest):
197 """Prove that we can do template resource updates."""
198
199 main_template = '''
200HeatTemplateFormatVersion: '2012-12-12'
201Resources:
202 the_nested:
203 Type: the.yaml
204 Properties:
205 one: my_name
206
207Outputs:
208 identifier:
209 Value: {Ref: the_nested}
210 value:
211 Value: {'Fn::GetAtt': [the_nested, the_str]}
212'''
213
214 main_template_2 = '''
215HeatTemplateFormatVersion: '2012-12-12'
216Resources:
217 the_nested:
218 Type: the.yaml
219 Properties:
220 one: updated_name
221
222Outputs:
223 identifier:
224 Value: {Ref: the_nested}
225 value:
226 Value: {'Fn::GetAtt': [the_nested, the_str]}
227'''
228
229 initial_tmpl = '''
230HeatTemplateFormatVersion: '2012-12-12'
231Parameters:
232 one:
233 Default: foo
234 Type: String
235Resources:
236 NestedResource:
237 Type: OS::Heat::RandomString
238 Properties:
239 salt: {Ref: one}
240Outputs:
241 the_str:
242 Value: {'Fn::GetAtt': [NestedResource, value]}
243'''
244 prop_change_tmpl = '''
245HeatTemplateFormatVersion: '2012-12-12'
246Parameters:
247 one:
248 Default: yikes
249 Type: String
250 two:
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 attr_change_tmpl = '''
263HeatTemplateFormatVersion: '2012-12-12'
264Parameters:
265 one:
266 Default: foo
267 Type: String
268Resources:
269 NestedResource:
270 Type: OS::Heat::RandomString
271 Properties:
272 salt: {Ref: one}
273Outputs:
274 the_str:
275 Value: {'Fn::GetAtt': [NestedResource, value]}
276 something_else:
277 Value: just_a_string
278'''
279 content_change_tmpl = '''
280HeatTemplateFormatVersion: '2012-12-12'
281Parameters:
282 one:
283 Default: foo
284 Type: String
285Resources:
286 NestedResource:
287 Type: OS::Heat::RandomString
288 Properties:
289 salt: yum
290Outputs:
291 the_str:
292 Value: {'Fn::GetAtt': [NestedResource, value]}
293'''
294
295 EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
296 scenarios = [
297 ('no_changes', dict(template=main_template,
298 provider=initial_tmpl,
299 expect=NOCHANGE)),
300 ('main_tmpl_change', dict(template=main_template_2,
301 provider=initial_tmpl,
302 expect=UPDATE)),
303 ('provider_change', dict(template=main_template,
304 provider=content_change_tmpl,
305 expect=UPDATE)),
306 ('provider_props_change', dict(template=main_template,
307 provider=prop_change_tmpl,
308 expect=NOCHANGE)),
309 ('provider_attr_change', dict(template=main_template,
310 provider=attr_change_tmpl,
311 expect=NOCHANGE)),
312 ]
313
314 def setUp(self):
315 super(TemplateResourceUpdateTest, self).setUp()
316 self.client = self.orchestration_client
317
318 def test_template_resource_update_template_schema(self):
319 stack_identifier = self.stack_create(
320 template=self.main_template,
321 files={'the.yaml': self.initial_tmpl})
322 stack = self.client.stacks.get(stack_identifier)
323 initial_id = self._stack_output(stack, 'identifier')
324 initial_val = self._stack_output(stack, 'value')
325
326 self.update_stack(stack_identifier,
327 self.template,
328 files={'the.yaml': self.provider})
329 stack = self.client.stacks.get(stack_identifier)
330 self.assertEqual(initial_id,
331 self._stack_output(stack, 'identifier'))
332 if self.expect == self.NOCHANGE:
333 self.assertEqual(initial_val,
334 self._stack_output(stack, 'value'))
335 else:
336 self.assertNotEqual(initial_val,
337 self._stack_output(stack, 'value'))
338
339
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000340class TemplateResourceUpdateFailedTest(test.HeatIntegrationTest):
341 """Prove that we can do updates on a nested stack to fix a stack."""
342 main_template = '''
343HeatTemplateFormatVersion: '2012-12-12'
344Resources:
345 keypair:
346 Type: OS::Nova::KeyPair
347 Properties:
348 name: replace-this
349 save_private_key: false
350 server:
351 Type: server_fail.yaml
352 DependsOn: keypair
353'''
354 nested_templ = '''
355HeatTemplateFormatVersion: '2012-12-12'
356Resources:
357 RealRandom:
358 Type: OS::Heat::RandomString
359'''
360
361 def setUp(self):
362 super(TemplateResourceUpdateFailedTest, self).setUp()
363 self.client = self.orchestration_client
364 if self.conf.keypair_name:
365 self.keypair_name = self.conf.keypair_name
366 else:
367 self.keypair = self.create_keypair()
368 self.keypair_name = self.keypair.id
369
370 def test_update_on_failed_create(self):
Vikas Jainac6b02f2015-02-11 11:31:46 -0800371 # create a stack with "server" dependent on "keypair", but
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000372 # keypair fails, so "server" is not created properly.
373 # We then fix the template and it should succeed.
374 broken_templ = self.main_template.replace('replace-this',
375 self.keypair_name)
376 stack_identifier = self.stack_create(
377 template=broken_templ,
378 files={'server_fail.yaml': self.nested_templ},
379 expected_status='CREATE_FAILED')
380
381 fixed_templ = self.main_template.replace('replace-this',
382 test.rand_name())
383 self.update_stack(stack_identifier,
384 fixed_templ,
385 files={'server_fail.yaml': self.nested_templ})
386
387
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000388class TemplateResourceAdoptTest(test.HeatIntegrationTest):
389 """Prove that we can do template resource adopt/abandon."""
390
391 main_template = '''
392HeatTemplateFormatVersion: '2012-12-12'
393Resources:
394 the_nested:
395 Type: the.yaml
396 Properties:
397 one: my_name
398Outputs:
399 identifier:
400 Value: {Ref: the_nested}
401 value:
402 Value: {'Fn::GetAtt': [the_nested, the_str]}
403'''
404
405 nested_templ = '''
406HeatTemplateFormatVersion: '2012-12-12'
407Parameters:
408 one:
409 Default: foo
410 Type: String
411Resources:
412 RealRandom:
413 Type: OS::Heat::RandomString
414 Properties:
415 salt: {Ref: one}
416Outputs:
417 the_str:
418 Value: {'Fn::GetAtt': [RealRandom, value]}
419'''
420
421 def setUp(self):
422 super(TemplateResourceAdoptTest, self).setUp()
423 self.client = self.orchestration_client
424
425 def _yaml_to_json(self, yaml_templ):
426 return yaml.load(yaml_templ)
427
428 def test_abandon(self):
429 stack_name = self._stack_rand_name()
430 self.client.stacks.create(
431 stack_name=stack_name,
432 template=self.main_template,
433 files={'the.yaml': self.nested_templ},
434 disable_rollback=True,
435 )
436 stack = self.client.stacks.get(stack_name)
437 stack_identifier = '%s/%s' % (stack_name, stack.id)
438 self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
439
440 info = self.client.stacks.abandon(stack_id=stack_identifier)
441 self.assertEqual(self._yaml_to_json(self.main_template),
442 info['template'])
443 self.assertEqual(self._yaml_to_json(self.nested_templ),
444 info['resources']['the_nested']['template'])
445
446 def test_adopt(self):
447 data = {
448 'resources': {
449 'the_nested': {
450 "type": "the.yaml",
451 "resources": {
452 "RealRandom": {
453 "type": "OS::Heat::RandomString",
454 'resource_data': {'value': 'goopie'},
455 'resource_id': 'froggy'
456 }
457 }
458 }
459 },
460 "environment": {"parameters": {}},
461 "template": yaml.load(self.main_template)
462 }
463
464 stack_identifier = self.stack_adopt(
465 adopt_data=json.dumps(data),
466 files={'the.yaml': self.nested_templ})
467
468 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
469 stack = self.client.stacks.get(stack_identifier)
470 self.assertEqual('goopie', self._stack_output(stack, 'value'))
Angus Salkeld87601722015-01-05 14:57:27 +1000471
472
473class TemplateResourceCheckTest(test.HeatIntegrationTest):
474 """Prove that we can do template resource check."""
475
476 main_template = '''
477HeatTemplateFormatVersion: '2012-12-12'
478Resources:
479 the_nested:
480 Type: the.yaml
481 Properties:
482 one: my_name
483Outputs:
484 identifier:
485 Value: {Ref: the_nested}
486 value:
487 Value: {'Fn::GetAtt': [the_nested, the_str]}
488'''
489
490 nested_templ = '''
491HeatTemplateFormatVersion: '2012-12-12'
492Parameters:
493 one:
494 Default: foo
495 Type: String
496Resources:
497 RealRandom:
498 Type: OS::Heat::RandomString
499 Properties:
500 salt: {Ref: one}
501Outputs:
502 the_str:
503 Value: {'Fn::GetAtt': [RealRandom, value]}
504'''
505
506 def setUp(self):
507 super(TemplateResourceCheckTest, self).setUp()
508 self.client = self.orchestration_client
509
510 def test_check(self):
511 stack_name = self._stack_rand_name()
512 self.client.stacks.create(
513 stack_name=stack_name,
514 template=self.main_template,
515 files={'the.yaml': self.nested_templ},
516 disable_rollback=True,
517 )
518 stack = self.client.stacks.get(stack_name)
519 stack_identifier = '%s/%s' % (stack_name, stack.id)
520 self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
521
522 self.client.actions.check(stack_id=stack_identifier)
523 self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')