Validate deletion policy during template parsing

Currently we pass the template value directly into ResourceDefinition
which can result in an assertion error from the constructor in the
stack.Stack.validate path.  We can avoid this by explicitly translating
the expected policies for each template dialect into the keys
supported by the resource definition.

Change-Id: I54de89ec6b7cd4af722c9c263577252c15aaad64
Closes-Bug: #1496502
This commit is contained in:
Steven Hardy 2015-09-16 21:38:47 +01:00
parent 99ae1702ff
commit 92bf3aa5dc
3 changed files with 68 additions and 2 deletions

View File

@ -56,6 +56,12 @@ class CfnTemplate(template.Template):
'Fn::Base64': cfn_funcs.Base64,
}
deletion_policies = {
'Delete': rsrc_defn.ResourceDefinition.DELETE,
'Retain': rsrc_defn.ResourceDefinition.RETAIN,
'Snapshot': rsrc_defn.ResourceDefinition.SNAPSHOT
}
def __getitem__(self, section):
'''Get the relevant section in the template.'''
if section not in self.SECTIONS:
@ -147,12 +153,20 @@ class CfnTemplate(template.Template):
if isinstance(depends, six.string_types):
depends = [depends]
deletion_policy = data.get(RES_DELETION_POLICY)
if deletion_policy is not None:
if deletion_policy not in self.deletion_policies:
msg = _('Invalid deletion policy "%s"') % deletion_policy
raise exception.StackValidationFailed(message=msg)
else:
deletion_policy = self.deletion_policies[deletion_policy]
kwargs = {
'resource_type': data.get(RES_TYPE),
'properties': data.get(RES_PROPERTIES),
'metadata': data.get(RES_METADATA),
'depends': depends,
'deletion_policy': data.get(RES_DELETION_POLICY),
'deletion_policy': deletion_policy,
'update_policy': data.get(RES_UPDATE_POLICY),
'description': data.get(RES_DESCRIPTION) or ''
}

View File

@ -83,6 +83,12 @@ class HOTemplate20130523(template.Template):
'get_file': hot_funcs.GetFile,
}
deletion_policies = {
'Delete': rsrc_defn.ResourceDefinition.DELETE,
'Retain': rsrc_defn.ResourceDefinition.RETAIN,
'Snapshot': rsrc_defn.ResourceDefinition.SNAPSHOT
}
def __getitem__(self, section):
""""Get the relevant section in the template."""
# first translate from CFN into HOT terminology if necessary
@ -250,12 +256,20 @@ class HOTemplate20130523(template.Template):
if isinstance(depends, six.string_types):
depends = [depends]
deletion_policy = data.get(RES_DELETION_POLICY)
if deletion_policy is not None:
if deletion_policy not in self.deletion_policies:
msg = _('Invalid deletion policy "%s"') % deletion_policy
raise exception.StackValidationFailed(message=msg)
else:
deletion_policy = self.deletion_policies[deletion_policy]
kwargs = {
'resource_type': data.get(RES_TYPE),
'properties': data.get(RES_PROPERTIES),
'metadata': data.get(RES_METADATA),
'depends': depends,
'deletion_policy': data.get(RES_DELETION_POLICY),
'deletion_policy': deletion_policy,
'update_policy': data.get(RES_UPDATE_POLICY),
'description': None
}

View File

@ -1920,6 +1920,44 @@ class StackTest(common.HeatTestCase):
self.assertIn('Outputs must contain Output. '
'Found a [%s] instead' % type([]), six.text_type(ex))
def test_incorrect_deletion_policy(self):
tmpl = template_format.parse("""
HeatTemplateFormatVersion: '2012-12-12'
Resources:
AResource:
Type: ResourceWithPropsType
DeletionPolicy: wibble
Properties:
Foo: abc
""")
self.stack = stack.Stack(self.ctx, 'stack_bad_delpol',
template.Template(tmpl))
ex = self.assertRaises(exception.StackValidationFailed,
self.stack.validate)
self.assertIn('Invalid deletion policy "wibble"',
six.text_type(ex))
def test_incorrect_deletion_policy_hot(self):
tmpl = template_format.parse("""
heat_template_version: 2013-05-23
resources:
AResource:
type: ResourceWithPropsType
deletion_policy: wibble
properties:
Foo: abc
""")
self.stack = stack.Stack(self.ctx, 'stack_bad_delpol',
template.Template(tmpl))
ex = self.assertRaises(exception.StackValidationFailed,
self.stack.validate)
self.assertIn('Invalid deletion policy "wibble"',
six.text_type(ex))
def test_incorrect_outputs_hot_get_attr(self):
tmpl = {'heat_template_version': '2013-05-23',
'resources': {