Do static template validation for nested stacks
Currently we don't fail fast, even if there is an error in the nested template which should fail validation. This change to recurse and do static template validation for nested stack resources. Change-Id: I572ded640582419e0888e4b9f8eed3a3432d6121 Closes-Bug: #1388140 Closes-Bug: #1389104
This commit is contained in:
parent
1cb2efc172
commit
c31c34f8df
|
@ -48,6 +48,18 @@ class StackResource(resource.Resource):
|
|||
super(StackResource, self).__init__(name, json_snippet, stack)
|
||||
self._nested = None
|
||||
|
||||
def validate(self):
|
||||
super(StackResource, self).validate()
|
||||
try:
|
||||
nested_stack = self._parse_nested_stack(
|
||||
self.stack.name,
|
||||
self.child_template(),
|
||||
self.child_params())
|
||||
nested_stack.validate()
|
||||
except Exception as ex:
|
||||
msg = _("Failed to validate: %s") % ex
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def _outputs_to_attribs(self, json_snippet):
|
||||
outputs = json_snippet.get('Outputs')
|
||||
if not self.attributes and outputs:
|
||||
|
|
|
@ -179,6 +179,9 @@ class TestGroupAdjust(common.HeatTestCase):
|
|||
t = template_format.parse(as_template)
|
||||
stack = utils.parse_stack(t, params=inline_templates.as_params)
|
||||
self.group = stack['WebServerGroup']
|
||||
self.stub_ImageConstraint_validate()
|
||||
self.stub_FlavorConstraint_validate()
|
||||
self.stub_SnapshotConstraint_validate()
|
||||
self.assertIsNone(self.group.validate())
|
||||
|
||||
def test_scaling_policy_cooldown_toosoon(self):
|
||||
|
|
|
@ -508,7 +508,7 @@ class AutoScalingTest(common.HeatTestCase):
|
|||
u'AvailabilityZones': ['abc', 'xyz']}}
|
||||
|
||||
self.m.StubOutWithMock(short_id, 'generate_id')
|
||||
short_id.generate_id().AndReturn('aaaabbbbcccc')
|
||||
short_id.generate_id().MultipleTimes().AndReturn('aaaabbbbcccc')
|
||||
|
||||
now = timeutils.utcnow()
|
||||
self._stub_meta_expected(now, 'ExactCapacity : 1')
|
||||
|
@ -556,7 +556,7 @@ class AutoScalingTest(common.HeatTestCase):
|
|||
}
|
||||
|
||||
self.m.StubOutWithMock(short_id, 'generate_id')
|
||||
short_id.generate_id().AndReturn('aaaabbbbcccc')
|
||||
short_id.generate_id().MultipleTimes().AndReturn('aaaabbbbcccc')
|
||||
|
||||
self.m.StubOutWithMock(neutron_lb.LoadBalancer, 'handle_update')
|
||||
neutron_lb.LoadBalancer.handle_update(expected,
|
||||
|
|
|
@ -75,7 +75,7 @@ class InstanceGroupTest(common.HeatTestCase):
|
|||
instead of instance.Instance.
|
||||
"""
|
||||
self.m.StubOutWithMock(parser.Stack, 'validate')
|
||||
parser.Stack.validate()
|
||||
parser.Stack.validate().MultipleTimes().AndReturn(None)
|
||||
self.stub_KeypairConstraint_validate()
|
||||
self.stub_ImageConstraint_validate()
|
||||
self.stub_FlavorConstraint_validate()
|
||||
|
@ -244,7 +244,7 @@ class InstanceGroupTest(common.HeatTestCase):
|
|||
stack = utils.parse_stack(t)
|
||||
|
||||
self.m.StubOutWithMock(parser.Stack, 'validate')
|
||||
parser.Stack.validate()
|
||||
parser.Stack.validate().MultipleTimes().AndReturn(None)
|
||||
self.stub_ImageConstraint_validate()
|
||||
self.stub_KeypairConstraint_validate()
|
||||
self.stub_FlavorConstraint_validate()
|
||||
|
@ -327,6 +327,7 @@ class InstanceGroupTest(common.HeatTestCase):
|
|||
stack = utils.parse_stack(t)
|
||||
|
||||
self._stub_create(2)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.create_resource(t, stack, 'JobServerConfig')
|
||||
rsrc = self.create_resource(t, stack, 'JobServerGroup')
|
||||
|
|
|
@ -438,6 +438,8 @@ Resources:
|
|||
urlfetch.get(
|
||||
'https://server.test/depth3.template').AndReturn(
|
||||
self.nested_template)
|
||||
self.m.StubOutWithMock(parser.Stack, 'validate')
|
||||
parser.Stack.validate().MultipleTimes().AndReturn(None)
|
||||
self.m.ReplayAll()
|
||||
self.create_stack(root_template)
|
||||
self.m.VerifyAll()
|
||||
|
|
|
@ -161,6 +161,7 @@ class ResourceGroupTest(common.HeatTestCase):
|
|||
AttributeResource = generic_resource.ResourceWithComplexAttributes
|
||||
resource._register_class("dummyattr.resource",
|
||||
AttributeResource)
|
||||
self.m.StubOutWithMock(stackm.Stack, 'validate')
|
||||
|
||||
def test_build_resource_definition(self):
|
||||
stack = utils.parse_stack(template)
|
||||
|
|
|
@ -70,6 +70,63 @@ simple_template = '''
|
|||
}
|
||||
'''
|
||||
|
||||
main_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
volume_server:
|
||||
type: nested.yaml
|
||||
'''
|
||||
|
||||
my_wrong_nested_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
server:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
image: F17-x86_64-gold
|
||||
flavor: m1.small
|
||||
volume:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
size: 10
|
||||
description: Volume for stack
|
||||
volume_attachment:
|
||||
type: OS::Cinder::VolumeAttachment
|
||||
properties:
|
||||
volume_id: { get_resource: volume }
|
||||
instance_uuid: { get_resource: instance }
|
||||
'''
|
||||
|
||||
resource_group_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
my_resource_group:
|
||||
type: OS::Heat::ResourceGroup
|
||||
properties:
|
||||
resource_def:
|
||||
type: idontexist
|
||||
'''
|
||||
|
||||
heat_autoscaling_group_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
my_autoscaling_group:
|
||||
type: OS::Heat::AutoScalingGroup
|
||||
properties:
|
||||
resource:
|
||||
type: idontexist
|
||||
desired_capacity: 2
|
||||
max_size: 4
|
||||
min_size: 1
|
||||
'''
|
||||
|
||||
nova_server_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
group_server:
|
||||
type: idontexist
|
||||
'''
|
||||
|
||||
|
||||
class MyStackResource(stack_resource.StackResource,
|
||||
generic_rsrc.GenericResource):
|
||||
|
@ -288,6 +345,62 @@ class StackResourceTest(common.HeatTestCase):
|
|||
self.assertFalse(nested_stack.validate.called)
|
||||
self.assertTrue(nested_stack.preview_resources.called)
|
||||
|
||||
def test_validate_error_reference(self):
|
||||
stack_name = 'validate_error_reference'
|
||||
tmpl = template_format.parse(main_template)
|
||||
files = {'nested.yaml': my_wrong_nested_template}
|
||||
stack = parser.Stack(utils.dummy_context(), stack_name,
|
||||
templatem.Template(tmpl, files=files))
|
||||
rsrc = stack['volume_server']
|
||||
raise_exc_msg = ('The specified reference "instance" ('
|
||||
'in volume_attachment.Properties.instance_uuid) '
|
||||
'is incorrect')
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertIn(raise_exc_msg, six.text_type(exc))
|
||||
|
||||
def _test_validate_unknown_resource_type(self, stack_name,
|
||||
tmpl, resource_name):
|
||||
raise_exc_msg = ('Unknown resource Type : idontexist')
|
||||
stack = parser.Stack(utils.dummy_context(), stack_name, tmpl)
|
||||
rsrc = stack[resource_name]
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertIn(raise_exc_msg, six.text_type(exc))
|
||||
|
||||
def test_validate_resource_group(self):
|
||||
# test validate without nested template
|
||||
stack_name = 'validate_resource_group_template'
|
||||
t = template_format.parse(resource_group_template)
|
||||
tmpl = templatem.Template(t)
|
||||
self._test_validate_unknown_resource_type(stack_name, tmpl,
|
||||
'my_resource_group')
|
||||
|
||||
# validate with nested template
|
||||
res_prop = t['resources']['my_resource_group']['properties']
|
||||
res_prop['resource_def']['type'] = 'nova_server.yaml'
|
||||
files = {'nova_server.yaml': nova_server_template}
|
||||
tmpl = templatem.Template(t, files=files)
|
||||
self._test_validate_unknown_resource_type(stack_name, tmpl,
|
||||
'my_resource_group')
|
||||
|
||||
def test_validate_heat_autoscaling_group(self):
|
||||
# test validate without nested template
|
||||
stack_name = 'validate_heat_autoscaling_group_template'
|
||||
t = template_format.parse(heat_autoscaling_group_template)
|
||||
tmpl = templatem.Template(t)
|
||||
self._test_validate_unknown_resource_type(stack_name, tmpl,
|
||||
'my_autoscaling_group')
|
||||
|
||||
# validate with nested template
|
||||
res_prop = t['resources']['my_autoscaling_group']['properties']
|
||||
res_prop['resource']['type'] = 'nova_server.yaml'
|
||||
files = {'nova_server.yaml': nova_server_template}
|
||||
tmpl = templatem.Template(t, files=files)
|
||||
self._test_validate_unknown_resource_type(stack_name, tmpl,
|
||||
'my_autoscaling_group')
|
||||
|
||||
def test__validate_nested_resources_checks_num_of_resources(self):
|
||||
stack_resource.cfg.CONF.set_override('max_resources_per_stack', 2)
|
||||
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
|
||||
|
|
Loading…
Reference in New Issue