Merge "Fix GetAttThenSelect for convergence"
This commit is contained in:
commit
34cc8bf5d7
@ -264,13 +264,9 @@ def load_resource(cnxt, resource_id, resource_data, is_update):
|
||||
return None, None, None
|
||||
|
||||
|
||||
def construct_input_data(rsrc, curr_stack):
|
||||
attributes = curr_stack.get_dep_attrs(
|
||||
six.itervalues(curr_stack.resources),
|
||||
curr_stack.outputs,
|
||||
rsrc.name)
|
||||
def _resolve_attributes(dep_attrs, rsrc):
|
||||
resolved_attributes = {}
|
||||
for attr in attributes:
|
||||
for attr in dep_attrs:
|
||||
try:
|
||||
if isinstance(attr, six.string_types):
|
||||
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)
|
||||
except exception.InvalidTemplateAttribute as 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,
|
||||
'name': rsrc.name,
|
||||
'reference_id': rsrc.get_reference_id(),
|
||||
'attrs': resolved_attributes,
|
||||
'attrs': _resolve_attributes(dep_attrs, rsrc),
|
||||
'status': rsrc.status,
|
||||
'action': rsrc.action,
|
||||
'uuid': rsrc.uuid}
|
||||
|
@ -148,19 +148,6 @@ 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):
|
||||
"""A function for resolving resource attributes.
|
||||
@ -186,6 +173,19 @@ class GetAtt(GetAttThenSelect):
|
||||
else:
|
||||
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):
|
||||
"""A function for resolving resource attributes.
|
||||
|
@ -124,7 +124,7 @@ resources:
|
||||
'''
|
||||
|
||||
attr_cache_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
heat_template_version: 2016-04-08
|
||||
resources:
|
||||
A:
|
||||
type: ResourceWithComplexAttributesType
|
||||
|
@ -12,19 +12,21 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import identifier
|
||||
from heat.common import template_format
|
||||
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 function
|
||||
from heat.engine.hot import functions as hot_functions
|
||||
from heat.engine.hot import parameters as hot_param
|
||||
from heat.engine.hot import template as hot_template
|
||||
from heat.engine import parameters
|
||||
from heat.engine import resource
|
||||
from heat.engine import resources
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import stack as parser
|
||||
@ -1699,6 +1701,172 @@ class StackAttributesTest(common.HeatTestCase):
|
||||
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):
|
||||
|
||||
def setUp(self):
|
||||
|
Loading…
Reference in New Issue
Block a user