Fix GetAttThenSelect for convergence
The GetAttThenSelect dep_attrs implementaion is changed to return the attribute only instead of (attribute + path component). It is wrong to return the (attribute + path component) and that was breaking the GetAttThenSelect for convergence. Change-Id: I117dc3e587386f4d48e70ef89c61bb857c751717 Closes-Bug: #1582649
This commit is contained in:
parent
b7cbfef696
commit
e585676170
|
@ -264,13 +264,9 @@ def load_resource(cnxt, resource_id, resource_data, is_update):
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
def construct_input_data(rsrc, curr_stack):
|
def _resolve_attributes(dep_attrs, rsrc):
|
||||||
attributes = curr_stack.get_dep_attrs(
|
|
||||||
six.itervalues(curr_stack.resources),
|
|
||||||
curr_stack.outputs,
|
|
||||||
rsrc.name)
|
|
||||||
resolved_attributes = {}
|
resolved_attributes = {}
|
||||||
for attr in attributes:
|
for attr in dep_attrs:
|
||||||
try:
|
try:
|
||||||
if isinstance(attr, six.string_types):
|
if isinstance(attr, six.string_types):
|
||||||
resolved_attributes[attr] = rsrc.get_attribute(attr)
|
resolved_attributes[attr] = rsrc.get_attribute(attr)
|
||||||
|
@ -278,11 +274,18 @@ def construct_input_data(rsrc, curr_stack):
|
||||||
resolved_attributes[attr] = rsrc.get_attribute(*attr)
|
resolved_attributes[attr] = rsrc.get_attribute(*attr)
|
||||||
except exception.InvalidTemplateAttribute as ita:
|
except exception.InvalidTemplateAttribute as ita:
|
||||||
LOG.info(six.text_type(ita))
|
LOG.info(six.text_type(ita))
|
||||||
|
return resolved_attributes
|
||||||
|
|
||||||
|
|
||||||
|
def construct_input_data(rsrc, curr_stack):
|
||||||
|
dep_attrs = curr_stack.get_dep_attrs(
|
||||||
|
six.itervalues(curr_stack.resources),
|
||||||
|
curr_stack.outputs,
|
||||||
|
rsrc.name)
|
||||||
input_data = {'id': rsrc.id,
|
input_data = {'id': rsrc.id,
|
||||||
'name': rsrc.name,
|
'name': rsrc.name,
|
||||||
'reference_id': rsrc.get_reference_id(),
|
'reference_id': rsrc.get_reference_id(),
|
||||||
'attrs': resolved_attributes,
|
'attrs': _resolve_attributes(dep_attrs, rsrc),
|
||||||
'status': rsrc.status,
|
'status': rsrc.status,
|
||||||
'action': rsrc.action,
|
'action': rsrc.action,
|
||||||
'uuid': rsrc.uuid}
|
'uuid': rsrc.uuid}
|
||||||
|
|
|
@ -133,19 +133,6 @@ class GetAttThenSelect(cfn_funcs.GetAtt):
|
||||||
path_components = function.resolve(self._path_components)
|
path_components = function.resolve(self._path_components)
|
||||||
return attributes.select_from_attribute(attribute, 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):
|
class GetAtt(GetAttThenSelect):
|
||||||
"""A function for resolving resource attributes.
|
"""A function for resolving resource attributes.
|
||||||
|
@ -171,6 +158,19 @@ class GetAtt(GetAttThenSelect):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
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 GetAttAllAttributes(GetAtt):
|
class GetAttAllAttributes(GetAtt):
|
||||||
"""A function for resolving resource attributes.
|
"""A function for resolving resource attributes.
|
||||||
|
|
|
@ -124,7 +124,7 @@ resources:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
attr_cache_template = '''
|
attr_cache_template = '''
|
||||||
heat_template_version: 2013-05-23
|
heat_template_version: 2016-04-08
|
||||||
resources:
|
resources:
|
||||||
A:
|
A:
|
||||||
type: ResourceWithComplexAttributesType
|
type: ResourceWithComplexAttributesType
|
||||||
|
|
|
@ -12,19 +12,21 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import mock
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import identifier
|
from heat.common import identifier
|
||||||
from heat.common import template_format
|
from heat.common import template_format
|
||||||
from heat.engine.cfn import functions as cfn_functions
|
from heat.engine.cfn import functions as cfn_functions
|
||||||
|
from heat.engine import check_resource as cr
|
||||||
from heat.engine import environment
|
from heat.engine import environment
|
||||||
from heat.engine import function
|
from heat.engine import function
|
||||||
from heat.engine.hot import functions as hot_functions
|
from heat.engine.hot import functions as hot_functions
|
||||||
from heat.engine.hot import parameters as hot_param
|
from heat.engine.hot import parameters as hot_param
|
||||||
from heat.engine.hot import template as hot_template
|
from heat.engine.hot import template as hot_template
|
||||||
from heat.engine import parameters
|
from heat.engine import parameters
|
||||||
|
from heat.engine import resource
|
||||||
from heat.engine import resources
|
from heat.engine import resources
|
||||||
from heat.engine import rsrc_defn
|
from heat.engine import rsrc_defn
|
||||||
from heat.engine import stack as parser
|
from heat.engine import stack as parser
|
||||||
|
@ -1604,6 +1606,172 @@ class StackAttributesTest(common.HeatTestCase):
|
||||||
self.assertEqual(self.expected, resolved)
|
self.assertEqual(self.expected, resolved)
|
||||||
|
|
||||||
|
|
||||||
|
class StackGetAttributesTestConvergence(common.HeatTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(StackGetAttributesTestConvergence, self).setUp()
|
||||||
|
|
||||||
|
self.ctx = utils.dummy_context()
|
||||||
|
|
||||||
|
self.m.ReplayAll()
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
# for hot template 2013-05-23, get_attr: hot_funcs.GetAttThenSelect
|
||||||
|
('get_flat_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_generic_resource,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1', 'foo']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={'Value': 'resource1'})),
|
||||||
|
('get_list_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1', 'list', 0]}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.list[0]})),
|
||||||
|
('get_flat_dict_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'flat_dict',
|
||||||
|
'key2']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
flat_dict['key2']})),
|
||||||
|
('get_nested_attr_list',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'nested_dict',
|
||||||
|
'list',
|
||||||
|
0]}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
nested_dict['list'][0]})),
|
||||||
|
('get_nested_attr_dict',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'nested_dict',
|
||||||
|
'dict',
|
||||||
|
'a']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
nested_dict['dict']['a']})),
|
||||||
|
('get_attr_none',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'none',
|
||||||
|
'who_cares']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={'Value': None})),
|
||||||
|
# for hot template version 2014-10-16 and 2015-04-30,
|
||||||
|
# get_attr: hot_funcs.GetAtt
|
||||||
|
('get_flat_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_generic_resource_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1', 'foo']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={'Value': 'resource1'})),
|
||||||
|
('get_list_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1', 'list', 0]}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.list[0]})),
|
||||||
|
('get_flat_dict_attr',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'flat_dict',
|
||||||
|
'key2']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
flat_dict['key2']})),
|
||||||
|
('get_nested_attr_list',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'nested_dict',
|
||||||
|
'list',
|
||||||
|
0]}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
nested_dict['list'][0]})),
|
||||||
|
('get_nested_attr_dict',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'nested_dict',
|
||||||
|
'dict',
|
||||||
|
'a']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={
|
||||||
|
'Value':
|
||||||
|
generic_rsrc.ResourceWithComplexAttributes.
|
||||||
|
nested_dict['dict']['a']})),
|
||||||
|
('get_attr_none',
|
||||||
|
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
|
||||||
|
snippet={'Value': {'get_attr': ['resource1',
|
||||||
|
'none',
|
||||||
|
'who_cares']}},
|
||||||
|
resource_name='resource1',
|
||||||
|
expected={'Value': None}))
|
||||||
|
]
|
||||||
|
|
||||||
|
def _prepare_cache_data(self, rsrc):
|
||||||
|
attributes = function.dep_attrs(
|
||||||
|
self.stack.t.parse(self.stack, self.snippet),
|
||||||
|
self.resource_name)
|
||||||
|
# store as cache data
|
||||||
|
self.stack.cache_data = {
|
||||||
|
rsrc.name: {
|
||||||
|
'attrs': cr._resolve_attributes(attributes, rsrc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_get_attr_convergence(self):
|
||||||
|
"""Test resolution of get_attr occurrences with convergence."""
|
||||||
|
|
||||||
|
self.stack = parser.Stack(self.ctx, 'test_get_attr',
|
||||||
|
template.Template(self.hot_tpl))
|
||||||
|
self.stack.store()
|
||||||
|
self.stack.create()
|
||||||
|
self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
|
||||||
|
self.stack.state)
|
||||||
|
rsrc = self.stack[self.resource_name]
|
||||||
|
self._prepare_cache_data(rsrc)
|
||||||
|
|
||||||
|
with mock.patch.object(resource.Resource, 'get_attribute') as mock_ga:
|
||||||
|
for action, status in (
|
||||||
|
(rsrc.CREATE, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.CREATE, rsrc.COMPLETE),
|
||||||
|
(rsrc.RESUME, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.RESUME, rsrc.COMPLETE),
|
||||||
|
(rsrc.SUSPEND, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.SUSPEND, rsrc.COMPLETE),
|
||||||
|
(rsrc.UPDATE, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.UPDATE, rsrc.COMPLETE),
|
||||||
|
(rsrc.SNAPSHOT, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.SNAPSHOT, rsrc.COMPLETE),
|
||||||
|
(rsrc.CHECK, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.CHECK, rsrc.COMPLETE),
|
||||||
|
(rsrc.ADOPT, rsrc.IN_PROGRESS),
|
||||||
|
(rsrc.ADOPT, rsrc.COMPLETE)):
|
||||||
|
rsrc.state_set(action, status)
|
||||||
|
|
||||||
|
resolved = function.resolve(self.stack.t.parse(self.stack,
|
||||||
|
self.snippet))
|
||||||
|
self.assertEqual(self.expected, resolved)
|
||||||
|
# get_attribute should never be called, everything
|
||||||
|
# should be resolved from cache data
|
||||||
|
self.assertFalse(mock_ga.called)
|
||||||
|
|
||||||
|
|
||||||
class StackGetAttrValidationTest(common.HeatTestCase):
|
class StackGetAttrValidationTest(common.HeatTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
Loading…
Reference in New Issue