Merge "Add resolve_outputs parameter to stacks API"

This commit is contained in:
Jenkins 2016-01-11 16:31:42 +00:00 committed by Gerrit Code Review
commit 3ea35bad40
11 changed files with 140 additions and 32 deletions

View File

@ -407,9 +407,16 @@ class StackController(object):
@util.identified_stack
def show(self, req, identity):
"""Gets detailed information for a stack."""
params = req.params
p_name = rpc_api.RESOLVE_OUTPUTS
if rpc_api.RESOLVE_OUTPUTS in params:
resolve_outputs = self._extract_bool_param(
p_name, params[p_name])
else:
resolve_outputs = True
stack_list = self.rpc_client.show_stack(req.context,
identity)
identity, resolve_outputs)
if not stack_list:
raise exc.HTTPInternalServerError()

View File

@ -204,7 +204,7 @@ def format_stack_output(stack, outputs, k, resolve_value=True):
return result
def format_stack(stack, preview=False):
def format_stack(stack, preview=False, resolve_outputs=True):
"""Return a representation of the given stack.
Return a representation of the given stack that matches the API output
@ -239,7 +239,8 @@ def format_stack(stack, preview=False):
info.update(update_info)
# allow users to view the outputs of stacks
if stack.action != stack.DELETE and stack.status != stack.IN_PROGRESS:
if (stack.action != stack.DELETE and stack.status != stack.IN_PROGRESS
and resolve_outputs):
info[rpc_api.STACK_OUTPUTS] = format_stack_outputs(stack,
stack.outputs,
resolve_value=True)

View File

@ -293,7 +293,7 @@ class EngineService(service.Service):
by the RPC caller.
"""
RPC_API_VERSION = '1.19'
RPC_API_VERSION = '1.20'
def __init__(self, host, topic):
super(EngineService, self).__init__()
@ -468,20 +468,24 @@ class EngineService(service.Service):
return s
@context.request_context
def show_stack(self, cnxt, stack_identity):
def show_stack(self, cnxt, stack_identity, resolve_outputs=True):
"""Return detailed information about one or all stacks.
:param cnxt: RPC context.
:param stack_identity: Name of the stack you want to show, or None
to show all
:param resolve_outputs: If True, outputs for given stack/stacks will
be resolved
"""
if stack_identity is not None:
db_stack = self._get_stack(cnxt, stack_identity, show_deleted=True)
stacks = [parser.Stack.load(cnxt, stack=db_stack)]
stacks = [parser.Stack.load(cnxt, stack=db_stack,
resolve_data=resolve_outputs)]
else:
stacks = parser.Stack.load_all(cnxt)
stacks = parser.Stack.load_all(cnxt, resolve_data=resolve_outputs)
return [api.format_stack(stack) for stack in stacks]
return [api.format_stack(
stack, resolve_outputs=resolve_outputs) for stack in stacks]
def get_revision(self, cnxt):
return cfg.CONF.revision['heat_revision']

View File

@ -19,13 +19,15 @@ PARAM_KEYS = (
PARAM_SHOW_DELETED, PARAM_SHOW_NESTED, PARAM_EXISTING,
PARAM_CLEAR_PARAMETERS, PARAM_GLOBAL_TENANT, PARAM_LIMIT,
PARAM_NESTED_DEPTH, PARAM_TAGS, PARAM_SHOW_HIDDEN, PARAM_TAGS_ANY,
PARAM_NOT_TAGS, PARAM_NOT_TAGS_ANY, TEMPLATE_TYPE, PARAM_WITH_DETAIL
PARAM_NOT_TAGS, PARAM_NOT_TAGS_ANY, TEMPLATE_TYPE, PARAM_WITH_DETAIL,
RESOLVE_OUTPUTS
) = (
'timeout_mins', 'disable_rollback', 'adopt_stack_data',
'show_deleted', 'show_nested', 'existing',
'clear_parameters', 'global_tenant', 'limit',
'nested_depth', 'tags', 'show_hidden', 'tags_any',
'not_tags', 'not_tags_any', 'template_type', 'with_detail',
'resolve_outputs',
)
STACK_KEYS = (

View File

@ -38,6 +38,7 @@ class EngineClient(object):
1.17 - Add files to validate_template
1.18 - Add show_nested to validate_template
1.19 - Add show_output and list_outputs for returning stack outputs
1.20 - Add resolve_outputs to stack show
"""
BASE_RPC_API_VERSION = '1.0'
@ -172,15 +173,18 @@ class EngineClient(object):
not_tags_any=not_tags_any),
version='1.8')
def show_stack(self, ctxt, stack_identity):
def show_stack(self, ctxt, stack_identity, resolve_outputs=True):
"""Returns detailed information about one or all stacks.
:param ctxt: RPC context.
:param stack_identity: Name of the stack you want to show, or None to
show all
:param resolve_outputs: If True, stack outputs will be resolved
"""
return self.call(ctxt, self.make_msg('show_stack',
stack_identity=stack_identity))
stack_identity=stack_identity,
resolve_outputs=resolve_outputs),
version='1.20')
def preview_stack(self, ctxt, stack_name, template, params, files, args):
"""Simulates a new stack using the provided template.

View File

@ -221,7 +221,9 @@ class CfnStackControllerTest(common.HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
dummy_req.context, ('show_stack', {'stack_identity': None})
dummy_req.context, ('show_stack', {'stack_identity': None,
'resolve_outputs': True}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -243,7 +245,9 @@ class CfnStackControllerTest(common.HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
dummy_req.context, ('show_stack', {'stack_identity': None})
dummy_req.context, ('show_stack', {'stack_identity': None,
'resolve_outputs': True}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -298,7 +302,9 @@ class CfnStackControllerTest(common.HeatTestCase):
).AndReturn(identity)
rpc_client.EngineClient.call(
dummy_req.context,
('show_stack', {'stack_identity': identity})
('show_stack', {'stack_identity': identity,
'resolve_outputs': True}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -387,7 +393,9 @@ class CfnStackControllerTest(common.HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
dummy_req.context,
('show_stack', {'stack_identity': identity})
('show_stack', {'stack_identity': identity,
'resolve_outputs': True}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -446,7 +454,9 @@ class CfnStackControllerTest(common.HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
dummy_req.context, ('show_stack', {'stack_identity': identity})
dummy_req.context, ('show_stack', {'stack_identity': identity,
'resolve_outputs': True},),
version='1.20'
).AndRaise(heat_exception.InvalidTenant(target='test',
actual='test'))
@ -469,7 +479,9 @@ class CfnStackControllerTest(common.HeatTestCase):
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
).AndReturn(identity)
rpc_client.EngineClient.call(
dummy_req.context, ('show_stack', {'stack_identity': identity})
dummy_req.context, ('show_stack', {'stack_identity': identity,
'resolve_outputs': True}),
version='1.20'
).AndRaise(AttributeError())
self.m.ReplayAll()

View File

@ -1399,8 +1399,8 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
def test_show(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'show', True)
identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6')
req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity)
req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity,
params={'resolve_outputs': True})
parameters = {u'DBUsername': u'admin',
u'LinuxDistribution': u'F17',
@ -1433,10 +1433,11 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('show_stack', {'stack_identity': dict(identity)})
('show_stack', {'stack_identity': dict(identity),
'resolve_outputs': True}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.show(req,
tenant_id=identity.tenant,
stack_name=identity.stack_name,
@ -1464,17 +1465,82 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
self.assertEqual(expected, response)
self.m.VerifyAll()
def test_show_without_resolve_outputs(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'show', True)
identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6')
req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity,
params={'resolve_outputs': False})
parameters = {u'DBUsername': u'admin',
u'LinuxDistribution': u'F17',
u'InstanceType': u'm1.large',
u'DBRootPassword': u'admin',
u'DBPassword': u'admin',
u'DBName': u'wordpress'}
engine_resp = [
{
u'stack_identity': dict(identity),
u'updated_time': u'2012-07-09T09:13:11Z',
u'parameters': parameters,
u'stack_status_reason': u'Stack successfully created',
u'creation_time': u'2012-07-09T09:12:45Z',
u'stack_name': identity.stack_name,
u'notification_topics': [],
u'stack_action': u'CREATE',
u'stack_status': u'COMPLETE',
u'description': u'blah',
u'disable_rollback': True,
u'timeout_mins':60,
u'capabilities': [],
}
]
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('show_stack', {'stack_identity': dict(identity),
'resolve_outputs': False}),
version='1.20'
).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.show(req,
tenant_id=identity.tenant,
stack_name=identity.stack_name,
stack_id=identity.stack_id)
expected = {
'stack': {
'links': [{"href": self._url(identity),
"rel": "self"}],
'id': '6',
u'updated_time': u'2012-07-09T09:13:11Z',
u'parameters': parameters,
u'description': u'blah',
u'stack_status_reason': u'Stack successfully created',
u'creation_time': u'2012-07-09T09:12:45Z',
u'stack_name': identity.stack_name,
u'stack_status': u'CREATE_COMPLETE',
u'capabilities': [],
u'notification_topics': [],
u'disable_rollback': True,
u'timeout_mins': 60,
}
}
self.assertEqual(expected, response)
self.m.VerifyAll()
def test_show_notfound(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'show', True)
identity = identifier.HeatIdentifier(self.tenant, 'wibble', '6')
req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity)
error = heat_exc.EntityNotFound(entity='Stack', name='a')
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('show_stack', {'stack_identity': dict(identity)})
('show_stack', {'stack_identity': dict(identity),
'resolve_outputs': True}),
version='1.20'
).AndRaise(tools.to_remote_error(error))
self.m.ReplayAll()

View File

@ -40,7 +40,7 @@ class ServiceEngineTest(common.HeatTestCase):
def test_make_sure_rpc_version(self):
self.assertEqual(
'1.19',
'1.20',
service.EngineService.RPC_API_VERSION,
('RPC version is changed, please update this test to new version '
'and make sure additional test cases are added for RPC APIs '

View File

@ -390,6 +390,14 @@ class FormatTest(common.HeatTestCase):
info = api.format_stack(self.stack)
self.assertEqual('foobar', info[rpc_api.STACK_OUTPUTS])
@mock.patch.object(api, 'format_stack_outputs')
def test_format_stack_without_resolving_outputs(self, mock_fmt_outputs):
mock_fmt_outputs.return_value = 'foobar'
self.stack.action = 'CREATE'
self.stack.status = 'COMPLETE'
info = api.format_stack(self.stack, resolve_outputs=False)
self.assertIsNone(info.get(rpc_api.STACK_OUTPUTS))
def test_format_stack_outputs(self):
tmpl = template.Template({
'HeatTemplateFormatVersion': '2012-12-12',

View File

@ -858,7 +858,8 @@ class StackServiceTest(common.HeatTestCase):
self.eng.abandon_stack(self.ctx, self.stack.identifier())
ex = self.assertRaises(dispatcher.ExpectedException,
self.eng.show_stack,
self.ctx, self.stack.identifier())
self.ctx, self.stack.identifier(),
resolve_outputs=True)
self.assertEqual(exception.EntityNotFound, ex.exc_info[0])
self.m.VerifyAll()
@ -877,7 +878,8 @@ class StackServiceTest(common.HeatTestCase):
ex = self.assertRaises(dispatcher.ExpectedException,
self.eng.show_stack,
self.ctx, non_exist_identifier)
self.ctx, non_exist_identifier,
resolve_outputs=True)
self.assertEqual(exception.EntityNotFound, ex.exc_info[0])
self.m.VerifyAll()
@ -896,7 +898,8 @@ class StackServiceTest(common.HeatTestCase):
ex = self.assertRaises(dispatcher.ExpectedException,
self.eng.show_stack,
self.ctx, non_exist_identifier)
self.ctx, non_exist_identifier,
resolve_outputs=True)
self.assertEqual(exception.InvalidTenant, ex.exc_info[0])
self.m.VerifyAll()
@ -910,7 +913,8 @@ class StackServiceTest(common.HeatTestCase):
show_deleted=True).AndReturn(s)
self.m.ReplayAll()
sl = self.eng.show_stack(self.ctx, self.stack.identifier())
sl = self.eng.show_stack(self.ctx, self.stack.identifier(),
resolve_outputs=True)
self.assertEqual(1, len(sl))
@ -931,7 +935,7 @@ class StackServiceTest(common.HeatTestCase):
@tools.stack_context('service_describe_all_test_stack', False)
def test_stack_describe_all(self):
sl = self.eng.show_stack(self.ctx, None)
sl = self.eng.show_stack(self.ctx, None, resolve_outputs=True)
self.assertEqual(1, len(sl))
@ -1103,7 +1107,7 @@ class StackServiceTest(common.HeatTestCase):
self.assertEqual(0, len(sl))
def test_stack_describe_all_empty(self):
sl = self.eng.show_stack(self.ctx, None)
sl = self.eng.show_stack(self.ctx, None, resolve_outputs=True)
self.assertEqual(0, len(sl))

View File

@ -78,7 +78,6 @@ class EngineRpcAPITestCase(common.HeatTestCase):
expected_retval = 'foo' if method == 'call' else None
kwargs.pop('version', None)
if 'expected_message' in kwargs:
expected_message = kwargs['expected_message']
del kwargs['expected_message']
@ -141,7 +140,8 @@ class EngineRpcAPITestCase(common.HeatTestCase):
stack_name='wordpress')
def test_show_stack(self):
self._test_engine_api('show_stack', 'call', stack_identity='wordpress')
self._test_engine_api('show_stack', 'call', stack_identity='wordpress',
resolve_outputs=True)
def test_preview_stack(self):
self._test_engine_api('preview_stack', 'call', stack_name='wordpress',