blob: f5f366d0f887b3886fed0cc21212e56b6a7bc1ae [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
Sergey Krayneva265c132015-02-13 03:51:03 -0500364 self.assign_keypair()
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000365
366 def test_update_on_failed_create(self):
Vikas Jainac6b02f2015-02-11 11:31:46 -0800367 # create a stack with "server" dependent on "keypair", but
Angus Salkeld5a3e1dd2015-02-03 15:31:47 +1000368 # keypair fails, so "server" is not created properly.
369 # We then fix the template and it should succeed.
370 broken_templ = self.main_template.replace('replace-this',
371 self.keypair_name)
372 stack_identifier = self.stack_create(
373 template=broken_templ,
374 files={'server_fail.yaml': self.nested_templ},
375 expected_status='CREATE_FAILED')
376
377 fixed_templ = self.main_template.replace('replace-this',
378 test.rand_name())
379 self.update_stack(stack_identifier,
380 fixed_templ,
381 files={'server_fail.yaml': self.nested_templ})
382
383
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000384class TemplateResourceAdoptTest(test.HeatIntegrationTest):
385 """Prove that we can do template resource adopt/abandon."""
386
387 main_template = '''
388HeatTemplateFormatVersion: '2012-12-12'
389Resources:
390 the_nested:
391 Type: the.yaml
392 Properties:
393 one: my_name
394Outputs:
395 identifier:
396 Value: {Ref: the_nested}
397 value:
398 Value: {'Fn::GetAtt': [the_nested, the_str]}
399'''
400
401 nested_templ = '''
402HeatTemplateFormatVersion: '2012-12-12'
403Parameters:
404 one:
405 Default: foo
406 Type: String
407Resources:
408 RealRandom:
409 Type: OS::Heat::RandomString
410 Properties:
411 salt: {Ref: one}
412Outputs:
413 the_str:
414 Value: {'Fn::GetAtt': [RealRandom, value]}
415'''
416
417 def setUp(self):
418 super(TemplateResourceAdoptTest, self).setUp()
419 self.client = self.orchestration_client
420
421 def _yaml_to_json(self, yaml_templ):
422 return yaml.load(yaml_templ)
423
424 def test_abandon(self):
425 stack_name = self._stack_rand_name()
426 self.client.stacks.create(
427 stack_name=stack_name,
428 template=self.main_template,
429 files={'the.yaml': self.nested_templ},
430 disable_rollback=True,
431 )
432 stack = self.client.stacks.get(stack_name)
433 stack_identifier = '%s/%s' % (stack_name, stack.id)
434 self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
435
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530436 info = self.stack_abandon(stack_id=stack_identifier)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000437 self.assertEqual(self._yaml_to_json(self.main_template),
438 info['template'])
439 self.assertEqual(self._yaml_to_json(self.nested_templ),
440 info['resources']['the_nested']['template'])
441
442 def test_adopt(self):
443 data = {
444 'resources': {
445 'the_nested': {
446 "type": "the.yaml",
447 "resources": {
448 "RealRandom": {
449 "type": "OS::Heat::RandomString",
450 'resource_data': {'value': 'goopie'},
451 'resource_id': 'froggy'
452 }
453 }
454 }
455 },
456 "environment": {"parameters": {}},
457 "template": yaml.load(self.main_template)
458 }
459
460 stack_identifier = self.stack_adopt(
461 adopt_data=json.dumps(data),
462 files={'the.yaml': self.nested_templ})
463
464 self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
465 stack = self.client.stacks.get(stack_identifier)
466 self.assertEqual('goopie', self._stack_output(stack, 'value'))
Angus Salkeld87601722015-01-05 14:57:27 +1000467
468
469class TemplateResourceCheckTest(test.HeatIntegrationTest):
470 """Prove that we can do template resource check."""
471
472 main_template = '''
473HeatTemplateFormatVersion: '2012-12-12'
474Resources:
475 the_nested:
476 Type: the.yaml
477 Properties:
478 one: my_name
479Outputs:
480 identifier:
481 Value: {Ref: the_nested}
482 value:
483 Value: {'Fn::GetAtt': [the_nested, the_str]}
484'''
485
486 nested_templ = '''
487HeatTemplateFormatVersion: '2012-12-12'
488Parameters:
489 one:
490 Default: foo
491 Type: String
492Resources:
493 RealRandom:
494 Type: OS::Heat::RandomString
495 Properties:
496 salt: {Ref: one}
497Outputs:
498 the_str:
499 Value: {'Fn::GetAtt': [RealRandom, value]}
500'''
501
502 def setUp(self):
503 super(TemplateResourceCheckTest, self).setUp()
504 self.client = self.orchestration_client
505
506 def test_check(self):
507 stack_name = self._stack_rand_name()
508 self.client.stacks.create(
509 stack_name=stack_name,
510 template=self.main_template,
511 files={'the.yaml': self.nested_templ},
512 disable_rollback=True,
513 )
514 stack = self.client.stacks.get(stack_name)
515 stack_identifier = '%s/%s' % (stack_name, stack.id)
516 self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
517
518 self.client.actions.check(stack_id=stack_identifier)
519 self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')