Fix amphora failover API to work with spares

Spares have no load_balancer yet, and the controller was blindly using
the load_balancer association for auth and status updates. Whoops.

Also properly log when the amp disappears between the API request and
the controller-worker failover attempt.

Change-Id: I6197e72ad9582db32c7c9ca7b6715a2a2abc68b4
This commit is contained in:
Adam Harwell 2018-04-24 18:30:32 -07:00
parent f33c461cb9
commit 4e3810c1ba
3 changed files with 77 additions and 6 deletions

View File

@ -101,13 +101,19 @@ class FailoverController(base.BaseController):
db_amp = self._get_db_amp(context.session, self.amp_id,
show_deleted=False)
self._auth_validate_action(
context, db_amp.load_balancer.project_id,
constants.RBAC_PUT_FAILOVER)
# Check to see if the amphora is a spare (not associated with an LB)
if db_amp.load_balancer:
self._auth_validate_action(
context, db_amp.load_balancer.project_id,
constants.RBAC_PUT_FAILOVER)
self.repositories.load_balancer.test_and_set_provisioning_status(
context.session, db_amp.load_balancer_id,
status=constants.PENDING_UPDATE, raise_exception=True)
else:
self._auth_validate_action(
context, context.project_id, constants.RBAC_PUT_FAILOVER)
self.repositories.load_balancer.test_and_set_provisioning_status(
context.session, db_amp.load_balancer_id,
status=constants.PENDING_UPDATE, raise_exception=True)
try:
LOG.info("Sending failover request for amphora %s to the handler",
self.amp_id)

View File

@ -702,6 +702,10 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
try:
amp = self._amphora_repo.get(db_apis.get_session(),
id=amphora_id)
if not amp:
LOG.warning("Could not fetch Amphora %s from DB, ignoring "
"failover request.", amphora_id)
return
self._perform_amphora_failover(
amp, constants.LB_CREATE_FAILOVER_PRIORITY)
if amp.load_balancer_id:

View File

@ -103,6 +103,24 @@ class TestAmphora(base.BaseAPITest):
[mock.call(self.amp)]
)
def test_failover_spare(self):
amp_args = {
'compute_id': uuidutils.generate_uuid(),
'status': constants.AMPHORA_READY,
'lb_network_ip': '192.168.1.2',
'cert_expiration': datetime.datetime.now(),
'cert_busy': False,
'cached_zone': 'zone1',
'created_at': datetime.datetime.now(),
'updated_at': datetime.datetime.now(),
'image_id': uuidutils.generate_uuid(),
}
amp = self.amphora_repo.create(self.session, **amp_args)
self.put(self.AMPHORA_FAILOVER_PATH.format(
amphora_id=amp.id), body={}, status=202)
self.handler_mock().amphora.failover.assert_has_calls(
[mock.call(amp)])
def test_failover_deleted(self):
new_amp = self._create_additional_amp()
self.amphora_repo.update(self.session, new_amp.id,
@ -156,6 +174,49 @@ class TestAmphora(base.BaseAPITest):
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)
def test_failover_authorized(self):
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
with mock.patch.object(octavia.common.context.Context, 'project_id',
self.project_id):
override_credentials = {
'service_user_id': None,
'user_domain_id': None,
'is_admin_project': True,
'service_project_domain_id': None,
'service_project_id': None,
'roles': ['load-balancer_member'],
'user_id': None,
'is_admin': True,
'service_user_domain_id': None,
'project_domain_id': None,
'service_roles': [],
'project_id': self.project_id}
with mock.patch(
"oslo_context.context.RequestContext.to_policy_values",
return_value=override_credentials):
self.put(self.AMPHORA_FAILOVER_PATH.format(
amphora_id=self.amp_id), body={}, status=202)
# Reset api auth setting
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
self.handler_mock().amphora.failover.assert_has_calls(
[mock.call(self.amp)])
def test_failover_not_authorized(self):
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
with mock.patch.object(octavia.common.context.Context, 'project_id',
uuidutils.generate_uuid()):
response = self.put(self.AMPHORA_FAILOVER_PATH.format(
amphora_id=self.amp_id), body={}, status=403)
# Reset api auth setting
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json)
self.handler_mock().amphora.failover.assert_not_called()
def test_get_deleted_gives_404(self):
new_amp = self._create_additional_amp()