Add resource_properties_data assoc. to resource, event objs

Add the resource_properties_data association to resource and event
objects. The resource and event engine objects do not use yet
it but will soon.

Change-Id: Idecaafffbc5e9bfcd2355e2a165836a5ed89b16f
This commit is contained in:
Crag Wolfe 2016-08-30 00:02:27 -04:00
parent 1ea6bb8bf6
commit 791c245c14
4 changed files with 109 additions and 8 deletions

View File

@ -20,7 +20,7 @@ from oslo_versionedobjects import fields
from heat.common import identifier from heat.common import identifier
from heat.db.sqlalchemy import api as db_api from heat.db.sqlalchemy import api as db_api
from heat.objects import base as heat_base from heat.objects import base as heat_base
from heat.objects import fields as heat_fields from heat.objects import resource_properties_data as rpd
class Event( class Event(
@ -37,7 +37,8 @@ class Event(
'physical_resource_id': fields.StringField(nullable=True), 'physical_resource_id': fields.StringField(nullable=True),
'resource_status_reason': fields.StringField(nullable=True), 'resource_status_reason': fields.StringField(nullable=True),
'resource_type': fields.StringField(nullable=True), 'resource_type': fields.StringField(nullable=True),
'resource_properties': heat_fields.JsonField(nullable=True), 'rsrc_prop_data': fields.ObjectField(
rpd.ResourcePropertiesData),
'created_at': fields.DateTimeField(read_only=True), 'created_at': fields.DateTimeField(read_only=True),
'updated_at': fields.DateTimeField(nullable=True), 'updated_at': fields.DateTimeField(nullable=True),
} }
@ -46,10 +47,22 @@ class Event(
def _from_db_object(context, event, db_event): def _from_db_object(context, event, db_event):
for field in event.fields: for field in event.fields:
event[field] = db_event[field] event[field] = db_event[field]
if db_event['rsrc_prop_data']:
event['rsrc_prop_data'] = \
rpd.ResourcePropertiesData._from_db_object(
rpd.ResourcePropertiesData(context), context,
db_event['rsrc_prop_data'])
event._resource_properties = event['rsrc_prop_data'].data
else:
event._resource_properties = db_event['resource_properties'] or {}
event._context = context event._context = context
event.obj_reset_changes() event.obj_reset_changes()
return event return event
@property
def resource_properties(self):
return self._resource_properties
@classmethod @classmethod
def get_by_id(cls, context, event_id): def get_by_id(cls, context, event_id):
db_event = db_api.event_get(context, event_id) db_event = db_api.event_get(context, event_id)

View File

@ -18,6 +18,7 @@
import collections import collections
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging
from oslo_versionedobjects import base from oslo_versionedobjects import base
from oslo_versionedobjects import fields from oslo_versionedobjects import fields
import six import six
@ -26,13 +27,17 @@ import tenacity
from heat.common import crypt from heat.common import crypt
from heat.common import exception from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.common.i18n import _LE
from heat.db.sqlalchemy import api as db_api from heat.db.sqlalchemy import api as db_api
from heat.objects import base as heat_base from heat.objects import base as heat_base
from heat.objects import fields as heat_fields from heat.objects import fields as heat_fields
from heat.objects import resource_data from heat.objects import resource_data
from heat.objects import resource_properties_data as rpd
cfg.CONF.import_opt('encrypt_parameters_and_properties', 'heat.common.config') cfg.CONF.import_opt('encrypt_parameters_and_properties', 'heat.common.config')
LOG = logging.getLogger(__name__)
def retry_on_conflict(func): def retry_on_conflict(func):
wrapper = tenacity.retry( wrapper = tenacity.retry(
@ -74,12 +79,14 @@ class Resource(
'status_reason': fields.StringField(nullable=True), 'status_reason': fields.StringField(nullable=True),
'action': fields.StringField(nullable=True), 'action': fields.StringField(nullable=True),
'rsrc_metadata': heat_fields.JsonField(nullable=True), 'rsrc_metadata': heat_fields.JsonField(nullable=True),
'properties_data': heat_fields.JsonField(nullable=True),
'properties_data_encrypted': fields.BooleanField(default=False),
'data': fields.ListOfObjectsField( 'data': fields.ListOfObjectsField(
resource_data.ResourceData, resource_data.ResourceData,
nullable=True nullable=True
), ),
'rsrc_prop_data': fields.ObjectField(
rpd.ResourcePropertiesData, nullable=True),
'rsrc_prop_data_id': fields.ObjectField(
fields.IntegerField(nullable=True)),
'engine_id': fields.StringField(nullable=True), 'engine_id': fields.StringField(nullable=True),
'atomic_key': fields.IntegerField(nullable=True), 'atomic_key': fields.IntegerField(nullable=True),
'current_template_id': fields.IntegerField(), 'current_template_id': fields.IntegerField(),
@ -102,14 +109,38 @@ class Resource(
else: else:
resource[field] = db_resource[field] resource[field] = db_resource[field]
if resource.properties_data_encrypted and resource.properties_data: if db_resource['rsrc_prop_data'] is not None:
decrypted_data = crypt.decrypted_dict(resource.properties_data) resource['rsrc_prop_data'] = \
resource.properties_data = decrypted_data rpd.ResourcePropertiesData._from_db_object(
rpd.ResourcePropertiesData(context), context,
db_resource['rsrc_prop_data'])
resource._properties_data = resource['rsrc_prop_data'].data
if db_resource['properties_data']:
LOG.error(
_LE('Unexpected condition where resource.rsrc_prop_data '
'and resource.properties_data are both not null. '
'rsrc_prop_data.id: %(rsrc_prop_data_id)s ,'
'resource id: %(res_id)s')
% {'rsrc_prop_data_id': resource['rsrc_prop_data'].id,
'res_id': resource['id']})
elif db_resource['properties_data']: # legacy field
if db_resource['properties_data_encrypted']:
decrypted_data = crypt.decrypted_dict(
db_resource['properties_data'])
resource._properties_data = decrypted_data
else:
resource._properties_data = db_resource['properties_data']
else:
resource._properties_data = {}
resource._context = context resource._context = context
resource.obj_reset_changes() resource.obj_reset_changes()
return resource return resource
@property
def properties_data(self):
return self._properties_data
@classmethod @classmethod
def get_obj(cls, context, resource_id, refresh=False): def get_obj(cls, context, resource_id, refresh=False):
resource_db = db_api.resource_get(context, resource_id, resource_db = db_api.resource_get(context, resource_id,

View File

@ -14,12 +14,15 @@
import mock import mock
from oslo_config import cfg from oslo_config import cfg
import oslo_db.exception import oslo_db.exception
import uuid
from heat.db.sqlalchemy import models
from heat.engine import event from heat.engine import event
from heat.engine import rsrc_defn from heat.engine import rsrc_defn
from heat.engine import stack from heat.engine import stack
from heat.engine import template from heat.engine import template
from heat.objects import event as event_object from heat.objects import event as event_object
from heat.objects import resource_properties_data as rpd_object
from heat.objects import stack as stack_object from heat.objects import stack as stack_object
from heat.tests import common from heat.tests import common
from heat.tests import generic_resource as generic_rsrc from heat.tests import generic_resource as generic_rsrc
@ -210,6 +213,24 @@ class EventTest(EventCommon):
'version': '0.1'}} 'version': '0.1'}}
self.assertEqual(expected, e.as_dict()) self.assertEqual(expected, e.as_dict())
def test_event_object_resource_properties_data(self):
cfg.CONF.set_override('encrypt_parameters_and_properties', True,
enforce_type=True)
data = {'p1': 'hello',
'p2': 'too soon?'}
rpd_obj = rpd_object.ResourcePropertiesData().create(self.ctx, data)
rpd_db_obj = self.ctx.session.query(
models.ResourcePropertiesData).get(rpd_obj.id)
e_obj = event_object.Event().create(
self.ctx,
{'stack_id': self.stack.id,
'uuid': str(uuid.uuid4()),
'rsrc_prop_data': rpd_db_obj})
e_obj = event_object.Event().get_by_id(utils.dummy_context(),
e_obj.id)
# properties data appears unencrypted to event object
self.assertEqual(data, e_obj.rsrc_prop_data.data)
class EventTestSingleLargeProp(EventCommon): class EventTestSingleLargeProp(EventCommon):

View File

@ -28,6 +28,7 @@ from heat.common.i18n import _
from heat.common import short_id from heat.common import short_id
from heat.common import timeutils from heat.common import timeutils
from heat.db.sqlalchemy import api as db_api from heat.db.sqlalchemy import api as db_api
from heat.db.sqlalchemy import models
from heat.engine import attributes from heat.engine import attributes
from heat.engine.cfn import functions as cfn_funcs from heat.engine.cfn import functions as cfn_funcs
from heat.engine import clients from heat.engine import clients
@ -48,6 +49,7 @@ from heat.engine import template
from heat.engine import translation from heat.engine import translation
from heat.objects import resource as resource_objects from heat.objects import resource as resource_objects
from heat.objects import resource_data as resource_data_object from heat.objects import resource_data as resource_data_object
from heat.objects import resource_properties_data as rpd_object
from heat.tests import common from heat.tests import common
from heat.tests import generic_resource as generic_rsrc from heat.tests import generic_resource as generic_rsrc
from heat.tests import utils from heat.tests import utils
@ -552,6 +554,40 @@ class ResourceTest(common.HeatTestCase):
res._store_or_update(res.UPDATE, res.COMPLETE, 'should not change') res._store_or_update(res.UPDATE, res.COMPLETE, 'should not change')
self.assertIsNone(res.updated_time) self.assertIsNone(res.updated_time)
def test_resource_object_resource_properties_data(self):
cfg.CONF.set_override('encrypt_parameters_and_properties', True,
enforce_type=True)
data = {'p1': 'i see',
'p2': 'good times, good times'}
rpd_obj = rpd_object.ResourcePropertiesData().create(
self.stack.context, data)
rpd_db_obj = self.stack.context.session.query(
models.ResourcePropertiesData).get(rpd_obj.id)
res_obj1 = resource_objects.Resource().create(
self.stack.context,
{'stack_id': self.stack.id,
'uuid': str(uuid.uuid4()),
'rsrc_prop_data': rpd_db_obj})
res_obj2 = resource_objects.Resource().create(
self.stack.context,
{'stack_id': self.stack.id,
'uuid': str(uuid.uuid4()),
'rsrc_prop_data_id': rpd_db_obj.id})
ctx2 = utils.dummy_context()
res_obj1 = resource_objects.Resource().get_obj(
ctx2, res_obj1.id)
res_obj2 = resource_objects.Resource().get_obj(
ctx2, res_obj2.id)
# verify the resource_properties_data association
# can be set by id or object
self.assertEqual(rpd_db_obj.id, res_obj1.rsrc_prop_data.id)
self.assertEqual(res_obj1.rsrc_prop_data.id,
res_obj2.rsrc_prop_data.id)
# properties data appears unencrypted to resource object
self.assertEqual(data, res_obj1.rsrc_prop_data.data)
self.assertEqual(data, res_obj2.rsrc_prop_data.data)
def test_store_or_update(self): def test_store_or_update(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo') tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo')
res = generic_rsrc.GenericResource('test_res_upd', tmpl, self.stack) res = generic_rsrc.GenericResource('test_res_upd', tmpl, self.stack)