diff --git a/heat/rpc/client.py b/heat/rpc/client.py index 33c2125e88..278f988c15 100644 --- a/heat/rpc/client.py +++ b/heat/rpc/client.py @@ -152,10 +152,22 @@ class EngineClient(object): :param files: files referenced from the environment. :param args: Request parameters/args passed from API """ + return self._create_stack(ctxt, stack_name, template, params, files, + args) + + def _create_stack(self, ctxt, stack_name, template, params, files, args, + owner_id=None): + """ + Internal create_stack interface for engine-to-engine communication via + RPC. Allows some additional options which should not be exposed to + users via the API: + :param owner_id: parent stack ID for nested stacks + """ return self.call(ctxt, self.make_msg('create_stack', stack_name=stack_name, template=template, - params=params, files=files, args=args)) + params=params, files=files, args=args, + owner_id=owner_id)) def update_stack(self, ctxt, stack_identity, template, params, files, args): diff --git a/heat/tests/test_api_cfn_v1.py b/heat/tests/test_api_cfn_v1.py index 1819672b71..d26c4443ba 100644 --- a/heat/tests/test_api_cfn_v1.py +++ b/heat/tests/test_api_cfn_v1.py @@ -511,7 +511,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndReturn(engine_resp) self.m.ReplayAll() @@ -557,7 +558,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndReturn(engine_resp) self.m.ReplayAll() @@ -603,7 +605,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndReturn(engine_resp) self.m.ReplayAll() @@ -649,7 +652,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndReturn(engine_resp) self.m.ReplayAll() @@ -695,7 +699,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndReturn(engine_resp) self.m.ReplayAll() @@ -779,7 +784,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndRaise(AttributeError()) policy.Enforcer.enforce(dummy_req.context, 'CreateStack' @@ -791,7 +797,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndRaise(heat_exception.UnknownUserParameter(key='test')) policy.Enforcer.enforce(dummy_req.context, 'CreateStack' @@ -803,7 +810,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndRaise(heat_exception.UserParameterMissing(key='test')) self.m.ReplayAll() @@ -842,7 +850,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndRaise(heat_exception.StackExists(stack_name='test')) self.m.ReplayAll() @@ -875,7 +884,8 @@ class CfnStackControllerTest(HeatTestCase): 'template': self.template, 'params': engine_parms, 'files': {}, - 'args': engine_args}) + 'args': engine_args, + 'owner_id': None}) ).AndRaise(heat_exception.StackValidationFailed( message='Something went wrong')) diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index da8e71288f..b805f0b78c 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -676,7 +676,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndReturn(dict(identity)) self.m.ReplayAll() @@ -713,7 +714,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {'my.yaml': 'This is the file contents.'}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndReturn(dict(identity)) self.m.ReplayAll() @@ -750,7 +752,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndRaise(to_remote_error(AttributeError())) rpc_client.EngineClient.call( req.context, @@ -760,7 +763,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndRaise(to_remote_error(unknown_parameter)) rpc_client.EngineClient.call( req.context, @@ -770,7 +774,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndRaise(to_remote_error(missing_parameter)) self.m.ReplayAll() resp = request_with_middleware(fault.FaultWrapper, @@ -817,7 +822,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndRaise(to_remote_error(error)) self.m.ReplayAll() @@ -870,7 +876,8 @@ class StackControllerTest(ControllerTest, HeatTestCase): 'params': {'parameters': parameters, 'resource_registry': {}}, 'files': {}, - 'args': {'timeout_mins': 30}}) + 'args': {'timeout_mins': 30}, + 'owner_id': None}) ).AndRaise(to_remote_error(error)) self.m.ReplayAll() diff --git a/heat/tests/test_rpc_client.py b/heat/tests/test_rpc_client.py index 1a072a3257..f0a4f9968e 100644 --- a/heat/tests/test_rpc_client.py +++ b/heat/tests/test_rpc_client.py @@ -18,6 +18,7 @@ Unit Tests for heat.rpc.client """ +import copy import mock import stubout import testtools @@ -47,7 +48,12 @@ class EngineRpcAPITestCase(testtools.TestCase): expected_retval = 'foo' if method == 'call' else None kwargs.pop('version', None) - expected_msg = self.rpcapi.make_msg(method, **kwargs) + + if 'expected_message' in kwargs: + expected_message = kwargs['expected_message'] + del kwargs['expected_message'] + else: + expected_message = self.rpcapi.make_msg(method, **kwargs) cast_and_call = ['delete_stack'] if rpc_method == 'call' and method in cast_and_call: @@ -59,7 +65,7 @@ class EngineRpcAPITestCase(testtools.TestCase): retval = getattr(self.rpcapi, method)(ctxt, **kwargs) self.assertEqual(expected_retval, retval) - expected_args = [ctxt, expected_msg, mock.ANY] + expected_args = [ctxt, expected_message, mock.ANY] actual_args, _ = mock_rpc_method.call_args for expected_arg, actual_arg in zip(expected_args, actual_args): @@ -103,11 +109,16 @@ class EngineRpcAPITestCase(testtools.TestCase): args={'timeout_mins': u'30'}) def test_create_stack(self): - self._test_engine_api('create_stack', 'call', stack_name='wordpress', - template={u'Foo': u'bar'}, - params={u'InstanceType': u'm1.xlarge'}, - files={u'a_file': u'the contents'}, - args={'timeout_mins': u'30'}) + kwargs = dict(stack_name='wordpress', + template={u'Foo': u'bar'}, + params={u'InstanceType': u'm1.xlarge'}, + files={u'a_file': u'the contents'}, + args={'timeout_mins': u'30'}) + call_kwargs = copy.deepcopy(kwargs) + call_kwargs['owner_id'] = None + expected_message = self.rpcapi.make_msg('create_stack', **call_kwargs) + kwargs['expected_message'] = expected_message + self._test_engine_api('create_stack', 'call', **kwargs) def test_update_stack(self): self._test_engine_api('update_stack', 'call',