Convergence: Fix FnGetAtt to fetch value from cache_data
Resource plugins will now override rsrc.get_attribute method to add extra logic to Fn::GetAtt specific to resource implementation. Also provides proper comment to rsrc.get_reference_id method. Change-Id: I7561afa1c824b2294dd6701be6a19723abbe0522 Closes-Bug: #1531840
This commit is contained in:
parent
8154b1feac
commit
5d070f9c59
@ -190,7 +190,7 @@ class GetAtt(function.Function):
|
||||
|
||||
attr = function.resolve(self._attribute)
|
||||
from heat.engine import resource
|
||||
if (type(res).FnGetAtt == resource.Resource.FnGetAtt and
|
||||
if (type(res).get_attribute == resource.Resource.get_attribute and
|
||||
attr not in six.iterkeys(res.attributes_schema)):
|
||||
raise exception.InvalidTemplateAttribute(
|
||||
resource=self._resource_name, key=attr)
|
||||
|
@ -1566,6 +1566,11 @@ class Resource(object):
|
||||
return (self.action, self.status)
|
||||
|
||||
def get_reference_id(self):
|
||||
"""Default implementation for function get_resource.
|
||||
|
||||
This may be overridden by resource plugins to add extra
|
||||
logic specific to the resource implementation.
|
||||
"""
|
||||
if self.resource_id is not None:
|
||||
return six.text_type(self.resource_id)
|
||||
else:
|
||||
@ -1587,6 +1592,20 @@ class Resource(object):
|
||||
else:
|
||||
return Resource.get_reference_id(self)
|
||||
|
||||
def get_attribute(self, key, *path):
|
||||
"""Default implementation for function get_attr and Fn::GetAtt.
|
||||
|
||||
This may be overridden by resource plugins to add extra
|
||||
logic specific to the resource implementation.
|
||||
"""
|
||||
try:
|
||||
attribute = self.attributes[key]
|
||||
except KeyError:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
return attributes.select_from_attribute(attribute, path)
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
"""For the intrinsic function Fn::GetAtt.
|
||||
|
||||
@ -1602,14 +1621,7 @@ class Resource(object):
|
||||
attribute = self.stack.cache_data_resource_attribute(
|
||||
self.name, complex_key)
|
||||
return attribute
|
||||
else:
|
||||
try:
|
||||
attribute = self.attributes[key]
|
||||
except KeyError:
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
||||
return attributes.select_from_attribute(attribute, path)
|
||||
return self.get_attribute(key, *path)
|
||||
|
||||
def FnGetAtts(self):
|
||||
"""For the intrinsic function get_attr which returns all attributes.
|
||||
|
@ -80,7 +80,7 @@ class NestedStack(stack_resource.StackResource):
|
||||
self.properties[self.TIMEOUT_IN_MINS],
|
||||
adopt_data=resource_adopt_data)
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
if key and not key.startswith('Outputs.'):
|
||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||
key=key)
|
||||
|
@ -162,7 +162,7 @@ class AutoScalingResourceGroup(aws_asg.AutoScalingGroup):
|
||||
self)._create_template(num_instances, num_replace,
|
||||
template_version=template_version)
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
if key == self.CURRENT_SIZE:
|
||||
return grouputils.get_size(self)
|
||||
if path:
|
||||
|
@ -45,7 +45,7 @@ class NoneResource(resource.Resource):
|
||||
def validate(self):
|
||||
pass
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
return None
|
||||
|
||||
|
||||
|
@ -151,7 +151,7 @@ class ResourceChain(stack_resource.StackResource):
|
||||
def child_params(self):
|
||||
return {}
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
if key.startswith('resource.'):
|
||||
return grouputils.get_nested_attrs(self, key, False, *path)
|
||||
|
||||
|
@ -408,7 +408,7 @@ class ResourceGroup(stack_resource.StackResource):
|
||||
checkers[0].start()
|
||||
return checkers
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
if key.startswith("resource."):
|
||||
return grouputils.get_nested_attrs(self, key, False, *path)
|
||||
|
||||
|
@ -513,7 +513,7 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
self.context, self.resource_id, details,
|
||||
timeutils.utcnow().isoformat())
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
"""Resource attributes map to deployment outputs values."""
|
||||
sd = self.rpc_client().show_software_deployment(
|
||||
self.context, self.resource_id)
|
||||
@ -640,7 +640,7 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
|
||||
'OS::Heat::SoftwareDeployment',
|
||||
props, None)
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
rg = super(SoftwareDeploymentGroup, self)
|
||||
if key == self.STDOUTS:
|
||||
n_attr = SoftwareDeployment.STDOUT
|
||||
@ -653,7 +653,7 @@ class SoftwareDeploymentGroup(resource_group.ResourceGroup):
|
||||
# including arbitrary outputs, so we can't validate here
|
||||
n_attr = key
|
||||
|
||||
rg_attr = rg.FnGetAtt(rg.ATTR_ATTRIBUTES, n_attr)
|
||||
rg_attr = rg.get_attribute(rg.ATTR_ATTRIBUTES, n_attr)
|
||||
return attributes.select_from_attribute(rg_attr, path)
|
||||
|
||||
|
||||
|
@ -313,7 +313,7 @@ class TemplateResource(stack_resource.StackResource):
|
||||
|
||||
return self.nested().identifier().arn()
|
||||
|
||||
def FnGetAtt(self, key, *path):
|
||||
def get_attribute(self, key, *path):
|
||||
stack = self.nested()
|
||||
if stack is None:
|
||||
return None
|
||||
|
@ -241,12 +241,12 @@ class ResourceWithDefaultClientNameExt(resource.Resource):
|
||||
|
||||
|
||||
class ResourceWithFnGetAttType(GenericResource):
|
||||
def FnGetAtt(self, name):
|
||||
def get_attribute(self, name):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceWithFnGetRefIdType(ResourceWithProps):
|
||||
def FnGetRefId(self):
|
||||
def get_reference_id(self):
|
||||
return 'ID-%s' % self.name
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ import copy
|
||||
import mock
|
||||
|
||||
from heat.common.exception import StackValidationFailed
|
||||
from heat.common import grouputils
|
||||
from heat.engine.resources.openstack.heat import resource_chain
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
@ -205,3 +206,22 @@ class ResourceChainTests(common.HeatTestCase):
|
||||
snip = self.stack.t.resource_definitions(self.stack)['test-chain']
|
||||
chain = resource_chain.ResourceChain('test', snip, self.stack)
|
||||
return chain
|
||||
|
||||
@mock.patch.object(grouputils, 'get_rsrc_id')
|
||||
def test_get_attribute(self, mock_get_rsrc_id):
|
||||
stack = utils.parse_stack(TEMPLATE)
|
||||
mock_get_rsrc_id.side_effect = ['0', '1']
|
||||
rsrc = stack['test-chain']
|
||||
self.assertEqual(['0', '1'], rsrc.FnGetAtt(rsrc.REFS))
|
||||
|
||||
def test_get_attribute_convg(self):
|
||||
cache_data = {'test-chain': {
|
||||
'uuid': mock.ANY,
|
||||
'id': mock.ANY,
|
||||
'action': 'CREATE',
|
||||
'status': 'COMPLETE',
|
||||
'attrs': {'refs': ['rsrc1', 'rsrc2']}
|
||||
}}
|
||||
stack = utils.parse_stack(TEMPLATE, cache_data=cache_data)
|
||||
rsrc = stack['test-chain']
|
||||
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt(rsrc.REFS))
|
||||
|
@ -769,6 +769,25 @@ class ResourceGroupAttrTest(common.HeatTestCase):
|
||||
self.assertRaises(exception.InvalidTemplateAttribute, resg.FnGetAtt,
|
||||
'resource.2')
|
||||
|
||||
@mock.patch.object(grouputils, 'get_rsrc_id')
|
||||
def test_get_attribute(self, mock_get_rsrc_id):
|
||||
stack = utils.parse_stack(template)
|
||||
mock_get_rsrc_id.side_effect = ['0', '1']
|
||||
rsrc = stack['group1']
|
||||
self.assertEqual(['0', '1'], rsrc.FnGetAtt(rsrc.REFS))
|
||||
|
||||
def test_get_attribute_convg(self):
|
||||
cache_data = {'group1': {
|
||||
'uuid': mock.ANY,
|
||||
'id': mock.ANY,
|
||||
'action': 'CREATE',
|
||||
'status': 'COMPLETE',
|
||||
'attrs': {'refs': ['rsrc1', 'rsrc2']}
|
||||
}}
|
||||
stack = utils.parse_stack(template, cache_data=cache_data)
|
||||
rsrc = stack['group1']
|
||||
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt(rsrc.REFS))
|
||||
|
||||
def _create_dummy_stack(self, template_data=template, expect_count=2,
|
||||
expect_attrs=None):
|
||||
stack = utils.parse_stack(template_data)
|
||||
@ -779,6 +798,7 @@ class ResourceGroupAttrTest(common.HeatTestCase):
|
||||
for resc in range(expect_count):
|
||||
res = str(resc)
|
||||
fake_res[res] = mock.Mock()
|
||||
fake_res[res].stack = stack
|
||||
fake_res[res].FnGetRefId.return_value = 'ID-%s' % res
|
||||
if res in expect_attrs:
|
||||
fake_res[res].FnGetAtt.return_value = expect_attrs[res]
|
||||
|
@ -160,12 +160,13 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
super(SoftwareDeploymentTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
|
||||
def _create_stack(self, tmpl):
|
||||
def _create_stack(self, tmpl, cache_data=None):
|
||||
self.stack = parser.Stack(
|
||||
self.ctx, 'software_deployment_test_stack',
|
||||
template.Template(tmpl),
|
||||
stack_id='42f6f66b-631a-44e7-8d01-e22fb54574a9',
|
||||
stack_user_project_id='65728b74-cfe7-4f17-9c15-11d4f686e591'
|
||||
stack_user_project_id='65728b74-cfe7-4f17-9c15-11d4f686e591',
|
||||
cache_data=cache_data
|
||||
)
|
||||
|
||||
self.patchobject(nova.NovaClientPlugin, 'get_server',
|
||||
@ -917,6 +918,17 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
self.deployment.FnGetAtt('deploy_stderr'))
|
||||
self.assertEqual(0, self.deployment.FnGetAtt('deploy_status_code'))
|
||||
|
||||
def test_fn_get_att_convg(self):
|
||||
cache_data = {'deployment_mysql': {
|
||||
'uuid': mock.ANY,
|
||||
'id': mock.ANY,
|
||||
'action': 'CREATE',
|
||||
'status': 'COMPLETE',
|
||||
'attrs': {'foo': 'bar'}
|
||||
}}
|
||||
self._create_stack(self.template, cache_data=cache_data)
|
||||
self.assertEqual('bar', self.deployment.FnGetAtt('foo'))
|
||||
|
||||
def test_fn_get_att_error(self):
|
||||
self._create_stack(self.template)
|
||||
|
||||
|
@ -286,6 +286,23 @@ Resources:
|
||||
nested_stack = stack['the_nested']
|
||||
self.assertEqual('the_nested_convg_mock', nested_stack.FnGetRefId())
|
||||
|
||||
def test_get_attribute(self):
|
||||
tmpl = template_format.parse(self.test_template)
|
||||
ctx = utils.dummy_context('test_username', 'aaaa', 'password')
|
||||
stack = parser.Stack(ctx, 'test',
|
||||
template.Template(tmpl))
|
||||
stack.store()
|
||||
|
||||
stack_res = stack['the_nested']
|
||||
stack_res._store()
|
||||
|
||||
nested_t = template_format.parse(self.nested_template)
|
||||
nested_stack = parser.Stack(ctx, 'test',
|
||||
template.Template(nested_t))
|
||||
nested_stack.store()
|
||||
stack_res.nested = mock.Mock(return_value=nested_stack)
|
||||
self.assertEqual('bar', stack_res.FnGetAtt('Outputs.Foo'))
|
||||
|
||||
|
||||
class ResDataResource(generic_rsrc.GenericResource):
|
||||
def handle_create(self):
|
||||
|
@ -428,6 +428,28 @@ class StackResourceTest(StackResourceBaseTest):
|
||||
self._test_validate_unknown_resource_type(stack_name, tmpl,
|
||||
'my_autoscaling_group')
|
||||
|
||||
def test_get_attribute_autoscaling(self):
|
||||
t = template_format.parse(heat_autoscaling_group_template)
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_att', tmpl)
|
||||
rsrc = stack['my_autoscaling_group']
|
||||
self.assertEqual(0, rsrc.FnGetAtt(rsrc.CURRENT_SIZE))
|
||||
|
||||
def test_get_attribute_autoscaling_convg(self):
|
||||
t = template_format.parse(heat_autoscaling_group_template)
|
||||
tmpl = templatem.Template(t)
|
||||
cache_data = {'my_autoscaling_group': {
|
||||
'uuid': mock.ANY,
|
||||
'id': mock.ANY,
|
||||
'action': 'CREATE',
|
||||
'status': 'COMPLETE',
|
||||
'attrs': {'current_size': 4}
|
||||
}}
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_att', tmpl,
|
||||
cache_data=cache_data)
|
||||
rsrc = stack['my_autoscaling_group']
|
||||
self.assertEqual(4, rsrc.FnGetAtt(rsrc.CURRENT_SIZE))
|
||||
|
||||
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
Block a user