Fix stack preview
Don't execute translation rule for property that contains a GetParam function that can't be resolved at the moment. Such situation happens when we try to resolve get_param function that refer to parameter with None value. We receive parameter value from parent stack, where this value is a reference to some resource without resource_id, so this situation is legal for stack preview and we shouldn't fail. Note, that we can reproduce this behaviour only with resources with hidden parameters and overrided get_resource_id method, that returns None if resoruce creation has not been started yet. Change-Id: Ia1097940db983721c8b5116db7ee0a2c4c45339d Closes-Bug: #1548802
This commit is contained in:
@@ -11,6 +11,9 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_utils import encodeutils
|
||||||
|
|
||||||
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.engine.cfn import functions as cfn_funcs
|
from heat.engine.cfn import functions as cfn_funcs
|
||||||
from heat.engine import function
|
from heat.engine import function
|
||||||
@@ -154,7 +157,14 @@ class TranslationRule(object):
|
|||||||
if isinstance(param, hot_funcs.Removed):
|
if isinstance(param, hot_funcs.Removed):
|
||||||
raise AttributeError(_('Property uses removed function.'))
|
raise AttributeError(_('Property uses removed function.'))
|
||||||
if isinstance(param, (hot_funcs.GetParam, cfn_funcs.ParamRef)):
|
if isinstance(param, (hot_funcs.GetParam, cfn_funcs.ParamRef)):
|
||||||
return function.resolve(param)
|
try:
|
||||||
|
return function.resolve(param)
|
||||||
|
except exception.UserParameterMissing as ex:
|
||||||
|
# We can't resolve parameter now. Abort translation.
|
||||||
|
err_msg = encodeutils.exception_to_unicode(ex)
|
||||||
|
raise AttributeError(
|
||||||
|
_('Can not resolve parameter '
|
||||||
|
'due to: %s') % err_msg)
|
||||||
elif isinstance(param, list):
|
elif isinstance(param, list):
|
||||||
return [resolve_param(param_item) for param_item in param]
|
return [resolve_param(param_item) for param_item in param]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import six
|
|||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.engine.cfn import functions as cfn_funcs
|
from heat.engine.cfn import functions as cfn_funcs
|
||||||
|
from heat.engine import function
|
||||||
from heat.engine.hot import functions as hot_funcs
|
from heat.engine.hot import functions as hot_funcs
|
||||||
from heat.engine import parameters
|
from heat.engine import parameters
|
||||||
from heat.engine import properties
|
from heat.engine import properties
|
||||||
@@ -797,3 +798,40 @@ class TestTranslationRule(common.HeatTestCase):
|
|||||||
rule.execute_rule()
|
rule.execute_rule()
|
||||||
|
|
||||||
self.assertEqual([param], props.data.get('far'))
|
self.assertEqual([param], props.data.get('far'))
|
||||||
|
|
||||||
|
def test_property_no_translation_if_user_parameter_missing(self):
|
||||||
|
"""Test translation in the case of missing parameter"""
|
||||||
|
schema = {
|
||||||
|
'source': properties.Schema(
|
||||||
|
properties.Schema.STRING
|
||||||
|
),
|
||||||
|
'destination': properties.Schema(
|
||||||
|
properties.Schema.STRING
|
||||||
|
)}
|
||||||
|
|
||||||
|
class DummyStack(dict):
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
return mock.Mock()
|
||||||
|
|
||||||
|
param = hot_funcs.GetParam(DummyStack(),
|
||||||
|
'get_param',
|
||||||
|
'source_param')
|
||||||
|
|
||||||
|
param.parameters = {}
|
||||||
|
|
||||||
|
data = {'source': param, 'destination': ''}
|
||||||
|
props = properties.Properties(schema, data,
|
||||||
|
resolver=function.resolve)
|
||||||
|
|
||||||
|
rule = translation.TranslationRule(
|
||||||
|
props,
|
||||||
|
translation.TranslationRule.REPLACE,
|
||||||
|
['destination'],
|
||||||
|
value_path=['source'])
|
||||||
|
|
||||||
|
rule.execute_rule()
|
||||||
|
|
||||||
|
# ensure that translation rule was not applied
|
||||||
|
self.assertEqual({'source': param, 'destination': ''},
|
||||||
|
data)
|
||||||
|
|||||||
@@ -186,3 +186,44 @@ outputs:
|
|||||||
|
|
||||||
self.assertEqual('abc', res['properties']['value'])
|
self.assertEqual('abc', res['properties']['value'])
|
||||||
self.assertEqual([], res['required_by'])
|
self.assertEqual([], res['required_by'])
|
||||||
|
|
||||||
|
def test_res_group_with_nested_template(self):
|
||||||
|
main_template = '''
|
||||||
|
heat_template_version: 2015-04-30
|
||||||
|
resources:
|
||||||
|
fixed_network:
|
||||||
|
type: "OS::Neutron::Net"
|
||||||
|
rg:
|
||||||
|
type: "OS::Heat::ResourceGroup"
|
||||||
|
properties:
|
||||||
|
count: 1
|
||||||
|
resource_def:
|
||||||
|
type: nested.yaml
|
||||||
|
properties:
|
||||||
|
fixed_network_id: {get_resource: fixed_network}
|
||||||
|
'''
|
||||||
|
nested_template = '''
|
||||||
|
heat_template_version: 2015-04-30
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
fixed_network_id:
|
||||||
|
type: string
|
||||||
|
resources:
|
||||||
|
port:
|
||||||
|
type: "OS::Neutron::Port"
|
||||||
|
properties:
|
||||||
|
network_id:
|
||||||
|
get_param: fixed_network_id
|
||||||
|
|
||||||
|
'''
|
||||||
|
stack_name = self._stack_rand_name()
|
||||||
|
result = self.client.stacks.preview(
|
||||||
|
disable_rollback=True,
|
||||||
|
stack_name=stack_name,
|
||||||
|
template=main_template,
|
||||||
|
files={'nested.yaml': nested_template}).to_dict()
|
||||||
|
# ensure that fixed network and port here
|
||||||
|
self.assertEqual('fixed_network',
|
||||||
|
result['resources'][0]['resource_name'])
|
||||||
|
self.assertEqual('port',
|
||||||
|
result['resources'][1][0][0]['resource_name'])
|
||||||
|
|||||||
Reference in New Issue
Block a user