Stack API improvements

The exception that would be raised from list_stacks() if it failed
would not have any type of error message (except the inner exception).
This could be confusing, so add some text that indicates where the
exception is being thrown. Unit tests for this method are added.

Also improve some comments in delete_stack() and add unit tests
for this method.

Change-Id: I979a2948938fc73b708c26d81599061bac7681d4
This commit is contained in:
David Shrewsbury 2015-12-11 16:29:32 -05:00
parent 8d5abfbf56
commit fb8ea73f27
3 changed files with 90 additions and 4 deletions

View File

@ -739,14 +739,14 @@ class OpenStackCloud(object):
def delete_stack(self, name_or_id): def delete_stack(self, name_or_id):
"""Delete a Heat Stack """Delete a Heat Stack
:param name_or_id: Stack name or id. :param string name_or_id: Stack name or id.
:returns: True if delete succeeded, False otherwise. :returns: True if delete succeeded, False if the stack was not found.
:raises: ``OpenStackCloudException`` if something goes wrong during :raises: ``OpenStackCloudException`` if something goes wrong during
the openstack API call the openstack API call
""" """
stack = self.get_stack(name_or_id=name_or_id) stack = self.get_stack(name_or_id)
if stack is None: if stack is None:
self.log.debug("Stack %s not found for deleting" % name_or_id) self.log.debug("Stack %s not found for deleting" % name_or_id)
return False return False
@ -1054,7 +1054,7 @@ class OpenStackCloud(object):
:raises: ``OpenStackCloudException`` if something goes wrong during the :raises: ``OpenStackCloudException`` if something goes wrong during the
openstack API call. openstack API call.
""" """
with _utils.shade_exceptions(): with _utils.shade_exceptions("Error fetching stack list"):
stacks = self.manager.submitTask(_tasks.StackList()) stacks = self.manager.submitTask(_tasks.StackList())
return stacks return stacks

View File

@ -199,3 +199,11 @@ class FakeHypervisor(object):
def __init__(self, id, hostname): def __init__(self, id, hostname):
self.id = id self.id = id
self.hypervisor_hostname = hostname self.hypervisor_hostname = hostname
class FakeStack(object):
def __init__(self, id, name, description=None, status=None):
self.id = id
self.stack_name = name
self.stack_description = description
self.stack_status = status

View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# 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 mock
import testtools
import shade
from shade import meta
from shade.tests import fakes
from shade.tests.unit import base
class TestStack(base.TestCase):
def setUp(self):
super(TestStack, self).setUp()
self.cloud = shade.openstack_cloud(validate=False)
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
def test_list_stacks(self, mock_heat):
fake_stacks = [
fakes.FakeStack('001', 'stack1'),
fakes.FakeStack('002', 'stack2'),
]
mock_heat.stacks.list.return_value = fake_stacks
stacks = self.cloud.list_stacks()
mock_heat.stacks.list.assert_called_once_with()
self.assertEqual(meta.obj_list_to_dict(fake_stacks), stacks)
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
def test_list_stacks_exception(self, mock_heat):
mock_heat.stacks.list.side_effect = Exception()
with testtools.ExpectedException(
shade.OpenStackCloudException,
"Error fetching stack list"
):
self.cloud.list_stacks()
@mock.patch.object(shade.OpenStackCloud, 'get_stack')
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
def test_delete_stack(self, mock_heat, mock_get):
stack = {'id': 'stack_id', 'name': 'stack_name'}
mock_get.return_value = stack
self.assertTrue(self.cloud.delete_stack('stack_name'))
mock_get.assert_called_once_with('stack_name')
mock_heat.stacks.delete.assert_called_once_with(id=stack['id'])
@mock.patch.object(shade.OpenStackCloud, 'get_stack')
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
def test_delete_stack_not_found(self, mock_heat, mock_get):
mock_get.return_value = None
self.assertFalse(self.cloud.delete_stack('stack_name'))
mock_get.assert_called_once_with('stack_name')
self.assertFalse(mock_heat.stacks.delete.called)
@mock.patch.object(shade.OpenStackCloud, 'get_stack')
@mock.patch.object(shade.OpenStackCloud, 'heat_client')
def test_delete_stack_exception(self, mock_heat, mock_get):
stack = {'id': 'stack_id', 'name': 'stack_name'}
mock_get.return_value = stack
mock_heat.stacks.delete.side_effect = Exception()
with testtools.ExpectedException(
shade.OpenStackCloudException,
"Failed to delete stack %s" % stack['id']
):
self.cloud.delete_stack('stack_name')