diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py index 25fc361555..f38973d0df 100644 --- a/heat/engine/hot/functions.py +++ b/heat/engine/hot/functions.py @@ -125,6 +125,19 @@ class GetAttThenSelect(cfn_funcs.GetAtt): path_components = function.resolve(self._path_components) return attributes.select_from_attribute(attribute, path_components) + def dep_attrs(self, resource_name): + if self._resource().name == resource_name: + path = function.resolve(self._path_components) + attr = [function.resolve(self._attribute)] + if path: + attrs = [tuple(attr + path)] + else: + attrs = attr + else: + attrs = [] + return itertools.chain(function.dep_attrs(self.args, resource_name), + attrs) + class GetAtt(GetAttThenSelect): ''' diff --git a/heat/engine/resource.py b/heat/engine/resource.py index dae5192f41..16340a8c88 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -1474,8 +1474,11 @@ class Resource(object): ''' if self.stack.has_cache_data(self.name): # Load from cache for lightweight resources. + complex_key = key + if path: + complex_key = tuple([key] + list(path)) attribute = self.stack.cache_data_resource_attribute( - self.name, key) + self.name, complex_key) else: try: attribute = self.attributes[key] diff --git a/heat/engine/worker.py b/heat/engine/worker.py index 92ba58874d..2c1970917a 100644 --- a/heat/engine/worker.py +++ b/heat/engine/worker.py @@ -315,7 +315,10 @@ def construct_input_data(rsrc): resolved_attributes = {} for attr in attributes: try: - resolved_attributes[attr] = rsrc.FnGetAtt(attr) + if isinstance(attr, six.string_types): + resolved_attributes[attr] = rsrc.FnGetAtt(attr) + else: + resolved_attributes[attr] = rsrc.FnGetAtt(*attr) except exception.InvalidTemplateAttribute as ita: LOG.info(six.text_type(ita)) diff --git a/heat/tests/engine/test_engine_worker.py b/heat/tests/engine/test_engine_worker.py index d9fbe361aa..43647154d1 100644 --- a/heat/tests/engine/test_engine_worker.py +++ b/heat/tests/engine/test_engine_worker.py @@ -520,12 +520,15 @@ class MiscMethodsTest(common.HeatTestCase): self.ctx = utils.dummy_context() self.stack = tools.get_stack( 'check_workflow_create_stack', self.ctx, - template=tools.string_template_five, convergence=True) + template=tools.attr_cache_template, convergence=True) self.stack.converge_stack(self.stack.t) self.resource = self.stack['A'] def test_construct_input_data_ok(self): - expected_input_data = {'attrs': {'value': None}, + expected_input_data = {'attrs': {(u'flat_dict', u'key2'): 'val2', + (u'flat_dict', u'key3'): 'val3', + (u'nested_dict', u'dict', u'a'): 1, + (u'nested_dict', u'dict', u'b'): 2}, 'id': mock.ANY, 'reference_id': 'A', 'name': 'A'} diff --git a/heat/tests/engine/tools.py b/heat/tests/engine/tools.py index e4a7ae17b7..3dcebf23f8 100644 --- a/heat/tests/engine/tools.py +++ b/heat/tests/engine/tools.py @@ -123,6 +123,32 @@ resources: salt: {get_param: salt} ''' +attr_cache_template = ''' +heat_template_version: 2013-05-23 +resources: + A: + type: ResourceWithComplexAttributesType + B: + type: OS::Heat::RandomString + properties: + salt: {get_attr: [A, flat_dict, key2]} + C: + type: OS::Heat::RandomString + depends_on: [A, B] + properties: + salt: {get_attr: [A, nested_dict, dict, a]} + D: + type: OS::Heat::RandomString + depends_on: C + properties: + salt: {get_attr: [A, nested_dict, dict, b]} + E: + type: OS::Heat::RandomString + depends_on: C + properties: + salt: {get_attr: [A, flat_dict, key3]} +''' + def get_stack(stack_name, ctx, template=None, with_params=True, convergence=False): diff --git a/heat/tests/test_stack_collect_attributes.py b/heat/tests/test_stack_collect_attributes.py index cbbc68fd47..fe5e52b2d6 100644 --- a/heat/tests/test_stack_collect_attributes.py +++ b/heat/tests/test_stack_collect_attributes.py @@ -130,6 +130,23 @@ outputs: {get_attr: [BResource, attr_B3]}] """ +tmpl6 = """ +heat_template_version: 2015-04-30 +resources: + AResource: + type: ResourceWithComplexAttributesType + BResource: + type: ResourceWithPropsType + properties: + Foo: {get_attr: [AResource, list, 1]} + Doo: {get_attr: [AResource, nested_dict, dict, b]} +outputs: + out1: + value: [{get_attr: [AResource, flat_dict, key2]}, + {get_attr: [AResource, nested_dict, string]}, + {get_attr: [BResource, attr_B3]}] +""" + class DepAttrsTest(common.HeatTestCase): @@ -159,7 +176,14 @@ class DepAttrsTest(common.HeatTestCase): 'attr_A3', 'attr_A4'}, 'BResource': {'attr_B1', 'attr_B2', 'meta_B2', 'attr_B3'}, - 'CResource': set()})) + 'CResource': set()})), + ('nested_attr', + dict(tmpl=tmpl6, + expected={'AResource': set([(u'flat_dict', u'key2'), + (u'list', 1), + (u'nested_dict', u'dict', u'b'), + (u'nested_dict', u'string')]), + 'BResource': set(['attr_B3'])})) ] def setUp(self):