d4361580ba
Add a "hidden_stack_tags" option that contains a list of tag names. Stacks that have one or more of these tags will be hidden. Add an option to show hidden stacks in the stack listing. blueprint stack-tags Change-Id: I45a5ac6d73a9a61629a56f88270e3a97fafb378a
1630 lines
70 KiB
Python
1630 lines
70 KiB
Python
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import json
|
|
import os
|
|
|
|
import mock
|
|
from oslo_config import cfg
|
|
import six
|
|
|
|
from heat.api.aws import exception
|
|
import heat.api.cfn.v1.stacks as stacks
|
|
from heat.common import exception as heat_exception
|
|
from heat.common import identifier
|
|
from heat.common import policy
|
|
from heat.common import wsgi
|
|
from heat.rpc import api as rpc_api
|
|
from heat.rpc import client as rpc_client
|
|
from heat.tests import common
|
|
from heat.tests import utils
|
|
|
|
policy_path = os.path.dirname(os.path.realpath(__file__)) + "/policy/"
|
|
|
|
|
|
class CfnStackControllerTest(common.HeatTestCase):
|
|
'''
|
|
Tests the API class which acts as the WSGI controller,
|
|
the endpoint processing API requests after they are routed
|
|
'''
|
|
|
|
def setUp(self):
|
|
super(CfnStackControllerTest, self).setUp()
|
|
|
|
opts = [
|
|
cfg.StrOpt('config_dir', default=policy_path),
|
|
cfg.StrOpt('config_file', default='foo'),
|
|
cfg.StrOpt('project', default='heat'),
|
|
]
|
|
cfg.CONF.register_opts(opts)
|
|
cfg.CONF.set_default('host', 'host')
|
|
self.topic = rpc_api.ENGINE_TOPIC
|
|
self.api_version = '1.0'
|
|
self.template = {u'AWSTemplateFormatVersion': u'2010-09-09',
|
|
u'Foo': u'bar'}
|
|
|
|
# Create WSGI controller instance
|
|
class DummyConfig(object):
|
|
bind_port = 8000
|
|
cfgopts = DummyConfig()
|
|
self.controller = stacks.StackController(options=cfgopts)
|
|
self.controller.policy.enforcer.policy_path = (policy_path +
|
|
'deny_stack_user.json')
|
|
self.addCleanup(self.m.VerifyAll)
|
|
|
|
def _dummy_GET_request(self, params=None):
|
|
# Mangle the params dict into a query string
|
|
params = params or {}
|
|
qs = "&".join(["=".join([k, str(params[k])]) for k in params])
|
|
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': qs}
|
|
req = wsgi.Request(environ)
|
|
req.context = utils.dummy_context()
|
|
return req
|
|
|
|
def _stub_enforce(self, req, action, allowed=True):
|
|
self.m.StubOutWithMock(policy.Enforcer, 'enforce')
|
|
if allowed:
|
|
policy.Enforcer.enforce(req.context, action
|
|
).AndReturn(True)
|
|
else:
|
|
policy.Enforcer.enforce(req.context, action
|
|
).AndRaise(heat_exception.Forbidden)
|
|
self.m.ReplayAll()
|
|
|
|
# The tests
|
|
def test_stackid_addprefix(self):
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller._id_format({
|
|
'StackName': 'Foo',
|
|
'StackId': {
|
|
u'tenant': u't',
|
|
u'stack_name': u'Foo',
|
|
u'stack_id': u'123',
|
|
u'path': u''
|
|
}
|
|
})
|
|
expected = {'StackName': 'Foo',
|
|
'StackId': 'arn:openstack:heat::t:stacks/Foo/123'}
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_enforce_ok(self):
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStacks')
|
|
response = self.controller._enforce(dummy_req, 'ListStacks')
|
|
self.assertIsNone(response)
|
|
|
|
def test_enforce_denied(self):
|
|
self.m.ReplayAll()
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStacks', False)
|
|
self.assertRaises(exception.HeatAccessDeniedError,
|
|
self.controller._enforce, dummy_req, 'ListStacks')
|
|
|
|
def test_enforce_ise(self):
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
dummy_req.context.roles = ['heat_stack_user']
|
|
|
|
self.m.StubOutWithMock(policy.Enforcer, 'enforce')
|
|
policy.Enforcer.enforce(dummy_req.context, 'ListStacks'
|
|
).AndRaise(AttributeError)
|
|
self.m.ReplayAll()
|
|
|
|
self.assertRaises(exception.HeatInternalFailureError,
|
|
self.controller._enforce, dummy_req, 'ListStacks')
|
|
|
|
@mock.patch.object(rpc_client.EngineClient, 'call')
|
|
def test_list(self, mock_call):
|
|
# Format a dummy GET request to pass into the WSGI handler
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStacks')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = [{u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'1',
|
|
u'path': u''},
|
|
u'updated_time': u'2012-07-09T09:13:11Z',
|
|
u'template_description': u'blah',
|
|
u'stack_status_reason': u'Stack successfully created',
|
|
u'creation_time': u'2012-07-09T09:12:45Z',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_action': u'CREATE',
|
|
u'stack_status': u'COMPLETE'}]
|
|
mock_call.return_value = engine_resp
|
|
|
|
# Call the list controller function and compare the response
|
|
result = self.controller.list(dummy_req)
|
|
expected = {'ListStacksResponse': {'ListStacksResult':
|
|
{'StackSummaries':
|
|
[{u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1',
|
|
u'LastUpdatedTime': u'2012-07-09T09:13:11Z',
|
|
u'TemplateDescription': u'blah',
|
|
u'StackStatusReason': u'Stack successfully created',
|
|
u'CreationTime': u'2012-07-09T09:12:45Z',
|
|
u'StackName': u'wordpress',
|
|
u'StackStatus': u'CREATE_COMPLETE'}]}}}
|
|
self.assertEqual(expected, result)
|
|
default_args = {'limit': None, 'sort_keys': None, 'marker': None,
|
|
'sort_dir': None, 'filters': None, 'tenant_safe': True,
|
|
'show_deleted': False, 'show_nested': False,
|
|
'show_hidden': False}
|
|
mock_call.assert_called_once_with(
|
|
dummy_req.context, ('list_stacks', default_args))
|
|
|
|
@mock.patch.object(rpc_client.EngineClient, 'call')
|
|
def test_list_rmt_aterr(self, mock_call):
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStacks')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
mock_call.side_effect = AttributeError
|
|
|
|
# Call the list controller function and compare the response
|
|
result = self.controller.list(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
mock_call.assert_called_once_with(
|
|
dummy_req.context, ('list_stacks', mock.ANY))
|
|
|
|
@mock.patch.object(rpc_client.EngineClient, 'call')
|
|
def test_list_rmt_interr(self, mock_call):
|
|
params = {'Action': 'ListStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStacks')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
mock_call.side_effect = Exception()
|
|
|
|
# Call the list controller function and compare the response
|
|
result = self.controller.list(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInternalFailureError)
|
|
mock_call.assert_called_once_with(
|
|
dummy_req.context, ('list_stacks', mock.ANY))
|
|
|
|
def test_describe_last_updated_time(self):
|
|
params = {'Action': 'DescribeStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
engine_resp = [{u'updated_time': '1970-01-01',
|
|
u'parameters': {},
|
|
u'stack_action': u'CREATE',
|
|
u'stack_status': u'COMPLETE'}]
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('show_stack', {'stack_identity': None})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe(dummy_req)
|
|
result = response['DescribeStacksResponse']['DescribeStacksResult']
|
|
stack = result['Stacks'][0]
|
|
self.assertEqual('1970-01-01', stack['LastUpdatedTime'])
|
|
|
|
def test_describe_no_last_updated_time(self):
|
|
params = {'Action': 'DescribeStacks'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
engine_resp = [{u'updated_time': None,
|
|
u'parameters': {},
|
|
u'stack_action': u'CREATE',
|
|
u'stack_status': u'COMPLETE'}]
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('show_stack', {'stack_identity': None})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe(dummy_req)
|
|
result = response['DescribeStacksResponse']['DescribeStacksResult']
|
|
stack = result['Stacks'][0]
|
|
self.assertNotIn('LastUpdatedTime', stack)
|
|
|
|
def test_describe(self):
|
|
# Format a dummy GET request to pass into the WSGI handler
|
|
stack_name = u"wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStacks', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
# Note the engine returns a load of keys we don't actually use
|
|
# so this is a subset of the real response format
|
|
engine_resp = [{u'stack_identity':
|
|
{u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'updated_time': u'2012-07-09T09:13:11Z',
|
|
u'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'},
|
|
u'outputs':
|
|
[{u'output_key': u'WebsiteURL',
|
|
u'description': u'URL for Wordpress wiki',
|
|
u'output_value': u'http://10.0.0.8/wordpress'}],
|
|
u'stack_status_reason': u'Stack successfully created',
|
|
u'creation_time': u'2012-07-09T09:12:45Z',
|
|
u'stack_name': u'wordpress',
|
|
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(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('show_stack', {'stack_identity': identity})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
# Call the list controller function and compare the response
|
|
response = self.controller.describe(dummy_req)
|
|
|
|
expected = {'DescribeStacksResponse':
|
|
{'DescribeStacksResult':
|
|
{'Stacks':
|
|
[{'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'StackStatusReason': u'Stack successfully created',
|
|
'Description': u'blah',
|
|
'Parameters':
|
|
[{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBUsername'},
|
|
{'ParameterValue': u'F17',
|
|
'ParameterKey': u'LinuxDistribution'},
|
|
{'ParameterValue': u'm1.large',
|
|
'ParameterKey': u'InstanceType'},
|
|
{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBRootPassword'},
|
|
{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBPassword'},
|
|
{'ParameterValue': u'wordpress',
|
|
'ParameterKey': u'DBName'}],
|
|
'Outputs':
|
|
[{'OutputKey': u'WebsiteURL',
|
|
'OutputValue': u'http://10.0.0.8/wordpress',
|
|
'Description': u'URL for Wordpress wiki'}],
|
|
'TimeoutInMinutes': 60,
|
|
'CreationTime': u'2012-07-09T09:12:45Z',
|
|
'Capabilities': [],
|
|
'StackName': u'wordpress',
|
|
'NotificationARNs': [],
|
|
'StackStatus': u'CREATE_COMPLETE',
|
|
'DisableRollback': 'true',
|
|
'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_describe_arn(self):
|
|
# Format a dummy GET request to pass into the WSGI handler
|
|
stack_name = u"wordpress"
|
|
stack_identifier = identifier.HeatIdentifier('t', stack_name, '6')
|
|
identity = dict(stack_identifier)
|
|
params = {'Action': 'DescribeStacks',
|
|
'StackName': stack_identifier.arn()}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
# Note the engine returns a load of keys we don't actually use
|
|
# so this is a subset of the real response format
|
|
engine_resp = [{u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'updated_time': u'2012-07-09T09:13:11Z',
|
|
u'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'},
|
|
u'outputs':
|
|
[{u'output_key': u'WebsiteURL',
|
|
u'description': u'URL for Wordpress wiki',
|
|
u'output_value': u'http://10.0.0.8/wordpress'}],
|
|
u'stack_status_reason': u'Stack successfully created',
|
|
u'creation_time': u'2012-07-09T09:12:45Z',
|
|
u'stack_name': u'wordpress',
|
|
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(
|
|
dummy_req.context,
|
|
('show_stack', {'stack_identity': identity})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
# Call the list controller function and compare the response
|
|
response = self.controller.describe(dummy_req)
|
|
|
|
expected = {'DescribeStacksResponse':
|
|
{'DescribeStacksResult':
|
|
{'Stacks':
|
|
[{'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'StackStatusReason': u'Stack successfully created',
|
|
'Description': u'blah',
|
|
'Parameters':
|
|
[{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBUsername'},
|
|
{'ParameterValue': u'F17',
|
|
'ParameterKey': u'LinuxDistribution'},
|
|
{'ParameterValue': u'm1.large',
|
|
'ParameterKey': u'InstanceType'},
|
|
{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBRootPassword'},
|
|
{'ParameterValue': u'admin',
|
|
'ParameterKey': u'DBPassword'},
|
|
{'ParameterValue': u'wordpress',
|
|
'ParameterKey': u'DBName'}],
|
|
'Outputs':
|
|
[{'OutputKey': u'WebsiteURL',
|
|
'OutputValue': u'http://10.0.0.8/wordpress',
|
|
'Description': u'URL for Wordpress wiki'}],
|
|
'TimeoutInMinutes': 60,
|
|
'CreationTime': u'2012-07-09T09:12:45Z',
|
|
'Capabilities': [],
|
|
'StackName': u'wordpress',
|
|
'NotificationARNs': [],
|
|
'StackStatus': u'CREATE_COMPLETE',
|
|
'DisableRollback': 'true',
|
|
'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_describe_arn_invalidtenant(self):
|
|
# Format a dummy GET request to pass into the WSGI handler
|
|
stack_name = u"wordpress"
|
|
stack_identifier = identifier.HeatIdentifier('wibble', stack_name, '6')
|
|
identity = dict(stack_identifier)
|
|
params = {'Action': 'DescribeStacks',
|
|
'StackName': stack_identifier.arn()}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('show_stack', {'stack_identity': identity})
|
|
).AndRaise(heat_exception.InvalidTenant(target='test',
|
|
actual='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_aterr(self):
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStacks', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('show_stack', {'stack_identity': identity})
|
|
).AndRaise(AttributeError())
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'DescribeStacks', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStacks')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_get_template_int_body(self):
|
|
'''Test the internal _get_template function.'''
|
|
params = {'TemplateBody': "abcdef"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
result = self.controller._get_template(dummy_req)
|
|
expected = "abcdef"
|
|
self.assertEqual(expected, result)
|
|
|
|
# TODO(shardy) : test the _get_template TemplateUrl case
|
|
|
|
def _stub_rpc_create_stack_call_failure(self, req_context, stack_name,
|
|
engine_parms, engine_args,
|
|
failure, need_stub=True):
|
|
if need_stub:
|
|
self.m.StubOutWithMock(policy.Enforcer, 'enforce')
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
policy.Enforcer.enforce(req_context,
|
|
'CreateStack').AndReturn(True)
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
rpc_client.EngineClient.call(
|
|
req_context,
|
|
('create_stack',
|
|
{'stack_name': stack_name,
|
|
'template': self.template,
|
|
'params': engine_parms,
|
|
'files': {},
|
|
'args': engine_args,
|
|
'owner_id': None,
|
|
'nested_depth': 0,
|
|
'user_creds_id': None,
|
|
'stack_user_project_id': None}),
|
|
version='1.2'
|
|
).AndRaise(failure)
|
|
|
|
def _stub_rpc_create_stack_call_success(self, stack_name, engine_parms,
|
|
engine_args, parameters):
|
|
dummy_req = self._dummy_GET_request(parameters)
|
|
self._stub_enforce(dummy_req, 'CreateStack')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'1',
|
|
u'path': u''}
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('create_stack',
|
|
{'stack_name': stack_name,
|
|
'template': self.template,
|
|
'params': engine_parms,
|
|
'files': {},
|
|
'args': engine_args,
|
|
'owner_id': None,
|
|
'nested_depth': 0,
|
|
'user_creds_id': None,
|
|
'stack_user_project_id': None}),
|
|
version='1.2'
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
return dummy_req
|
|
|
|
def test_create(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'DisableRollback': 'true',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30', 'disable_rollback': 'true'}
|
|
dummy_req = self._stub_rpc_create_stack_call_success(stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
params)
|
|
response = self.controller.create(dummy_req)
|
|
|
|
expected = {
|
|
'CreateStackResponse': {
|
|
'CreateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_create_rollback(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'DisableRollback': 'false',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30', 'disable_rollback': 'false'}
|
|
dummy_req = self._stub_rpc_create_stack_call_success(stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
params)
|
|
|
|
response = self.controller.create(dummy_req)
|
|
|
|
expected = {
|
|
'CreateStackResponse': {
|
|
'CreateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_create_onfailure_true(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'OnFailure': 'DO_NOTHING',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30', 'disable_rollback': 'true'}
|
|
dummy_req = self._stub_rpc_create_stack_call_success(stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
params)
|
|
|
|
response = self.controller.create(dummy_req)
|
|
|
|
expected = {
|
|
'CreateStackResponse': {
|
|
'CreateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_create_onfailure_false_delete(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'OnFailure': 'DELETE',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30', 'disable_rollback': 'false'}
|
|
dummy_req = self._stub_rpc_create_stack_call_success(stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
params)
|
|
|
|
response = self.controller.create(dummy_req)
|
|
|
|
expected = {
|
|
'CreateStackResponse': {
|
|
'CreateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_create_onfailure_false_rollback(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'OnFailure': 'ROLLBACK',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30', 'disable_rollback': 'false'}
|
|
dummy_req = self._stub_rpc_create_stack_call_success(stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
params)
|
|
|
|
response = self.controller.create(dummy_req)
|
|
|
|
expected = {
|
|
'CreateStackResponse': {
|
|
'CreateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_create_onfailure_err(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'DisableRollback': 'true',
|
|
'OnFailure': 'DO_NOTHING',
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'CreateStack')
|
|
|
|
self.assertRaises(exception.HeatInvalidParameterCombinationError,
|
|
self.controller.create, dummy_req)
|
|
|
|
def test_create_err_no_template(self):
|
|
# Format a dummy request with a missing template field
|
|
stack_name = "wordpress"
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'CreateStack')
|
|
|
|
result = self.controller.create(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatMissingParameterError)
|
|
|
|
def test_create_err_inval_template(self):
|
|
# Format a dummy request with an invalid TemplateBody
|
|
stack_name = "wordpress"
|
|
json_template = "!$%**_+}@~?"
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'CreateStack')
|
|
|
|
result = self.controller.create(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_create_err_rpcerr(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_rpc_create_stack_call_failure(dummy_req.context,
|
|
stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
AttributeError())
|
|
failure = heat_exception.UnknownUserParameter(key='test')
|
|
self._stub_rpc_create_stack_call_failure(dummy_req.context,
|
|
stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
failure,
|
|
False)
|
|
failure = heat_exception.UserParameterMissing(key='test')
|
|
self._stub_rpc_create_stack_call_failure(dummy_req.context,
|
|
stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
failure,
|
|
False)
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.create(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
result = self.controller.create(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
result = self.controller.create(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_create_err_exists(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30'}
|
|
failure = heat_exception.StackExists(stack_name='test')
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_rpc_create_stack_call_failure(dummy_req.context,
|
|
stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
failure)
|
|
|
|
self.m.ReplayAll()
|
|
result = self.controller.create(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.AlreadyExistsError)
|
|
|
|
def test_create_err_engine(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'CreateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'TimeoutInMinutes': 30,
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {'timeout_mins': u'30'}
|
|
failure = heat_exception.StackValidationFailed(
|
|
message='Something went wrong')
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_rpc_create_stack_call_failure(dummy_req.context,
|
|
stack_name,
|
|
engine_parms,
|
|
engine_args,
|
|
failure)
|
|
self.m.ReplayAll()
|
|
result = self.controller.create(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_update(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'UpdateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
engine_parms = {u'InstanceType': u'm1.xlarge'}
|
|
engine_args = {}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'UpdateStack')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '1'))
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('update_stack',
|
|
{'stack_identity': identity,
|
|
'template': self.template,
|
|
'params': engine_parms,
|
|
'files': {},
|
|
'args': engine_args})
|
|
).AndReturn(identity)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.update(dummy_req)
|
|
|
|
expected = {
|
|
'UpdateStackResponse': {
|
|
'UpdateStackResult': {
|
|
u'StackId': u'arn:openstack:heat::t:stacks/wordpress/1'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_cancel_update(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
params = {'Action': 'CancelUpdateStack', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'CancelUpdateStack')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '1'))
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('stack_cancel_update',
|
|
{'stack_identity': identity})
|
|
).AndReturn(identity)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.cancel_update(dummy_req)
|
|
|
|
expected = {
|
|
'CancelUpdateStackResponse': {
|
|
'CancelUpdateStackResult': {}
|
|
}
|
|
}
|
|
|
|
self.assertEqual(response, expected)
|
|
|
|
def test_update_bad_name(self):
|
|
stack_name = "wibble"
|
|
json_template = json.dumps(self.template)
|
|
params = {'Action': 'UpdateStack', 'StackName': stack_name,
|
|
'TemplateBody': '%s' % json_template,
|
|
'Parameters.member.1.ParameterKey': 'InstanceType',
|
|
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'UpdateStack')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.update(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_create_or_update_err(self):
|
|
result = self.controller.create_or_update(req={}, action="dsdgfdf")
|
|
self.assertIsInstance(result, exception.HeatInternalFailureError)
|
|
|
|
def test_get_template(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'GetTemplate', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'GetTemplate')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = self.template
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('get_template', {'stack_identity': identity})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.get_template(dummy_req)
|
|
|
|
expected = {'GetTemplateResponse':
|
|
{'GetTemplateResult':
|
|
{'TemplateBody': self.template}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_get_template_err_rpcerr(self):
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'GetTemplate', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'GetTemplate')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('get_template', {'stack_identity': identity})
|
|
).AndRaise(AttributeError())
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.get_template(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_get_template_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'GetTemplate', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'GetTemplate')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.get_template(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_get_template_err_none(self):
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'GetTemplate', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'GetTemplate')
|
|
|
|
# Stub out the RPC call to the engine to return None
|
|
# this test the "no such stack" error path
|
|
engine_resp = None
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('get_template', {'stack_identity': identity})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.get_template(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_validate_err_no_template(self):
|
|
# Format a dummy request with a missing template field
|
|
params = {'Action': 'ValidateTemplate'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ValidateTemplate')
|
|
|
|
result = self.controller.validate_template(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatMissingParameterError)
|
|
|
|
def test_validate_err_inval_template(self):
|
|
# Format a dummy request with an invalid TemplateBody
|
|
json_template = "!$%**_+}@~?"
|
|
params = {'Action': 'ValidateTemplate',
|
|
'TemplateBody': '%s' % json_template}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ValidateTemplate')
|
|
|
|
result = self.controller.validate_template(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_bad_resources_in_template(self):
|
|
# Format a dummy request
|
|
json_template = {
|
|
'AWSTemplateFormatVersion': '2010-09-09',
|
|
'Resources': {
|
|
'Type': 'AWS: : EC2: : Instance',
|
|
},
|
|
}
|
|
params = {'Action': 'ValidateTemplate',
|
|
'TemplateBody': '%s' % json.dumps(json_template)}
|
|
response = {'Error': 'Resources must contain Resource. '
|
|
'Found a [string] instead'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ValidateTemplate')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('validate_template', {'template': json_template, 'params': None})
|
|
).AndReturn(response)
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.validate_template(dummy_req)
|
|
|
|
expected = {'ValidateTemplateResponse':
|
|
{'ValidateTemplateResult':
|
|
'Resources must contain Resource. '
|
|
'Found a [string] instead'}}
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_delete(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '1'))
|
|
params = {'Action': 'DeleteStack', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DeleteStack')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
# Engine returns None when delete successful
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('delete_stack', {'stack_identity': identity})
|
|
).AndReturn(None)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.delete(dummy_req)
|
|
|
|
expected = {'DeleteStackResponse': {'DeleteStackResult': ''}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_delete_err_rpcerr(self):
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '1'))
|
|
params = {'Action': 'DeleteStack', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DeleteStack')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('delete_stack', {'stack_identity': identity})
|
|
).AndRaise(AttributeError())
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.delete(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_delete_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'DeleteStack', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DeleteStack')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.delete(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_events_list_event_id_integer(self):
|
|
self._test_events_list('42')
|
|
|
|
def test_events_list_event_id_uuid(self):
|
|
self._test_events_list('a3455d8c-9f88-404d-a85b-5315293e67de')
|
|
|
|
def _test_events_list(self, event_id):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackEvents', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackEvents')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = [{u'stack_name': u'wordpress',
|
|
u'event_time': u'2012-07-23T13:05:39Z',
|
|
u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'resource_name': u'WikiDatabase',
|
|
u'resource_status_reason': u'state changed',
|
|
u'event_identity':
|
|
{u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u'/resources/WikiDatabase/events/{0}'.format(
|
|
event_id)},
|
|
u'resource_action': u'TEST',
|
|
u'resource_status': u'IN_PROGRESS',
|
|
u'physical_resource_id': None,
|
|
u'resource_properties': {u'UserData': u'blah'},
|
|
u'resource_type': u'AWS::EC2::Instance'}]
|
|
|
|
kwargs = {'stack_identity': identity,
|
|
'limit': None, 'sort_keys': None, 'marker': None,
|
|
'sort_dir': None, 'filters': None}
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('list_events', kwargs)
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.events_list(dummy_req)
|
|
|
|
expected = {'DescribeStackEventsResponse':
|
|
{'DescribeStackEventsResult':
|
|
{'StackEvents':
|
|
[{'EventId': six.text_type(event_id),
|
|
'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'ResourceStatus': u'TEST_IN_PROGRESS',
|
|
'ResourceType': u'AWS::EC2::Instance',
|
|
'Timestamp': u'2012-07-23T13:05:39Z',
|
|
'StackName': u'wordpress',
|
|
'ResourceProperties':
|
|
json.dumps({u'UserData': u'blah'}),
|
|
'PhysicalResourceId': None,
|
|
'ResourceStatusReason': u'state changed',
|
|
'LogicalResourceId': u'WikiDatabase'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_events_list_err_rpcerr(self):
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackEvents', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackEvents')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('list_events', {'stack_identity': identity})
|
|
).AndRaise(Exception())
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.events_list(dummy_req)
|
|
|
|
self.assertIsInstance(result, exception.HeatInternalFailureError)
|
|
|
|
def test_events_list_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'DescribeStackEvents', 'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackEvents')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.events_list(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_stack_resource(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackResource',
|
|
'StackName': stack_name,
|
|
'LogicalResourceId': "WikiDatabase"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResource')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = {u'description': u'',
|
|
u'resource_identity': {
|
|
u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u'resources/WikiDatabase'
|
|
},
|
|
u'stack_name': u'wordpress',
|
|
u'resource_name': u'WikiDatabase',
|
|
u'resource_status_reason': None,
|
|
u'updated_time': u'2012-07-23T13:06:00Z',
|
|
u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'resource_action': u'CREATE',
|
|
u'resource_status': u'COMPLETE',
|
|
u'physical_resource_id':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
u'resource_type': u'AWS::EC2::Instance',
|
|
u'metadata': {u'wordpress': []}}
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
args = {
|
|
'stack_identity': identity,
|
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
|
'with_attr': None,
|
|
}
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('describe_stack_resource', args), version='1.2'
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe_stack_resource(dummy_req)
|
|
|
|
expected = {'DescribeStackResourceResponse':
|
|
{'DescribeStackResourceResult':
|
|
{'StackResourceDetail':
|
|
{'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'ResourceStatus': u'CREATE_COMPLETE',
|
|
'Description': u'',
|
|
'ResourceType': u'AWS::EC2::Instance',
|
|
'ResourceStatusReason': None,
|
|
'LastUpdatedTimestamp': u'2012-07-23T13:06:00Z',
|
|
'StackName': u'wordpress',
|
|
'PhysicalResourceId':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
'Metadata': {u'wordpress': []},
|
|
'LogicalResourceId': u'WikiDatabase'}}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_describe_stack_resource_nonexistent_stack(self):
|
|
# Format a dummy request
|
|
stack_name = "wibble"
|
|
params = {'Action': 'DescribeStackResource',
|
|
'StackName': stack_name,
|
|
'LogicalResourceId': "WikiDatabase"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResource')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe_stack_resource(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_stack_resource_nonexistent(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackResource',
|
|
'StackName': stack_name,
|
|
'LogicalResourceId': "wibble"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResource')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
args = {
|
|
'stack_identity': identity,
|
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
|
'with_attr': None,
|
|
}
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('describe_stack_resource', args), version='1.2'
|
|
).AndRaise(heat_exception.ResourceNotFound(
|
|
resource_name='test', stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe_stack_resource(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_stack_resources(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackResources',
|
|
'StackName': stack_name,
|
|
'LogicalResourceId': "WikiDatabase"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResources')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = [{u'description': u'',
|
|
u'resource_identity': {
|
|
u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u'resources/WikiDatabase'
|
|
},
|
|
u'stack_name': u'wordpress',
|
|
u'resource_name': u'WikiDatabase',
|
|
u'resource_status_reason': None,
|
|
u'updated_time': u'2012-07-23T13:06:00Z',
|
|
u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'resource_action': u'CREATE',
|
|
u'resource_status': u'COMPLETE',
|
|
u'physical_resource_id':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
u'resource_type': u'AWS::EC2::Instance',
|
|
u'metadata': {u'ensureRunning': u'true''true'}}]
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
args = {
|
|
'stack_identity': identity,
|
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
|
}
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('describe_stack_resources', args)
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe_stack_resources(dummy_req)
|
|
|
|
expected = {'DescribeStackResourcesResponse':
|
|
{'DescribeStackResourcesResult':
|
|
{'StackResources':
|
|
[{'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'ResourceStatus': u'CREATE_COMPLETE',
|
|
'Description': u'',
|
|
'ResourceType': u'AWS::EC2::Instance',
|
|
'Timestamp': u'2012-07-23T13:06:00Z',
|
|
'ResourceStatusReason': None,
|
|
'StackName': u'wordpress',
|
|
'PhysicalResourceId':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
'LogicalResourceId': u'WikiDatabase'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_describe_stack_resources_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'DescribeStackResources',
|
|
'StackName': stack_name,
|
|
'LogicalResourceId': "WikiDatabase"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResources')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.describe_stack_resources(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_stack_resources_physical(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'DescribeStackResources',
|
|
'LogicalResourceId': "WikiDatabase",
|
|
'PhysicalResourceId': 'a3455d8c-9f88-404d-a85b-5315293e67de'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResources')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = [{u'description': u'',
|
|
u'resource_identity': {
|
|
u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u'resources/WikiDatabase'
|
|
},
|
|
u'stack_name': u'wordpress',
|
|
u'resource_name': u'WikiDatabase',
|
|
u'resource_status_reason': None,
|
|
u'updated_time': u'2012-07-23T13:06:00Z',
|
|
u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'resource_action': u'CREATE',
|
|
u'resource_status': u'COMPLETE',
|
|
u'physical_resource_id':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
u'resource_type': u'AWS::EC2::Instance',
|
|
u'metadata': {u'ensureRunning': u'true''true'}}]
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('find_physical_resource',
|
|
{'physical_resource_id': 'a3455d8c-9f88-404d-a85b-5315293e67de'})
|
|
).AndReturn(identity)
|
|
args = {
|
|
'stack_identity': identity,
|
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
|
}
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('describe_stack_resources', args)
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe_stack_resources(dummy_req)
|
|
|
|
expected = {'DescribeStackResourcesResponse':
|
|
{'DescribeStackResourcesResult':
|
|
{'StackResources':
|
|
[{'StackId': u'arn:openstack:heat::t:stacks/wordpress/6',
|
|
'ResourceStatus': u'CREATE_COMPLETE',
|
|
'Description': u'',
|
|
'ResourceType': u'AWS::EC2::Instance',
|
|
'Timestamp': u'2012-07-23T13:06:00Z',
|
|
'ResourceStatusReason': None,
|
|
'StackName': u'wordpress',
|
|
'PhysicalResourceId':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
'LogicalResourceId': u'WikiDatabase'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_describe_stack_resources_physical_not_found(self):
|
|
# Format a dummy request
|
|
params = {'Action': 'DescribeStackResources',
|
|
'LogicalResourceId': "WikiDatabase",
|
|
'PhysicalResourceId': 'aaaaaaaa-9f88-404d-cccc-ffffffffffff'}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResources')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('find_physical_resource',
|
|
{'physical_resource_id': 'aaaaaaaa-9f88-404d-cccc-ffffffffffff'})
|
|
).AndRaise(heat_exception.PhysicalResourceNotFound(
|
|
resource_id='1'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.describe_stack_resources(dummy_req)
|
|
|
|
self.assertIsInstance(response,
|
|
exception.HeatInvalidParameterValueError)
|
|
|
|
def test_describe_stack_resources_err_inval(self):
|
|
# Format a dummy request containing both StackName and
|
|
# PhysicalResourceId, which is invalid and should throw a
|
|
# HeatInvalidParameterCombinationError
|
|
stack_name = "wordpress"
|
|
params = {'Action': 'DescribeStackResources',
|
|
'StackName': stack_name,
|
|
'PhysicalResourceId': "123456"}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'DescribeStackResources')
|
|
ret = self.controller.describe_stack_resources(dummy_req)
|
|
self.assertIsInstance(ret,
|
|
exception.HeatInvalidParameterCombinationError)
|
|
|
|
def test_list_stack_resources(self):
|
|
# Format a dummy request
|
|
stack_name = "wordpress"
|
|
identity = dict(identifier.HeatIdentifier('t', stack_name, '6'))
|
|
params = {'Action': 'ListStackResources',
|
|
'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStackResources')
|
|
|
|
# Stub out the RPC call to the engine with a pre-canned response
|
|
engine_resp = [{u'resource_identity':
|
|
{u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u'/resources/WikiDatabase'},
|
|
u'stack_name': u'wordpress',
|
|
u'resource_name': u'WikiDatabase',
|
|
u'resource_status_reason': None,
|
|
u'updated_time': u'2012-07-23T13:06:00Z',
|
|
u'stack_identity': {u'tenant': u't',
|
|
u'stack_name': u'wordpress',
|
|
u'stack_id': u'6',
|
|
u'path': u''},
|
|
u'resource_action': u'CREATE',
|
|
u'resource_status': u'COMPLETE',
|
|
u'physical_resource_id':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
u'resource_type': u'AWS::EC2::Instance'}]
|
|
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndReturn(identity)
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context,
|
|
('list_stack_resources', {'stack_identity': identity,
|
|
'nested_depth': 0})
|
|
).AndReturn(engine_resp)
|
|
|
|
self.m.ReplayAll()
|
|
|
|
response = self.controller.list_stack_resources(dummy_req)
|
|
|
|
expected = {'ListStackResourcesResponse': {'ListStackResourcesResult':
|
|
{'StackResourceSummaries':
|
|
[{'ResourceStatus': u'CREATE_COMPLETE',
|
|
'ResourceType': u'AWS::EC2::Instance',
|
|
'ResourceStatusReason': None,
|
|
'LastUpdatedTimestamp': u'2012-07-23T13:06:00Z',
|
|
'PhysicalResourceId':
|
|
u'a3455d8c-9f88-404d-a85b-5315293e67de',
|
|
'LogicalResourceId': u'WikiDatabase'}]}}}
|
|
|
|
self.assertEqual(expected, response)
|
|
|
|
def test_list_stack_resources_bad_name(self):
|
|
stack_name = "wibble"
|
|
params = {'Action': 'ListStackResources',
|
|
'StackName': stack_name}
|
|
dummy_req = self._dummy_GET_request(params)
|
|
self._stub_enforce(dummy_req, 'ListStackResources')
|
|
|
|
# Insert an engine RPC error and ensure we map correctly to the
|
|
# heat exception type
|
|
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
|
|
rpc_client.EngineClient.call(
|
|
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
|
|
).AndRaise(heat_exception.StackNotFound(stack_name='test'))
|
|
|
|
self.m.ReplayAll()
|
|
|
|
result = self.controller.list_stack_resources(dummy_req)
|
|
self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
|