Move template resource tests to functional
Note: this enables adopt and abandon in the gate so we can test this feature. Part of blueprint decouple-nested Change-Id: Id1e63fc4b4e609f699d718b8569c25d246e83faa
This commit is contained in:
parent
d193b74eb6
commit
5cab70e5da
@ -15,7 +15,6 @@ import json
|
||||
import os
|
||||
import six
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
import mock
|
||||
|
||||
@ -703,413 +702,6 @@ class ProviderTemplateTest(common.HeatTestCase):
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class NestedProvider(common.HeatTestCase):
|
||||
"""Prove that we can use the registry in a nested provider."""
|
||||
|
||||
def setUp(self):
|
||||
super(NestedProvider, self).setUp()
|
||||
utils.setup_dummy_db()
|
||||
|
||||
def test_nested_env(self):
|
||||
main_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: My::NestedSecret
|
||||
outputs:
|
||||
secret1:
|
||||
value: { get_attr: [secret1, value] }
|
||||
'''
|
||||
|
||||
nested_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: My::Secret
|
||||
outputs:
|
||||
value:
|
||||
value: { get_attr: [secret2, value] }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"My::Secret": "OS::Heat::RandomString"
|
||||
"My::NestedSecret": nested.yaml
|
||||
'''
|
||||
|
||||
env = environment.Environment()
|
||||
env.load(yaml.load(env_templ))
|
||||
templ = parser.Template(template_format.parse(main_templ),
|
||||
files={'nested.yaml': nested_templ})
|
||||
stack = parser.Stack(utils.dummy_context(),
|
||||
utils.random_name(),
|
||||
templ, env=env)
|
||||
stack.store()
|
||||
stack.create()
|
||||
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
|
||||
|
||||
def test_no_infinite_recursion(self):
|
||||
"""Prove that we can override a python resource.
|
||||
|
||||
And use that resource within the template resource.
|
||||
"""
|
||||
|
||||
main_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
secret1:
|
||||
value: { get_attr: [secret1, value] }
|
||||
'''
|
||||
|
||||
nested_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
value:
|
||||
value: { get_attr: [secret2, value] }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"OS::Heat::RandomString": nested.yaml
|
||||
'''
|
||||
|
||||
env = environment.Environment()
|
||||
env.load(yaml.load(env_templ))
|
||||
templ = parser.Template(template_format.parse(main_templ),
|
||||
files={'nested.yaml': nested_templ})
|
||||
stack = parser.Stack(utils.dummy_context(),
|
||||
utils.random_name(),
|
||||
templ, env=env)
|
||||
stack.store()
|
||||
stack.create()
|
||||
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
|
||||
|
||||
|
||||
class NestedAttributes(common.HeatTestCase):
|
||||
"""Prove that we can use the template resource references."""
|
||||
|
||||
main_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret2:
|
||||
type: My::NestedSecret
|
||||
outputs:
|
||||
old_way:
|
||||
value: { get_attr: [secret2, nested_str]}
|
||||
test_attr1:
|
||||
value: { get_attr: [secret2, resource.secret1, value]}
|
||||
test_attr2:
|
||||
value: { get_attr: [secret2, resource.secret1.value]}
|
||||
test_ref:
|
||||
value: { get_resource: secret2 }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"My::NestedSecret": nested.yaml
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
super(NestedAttributes, self).setUp()
|
||||
utils.setup_dummy_db()
|
||||
|
||||
def _create_dummy_stack(self, nested_templ):
|
||||
env = environment.Environment()
|
||||
env.load(yaml.load(self.env_templ))
|
||||
templ = parser.Template(template_format.parse(self.main_templ),
|
||||
files={'nested.yaml': nested_templ})
|
||||
stack = parser.Stack(utils.dummy_context(),
|
||||
utils.random_name(),
|
||||
templ, env=env)
|
||||
stack.store()
|
||||
stack.create()
|
||||
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
|
||||
return stack
|
||||
|
||||
def test_stack_ref(self):
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
'''
|
||||
stack = self._create_dummy_stack(nested_templ)
|
||||
test_ref = stack.output('test_ref')
|
||||
self.assertIn('arn:openstack:heat:', test_ref)
|
||||
|
||||
def test_transparent_ref(self):
|
||||
"""With the addition of OS::stack_id we can now use the nested resource
|
||||
more transparently.
|
||||
"""
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
OS::stack_id:
|
||||
value: {get_resource: secret1}
|
||||
nested_str:
|
||||
value: {get_attr: [secret1, value]}
|
||||
'''
|
||||
stack = self._create_dummy_stack(nested_templ)
|
||||
test_ref = stack.output('test_ref')
|
||||
test_attr = stack.output('old_way')
|
||||
|
||||
self.assertNotIn('arn:openstack:heat', test_ref)
|
||||
self.assertEqual(test_attr, test_ref)
|
||||
|
||||
def test_nested_attributes(self):
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
nested_str:
|
||||
value: {get_attr: [secret1, value]}
|
||||
'''
|
||||
stack = self._create_dummy_stack(nested_templ)
|
||||
old_way = stack.output('old_way')
|
||||
test_attr1 = stack.output('test_attr1')
|
||||
test_attr2 = stack.output('test_attr2')
|
||||
|
||||
self.assertEqual(old_way, test_attr1)
|
||||
self.assertEqual(old_way, test_attr2)
|
||||
|
||||
|
||||
class ProviderTemplateUpdateTest(common.HeatTestCase):
|
||||
main_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: my_name
|
||||
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
main_template_2 = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: updated_name
|
||||
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
initial_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
prop_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: yikes
|
||||
Type: String
|
||||
two:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
attr_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
something_else:
|
||||
Value: just_a_string
|
||||
'''
|
||||
content_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: yum
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
|
||||
EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
|
||||
scenarios = [
|
||||
('no_changes', dict(template=main_template,
|
||||
provider=initial_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
('main_tmpl_change', dict(template=main_template_2,
|
||||
provider=initial_tmpl,
|
||||
expect=UPDATE)),
|
||||
('provider_change', dict(template=main_template,
|
||||
provider=content_change_tmpl,
|
||||
expect=UPDATE)),
|
||||
('provider_props_change', dict(template=main_template,
|
||||
provider=prop_change_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
('provider_attr_change', dict(template=main_template,
|
||||
provider=attr_change_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(ProviderTemplateUpdateTest, self).setUp()
|
||||
self.ctx = utils.dummy_context('test_username', 'aaaa', 'password')
|
||||
|
||||
def create_stack(self):
|
||||
t = template_format.parse(self.main_template)
|
||||
tmpl = parser.Template(t, files={'the.yaml': self.initial_tmpl})
|
||||
stack = parser.Stack(self.ctx, utils.random_name(), tmpl)
|
||||
stack.store()
|
||||
stack.create()
|
||||
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
|
||||
return stack
|
||||
|
||||
def test_template_resource_update_template_schema(self):
|
||||
stack = self.create_stack()
|
||||
self.stack = stack
|
||||
initial_id = stack.output('identifier')
|
||||
initial_val = stack.output('value')
|
||||
tmpl = parser.Template(template_format.parse(self.template),
|
||||
files={'the.yaml': self.provider})
|
||||
updated_stack = parser.Stack(self.ctx, stack.name, tmpl)
|
||||
stack.update(updated_stack)
|
||||
self.assertEqual(('UPDATE', 'COMPLETE'), stack.state)
|
||||
self.assertEqual(initial_id,
|
||||
stack.output('identifier'))
|
||||
if self.expect == self.NOCHANGE:
|
||||
self.assertEqual(initial_val,
|
||||
stack.output('value'))
|
||||
else:
|
||||
self.assertNotEqual(initial_val,
|
||||
stack.output('value'))
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class ProviderTemplateAdoptTest(common.HeatTestCase):
|
||||
main_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: my_name
|
||||
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
nested_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
RealRandom:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [RealRandom, value]}
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
super(ProviderTemplateAdoptTest, self).setUp()
|
||||
self.ctx = utils.dummy_context('test_username', 'aaaa', 'password')
|
||||
|
||||
def _yaml_to_json(self, yaml_templ):
|
||||
return yaml.load(yaml_templ)
|
||||
|
||||
def test_abandon(self):
|
||||
t = template_format.parse(self.main_template)
|
||||
tmpl = parser.Template(t, files={'the.yaml': self.nested_tmpl})
|
||||
stack = parser.Stack(self.ctx, utils.random_name(), tmpl)
|
||||
stack.store()
|
||||
stack.create()
|
||||
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
|
||||
info = stack.prepare_abandon()
|
||||
self.assertEqual(self._yaml_to_json(self.main_template),
|
||||
info['template'])
|
||||
self.assertEqual(self._yaml_to_json(self.nested_tmpl),
|
||||
info['resources']['the_nested']['template'])
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_adopt(self):
|
||||
data = {'resources': {u'the_nested': {
|
||||
'resources': {u'RealRandom': {
|
||||
'resource_data': {
|
||||
u'value': u'N8hE5C7ijdGn4RwnuygbAokGHnTq4cFJ'},
|
||||
'resource_id': 'N8hE5C7ijdGn4RwnuygbAokGHnTq4cFJ'}}}}}
|
||||
|
||||
t = template_format.parse(self.main_template)
|
||||
tmpl = parser.Template(t, files={'the.yaml': self.nested_tmpl})
|
||||
stack = parser.Stack(self.ctx, utils.random_name(), tmpl,
|
||||
adopt_stack_data=data)
|
||||
self.stack = stack
|
||||
stack.store()
|
||||
stack.adopt()
|
||||
|
||||
self.assertEqual(('ADOPT', 'COMPLETE'), stack.state)
|
||||
nested_res = data['resources']['the_nested']['resources']
|
||||
self.assertEqual(nested_res['RealRandom']['resource_data']['value'],
|
||||
stack.output('value'))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class TemplateResourceCrudTest(common.HeatTestCase):
|
||||
provider = {
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
|
@ -326,6 +326,22 @@ class HeatIntegrationTest(testscenarios.WithScenarios,
|
||||
)
|
||||
self._wait_for_stack_status(stack_identifier, 'UPDATE_COMPLETE')
|
||||
|
||||
def assert_resource_is_a_stack(self, stack_identifier, res_name):
|
||||
rsrc = self.client.resources.get(stack_identifier, res_name)
|
||||
nested_link = [l for l in rsrc.links if l['rel'] == 'nested']
|
||||
nested_href = nested_link[0]['href']
|
||||
nested_id = nested_href.split('/')[-1]
|
||||
nested_identifier = '/'.join(nested_href.split('/')[-2:])
|
||||
self.assertEqual(rsrc.physical_resource_id, nested_id)
|
||||
|
||||
nested_stack = self.client.stacks.get(nested_id)
|
||||
nested_identifier2 = '%s/%s' % (nested_stack.stack_name,
|
||||
nested_stack.id)
|
||||
self.assertEqual(nested_identifier, nested_identifier2)
|
||||
parent_id = stack_identifier.split("/")[-1]
|
||||
self.assertEqual(parent_id, nested_stack.parent)
|
||||
return nested_identifier
|
||||
|
||||
def list_resources(self, stack_identifier):
|
||||
resources = self.client.resources.list(stack_identifier)
|
||||
return dict((r.resource_name, r.resource_type) for r in resources)
|
||||
@ -351,3 +367,25 @@ class HeatIntegrationTest(testscenarios.WithScenarios,
|
||||
stack_identifier = '%s/%s' % (name, stack.id)
|
||||
self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
return stack_identifier
|
||||
|
||||
def stack_adopt(self, stack_name=None, files=None,
|
||||
parameters=None, environment=None, adopt_data=None,
|
||||
wait_for_status='ADOPT_COMPLETE'):
|
||||
name = stack_name or self._stack_rand_name()
|
||||
templ_files = files or {}
|
||||
params = parameters or {}
|
||||
env = environment or {}
|
||||
self.client.stacks.create(
|
||||
stack_name=name,
|
||||
files=templ_files,
|
||||
disable_rollback=True,
|
||||
parameters=params,
|
||||
environment=env,
|
||||
adopt_stack_data=adopt_data,
|
||||
)
|
||||
self.addCleanup(self.client.stacks.delete, name)
|
||||
|
||||
stack = self.client.stacks.get(name)
|
||||
stack_identifier = '%s/%s' % (name, stack.id)
|
||||
self._wait_for_stack_status(stack_identifier, wait_for_status)
|
||||
return stack_identifier
|
||||
|
421
heat_integrationtests/functional/test_template_resource.py
Normal file
421
heat_integrationtests/functional/test_template_resource.py
Normal file
@ -0,0 +1,421 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import yaml
|
||||
|
||||
from heat_integrationtests.common import test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TemplateResourceTest(test.HeatIntegrationTest):
|
||||
"""Prove that we can use the registry in a nested provider."""
|
||||
def setUp(self):
|
||||
super(TemplateResourceTest, self).setUp()
|
||||
self.client = self.orchestration_client
|
||||
|
||||
def test_nested_env(self):
|
||||
main_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret1:
|
||||
type: My::NestedSecret
|
||||
outputs:
|
||||
secret-out:
|
||||
value: { get_attr: [secret1, value] }
|
||||
'''
|
||||
|
||||
nested_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: My::Secret
|
||||
outputs:
|
||||
value:
|
||||
value: { get_attr: [secret2, value] }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"My::Secret": "OS::Heat::RandomString"
|
||||
"My::NestedSecret": nested.yaml
|
||||
'''
|
||||
|
||||
stack_identifier = self.stack_create(
|
||||
template=main_templ,
|
||||
files={'nested.yaml': nested_templ},
|
||||
environment=env_templ)
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'secret1')
|
||||
|
||||
def test_no_infinite_recursion(self):
|
||||
"""Prove that we can override a python resource.
|
||||
|
||||
And use that resource within the template resource.
|
||||
"""
|
||||
|
||||
main_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
secret-out:
|
||||
value: { get_attr: [secret1, value] }
|
||||
'''
|
||||
|
||||
nested_templ = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
secret2:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
value:
|
||||
value: { get_attr: [secret2, value] }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"OS::Heat::RandomString": nested.yaml
|
||||
'''
|
||||
|
||||
stack_identifier = self.stack_create(
|
||||
template=main_templ,
|
||||
files={'nested.yaml': nested_templ},
|
||||
environment=env_templ)
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'secret1')
|
||||
|
||||
|
||||
class NestedAttributesTest(test.HeatIntegrationTest):
|
||||
"""Prove that we can use the template resource references."""
|
||||
|
||||
main_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret2:
|
||||
type: My::NestedSecret
|
||||
outputs:
|
||||
old_way:
|
||||
value: { get_attr: [secret2, nested_str]}
|
||||
test_attr1:
|
||||
value: { get_attr: [secret2, resource.secret1, value]}
|
||||
test_attr2:
|
||||
value: { get_attr: [secret2, resource.secret1.value]}
|
||||
test_ref:
|
||||
value: { get_resource: secret2 }
|
||||
'''
|
||||
|
||||
env_templ = '''
|
||||
resource_registry:
|
||||
"My::NestedSecret": nested.yaml
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
super(NestedAttributesTest, self).setUp()
|
||||
self.client = self.orchestration_client
|
||||
|
||||
def test_stack_ref(self):
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
'''
|
||||
stack_identifier = self.stack_create(
|
||||
template=self.main_templ,
|
||||
files={'nested.yaml': nested_templ},
|
||||
environment=self.env_templ)
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
test_ref = self._stack_output(stack, 'test_ref')
|
||||
self.assertIn('arn:openstack:heat:', test_ref)
|
||||
|
||||
def test_transparent_ref(self):
|
||||
"""With the addition of OS::stack_id we can now use the nested resource
|
||||
more transparently.
|
||||
"""
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
OS::stack_id:
|
||||
value: {get_resource: secret1}
|
||||
nested_str:
|
||||
value: {get_attr: [secret1, value]}
|
||||
'''
|
||||
stack_identifier = self.stack_create(
|
||||
template=self.main_templ,
|
||||
files={'nested.yaml': nested_templ},
|
||||
environment=self.env_templ)
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
test_ref = self._stack_output(stack, 'test_ref')
|
||||
test_attr = self._stack_output(stack, 'old_way')
|
||||
|
||||
self.assertNotIn('arn:openstack:heat', test_ref)
|
||||
self.assertEqual(test_attr, test_ref)
|
||||
|
||||
def test_nested_attributes(self):
|
||||
nested_templ = '''
|
||||
heat_template_version: 2014-10-16
|
||||
resources:
|
||||
secret1:
|
||||
type: OS::Heat::RandomString
|
||||
outputs:
|
||||
nested_str:
|
||||
value: {get_attr: [secret1, value]}
|
||||
'''
|
||||
stack_identifier = self.stack_create(
|
||||
template=self.main_templ,
|
||||
files={'nested.yaml': nested_templ},
|
||||
environment=self.env_templ)
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
old_way = self._stack_output(stack, 'old_way')
|
||||
test_attr1 = self._stack_output(stack, 'test_attr1')
|
||||
test_attr2 = self._stack_output(stack, 'test_attr2')
|
||||
|
||||
self.assertEqual(old_way, test_attr1)
|
||||
self.assertEqual(old_way, test_attr2)
|
||||
|
||||
|
||||
class TemplateResourceUpdateTest(test.HeatIntegrationTest):
|
||||
"""Prove that we can do template resource updates."""
|
||||
|
||||
main_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: my_name
|
||||
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
main_template_2 = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: updated_name
|
||||
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
initial_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
prop_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: yikes
|
||||
Type: String
|
||||
two:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
attr_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
something_else:
|
||||
Value: just_a_string
|
||||
'''
|
||||
content_change_tmpl = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: yum
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [NestedResource, value]}
|
||||
'''
|
||||
|
||||
EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
|
||||
scenarios = [
|
||||
('no_changes', dict(template=main_template,
|
||||
provider=initial_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
('main_tmpl_change', dict(template=main_template_2,
|
||||
provider=initial_tmpl,
|
||||
expect=UPDATE)),
|
||||
('provider_change', dict(template=main_template,
|
||||
provider=content_change_tmpl,
|
||||
expect=UPDATE)),
|
||||
('provider_props_change', dict(template=main_template,
|
||||
provider=prop_change_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
('provider_attr_change', dict(template=main_template,
|
||||
provider=attr_change_tmpl,
|
||||
expect=NOCHANGE)),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TemplateResourceUpdateTest, self).setUp()
|
||||
self.client = self.orchestration_client
|
||||
|
||||
def test_template_resource_update_template_schema(self):
|
||||
stack_identifier = self.stack_create(
|
||||
template=self.main_template,
|
||||
files={'the.yaml': self.initial_tmpl})
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
initial_id = self._stack_output(stack, 'identifier')
|
||||
initial_val = self._stack_output(stack, 'value')
|
||||
|
||||
self.update_stack(stack_identifier,
|
||||
self.template,
|
||||
files={'the.yaml': self.provider})
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
self.assertEqual(initial_id,
|
||||
self._stack_output(stack, 'identifier'))
|
||||
if self.expect == self.NOCHANGE:
|
||||
self.assertEqual(initial_val,
|
||||
self._stack_output(stack, 'value'))
|
||||
else:
|
||||
self.assertNotEqual(initial_val,
|
||||
self._stack_output(stack, 'value'))
|
||||
|
||||
|
||||
class TemplateResourceAdoptTest(test.HeatIntegrationTest):
|
||||
"""Prove that we can do template resource adopt/abandon."""
|
||||
|
||||
main_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: my_name
|
||||
Outputs:
|
||||
identifier:
|
||||
Value: {Ref: the_nested}
|
||||
value:
|
||||
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
||||
'''
|
||||
|
||||
nested_templ = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Default: foo
|
||||
Type: String
|
||||
Resources:
|
||||
RealRandom:
|
||||
Type: OS::Heat::RandomString
|
||||
Properties:
|
||||
salt: {Ref: one}
|
||||
Outputs:
|
||||
the_str:
|
||||
Value: {'Fn::GetAtt': [RealRandom, value]}
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
super(TemplateResourceAdoptTest, self).setUp()
|
||||
self.client = self.orchestration_client
|
||||
|
||||
def _yaml_to_json(self, yaml_templ):
|
||||
return yaml.load(yaml_templ)
|
||||
|
||||
def test_abandon(self):
|
||||
stack_name = self._stack_rand_name()
|
||||
self.client.stacks.create(
|
||||
stack_name=stack_name,
|
||||
template=self.main_template,
|
||||
files={'the.yaml': self.nested_templ},
|
||||
disable_rollback=True,
|
||||
)
|
||||
stack = self.client.stacks.get(stack_name)
|
||||
stack_identifier = '%s/%s' % (stack_name, stack.id)
|
||||
self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
info = self.client.stacks.abandon(stack_id=stack_identifier)
|
||||
self.assertEqual(self._yaml_to_json(self.main_template),
|
||||
info['template'])
|
||||
self.assertEqual(self._yaml_to_json(self.nested_templ),
|
||||
info['resources']['the_nested']['template'])
|
||||
|
||||
def test_adopt(self):
|
||||
data = {
|
||||
'resources': {
|
||||
'the_nested': {
|
||||
"type": "the.yaml",
|
||||
"resources": {
|
||||
"RealRandom": {
|
||||
"type": "OS::Heat::RandomString",
|
||||
'resource_data': {'value': 'goopie'},
|
||||
'resource_id': 'froggy'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": {"parameters": {}},
|
||||
"template": yaml.load(self.main_template)
|
||||
}
|
||||
|
||||
stack_identifier = self.stack_adopt(
|
||||
adopt_data=json.dumps(data),
|
||||
files={'the.yaml': self.nested_templ})
|
||||
|
||||
self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
|
||||
stack = self.client.stacks.get(stack_identifier)
|
||||
self.assertEqual('goopie', self._stack_output(stack, 'value'))
|
@ -101,13 +101,9 @@ resources:
|
||||
self.list_resources(stack_identifier))
|
||||
|
||||
# Prove the resource is backed by a nested stack, save the ID
|
||||
rsrc = self.client.resources.get(stack_identifier, 'random1')
|
||||
nested_link = [l for l in rsrc.links if l['rel'] == 'nested']
|
||||
nested_href = nested_link[0]['href']
|
||||
nested_id = nested_href.split('/')[-1]
|
||||
nested_identifier = '/'.join(nested_href.split('/')[-2:])
|
||||
physical_resource_id = rsrc.physical_resource_id
|
||||
self.assertEqual(physical_resource_id, nested_id)
|
||||
nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
|
||||
'random1')
|
||||
nested_id = nested_identifier.split('/')[-1]
|
||||
|
||||
# Then check the expected resources are in the nested stack
|
||||
nested_resources = {'random1': 'OS::Heat::RandomString'}
|
||||
@ -153,14 +149,8 @@ resources:
|
||||
self.list_resources(stack_identifier))
|
||||
|
||||
# Prove the resource is backed by a nested stack, save the ID
|
||||
rsrc = self.client.resources.get(stack_identifier, 'random_group')
|
||||
physical_resource_id = rsrc.physical_resource_id
|
||||
|
||||
nested_stack = self.client.stacks.get(physical_resource_id)
|
||||
nested_identifier = '%s/%s' % (nested_stack.stack_name,
|
||||
nested_stack.id)
|
||||
parent_id = stack_identifier.split("/")[-1]
|
||||
self.assertEqual(parent_id, nested_stack.parent)
|
||||
nested_identifier = self.assert_resource_is_a_stack(stack_identifier,
|
||||
'random_group')
|
||||
|
||||
# Then check the expected resources are in the nested stack
|
||||
nested_resources = {'0': 'My::RandomString',
|
||||
|
@ -14,3 +14,5 @@
|
||||
|
||||
# This script is executed inside pre_test_hook function in devstack gate.
|
||||
|
||||
localrc_path=$BASE/new/devstack/localrc
|
||||
echo "HEAT_ENABLE_ADOPT_ABANDON=True" >> $localrc_path
|
||||
|
Loading…
x
Reference in New Issue
Block a user