Lazy-load outputs
Rather than passing around an explicit flag to say whether a Stack object should parse its outputs during initialisation, just parse them the first time they are actually needed. Change-Id: I4b8a84883902261592fbfe8d5d5b1892702a3d03changes/76/365276/2
parent
bb26347e81
commit
9ce052f67b
|
@ -510,10 +510,9 @@ class EngineService(service.Service):
|
|||
"""
|
||||
if stack_identity is not None:
|
||||
db_stack = self._get_stack(cnxt, stack_identity, show_deleted=True)
|
||||
stacks = [parser.Stack.load(cnxt, stack=db_stack,
|
||||
resolve_data=resolve_outputs)]
|
||||
stacks = [parser.Stack.load(cnxt, stack=db_stack)]
|
||||
else:
|
||||
stacks = parser.Stack.load_all(cnxt, resolve_data=resolve_outputs)
|
||||
stacks = parser.Stack.load_all(cnxt)
|
||||
|
||||
return [api.format_stack(
|
||||
stack, resolve_outputs=resolve_outputs) for stack in stacks]
|
||||
|
@ -1307,7 +1306,7 @@ class EngineService(service.Service):
|
|||
:return: list of stack outputs in defined format.
|
||||
"""
|
||||
s = self._get_stack(cntx, stack_identity)
|
||||
stack = parser.Stack.load(cntx, stack=s, resolve_data=False)
|
||||
stack = parser.Stack.load(cntx, stack=s)
|
||||
|
||||
return api.format_stack_outputs(stack, stack.t[stack.t.OUTPUTS])
|
||||
|
||||
|
@ -1321,7 +1320,7 @@ class EngineService(service.Service):
|
|||
:return: dict with output key, value and description in defined format.
|
||||
"""
|
||||
s = self._get_stack(cntx, stack_identity)
|
||||
stack = parser.Stack.load(cntx, stack=s, resolve_data=False)
|
||||
stack = parser.Stack.load(cntx, stack=s)
|
||||
|
||||
outputs = stack.t[stack.t.OUTPUTS]
|
||||
|
||||
|
@ -1330,9 +1329,6 @@ class EngineService(service.Service):
|
|||
'found.') % output_key)
|
||||
output = stack.resolve_outputs_data({output_key: outputs[output_key]})
|
||||
|
||||
if not stack.outputs:
|
||||
stack.outputs.update(output)
|
||||
|
||||
return api.format_stack_output(stack, output, output_key)
|
||||
|
||||
def _remote_call(self, cnxt, lock_engine_id, call, **kwargs):
|
||||
|
@ -2332,8 +2328,7 @@ class EngineService(service.Service):
|
|||
|
||||
stk = parser.Stack.load(cnxt, stack=s,
|
||||
service_check_defer=True,
|
||||
resource_validate=False,
|
||||
resolve_data=False)
|
||||
resource_validate=False)
|
||||
LOG.info(_LI('Engine %(engine)s went down when stack '
|
||||
'%(stack_id)s was in action %(action)s'),
|
||||
{'engine': engine_id, 'action': stk.action,
|
||||
|
|
|
@ -118,7 +118,7 @@ class Stack(collections.Mapping):
|
|||
|
||||
def __init__(self, context, stack_name, tmpl,
|
||||
stack_id=None, action=None, status=None,
|
||||
status_reason='', timeout_mins=None, resolve_data=True,
|
||||
status_reason='', timeout_mins=None,
|
||||
disable_rollback=True, parent_resource=None, owner_id=None,
|
||||
adopt_stack_data=None, stack_user_project_id=None,
|
||||
created_time=None, updated_time=None,
|
||||
|
@ -168,6 +168,7 @@ class Stack(collections.Mapping):
|
|||
self.disable_rollback = disable_rollback
|
||||
self.parent_resource_name = parent_resource
|
||||
self._parent_stack = None
|
||||
self._outputs = None
|
||||
self._resources = None
|
||||
self._dependencies = None
|
||||
self._access_allowed_handlers = {}
|
||||
|
@ -227,12 +228,6 @@ class Stack(collections.Mapping):
|
|||
param_defaults=self.env.param_defaults)
|
||||
self._set_param_stackid()
|
||||
|
||||
if resolve_data:
|
||||
self.outputs = self.resolve_outputs_data(
|
||||
self.t[self.t.OUTPUTS], path=self.t.OUTPUTS)
|
||||
else:
|
||||
self.outputs = {}
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
if self._tags is None:
|
||||
|
@ -299,6 +294,13 @@ class Stack(collections.Mapping):
|
|||
msg = _("Attempt to use stored_context with no user_creds")
|
||||
raise exception.Error(msg)
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
if self._outputs is None:
|
||||
self._outputs = self.resolve_outputs_data(self.t[self.t.OUTPUTS],
|
||||
path=self.t.OUTPUTS)
|
||||
return self._outputs
|
||||
|
||||
@property
|
||||
def resources(self):
|
||||
if self._resources is None:
|
||||
|
@ -484,7 +486,7 @@ class Stack(collections.Mapping):
|
|||
@classmethod
|
||||
def load(cls, context, stack_id=None, stack=None, show_deleted=True,
|
||||
use_stored_context=False, force_reload=False, cache_data=None,
|
||||
resolve_data=True, service_check_defer=False,
|
||||
service_check_defer=False,
|
||||
resource_validate=True):
|
||||
"""Retrieve a Stack from the database."""
|
||||
if stack is None:
|
||||
|
@ -501,14 +503,14 @@ class Stack(collections.Mapping):
|
|||
|
||||
return cls._from_db(context, stack,
|
||||
use_stored_context=use_stored_context,
|
||||
cache_data=cache_data, resolve_data=resolve_data,
|
||||
cache_data=cache_data,
|
||||
service_check_defer=service_check_defer,
|
||||
resource_validate=resource_validate)
|
||||
|
||||
@classmethod
|
||||
def load_all(cls, context, limit=None, marker=None, sort_keys=None,
|
||||
sort_dir=None, filters=None,
|
||||
show_deleted=False, resolve_data=True,
|
||||
show_deleted=False,
|
||||
show_nested=False, show_hidden=False, tags=None,
|
||||
tags_any=None, not_tags=None, not_tags_any=None):
|
||||
stacks = stack_object.Stack.get_all(
|
||||
|
@ -527,14 +529,14 @@ class Stack(collections.Mapping):
|
|||
not_tags_any=not_tags_any)
|
||||
for stack in stacks:
|
||||
try:
|
||||
yield cls._from_db(context, stack, resolve_data=resolve_data)
|
||||
yield cls._from_db(context, stack)
|
||||
except exception.NotFound:
|
||||
# We're in a different transaction than the get_all, so a stack
|
||||
# returned above can be deleted by the time we try to load it.
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _from_db(cls, context, stack, resolve_data=True,
|
||||
def _from_db(cls, context, stack,
|
||||
use_stored_context=False, cache_data=None,
|
||||
service_check_defer=False, resource_validate=True):
|
||||
template = tmpl.Template.load(
|
||||
|
@ -544,7 +546,6 @@ class Stack(collections.Mapping):
|
|||
action=stack.action, status=stack.status,
|
||||
status_reason=stack.status_reason,
|
||||
timeout_mins=stack.timeout,
|
||||
resolve_data=resolve_data,
|
||||
disable_rollback=stack.disable_rollback,
|
||||
parent_resource=stack.parent_resource_name,
|
||||
owner_id=stack.owner_id,
|
||||
|
@ -1517,9 +1518,7 @@ class Stack(collections.Mapping):
|
|||
# flip the template to the newstack values
|
||||
previous_template_id = self.t.id
|
||||
self.t = newstack.t
|
||||
template_outputs = self.t[self.t.OUTPUTS]
|
||||
self.outputs = self.resolve_outputs_data(
|
||||
template_outputs, path=self.t.OUTPUTS)
|
||||
self._outputs = None
|
||||
finally:
|
||||
if should_rollback:
|
||||
# Already handled in rollback task
|
||||
|
|
|
@ -1027,8 +1027,7 @@ class StackServiceTest(common.HeatTestCase):
|
|||
t['outputs'] = {'test': {'value': 'first', 'description': 'sec'},
|
||||
'test2': {'value': 'sec'}}
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl,
|
||||
resolve_data=False)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl)
|
||||
|
||||
self.patchobject(self.eng, '_get_stack')
|
||||
self.patchobject(parser.Stack, 'load', return_value=stack)
|
||||
|
@ -1050,8 +1049,7 @@ class StackServiceTest(common.HeatTestCase):
|
|||
t = template_format.parse(tools.wp_template)
|
||||
t['outputs'] = {'test': {'value': 'first', 'description': 'sec'}}
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl,
|
||||
resolve_data=False)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl)
|
||||
|
||||
self.patchobject(self.eng, '_get_stack')
|
||||
self.patchobject(parser.Stack, 'load', return_value=stack)
|
||||
|
@ -1311,8 +1309,7 @@ class StackServiceTest(common.HeatTestCase):
|
|||
mock_stack_load.assert_called_once_with(self.ctx,
|
||||
stack=db_stack,
|
||||
service_check_defer=True,
|
||||
resource_validate=False,
|
||||
resolve_data=False)
|
||||
resource_validate=False)
|
||||
self.assertTrue(lock2.release.called)
|
||||
mock_thread.start_with_acquired_lock.assert_called_once_with(
|
||||
fake_stack, lock1,
|
||||
|
|
|
@ -425,7 +425,7 @@ class StackTest(common.HeatTestCase):
|
|||
stack.Stack.__init__(self.ctx, stk.name, t, stack_id=stk.id,
|
||||
action=stk.action, status=stk.status,
|
||||
status_reason=stk.status_reason,
|
||||
timeout_mins=stk.timeout, resolve_data=True,
|
||||
timeout_mins=stk.timeout,
|
||||
disable_rollback=stk.disable_rollback,
|
||||
parent_resource='parent', owner_id=None,
|
||||
stack_user_project_id=None,
|
||||
|
@ -2717,7 +2717,7 @@ class StackTest(common.HeatTestCase):
|
|||
mock_dependency.validate.assert_called_once_with()
|
||||
|
||||
stc = stack.Stack(self.ctx, utils.random_name(), self.tmpl)
|
||||
stc.outputs = {'foo': {'Value': 'bar'}}
|
||||
stc._outputs = {'foo': {'Value': 'bar'}}
|
||||
func_val.side_effect = AssertionError(expected_msg)
|
||||
expected_exception = self.assertRaises(AssertionError, stc.validate)
|
||||
self.assertEqual(expected_msg, six.text_type(expected_exception))
|
||||
|
@ -2727,8 +2727,7 @@ class StackTest(common.HeatTestCase):
|
|||
expected_message = 'Expected Assertion Error'
|
||||
tmpl.parse.side_effect = AssertionError(expected_message)
|
||||
|
||||
stc = stack.Stack(self.ctx, utils.random_name(),
|
||||
tmpl, resolve_data=False)
|
||||
stc = stack.Stack(self.ctx, utils.random_name(), tmpl)
|
||||
expected_exception = self.assertRaises(AssertionError,
|
||||
stc.resolve_outputs_data,
|
||||
None)
|
||||
|
|
|
@ -320,8 +320,7 @@ class TestTemplateConditionParser(common.HeatTestCase):
|
|||
{'get_attr': [None, 'att']}]}}}
|
||||
# test with get_attr in equals
|
||||
tmpl = template.Template(t)
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl,
|
||||
resolve_data=False)
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl)
|
||||
ex = self.assertRaises(exception.InvalidConditionFunction,
|
||||
tmpl._resolve_conditions, stk)
|
||||
self.assertIn('The function is not supported in condition: get_attr',
|
||||
|
@ -329,8 +328,7 @@ class TestTemplateConditionParser(common.HeatTestCase):
|
|||
|
||||
# test with get_resource in top level of a condition
|
||||
tmpl.t['conditions']['prod_env'] = {'get_resource': 'R1'}
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl,
|
||||
resolve_data=False)
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl)
|
||||
ex = self.assertRaises(exception.InvalidConditionFunction,
|
||||
tmpl._resolve_conditions, stk)
|
||||
self.assertIn('The function is not supported in condition: '
|
||||
|
@ -338,8 +336,7 @@ class TestTemplateConditionParser(common.HeatTestCase):
|
|||
|
||||
# test with get_attr in top level of a condition
|
||||
tmpl.t['conditions']['prod_env'] = {'get_attr': [None, 'att']}
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl,
|
||||
resolve_data=False)
|
||||
stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl)
|
||||
ex = self.assertRaises(exception.InvalidConditionFunction,
|
||||
tmpl._resolve_conditions, stk)
|
||||
self.assertIn('The function is not supported in condition: get_attr',
|
||||
|
|
|
@ -1622,8 +1622,9 @@ class ValidateTest(common.HeatTestCase):
|
|||
def test_validate_invalid_outputs(self):
|
||||
t = template_format.parse(test_template_invalid_outputs)
|
||||
template = tmpl.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'test_stack', template)
|
||||
err = self.assertRaises(exception.StackValidationFailed,
|
||||
parser.Stack, self.ctx, 'test_stack', template)
|
||||
stack.validate)
|
||||
error_message = ('outputs.string.value.get_attr: Arguments to '
|
||||
'"get_attr" must be of the form '
|
||||
'[resource_name, attribute, (path), ...]')
|
||||
|
|
Loading…
Reference in New Issue