diff --git a/keystone/limit/backends/base.py b/keystone/limit/backends/base.py index 42188bf788..f0634106e7 100644 --- a/keystone/limit/backends/base.py +++ b/keystone/limit/backends/base.py @@ -166,3 +166,15 @@ class UnifiedLimitDriverBase(object): """ raise exception.NotImplemented() # pragma: no cover + + @abc.abstractmethod + def delete_limits_for_project(self, project_id): + """Delete the existing limits which belong to the specified project. + + :param project_id: the limits' project id. + + :returns: a dictionary representing the deleted limits id. Used for + cache invalidating. + + """ + raise exception.NotImplemented() # pragma: no cover diff --git a/keystone/limit/backends/sql.py b/keystone/limit/backends/sql.py index fe4700b489..afc6474453 100644 --- a/keystone/limit/backends/sql.py +++ b/keystone/limit/backends/sql.py @@ -348,3 +348,13 @@ class UnifiedLimit(base.UnifiedLimitDriverBase): ref = self._get_limit(session, limit_id) session.delete(ref) + + def delete_limits_for_project(self, project_id): + limit_ids = [] + with sql.session_for_write() as session: + query = session.query(LimitModel) + query = query.filter_by(project_id=project_id) + for limit in query.all(): + limit_ids.append(limit.id) + query.delete() + return limit_ids diff --git a/keystone/limit/core.py b/keystone/limit/core.py index 3c2109d55e..349526fc9c 100644 --- a/keystone/limit/core.py +++ b/keystone/limit/core.py @@ -119,3 +119,8 @@ class Manager(manager.Manager): def delete_limit(self, limit_id): self.driver.delete_limit(limit_id) self.get_limit.invalidate(self, limit_id) + + def delete_limits_for_project(self, project_id): + limit_ids = self.driver.delete_limits_for_project(project_id) + for limit_id in limit_ids: + self.get_limit.invalidate(self, limit_id) diff --git a/keystone/resource/core.py b/keystone/resource/core.py index e4ec7e9756..eaa6af1339 100644 --- a/keystone/resource/core.py +++ b/keystone/resource/core.py @@ -443,6 +443,7 @@ class Manager(manager.Manager): assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate() PROVIDERS.credential_api.delete_credentials_for_project(project_id) PROVIDERS.trust_api.delete_trusts_for_project(project_id) + PROVIDERS.unified_limit_api.delete_limits_for_project(project_id) finally: # attempt to send audit event even if the cache invalidation raises notifications.Audit.deleted(self._PROJECT, project_id, initiator) diff --git a/keystone/tests/unit/limit/test_backends.py b/keystone/tests/unit/limit/test_backends.py index 78dd8d7d7f..eae88af85c 100644 --- a/keystone/tests/unit/limit/test_backends.py +++ b/keystone/tests/unit/limit/test_backends.py @@ -682,3 +682,27 @@ class LimitTests(object): self.assertRaises(exception.LimitNotFound, PROVIDERS.unified_limit_api.delete_limit, uuid.uuid4().hex) + + def test_delete_limit_project(self): + # create two limits + limit_1 = unit.new_limit_ref( + project_id=self.tenant_bar['id'], + service_id=self.service_one['id'], + region_id=self.region_one['id'], + resource_name='volume', resource_limit=10, id=uuid.uuid4().hex) + limit_2 = unit.new_limit_ref( + project_id=self.tenant_bar['id'], + service_id=self.service_one['id'], + region_id=self.region_two['id'], + resource_name='snapshot', resource_limit=5, id=uuid.uuid4().hex) + PROVIDERS.unified_limit_api.create_limits([limit_1, limit_2]) + + # delete a unrelated project, the limits should still be there. + PROVIDERS.resource_api.delete_project(self.tenant_baz['id']) + ref = PROVIDERS.unified_limit_api.list_limits() + self.assertEqual(2, len(ref)) + + # delete the referenced project, the limits should be deleted as well. + PROVIDERS.resource_api.delete_project(self.tenant_bar['id']) + ref = PROVIDERS.unified_limit_api.list_limits() + self.assertEqual([], ref) diff --git a/releasenotes/notes/bug-1779903-f2b22cf23a9e01f9.yaml b/releasenotes/notes/bug-1779903-f2b22cf23a9e01f9.yaml new file mode 100644 index 0000000000..59b8eeea81 --- /dev/null +++ b/releasenotes/notes/bug-1779903-f2b22cf23a9e01f9.yaml @@ -0,0 +1,6 @@ +--- +features: + - > + [`bug 1779903 `_] + When a project is deleted, the limits which belong to it will be deleted + as well.