diff --git a/features/patroni_api.feature b/features/patroni_api.feature index d6d53a17..edda1220 100644 --- a/features/patroni_api.feature +++ b/features/patroni_api.feature @@ -8,6 +8,8 @@ Scenario: check API requests on a stand-alone server Then I receive a response code 200 And I receive a response state running And I receive a response role master + When I issue a GET request to http://127.0.0.1:8008/standby_leader + Then I receive a response code 503 When I issue a GET request to http://127.0.0.1:8008/health Then I receive a response code 200 When I issue a GET request to http://127.0.0.1:8008/replica diff --git a/features/standby_cluster.feature b/features/standby_cluster.feature index 44310290..78e488b6 100644 --- a/features/standby_cluster.feature +++ b/features/standby_cluster.feature @@ -26,7 +26,7 @@ Feature: standby cluster When I add the table foo to postgres0 Then table foo is present on postgres1 after 20 seconds When I issue a GET request to http://127.0.0.1:8009/master - Then I receive a response code 200 + Then I receive a response code 503 When I issue a GET request to http://127.0.0.1:8009/standby_leader Then I receive a response code 200 And I receive a response role standby_leader @@ -40,7 +40,7 @@ Feature: standby cluster And I kill postmaster on postgres1 Then postgres2 is replicating from postgres0 after 32 seconds When I issue a GET request to http://127.0.0.1:8010/master - Then I receive a response code 200 + Then I receive a response code 503 When I issue a GET request to http://127.0.0.1:8010/standby_leader Then I receive a response code 200 And I receive a response role standby_leader diff --git a/patroni/api.py b/patroni/api.py index 3cd005d1..4616595e 100644 --- a/patroni/api.py +++ b/patroni/api.py @@ -89,23 +89,32 @@ class RestApiHandler(BaseHTTPRequestHandler): patroni = self.server.patroni cluster = patroni.dcs.cluster - if not cluster and patroni.ha.is_paused(): - primary_status_code = 200 if response['role'] == 'master' else 503 - else: - primary_status_code = 200 if patroni.ha.is_leader() else 503 - replica_status_code = 200 if not patroni.noloadbalance and \ response.get('role') == 'replica' and response.get('state') == 'running' else 503 + + if not cluster and patroni.ha.is_paused(): + primary_status_code = 200 if response.get('role') == 'master' else 503 + standby_leader_status_code = 200 if response.get('role') == 'standby_leader' else 503 + elif patroni.ha.is_leader(): + if patroni.ha.is_standby_cluster(): + primary_status_code = replica_status_code = 503 + standby_leader_status_code = 200 if response.get('role') in ('replica', 'standby_leader') else 503 + else: + primary_status_code = 200 + standby_leader_status_code = 503 + else: + primary_status_code = standby_leader_status_code = 503 + status_code = 503 - if patroni.ha.is_standby_cluster() and ('standby_leader' in path or 'standby-leader' in path): - status_code = 200 if patroni.ha.is_leader() else 503 + if 'standby_leader' in path or 'standby-leader' in path: + status_code = standby_leader_status_code elif 'master' in path or 'leader' in path or 'primary' in path or 'read-write' in path: status_code = primary_status_code elif 'replica' in path: status_code = replica_status_code elif 'read-only' in path: - status_code = 200 if primary_status_code == 200 else replica_status_code + status_code = 200 if 200 in (primary_status_code, standby_leader_status_code) else replica_status_code elif 'health' in path: status_code = 200 if response.get('state') == 'running' else 503 elif cluster: # dcs is available diff --git a/patroni/ha.py b/patroni/ha.py index 8afe7aa2..d144dd02 100644 --- a/patroni/ha.py +++ b/patroni/ha.py @@ -391,10 +391,13 @@ class Ha(object): if self.is_paused(): if not (self._rewind.is_needed and self._rewind.can_rewind_or_reinitialize_allowed)\ or self.cluster.is_unlocked(): - self.state_handler.set_role('master' if is_leader else 'replica') if is_leader: + self.state_handler.set_role('master') return 'continue to run as master without lock' - elif not node_to_follow: + elif self.state_handler.role != 'standby_leader': + self.state_handler.set_role('replica') + + if not node_to_follow: return 'no action' elif is_leader: self.demote('immediate-nolock') diff --git a/tests/test_api.py b/tests/test_api.py index 928def0c..92ea89c2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -179,6 +179,8 @@ class TestRestApiHandler(unittest.TestCase): MockRestApiServer(RestApiHandler, 'GET /asynchronous') MockPatroni.ha.is_leader = Mock(return_value=True) MockRestApiServer(RestApiHandler, 'GET /replica') + with patch.object(MockHa, 'is_standby_cluster', Mock(return_value=True)): + MockRestApiServer(RestApiHandler, 'GET /standby_leader') MockPatroni.dcs.cluster = None with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'master'})): MockRestApiServer(RestApiHandler, 'GET /master')