Add owner_id to new _create_stack RPC interface

Adds a new "internal" _create_stack RPC call, which exposes a superset
of the existing RPC interfaces, intended only for engine-to-engine
usage, not for exposure via the user-facing API's (hence the prefix)

This patch exposes "owner_id" via the new call, which enables creation
of nested stacks via an RPC call, instead of creating the stack directly.

Change-Id: I355c608e657cb9dae2d2b38658b0b247040de6b9
blueprint: decouple-nested
This commit is contained in:
Steven Hardy 2014-08-18 14:46:09 +01:00
parent 3dee21c30d
commit f899548500
4 changed files with 65 additions and 25 deletions

View File

@ -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):

View File

@ -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'))

View File

@ -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()

View File

@ -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',