diff --git a/octavia/controller/worker/v2/controller_worker.py b/octavia/controller/worker/v2/controller_worker.py index fa4873df1b..fa1feb2164 100644 --- a/octavia/controller/worker/v2/controller_worker.py +++ b/octavia/controller/worker/v2/controller_worker.py @@ -431,8 +431,8 @@ class ControllerWorker: constants.SERVER_GROUP_ID: db_lb.server_group_id, constants.PROJECT_ID: db_lb.project_id} if cascade: - listeners = flow_utils.get_listeners_on_lb(db_lb) - pools = flow_utils.get_pools_on_lb(db_lb) + listeners = flow_utils.get_listeners_on_lb(db_lb, True) + pools = flow_utils.get_pools_on_lb(db_lb, True) self.run_flow( flow_utils.get_cascade_delete_load_balancer_flow, diff --git a/octavia/controller/worker/v2/flows/flow_utils.py b/octavia/controller/worker/v2/flows/flow_utils.py index 9e0ba2c0fb..f9528cc968 100644 --- a/octavia/controller/worker/v2/flows/flow_utils.py +++ b/octavia/controller/worker/v2/flows/flow_utils.py @@ -40,29 +40,31 @@ def get_delete_load_balancer_flow(lb): return LB_FLOWS.get_delete_load_balancer_flow(lb) -def get_listeners_on_lb(db_lb): +def get_listeners_on_lb(db_lb, for_delete=False): """Get a list of the listeners on a load balancer. :param db_lb: A load balancer database model object. + :param for_delete: Skip errors on tls certs loading. :returns: A list of provider dict format listeners. """ listener_dicts = [] for listener in db_lb.listeners: prov_listener = provider_utils.db_listener_to_provider_listener( - listener) + listener, for_delete) listener_dicts.append(prov_listener.to_dict()) return listener_dicts -def get_pools_on_lb(db_lb): +def get_pools_on_lb(db_lb, for_delete=False): """Get a list of the pools on a load balancer. :param db_lb: A load balancer database model object. + :param for_delete: Skip errors on tls certs loading. :returns: A list of provider dict format pools. """ pool_dicts = [] for pool in db_lb.pools: - prov_pool = provider_utils.db_pool_to_provider_pool(pool) + prov_pool = provider_utils.db_pool_to_provider_pool(pool, for_delete) pool_dicts.append(prov_pool.to_dict()) return pool_dicts diff --git a/octavia/tests/unit/controller/worker/v2/test_controller_worker.py b/octavia/tests/unit/controller/worker/v2/test_controller_worker.py index 0b6c6d83b1..71285ce391 100644 --- a/octavia/tests/unit/controller/worker/v2/test_controller_worker.py +++ b/octavia/tests/unit/controller/worker/v2/test_controller_worker.py @@ -27,7 +27,7 @@ from octavia.controller.worker.v2 import controller_worker from octavia.controller.worker.v2.flows import flow_utils import octavia.tests.unit.base as base - +TLS_CERT_ID = uuidutils.generate_uuid() AMP_ID = uuidutils.generate_uuid() LB_ID = uuidutils.generate_uuid() LISTENER_ID = uuidutils.generate_uuid() @@ -804,6 +804,61 @@ class TestControllerWorker(base.TestCase): }) ) + @mock.patch( + "octavia.common.tls_utils.cert_parser.load_certificates_data", + side_effect=RuntimeError + ) + def test_delete_load_balancer_with_cascade_tls_unavailable( + self, + mock_load_tls_cert, + mock_api_get_session, + mock_dyn_log_listener, + mock_taskflow_load, + mock_pool_repo_get, + mock_member_repo_get, + mock_l7rule_repo_get, + mock_l7policy_repo_get, + mock_listener_repo_get, + mock_lb_repo_get, + mock_health_mon_repo_get, + mock_amp_repo_get + ): + _flow_mock.reset_mock() + + _listener_mock.tls_certificate_id = TLS_CERT_ID + _listener_mock.to_dict.return_value[ + constants.TLS_CERTIFICATE_ID] = TLS_CERT_ID + + cw = controller_worker.ControllerWorker() + cw.delete_load_balancer(_load_balancer_mock, cascade=True) + + mock_lb_repo_get.assert_called_once_with( + _db_session, + id=LB_ID) + + # Check load_certificates_data called and error is raised + # Error must be ignored because it is not critical for current flow + mock_load_tls_cert.assert_called_once() + + listener_list = [{constants.LISTENER_ID: LISTENER_ID, + constants.LOADBALANCER_ID: LB_ID, + constants.PROJECT_ID: PROJECT_ID, + "default_tls_container_ref": TLS_CERT_ID}] + + (cw.services_controller.run_poster. + assert_called_once_with( + flow_utils.get_cascade_delete_load_balancer_flow, + _load_balancer_mock, listener_list, [], + store={constants.LOADBALANCER: _load_balancer_mock, + constants.LOADBALANCER_ID: LB_ID, + constants.SERVER_GROUP_ID: + _db_load_balancer_mock.server_group_id, + constants.PROJECT_ID: _db_load_balancer_mock.project_id, + }) + ) + + _listener_mock.reset_mock() + @mock.patch('octavia.db.repositories.ListenerRepository.get_all', return_value=([_listener_mock], None)) def test_update_load_balancer(self, diff --git a/releasenotes/notes/fix-loadbalancer-stuck-on-delete-da5950cf87fc8507.yaml b/releasenotes/notes/fix-loadbalancer-stuck-on-delete-da5950cf87fc8507.yaml new file mode 100644 index 0000000000..09129057fe --- /dev/null +++ b/releasenotes/notes/fix-loadbalancer-stuck-on-delete-da5950cf87fc8507.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix load balancer stuck in PENDING_DELETE if TLS storage unavailable or + returns error