diff --git a/releasenotes/notes/delete_project-399f9b3107014dde.yaml b/releasenotes/notes/delete_project-399f9b3107014dde.yaml new file mode 100644 index 000000000..e4cf39fb9 --- /dev/null +++ b/releasenotes/notes/delete_project-399f9b3107014dde.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - The delete_project() API now conforms to our standard of returning True + when the delete succeeds, or False when the project was not found. It + would previously raise an expection if the project was not found. diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 76b3484f1..0d1818d3f 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -519,10 +519,25 @@ class OpenStackCloud(object): return project def delete_project(self, name_or_id): + """Delete a project + + :param string name_or_id: Project name or id. + + :returns: True if delete succeeded, False if the project was not found. + + :raises: ``OpenStackCloudException`` if something goes wrong during + the openstack API call + """ + with _utils.shade_exceptions( "Error in deleting project {project}".format( project=name_or_id)): project = self.get_project(name_or_id) + if project is None: + self.log.debug( + "Project {0} not found for deleting".format(name_or_id)) + return False + params = {} if self.cloud_config.get_api_version('identity') == '3': params['project'] = project['id'] @@ -530,6 +545,8 @@ class OpenStackCloud(object): params['tenant'] = project['id'] self.manager.submitTask(_tasks.ProjectDelete(**params)) + return True + @_utils.cache_on_arguments() def list_users(self): """List Keystone Users. diff --git a/shade/tests/functional/test_project.py b/shade/tests/functional/test_project.py new file mode 100644 index 000000000..029b14895 --- /dev/null +++ b/shade/tests/functional/test_project.py @@ -0,0 +1,77 @@ +# Copyright (c) 2016 IBM +# +# 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. + +""" +test_project +---------------------------------- + +Functional tests for `shade` project resource. +""" + +from shade.exc import OpenStackCloudException +from shade.tests.functional import base + + +class TestProject(base.BaseFunctionalTestCase): + + def setUp(self): + super(TestProject, self).setUp() + self.new_project_name = self.getUniqueString('project') + self.identity_version = \ + self.operator_cloud.cloud_config.get_api_version('identity') + self.addCleanup(self._cleanup_projects) + + def _cleanup_projects(self): + exception_list = list() + for p in self.operator_cloud.list_projects(): + if p['name'].startswith(self.new_project_name): + try: + self.operator_cloud.delete_project(p['id']) + except Exception as e: + exception_list.append(str(e)) + continue + if exception_list: + raise OpenStackCloudException('\n'.join(exception_list)) + + def test_create_project(self): + project_name = self.new_project_name + '_create' + + params = { + 'name': project_name, + 'description': 'test_create_project', + } + if self.identity_version == '3': + params['domain_id'] = \ + self.operator_cloud.get_domain('default')['id'] + + project = self.operator_cloud.create_project(**params) + + self.assertIsNotNone(project) + self.assertEqual(project_name, project['name']) + self.assertEqual('test_create_project', project['description']) + + def test_delete_project(self): + project_name = self.new_project_name + '_delete' + params = {'name': project_name} + if self.identity_version == '3': + params['domain_id'] = \ + self.operator_cloud.get_domain('default')['id'] + project = self.operator_cloud.create_project(**params) + self.assertIsNotNone(project) + self.assertTrue(self.operator_cloud.delete_project(project['id'])) + + def test_delete_project_not_found(self): + self.assertFalse(self.operator_cloud.delete_project('doesNotExist')) diff --git a/shade/tests/unit/test_project.py b/shade/tests/unit/test_project.py index 47d493734..dc1dd7935 100644 --- a/shade/tests/unit/test_project.py +++ b/shade/tests/unit/test_project.py @@ -73,7 +73,7 @@ class TestProject(base.TestCase): mock_api_version): mock_api_version.return_value = '2' mock_get.return_value = dict(id='123') - self.cloud.delete_project('123') + self.assertTrue(self.cloud.delete_project('123')) mock_get.assert_called_once_with('123') mock_keystone.tenants.delete.assert_called_once_with(tenant='123') @@ -84,7 +84,7 @@ class TestProject(base.TestCase): mock_api_version): mock_api_version.return_value = '3' mock_get.return_value = dict(id='123') - self.cloud.delete_project('123') + self.assertTrue(self.cloud.delete_project('123')) mock_get.assert_called_once_with('123') mock_keystone.projects.delete.assert_called_once_with(project='123')