return IMPL.raw_template_create(context, values)
+def resource_data_get(resource, key):
+ return IMPL.resource_data_get(resource, key)
+
+
+def resource_data_set(resource, key, value, redact=False):
+ return IMPL.resource_data_set(resource, key, value, redact=redact)
+
+
+def resource_data_get_by_key(context, resource_id, key):
+ return IMPL.resource_data_get_by_key(context, resource_id, key)
+
+
def resource_get(context, resource_id):
return IMPL.resource_get(context, resource_id)
def resource_data_get(resource, key):
"""Lookup value of resource's data by key."""
- data_lst = filter(lambda x: x.key == key, resource.data)
- if not data_lst:
- return None
- assert len(data_lst) == 1
- data = data_lst[0]
+ result = resource_data_get_by_key(resource.context, resource.id, key)
+ return result.value
+
- if data.redact:
- return crypt.decrypt(data.value)
- else:
- return data.value
+def resource_data_get_by_key(context, resource_id, key):
+ result = (model_query(context, models.ResourceData)
+ .filter_by(resource_id=resource_id)
+ .filter_by(key=key)
+ .first())
+ if not result:
+ raise exception.NotFound('No resource data found')
+ if result.redact and result.value:
+ result.value = crypt.decrypt(result.value)
+ return result
def resource_data_set(resource, key, value, redact=False):
"""Save resource's key/value pair to database."""
if redact:
value = crypt.encrypt(value)
- data_lst = filter(lambda x: x.key == key, resource.data)
-
- if data_lst: # Key exists in db, so check value & replace if necessary
- assert len(data_lst) == 1
- resource_data = data_lst[0]
-
- # If the new value is the same, do nothing
- if value == resource_data.value:
- return None
-
- # Otherwise, delete the old value
- for i, d in enumerate(resource.data):
- if d.key == key:
- index = i
- del(resource.data[index])
-
- else: # Build a new key/value
- resource_data = models.ResourceData()
- resource_data.key = key
- resource_data.resource_id = resource.id
- resource_data.redact = True
-
- resource_data.value = value
- resource.data.append(resource_data)
-
- # Save to new key/value pair to database
- rs = model_query(resource.context, models.Resource).get(resource.id)
- rs.update_and_save({'data': resource.data})
+ try:
+ current = resource_data_get_by_key(resource.context, resource.id, key)
+ except exception.NotFound:
+ current = models.ResourceData()
+ current.key = key
+ current.resource_id = resource.id
+ current.redact = redact
+ current.value = value
+ current.save()
+ return current
def resource_create(context, values):
from heat.common import context
from heat.common import exception
from heat.common import template_format
+from heat.common import urlfetch
+from heat.db import api as db_api
from heat.engine import parser
from heat.engine import resource
from heat.engine import scheduler
-from heat.common import urlfetch
-from heat.tests.common import HeatTestCase
+from heat.tests import generic_resource as generic_rsrc
from heat.tests import utils
+from heat.tests.common import HeatTestCase
from heat.tests.utils import setup_dummy_db
rsrc.delete()
self.m.VerifyAll()
+
+
+class ResDataResource(generic_rsrc.GenericResource):
+ def handle_create(self):
+ db_api.resource_data_set(self, "test", 'A secret value', True)
+
+
+class ResDataNestedStackTest(NestedStackTest):
+
+ nested_template = '''
+HeatTemplateFormatVersion: "2012-12-12"
+Parameters:
+ KeyName:
+ Type: String
+Resources:
+ nested_res:
+ Type: "res.data.resource"
+Outputs:
+ Foo:
+ Value: bar
+'''
+
+ def setUp(self):
+ resource._register_class("res.data.resource", ResDataResource)
+ super(ResDataNestedStackTest, self).setUp()
+
+ def test_res_data_delete(self):
+ urlfetch.get('https://localhost/the.template').AndReturn(
+ self.nested_template)
+ self.m.ReplayAll()
+ stack = self.create_stack(self.test_template)
+ res = stack['the_nested'].nested()['nested_res']
+ stack.delete()
+ self.assertEqual(stack.state, (stack.DELETE, stack.COMPLETE))
+ self.assertRaises(exception.NotFound, db_api.resource_data_get, res,
+ 'test')