From: Randall Burt Date: Wed, 17 Jul 2013 00:31:25 +0000 (-0500) Subject: Add tests for resource-data delete bug. X-Git-Tag: 2014.1~360^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=5b9d98fb6fd1a0459474dad196b9f63c72ef6ee7;p=openstack-build%2Fheat-build.git Add tests for resource-data delete bug. Fixes bug 1201974 Change-Id: Id5fdfcde8541e83f68406c67250b815b35fbbf54 --- diff --git a/heat/db/api.py b/heat/db/api.py index 23cc8b9b..e4816120 100644 --- a/heat/db/api.py +++ b/heat/db/api.py @@ -66,6 +66,18 @@ def raw_template_create(context, values): 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) diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index 98e85c8a..72aa18a8 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -99,50 +99,36 @@ def resource_get_all(context): 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): diff --git a/heat/tests/test_nested_stack.py b/heat/tests/test_nested_stack.py index 45fef55e..9de472be 100644 --- a/heat/tests/test_nested_stack.py +++ b/heat/tests/test_nested_stack.py @@ -16,12 +16,14 @@ 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 @@ -113,3 +115,39 @@ Outputs: 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')