From: Steve Baker Date: Fri, 8 Feb 2013 01:54:57 +0000 (+1300) Subject: Validation failures now raise StackValidationFailed X-Git-Tag: 2014.1~815^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=cadcf10ac381eaea4d0f695fddc25e5ed1477160;p=openstack-build%2Fheat-build.git Validation failures now raise StackValidationFailed This changes the way validation failures are presented to heat-cfn as per bug 1072939 Change-Id: Iddaaf0c83c5d0df7c0143c6805096a83f8e5dc51 --- diff --git a/heat/api/aws/exception.py b/heat/api/aws/exception.py index 2a58212f..b4ece2b3 100644 --- a/heat/api/aws/exception.py +++ b/heat/api/aws/exception.py @@ -253,6 +253,7 @@ def map_remote_error(ex): 'PhysicalResourceNotFound', 'WatchRuleNotFound', 'StackExists', + 'StackValidationFailed', ) denied_errors = ('Forbidden', 'NotAuthorized') diff --git a/heat/api/openstack/v1/stacks.py b/heat/api/openstack/v1/stacks.py index bf329b1f..162890a1 100644 --- a/heat/api/openstack/v1/stacks.py +++ b/heat/api/openstack/v1/stacks.py @@ -183,9 +183,6 @@ class StackController(object): except rpc_common.RemoteError as ex: return util.remote_error(ex) - if 'Description' in result: - raise exc.HTTPBadRequest(result['Description']) - raise exc.HTTPCreated(location=util.make_url(req, result)) @util.tenant_local @@ -259,9 +256,6 @@ class StackController(object): except rpc_common.RemoteError as ex: return util.remote_error(ex) - if 'Description' in res: - raise exc.HTTPBadRequest(res['Description']) - raise exc.HTTPAccepted() @util.identified_stack diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py index ebe7c2cc..f3aebc1f 100644 --- a/heat/api/openstack/v1/util.py +++ b/heat/api/openstack/v1/util.py @@ -96,6 +96,7 @@ def remote_error(ex): 'PhysicalResourceNotFound': exc.HTTPNotFound, 'InvalidTenant': exc.HTTPForbidden, 'StackExists': exc.HTTPConflict, + 'StackValidationFailed': exc.HTTPBadRequest, } Exc = error_map.get(ex.exc_type, exc.HTTPInternalServerError) diff --git a/heat/common/exception.py b/heat/common/exception.py index 7d61379a..00383559 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -221,6 +221,10 @@ class StackExists(OpenstackException): message = _("The Stack (%(stack_name)s) already exists.") +class StackValidationFailed(OpenstackException): + message = _("%(message)s") + + class ResourceNotFound(OpenstackException): message = _("The Resource (%(resource_name)s) could not be found " "in Stack %(stack_name)s.") diff --git a/heat/engine/parser.py b/heat/engine/parser.py index da0157d2..4d627492 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -28,6 +28,7 @@ from heat.engine.clients import Clients from heat.db import api as db_api from heat.openstack.common import log as logging +from heat.common.exception import StackValidationFailed logger = logging.getLogger(__name__) @@ -224,11 +225,9 @@ class Stack(object): try: result = res.validate() except Exception as ex: - logger.exception('validate') - result = str(ex) - + raise StackValidationFailed(message=str(ex)) if result: - return 'Malformed Query Response %s' % result + raise StackValidationFailed(message=result) def state_set(self, new_status, reason): '''Update the stack state in the database''' diff --git a/heat/engine/service.py b/heat/engine/service.py index ee8bbceb..a58b0512 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -216,9 +216,7 @@ class EngineService(service.Service): stack = parser.Stack(context, stack_name, tmpl, template_params, **common_params) - response = stack.validate() - if response: - return {'Description': response} + stack.validate() stack_id = stack.store() @@ -256,9 +254,7 @@ class EngineService(service.Service): updated_stack = parser.Stack(context, stack_name, tmpl, template_params, **common_params) - response = updated_stack.validate() - if response: - return {'Description': response} + updated_stack.validate() self._start_in_thread(db_stack.id, current_stack.update, updated_stack) diff --git a/heat/tests/test_api_cfn_v1.py b/heat/tests/test_api_cfn_v1.py index c8a1cbe8..911f4167 100644 --- a/heat/tests/test_api_cfn_v1.py +++ b/heat/tests/test_api_cfn_v1.py @@ -589,7 +589,6 @@ class CfnStackControllerTest(unittest.TestCase): # Stub out the RPC call to the engine with a pre-canned response self.m.StubOutWithMock(rpc, 'call') - engine_err = {'Description': 'Something went wrong'} rpc.call(dummy_req.context, self.topic, {'method': 'create_stack', @@ -597,15 +596,17 @@ class CfnStackControllerTest(unittest.TestCase): 'template': template, 'params': engine_parms, 'args': engine_args}, - 'version': self.api_version}, None).AndReturn(engine_err) + 'version': self.api_version}, None).AndRaise( + rpc_common.RemoteError( + 'StackValidationFailed', + 'Something went wrong')) self.m.ReplayAll() result = self.controller.create(dummy_req) - expected = {'CreateStackResponse': {'CreateStackResult': engine_err}} - - self.assertEqual(result, expected) + self.assertEqual(type(result), + exception.HeatInvalidParameterValueError) self.m.VerifyAll() def test_update(self): diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index aaf0fe71..d1a5edeb 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -419,7 +419,6 @@ class StackControllerTest(ControllerTest, unittest.TestCase): req = self._post('/stacks', json.dumps(body)) self.m.StubOutWithMock(rpc, 'call') - engine_err = {'Description': 'Something went wrong'} rpc.call(req.context, self.topic, {'method': 'create_stack', 'args': {'stack_name': stack_name, @@ -427,7 +426,9 @@ class StackControllerTest(ControllerTest, unittest.TestCase): 'params': parameters, 'args': {'timeout_mins': 30}}, 'version': self.api_version}, - None).AndReturn(engine_err) + None).AndRaise(rpc_common.RemoteError( + 'StackValidationFailed', + 'Something went wrong')) self.m.ReplayAll() self.assertRaises(webob.exc.HTTPBadRequest, diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index ff71a259..81880972 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -29,6 +29,7 @@ from heat.common import identifier from heat.common import template_format from heat.engine import parser from heat.engine import service +from heat.engine.properties import Properties from heat.engine.resources import instance as instances from heat.engine import watchrule from heat.openstack.common import threadgroup @@ -215,14 +216,16 @@ class stackServiceCreateUpdateDeleteTest(unittest.TestCase): stack.t, stack.parameters).AndReturn(stack) self.m.StubOutWithMock(stack, 'validate') - error = 'fubar' - stack.validate().AndReturn(error) + stack.validate().AndRaise(exception.StackValidationFailed( + message='fubar')) self.m.ReplayAll() - result = self.man.create_stack(self.ctx, stack_name, - template, params, {}) - self.assertEqual(result, {'Description': error}) + self.assertRaises( + exception.StackValidationFailed, + self.man.create_stack, + self.ctx, stack_name, + template, params, {}) self.m.VerifyAll() def test_stack_create_invalid_stack_name(self): @@ -245,6 +248,34 @@ class stackServiceCreateUpdateDeleteTest(unittest.TestCase): self.ctx, stack_name, stack.t, {}, {}) + def test_stack_validate(self): + stack_name = 'service_create_test_validate' + stack = get_wordpress_stack(stack_name, self.ctx) + setup_mocks(self.m, stack) + + template = dict(stack.t) + template['Parameters']['KeyName']['Default'] = 'test' + resource = stack['WebServer'] + + self.m.ReplayAll() + + resource.properties = Properties( + resource.properties_schema, + { + 'ImageId': 'foo', + 'KeyName': 'test', + 'InstanceType': 'm1.large' + }) + stack.validate() + + resource.properties = Properties( + resource.properties_schema, + { + 'KeyName': 'test', + 'InstanceType': 'm1.large' + }) + self.assertRaises(exception.StackValidationFailed, stack.validate) + def test_stack_delete(self): stack_name = 'service_delete_test_stack' stack = get_wordpress_stack(stack_name, self.ctx) @@ -336,14 +367,16 @@ class stackServiceCreateUpdateDeleteTest(unittest.TestCase): stack.t, stack.parameters).AndReturn(stack) self.m.StubOutWithMock(stack, 'validate') - error = 'fubar' - stack.validate().AndReturn(error) + stack.validate().AndRaise(exception.StackValidationFailed( + message='fubar')) self.m.ReplayAll() - result = self.man.update_stack(self.ctx, old_stack.identifier(), - template, params, {}) - self.assertEqual(result, {'Description': error}) + self.assertRaises( + exception.StackValidationFailed, + self.man.update_stack, + self.ctx, old_stack.identifier(), + template, params, {}) self.m.VerifyAll() def test_stack_update_nonexist(self): diff --git a/heat/tests/test_properties.py b/heat/tests/test_properties.py index 435ab787..ac7dd6d9 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -316,7 +316,7 @@ class PropertiesValidationTest(unittest.TestCase): def test_missing_required(self): schema = {'foo': {'Type': 'String', 'Required': True}} props = properties.Properties(schema, {}) - self.assertNotEqual(props.validate(), None) + self.assertEqual(props.validate(), 'Property foo not assigned') def test_missing_unimplemented(self): schema = {'foo': {'Type': 'String', 'Implemented': False}} @@ -326,7 +326,7 @@ class PropertiesValidationTest(unittest.TestCase): def test_present_unimplemented(self): schema = {'foo': {'Type': 'String', 'Implemented': False}} props = properties.Properties(schema, {'foo': 'bar'}) - self.assertNotEqual(props.validate(), None) + self.assertEqual(props.validate(), 'foo Property not implemented yet') def test_missing(self): schema = {'foo': {'Type': 'String'}} @@ -336,4 +336,4 @@ class PropertiesValidationTest(unittest.TestCase): def test_bad_data(self): schema = {'foo': {'Type': 'String'}} props = properties.Properties(schema, {'foo': 42}) - self.assertNotEqual(props.validate(), None) + self.assertEqual(props.validate(), 'foo Value must be a string')