From 7cd0d244640c188e40448841af50352deee7c45a Mon Sep 17 00:00:00 2001 From: Crag Wolfe Date: Wed, 19 Oct 2016 17:59:41 -0700 Subject: [PATCH] Allow lazy load of raw_template but log warning To avoid hard-to-debug bugs, rather than represent a db_stack's raw_template as None if it hasn't been eagerly loaded, lazy load it. In the lazy loading case, log a warning so developers are aware there is a non-eager loading query that needs to be fixed. Change-Id: Ic06a4bd7afbd4838ca97be302d75dede5402ae6e --- heat/objects/stack.py | 26 +++++++++++++++++++++++--- heat/tests/test_stack.py | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/heat/objects/stack.py b/heat/objects/stack.py index cd52cbd2c4..a6904865d3 100644 --- a/heat/objects/stack.py +++ b/heat/objects/stack.py @@ -15,6 +15,7 @@ """Stack object.""" +from oslo_log import log as logging from oslo_versionedobjects import base from oslo_versionedobjects import fields import six @@ -28,6 +29,8 @@ from heat.objects import fields as heat_fields from heat.objects import raw_template from heat.objects import stack_tag +LOG = logging.getLogger(__name__) + class Stack( heat_base.HeatObject, @@ -53,7 +56,7 @@ class Stack( 'action': fields.StringField(nullable=True), 'status': fields.StringField(nullable=True), 'status_reason': fields.StringField(nullable=True), - 'raw_template': fields.ObjectField('RawTemplate'), + 'raw_template_obj': fields.ObjectField('RawTemplate'), 'convergence': fields.BooleanField(), 'current_traversal': fields.StringField(), 'current_deps': heat_fields.JsonField(), @@ -65,7 +68,7 @@ class Stack( @staticmethod def _from_db_object(context, stack, db_stack): for field in stack.fields: - if field == 'raw_template': + if field == 'raw_template_obj': raw_template_obj = db_stack.__dict__.get('raw_template') if raw_template_obj is not None: # Object is already lazy loaded @@ -74,13 +77,30 @@ class Stack( context, raw_template.RawTemplate(), raw_template_obj)) - stack['raw_template'] = raw_template_obj + stack._raw_template = raw_template_obj else: stack[field] = db_stack.__dict__.get(field) stack._context = context stack.obj_reset_changes() return stack + @property + def raw_template(self): + if hasattr(self, '_raw_template'): + return self._raw_template + + LOG.warning('Loading a raw_template that should have been ' + 'eagerly loaded for stack id %s' % self.id) + self._raw_template = raw_template.RawTemplate.get_by_id( + self._context, + self['raw_template_id']) + return self._raw_template + + @raw_template.setter + def raw_template(self, value): + self['raw_template_obj'] = value + self._raw_template = value + @classmethod def get_root_id(cls, context, stack_id): return db_api.stack_get_root_id(context, stack_id) diff --git a/heat/tests/test_stack.py b/heat/tests/test_stack.py index 94f42ea2c1..bae2f1d516 100644 --- a/heat/tests/test_stack.py +++ b/heat/tests/test_stack.py @@ -1710,6 +1710,28 @@ class StackTest(common.HeatTestCase): self.stack.state) self.m.VerifyAll() + def test_stack_eager_or_lazy_load_templ(self): + self.stack = stack.Stack(self.ctx, 'test_stack_eager_or_lazy_tmpl', + self.tmpl) + self.stack.store() + + ctx1 = utils.dummy_context() + s1_db_result = db_api.stack_get(ctx1, self.stack.id, eager_load=True) + s1_obj = stack_object.Stack._from_db_object(ctx1, stack_object.Stack(), + s1_db_result) + self.assertIsNotNone(s1_obj._raw_template) + self.assertIsNotNone(s1_obj.raw_template) + + ctx2 = utils.dummy_context() + s2_db_result = db_api.stack_get(ctx2, self.stack.id, eager_load=False) + s2_obj = stack_object.Stack._from_db_object(ctx2, stack_object.Stack(), + s2_db_result) + # _raw_template has not been set since it not eagerly loaded + self.assertFalse(hasattr(s2_obj, "_raw_template")) + # accessing raw_template lazy loads it + self.assertIsNotNone(s2_obj.raw_template) + self.assertIsNotNone(s2_obj._raw_template) + def test_preview_resources_returns_list_of_resource_previews(self): tmpl = {'HeatTemplateFormatVersion': '2012-12-12', 'Resources': {'AResource': {'Type': 'GenericResourceType'}}}