Adding validation algorithm for get attr functions
Turn on validation for functions Fn::GetAtt and get_attr. Validation will be skipped if resource has overwritten FnGetAtt method. Related-Bug: #1273490 Change-Id: I15b6b7447face0cf2fe1d6059d3fb900778b66cc
This commit is contained in:
parent
b5f498654c
commit
312f331348
@ -20,6 +20,7 @@ import six
|
||||
from heat.api.aws import utils as aws_utils
|
||||
from heat.common import exception
|
||||
from heat.engine import function
|
||||
from heat.engine import resource
|
||||
|
||||
|
||||
class FindInMap(function.Function):
|
||||
@ -173,6 +174,15 @@ class GetAtt(function.Function):
|
||||
return itertools.chain(super(GetAtt, self).dependencies(path),
|
||||
[self._resource(path)])
|
||||
|
||||
def validate(self):
|
||||
super(GetAtt, self).validate()
|
||||
res = self._resource()
|
||||
attr = function.resolve(self._attribute)
|
||||
if (type(res).FnGetAtt == resource.Resource.FnGetAtt and
|
||||
attr not in res.attributes_schema.keys()):
|
||||
raise exception.InvalidTemplateAttribute(
|
||||
resource=self._resource_name, key=attr)
|
||||
|
||||
def result(self):
|
||||
attribute = function.resolve(self._attribute)
|
||||
|
||||
|
@ -13,9 +13,18 @@
|
||||
|
||||
import copy
|
||||
import six
|
||||
import uuid
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.cfn import functions
|
||||
from heat.engine import environment
|
||||
from heat.engine import function
|
||||
from heat.engine import parser
|
||||
from heat.engine import resource
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import generic_resource as generic_rsrc
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class TestFunction(function.Function):
|
||||
@ -143,3 +152,66 @@ class DependenciesTest(HeatTestCase):
|
||||
self.assertIn('foo', deps)
|
||||
self.assertIn('bar', deps)
|
||||
self.assertEqual(2, len(deps))
|
||||
|
||||
|
||||
class ValidateGetAttTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
super(ValidateGetAttTest, self).setUp()
|
||||
|
||||
resource._register_class('GenericResourceType',
|
||||
generic_rsrc.GenericResource)
|
||||
|
||||
env = environment.Environment()
|
||||
env.load({u'resource_registry':
|
||||
{u'OS::Test::GenericResource': u'GenericResourceType'}})
|
||||
|
||||
class FakeResource(generic_rsrc.GenericResource):
|
||||
def FnGetAtt(self, name):
|
||||
pass
|
||||
|
||||
resource._register_class('OverwrittenFnGetAttType', FakeResource)
|
||||
env.load({u'resource_registry':
|
||||
{u'OS::Test::FakeResource': u'OverwrittenFnGetAttType'}})
|
||||
|
||||
self.stack = parser.Stack(
|
||||
utils.dummy_context(), 'test_stack',
|
||||
parser.Template({"HeatTemplateFormatVersion": "2012-12-12"}),
|
||||
env=env, stack_id=str(uuid.uuid4()))
|
||||
res_defn = rsrc_defn.ResourceDefinition('test_rsrc',
|
||||
'OS::Test::GenericResource')
|
||||
self.rsrc = resource.Resource('test_rsrc', res_defn, self.stack)
|
||||
self.stack.add_resource(self.rsrc)
|
||||
|
||||
def test_resource_is_appear_in_stack(self):
|
||||
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
|
||||
[self.rsrc.name, 'Foo'])
|
||||
self.assertIsNone(func.validate())
|
||||
|
||||
def test_resource_is_not_appear_in_stack(self):
|
||||
self.stack.remove_resource(self.rsrc.name)
|
||||
|
||||
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
|
||||
[self.rsrc.name, 'Foo'])
|
||||
ex = self.assertRaises(exception.InvalidTemplateReference,
|
||||
func.validate)
|
||||
self.assertEqual('The specified reference "test_rsrc" (in unknown) '
|
||||
'is incorrect.', str(ex))
|
||||
|
||||
def test_resource_no_attribute_with_default_fn_get_att(self):
|
||||
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
|
||||
[self.rsrc.name, 'Bar'])
|
||||
ex = self.assertRaises(exception.InvalidTemplateAttribute,
|
||||
func.validate)
|
||||
self.assertEqual('The Referenced Attribute (test_rsrc Bar) '
|
||||
'is incorrect.', str(ex))
|
||||
|
||||
def test_resource_no_attribute_with_overwritten_fn_get_att(self):
|
||||
res_defn = rsrc_defn.ResourceDefinition('test_rsrc',
|
||||
'OS::Test::FakeResource')
|
||||
self.rsrc = resource.Resource('test_rsrc', res_defn, self.stack)
|
||||
self.stack.add_resource(self.rsrc)
|
||||
self.rsrc.attributes_schema = {}
|
||||
|
||||
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
|
||||
[self.rsrc.name, 'Foo'])
|
||||
self.assertIsNone(func.validate())
|
||||
|
@ -3684,3 +3684,43 @@ class StackTest(HeatTestCase):
|
||||
fake_snapshot = collections.namedtuple('Snapshot', ('data',))(data)
|
||||
self.stack.delete_snapshot(fake_snapshot)
|
||||
self.assertEqual([data['resources']['AResource']], snapshots)
|
||||
|
||||
def test_incorrect_outputs_cfn_get_attr(self):
|
||||
tmpl = {'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Resources': {
|
||||
'AResource': {'Type': 'ResourceWithPropsType',
|
||||
'Properties': {'Foo': 'abc'}}},
|
||||
'Outputs': {
|
||||
'Resource_attr': {
|
||||
'Value': {
|
||||
'Fn::GetAtt': ['AResource', 'Bar']}}}}
|
||||
|
||||
self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
|
||||
template.Template(tmpl))
|
||||
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
self.stack.validate)
|
||||
|
||||
self.assertEqual('Output validation error: The Referenced Attribute '
|
||||
'(AResource Bar) is incorrect.',
|
||||
str(ex))
|
||||
|
||||
def test_incorrect_outputs_hot_get_attr(self):
|
||||
tmpl = {'heat_template_version': '2013-05-23',
|
||||
'resources': {
|
||||
'AResource': {'type': 'ResourceWithPropsType',
|
||||
'properties': {'Foo': 'abc'}}},
|
||||
'outputs': {
|
||||
'resource_attr': {
|
||||
'value': {
|
||||
'get_attr': ['AResource', 'Bar']}}}}
|
||||
|
||||
self.stack = parser.Stack(self.ctx, 'stack_with_correct_outputs',
|
||||
template.Template(tmpl))
|
||||
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
self.stack.validate)
|
||||
|
||||
self.assertEqual('Output validation error: The Referenced Attribute '
|
||||
'(AResource Bar) is incorrect.',
|
||||
str(ex))
|
||||
|
Loading…
Reference in New Issue
Block a user