diff --git a/patroni/api.py b/patroni/api.py index 0018e537..cb7b2d1f 100644 --- a/patroni/api.py +++ b/patroni/api.py @@ -110,7 +110,7 @@ class RestApiHandler(BaseHTTPRequestHandler): status, msg = self.server.patroni.ha.restart() status_code = 200 if status else 503 data = msg.encode('utf-8') - except: + except Exception: logger.exception('Exception during restart') self.send_response(status_code) @@ -148,11 +148,15 @@ class RestApiHandler(BaseHTTPRequestHandler): try: cluster = self.server.patroni.dcs.get_cluster() if cluster.leader and cluster.leader.name != leader: - return 200, ('Successfully failed over to ' + cluster.leader.name).encode('utf-8') + if not candidate or candidate == cluster.leader.name: + return 200, ('Successfully failed over to ' + cluster.leader.name).encode('utf-8') + else: + return 200, 'Failed over to "{0}" instead of "{1}"'.format(cluster.leader.name, + candidate).encode('utf-8') if not cluster.failover: return 503, b'Failover failed' - except: - pass + except Exception as e: + logger.debug('Exception occured during polling failover result: %s', e) return 503, b'Failover status unknown' def is_failover_possible(self, cluster, leader, candidate): diff --git a/tests/test_api.py b/tests/test_api.py index 0d6dd151..5790f8d4 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -113,8 +113,8 @@ class TestRestApiHandler(unittest.TestCase): # make sure socket.error gets propagated via wfile object in finalize() with patch.object(MockRequest, 'makefile') as makefile: makefile.return_value.closed = False - makefile.return_value.readline.side_effect = lambda x: b"foo" - makefile.return_value.flush = Mock(side_effect=socket.error("foo")) + makefile.return_value.readline = Mock(return_value=b'foo') + makefile.return_value.flush = Mock(side_effect=socket.error('foo')) MockRestApiServer(RestApiHandler, b'OPTIONS / HTTP/1.0') def test_do_GET_patroni(self): @@ -157,29 +157,40 @@ class TestRestApiHandler(unittest.TestCase): request = b'POST /failover HTTP/1.0\nAuthorization: Basic dGVzdDp0ZXN0\n' +\ b'Content-Length: 0\n\n' MockRestApiServer(RestApiHandler, request) + + cluster.leader.name = 'postgresql1' + MockRestApiServer(RestApiHandler, request) + request = b'POST /failover HTTP/1.0\nAuthorization: Basic dGVzdDp0ZXN0\n' +\ b'Content-Length: 25\n\n{"leader": "postgresql1"}' MockRestApiServer(RestApiHandler, request) + + cluster.leader.name = 'postgresql2' + request = b'POST /failover HTTP/1.0\nAuthorization: Basic dGVzdDp0ZXN0\n' +\ + b'Content-Length: 53\n\n{"leader": "postgresql1", "candidate": "postgresql2"}' + MockRestApiServer(RestApiHandler, request) + cluster.leader.name = 'postgresql1' MockRestApiServer(RestApiHandler, request) - cluster.members = [Member(0, 'postgresql0', 30, {'api_url': 'http'})] + + cluster.members = [Member(0, 'postgresql0', 30, {'api_url': 'http'}), + Member(0, 'postgresql2', 30, {'api_url': 'http'})] MockRestApiServer(RestApiHandler, request) with patch.object(MockPatroni, 'dcs') as d: cluster = d.get_cluster.return_value cluster.leader.name = 'postgresql0' MockRestApiServer(RestApiHandler, request) + cluster.leader.name = 'postgresql2' + MockRestApiServer(RestApiHandler, request) cluster.leader.name = 'postgresql1' cluster.failover = None MockRestApiServer(RestApiHandler, request) - d.get_cluster = Mock(side_effect=Exception()) + d.get_cluster = Mock(side_effect=Exception) MockRestApiServer(RestApiHandler, request) d.manual_failover.return_value = False MockRestApiServer(RestApiHandler, request) with patch.object(MockHa, 'fetch_nodes_statuses', Mock(return_value=[])): MockRestApiServer(RestApiHandler, request) - request = b'POST /failover HTTP/1.0\nAuthorization: Basic dGVzdDp0ZXN0\n' +\ - b'Content-Length: 50\n\n{"leader": "postgresql1", "member": "postgresql2"}' - MockRestApiServer(RestApiHandler, request) # Valid future date request = b'POST /failover HTTP/1.0\nAuthorization: Basic dGVzdDp0ZXN0\nContent-Length: 103\n\n{"leader": ' +\