Remove singleton pattern from API controllers
This fixes some testing issues, and I don't think it was intended to begin with... Also fixes a bug in the v2 listeners controller that was exposed by this testing, so the LB will return to ACTIVE status as expected on failure to create a listener. Depends-On: I77658cca1aa82240409a91a2d040c105107ff2e2 Change-Id: Ib23a5253acf30b1a820d2d3fba2e60d2f67d321e
This commit is contained in:
parent
04a347a8d1
commit
13730f5bfe
|
@ -12,7 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pecan
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
@ -22,8 +21,13 @@ from octavia.api.v2 import controllers as v2_controller
|
|||
|
||||
|
||||
class RootController(rest.RestController):
|
||||
"""The controller in which the pecan wsgi app should be created with."""
|
||||
v1 = v1_controller.V1Controller()
|
||||
"""The controller with which the pecan wsgi app should be created."""
|
||||
v1 = None
|
||||
|
||||
def __init__(self):
|
||||
super(RootController, self).__init__()
|
||||
self.v1 = v1_controller.V1Controller()
|
||||
setattr(self, 'v2.0', v2_controller.V2Controller())
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
|
@ -36,8 +40,3 @@ class RootController(rest.RestController):
|
|||
'updated': '2016-12-11T00:00:00Z',
|
||||
'id': 'v2.0'}
|
||||
]}
|
||||
|
||||
|
||||
# This controller cannot be specified directly as a member of RootController
|
||||
# as its path is not a valid python identifier
|
||||
pecan.route(RootController, 'v2.0', v2_controller.V2Controller())
|
||||
|
|
|
@ -22,8 +22,13 @@ from octavia.api.v1.controllers import quotas
|
|||
|
||||
class V1Controller(base.BaseController):
|
||||
|
||||
loadbalancers = load_balancer.LoadBalancersController()
|
||||
quotas = quotas.QuotasController()
|
||||
loadbalancers = None
|
||||
quotas = None
|
||||
|
||||
def __init__(self):
|
||||
super(V1Controller, self).__init__()
|
||||
self.loadbalancers = load_balancer.LoadBalancersController()
|
||||
self.quotas = quotas.QuotasController()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
|
|
|
@ -23,10 +23,17 @@ from octavia.api.v2.controllers import pool
|
|||
|
||||
|
||||
class BaseV2Controller(base.BaseController):
|
||||
loadbalancers = load_balancer.LoadBalancersController()
|
||||
listeners = listener.ListenersController()
|
||||
pools = pool.PoolsController()
|
||||
l7policies = l7policy.L7PolicyController()
|
||||
loadbalancers = None
|
||||
listeners = None
|
||||
pools = None
|
||||
l7policies = None
|
||||
|
||||
def __init__(self):
|
||||
super(BaseV2Controller, self).__init__()
|
||||
self.loadbalancers = load_balancer.LoadBalancersController()
|
||||
self.listeners = listener.ListenersController()
|
||||
self.pools = pool.PoolsController()
|
||||
self.l7policies = l7policy.L7PolicyController()
|
||||
|
||||
@wsme_pecan.wsexpose(wtypes.text)
|
||||
def get(self):
|
||||
|
@ -34,4 +41,8 @@ class BaseV2Controller(base.BaseController):
|
|||
|
||||
|
||||
class V2Controller(BaseV2Controller):
|
||||
lbaas = BaseV2Controller()
|
||||
lbaas = None
|
||||
|
||||
def __init__(self):
|
||||
super(V2Controller, self).__init__()
|
||||
self.lbaas = BaseV2Controller()
|
||||
|
|
|
@ -27,6 +27,7 @@ from octavia.api.v2.types import listener as listener_types
|
|||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.db import api as db_api
|
||||
from octavia.db import prepare as db_prepare
|
||||
from octavia.i18n import _LI
|
||||
|
||||
|
@ -111,6 +112,12 @@ class ListenersController(base.BaseController):
|
|||
raise exceptions.NotFound(
|
||||
resource=data_models.Pool._name(), id=pool_id)
|
||||
|
||||
def _reset_lb_status(self, session, lb_id):
|
||||
# Setting LB back to active because this should be a recoverable error
|
||||
self.repositories.load_balancer.update(
|
||||
session, lb_id,
|
||||
provisioning_status=constants.ACTIVE)
|
||||
|
||||
def _validate_listener(self, session, lb_id, listener_dict):
|
||||
"""Validate listener for wrong protocol or duplicate listeners
|
||||
|
||||
|
@ -165,9 +172,13 @@ class ListenersController(base.BaseController):
|
|||
db_listener.id)
|
||||
self.handler.create(db_listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_status(
|
||||
lock_session, lb_id=db_listener.load_balancer_id)
|
||||
# Listener now goes to ERROR
|
||||
self.repositories.listener.update(
|
||||
session, db_listener.id,
|
||||
lock_session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self._get_db_listener(session, db_listener.id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
|
@ -222,9 +233,14 @@ class ListenersController(base.BaseController):
|
|||
LOG.info(_LI("Sending Update of Listener %s to handler"), id)
|
||||
self.handler.update(db_listener, listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_status(
|
||||
lock_session, lb_id=db_listener.load_balancer_id)
|
||||
# Listener now goes to ERROR
|
||||
self.repositories.listener.update(
|
||||
context.session, id, provisioning_status=constants.ERROR)
|
||||
lock_session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self._get_db_listener(context.session, id)
|
||||
result = self._convert_db_to_type(db_listener,
|
||||
listener_types.ListenerResponse)
|
||||
|
@ -245,9 +261,13 @@ class ListenersController(base.BaseController):
|
|||
db_listener.id)
|
||||
self.handler.delete(db_listener)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
with excutils.save_and_reraise_exception(
|
||||
reraise=False), db_api.get_lock_session() as lock_session:
|
||||
self._reset_lb_status(
|
||||
lock_session, lb_id=db_listener.load_balancer_id)
|
||||
# Listener now goes to ERROR
|
||||
self.repositories.listener.update(
|
||||
context.session, db_listener.id,
|
||||
lock_session, db_listener.id,
|
||||
provisioning_status=constants.ERROR)
|
||||
db_listener = self.repositories.listener.get(
|
||||
context.session, id=db_listener.id)
|
||||
|
|
|
@ -80,12 +80,6 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase):
|
|||
'octavia.db.repositories.Repositories.check_quota_met',
|
||||
return_value=True)
|
||||
self.app = self._make_app()
|
||||
# For no apparent reason, the controller code for v2 uses a static
|
||||
# handler mock (the one generated on the initial run) so we need to
|
||||
# retrieve it so we use the "correct" mock instead of the one above
|
||||
self.handler_mock_bug_workaround = getattr(
|
||||
self.app.app.application.application.application.root,
|
||||
'v2.0').handler
|
||||
self.project_id = uuidutils.generate_uuid()
|
||||
|
||||
def reset_pecan():
|
||||
|
|
|
@ -273,12 +273,10 @@ class TestL7Policy(base.BaseAPITest):
|
|||
self.post(self.L7POLICIES_PATH, self._build_body(l7policy), status=400)
|
||||
|
||||
def test_create_with_bad_handler(self):
|
||||
(self.handler_mock_bug_workaround.
|
||||
l7policy.create.side_effect) = Exception
|
||||
self.handler_mock().l7policy.create.side_effect = Exception()
|
||||
api_l7policy = self.create_l7policy(self.listener_id,
|
||||
constants.L7POLICY_ACTION_REJECT,
|
||||
).get(self.root_tag)
|
||||
self.handler_mock_bug_workaround.l7policy.create.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
l7policy_id=api_l7policy.get('id'),
|
||||
|
@ -355,12 +353,10 @@ class TestL7Policy(base.BaseAPITest):
|
|||
new_l7policy = {
|
||||
'action': constants.L7POLICY_ACTION_REDIRECT_TO_URL,
|
||||
'redirect_url': 'http://www.example.com'}
|
||||
(self.handler_mock_bug_workaround.
|
||||
l7policy.update.side_effect) = Exception
|
||||
self.handler_mock().l7policy.update.side_effect = Exception()
|
||||
self.put(self.L7POLICY_PATH.format(
|
||||
l7policy_id=api_l7policy.get('id')),
|
||||
self._build_body(new_l7policy))
|
||||
self.handler_mock_bug_workaround.l7policy.update.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
l7policy_id=api_l7policy.get('id'),
|
||||
|
@ -445,11 +441,9 @@ class TestL7Policy(base.BaseAPITest):
|
|||
self.assertIsNone(api_l7policy.pop('updated_at'))
|
||||
self.assertIsNotNone(response.pop('updated_at'))
|
||||
self.assertEqual(api_l7policy, response)
|
||||
(self.handler_mock_bug_workaround.
|
||||
l7policy.delete.side_effect) = Exception
|
||||
self.handler_mock().l7policy.delete.side_effect = Exception()
|
||||
self.delete(self.L7POLICY_PATH.format(
|
||||
l7policy_id=api_l7policy.get('id')))
|
||||
self.handler_mock_bug_workaround.l7policy.delete.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
l7policy_id=api_l7policy.get('id'),
|
||||
|
|
|
@ -277,6 +277,53 @@ class TestListener(base.BaseAPITest):
|
|||
self.assert_final_lb_statuses(self.lb_id)
|
||||
self.assert_final_listener_statuses(self.lb_id, listener_api['id'])
|
||||
|
||||
def test_create_with_bad_handler(self):
|
||||
self.handler_mock().listener.create.side_effect = Exception()
|
||||
api_listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80,
|
||||
self.lb_id).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id,
|
||||
listener_id=api_listener.get('id'),
|
||||
listener_prov_status=constants.ERROR,
|
||||
listener_op_status=constants.OFFLINE)
|
||||
|
||||
def test_update_with_bad_handler(self):
|
||||
api_listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80,
|
||||
self.lb_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
new_listener = {'name': 'new_name'}
|
||||
self.handler_mock().listener.update.side_effect = Exception()
|
||||
self.put(self.LISTENER_PATH.format(listener_id=api_listener.get('id')),
|
||||
self._build_body(new_listener))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id,
|
||||
listener_id=api_listener.get('id'),
|
||||
listener_prov_status=constants.ERROR)
|
||||
|
||||
def test_delete_with_bad_handler(self):
|
||||
api_listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80,
|
||||
self.lb_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
# Set status to ACTIVE/ONLINE because set_lb_status did it in the db
|
||||
api_listener['provisioning_status'] = constants.ACTIVE
|
||||
api_listener['operating_status'] = constants.ONLINE
|
||||
response = self.get(self.LISTENER_PATH.format(
|
||||
listener_id=api_listener.get('id'))).json.get(self.root_tag)
|
||||
|
||||
self.assertIsNone(api_listener.pop('updated_at'))
|
||||
self.assertIsNotNone(response.pop('updated_at'))
|
||||
self.assertEqual(api_listener, response)
|
||||
self.handler_mock().listener.delete.side_effect = Exception()
|
||||
self.delete(self.LISTENER_PATH.format(
|
||||
listener_id=api_listener.get('id')))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id,
|
||||
listener_id=api_listener.get('id'),
|
||||
listener_prov_status=constants.ERROR)
|
||||
|
||||
def test_update(self):
|
||||
tls_uuid = uuidutils.generate_uuid()
|
||||
listener = self.create_listener(
|
||||
|
|
|
@ -505,6 +505,46 @@ class TestLoadBalancer(base.BaseAPITest):
|
|||
path = self.LB_PATH.format(lb_id='bad_uuid')
|
||||
self.delete(path, status=404)
|
||||
|
||||
def test_create_with_bad_handler(self):
|
||||
self.handler_mock().load_balancer.create.side_effect = Exception()
|
||||
api_lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=api_lb.get('id'),
|
||||
lb_prov_status=constants.ERROR,
|
||||
lb_op_status=constants.OFFLINE)
|
||||
|
||||
def test_update_with_bad_handler(self):
|
||||
api_lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=api_lb.get('id'))
|
||||
new_listener = {'name': 'new_name'}
|
||||
self.handler_mock().load_balancer.update.side_effect = Exception()
|
||||
self.put(self.LB_PATH.format(lb_id=api_lb.get('id')),
|
||||
self._build_body(new_listener))
|
||||
self.assert_correct_status(
|
||||
lb_id=api_lb.get('id'),
|
||||
lb_prov_status=constants.ERROR)
|
||||
|
||||
def test_delete_with_bad_handler(self):
|
||||
api_lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=api_lb.get('id'))
|
||||
# Set status to ACTIVE/ONLINE because set_lb_status did it in the db
|
||||
api_lb['provisioning_status'] = constants.ACTIVE
|
||||
api_lb['operating_status'] = constants.ONLINE
|
||||
response = self.get(self.LB_PATH.format(
|
||||
lb_id=api_lb.get('id'))).json.get(self.root_tag)
|
||||
|
||||
self.assertIsNone(api_lb.pop('updated_at'))
|
||||
self.assertIsNotNone(response.pop('updated_at'))
|
||||
self.assertEqual(api_lb, response)
|
||||
self.handler_mock().load_balancer.delete.side_effect = Exception()
|
||||
self.delete(self.LB_PATH.format(lb_id=api_lb.get('id')))
|
||||
self.assert_correct_status(
|
||||
lb_id=api_lb.get('id'),
|
||||
lb_prov_status=constants.ERROR)
|
||||
|
||||
|
||||
class TestLoadBalancerGraph(base.BaseAPITest):
|
||||
|
||||
|
|
|
@ -314,14 +314,12 @@ class TestPool(base.BaseAPITest):
|
|||
self.post(self.POOLS_PATH, self._build_body(lb_pool), status=400)
|
||||
|
||||
def test_create_with_bad_handler(self):
|
||||
self.handler_mock_bug_workaround.pool.create.side_effect = Exception()
|
||||
self.handler_mock().pool.create.side_effect = Exception()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
# This mock doesn't recycle properly so we have to do cleanup manually
|
||||
self.handler_mock_bug_workaround.pool.create.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
|
@ -388,11 +386,9 @@ class TestPool(base.BaseAPITest):
|
|||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
new_pool = {'name': 'new_name'}
|
||||
self.handler_mock_bug_workaround.pool.update.side_effect = Exception()
|
||||
self.handler_mock().pool.update.side_effect = Exception()
|
||||
self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool))
|
||||
# This mock doesn't recycle properly so we have to do cleanup manually
|
||||
self.handler_mock_bug_workaround.pool.update.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
|
@ -458,10 +454,8 @@ class TestPool(base.BaseAPITest):
|
|||
self.assertIsNone(api_pool.pop('updated_at'))
|
||||
self.assertIsNotNone(response.pop('updated_at'))
|
||||
self.assertEqual(api_pool, response)
|
||||
self.handler_mock_bug_workaround.pool.delete.side_effect = Exception()
|
||||
self.handler_mock().pool.delete.side_effect = Exception()
|
||||
self.delete(self.POOL_PATH.format(pool_id=api_pool.get('id')))
|
||||
# This mock doesn't recycle properly so we have to do cleanup manually
|
||||
self.handler_mock_bug_workaround.pool.delete.side_effect = None
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
|
|
Loading…
Reference in New Issue