5
5
import platform
6
6
import subprocess
7
7
import unittest
8
- from unittest .mock import MagicMock , Mock , PropertyMock , mock_open , patch
8
+ from unittest .mock import MagicMock , Mock , PropertyMock , call , mock_open , patch
9
9
10
10
import pytest
11
11
from charms .operator_libs_linux .v2 import snap
14
14
PostgreSQLEnableDisableExtensionError ,
15
15
PostgreSQLUpdateUserPasswordError ,
16
16
)
17
+ from ops import Unit
17
18
from ops .framework import EventBase
18
19
from ops .model import (
19
20
ActiveStatus ,
26
27
from parameterized import parameterized
27
28
from tenacity import RetryError
28
29
29
- from charm import EXTENSIONS_DEPENDENCY_MESSAGE , NO_PRIMARY_MESSAGE , PostgresqlOperatorCharm
30
+ from charm import (
31
+ EXTENSIONS_DEPENDENCY_MESSAGE ,
32
+ PRIMARY_NOT_REACHABLE_MESSAGE ,
33
+ PostgresqlOperatorCharm ,
34
+ )
30
35
from cluster import RemoveRaftMemberFailedError
31
36
from constants import PEER , POSTGRESQL_SNAP_NAME , SECRET_INTERNAL_LABEL , SNAP_PACKAGES
32
37
from tests .helpers import patch_network_get
@@ -208,12 +213,12 @@ def test_on_leader_elected(
208
213
_update_relation_endpoints .assert_called_once ()
209
214
self .assertFalse (isinstance (self .harness .model .unit .status , BlockedStatus ))
210
215
211
- # Check for a BlockedStatus when there is no primary endpoint .
216
+ # Check for a WaitingStatus when the primary is not reachable yet .
212
217
_primary_endpoint .return_value = None
213
218
self .harness .set_leader (False )
214
219
self .harness .set_leader ()
215
220
_update_relation_endpoints .assert_called_once () # Assert it was not called again.
216
- self .assertTrue (isinstance (self .harness .model .unit .status , BlockedStatus ))
221
+ self .assertTrue (isinstance (self .harness .model .unit .status , WaitingStatus ))
217
222
218
223
def test_is_cluster_initialised (self ):
219
224
# Test when the cluster was not initialised yet.
@@ -1270,15 +1275,14 @@ def test_on_cluster_topology_change(self, _primary_endpoint, _update_relation_en
1270
1275
def test_on_cluster_topology_change_keep_blocked (
1271
1276
self , _update_relation_endpoints , _primary_endpoint
1272
1277
):
1273
- self .harness .model .unit .status = BlockedStatus ( NO_PRIMARY_MESSAGE )
1278
+ self .harness .model .unit .status = WaitingStatus ( PRIMARY_NOT_REACHABLE_MESSAGE )
1274
1279
1275
1280
self .charm ._on_cluster_topology_change (Mock ())
1276
1281
1277
1282
_update_relation_endpoints .assert_not_called ()
1278
- self .assertEqual (_primary_endpoint .call_count , 2 )
1279
- _primary_endpoint .assert_called_with ()
1280
- self .assertTrue (isinstance (self .harness .model .unit .status , BlockedStatus ))
1281
- self .assertEqual (self .harness .model .unit .status .message , NO_PRIMARY_MESSAGE )
1283
+ _primary_endpoint .assert_called_once_with ()
1284
+ self .assertTrue (isinstance (self .harness .model .unit .status , WaitingStatus ))
1285
+ self .assertEqual (self .harness .model .unit .status .message , PRIMARY_NOT_REACHABLE_MESSAGE )
1282
1286
1283
1287
@patch (
1284
1288
"charm.PostgresqlOperatorCharm.primary_endpoint" ,
@@ -1289,13 +1293,12 @@ def test_on_cluster_topology_change_keep_blocked(
1289
1293
def test_on_cluster_topology_change_clear_blocked (
1290
1294
self , _update_relation_endpoints , _primary_endpoint
1291
1295
):
1292
- self .harness .model .unit .status = BlockedStatus ( NO_PRIMARY_MESSAGE )
1296
+ self .harness .model .unit .status = WaitingStatus ( PRIMARY_NOT_REACHABLE_MESSAGE )
1293
1297
1294
1298
self .charm ._on_cluster_topology_change (Mock ())
1295
1299
1296
1300
_update_relation_endpoints .assert_called_once_with ()
1297
- self .assertEqual (_primary_endpoint .call_count , 2 )
1298
- _primary_endpoint .assert_called_with ()
1301
+ _primary_endpoint .assert_called_once_with ()
1299
1302
self .assertTrue (isinstance (self .harness .model .unit .status , ActiveStatus ))
1300
1303
1301
1304
@patch_network_get (private_address = "1.1.1.1" )
@@ -1936,3 +1939,213 @@ def test_migration_from_single_secret(self, scope, is_leader, _, __):
1936
1939
assert SECRET_INTERNAL_LABEL not in self .harness .get_relation_data (
1937
1940
self .rel_id , getattr (self .charm , scope ).name
1938
1941
)
1942
+
1943
+ @patch ("charm.PostgresqlOperatorCharm._update_relation_endpoints" )
1944
+ @patch ("charm.PostgresqlOperatorCharm.primary_endpoint" , new_callable = PropertyMock )
1945
+ @patch ("charm.PostgresqlOperatorCharm.update_config" )
1946
+ @patch ("charm.PostgresqlOperatorCharm._remove_from_members_ips" )
1947
+ @patch ("charm.Patroni.are_all_members_ready" )
1948
+ @patch ("charm.PostgresqlOperatorCharm._get_ips_to_remove" )
1949
+ @patch ("charm.PostgresqlOperatorCharm._updated_synchronous_node_count" )
1950
+ @patch ("charm.Patroni.remove_raft_member" )
1951
+ @patch ("charm.PostgresqlOperatorCharm._unit_ip" )
1952
+ @patch ("charm.Patroni.get_member_ip" )
1953
+ def test_on_peer_relation_departed (
1954
+ self ,
1955
+ _get_member_ip ,
1956
+ _unit_ip ,
1957
+ _remove_raft_member ,
1958
+ _updated_synchronous_node_count ,
1959
+ _get_ips_to_remove ,
1960
+ _are_all_members_ready ,
1961
+ _remove_from_members_ips ,
1962
+ _update_config ,
1963
+ _primary_endpoint ,
1964
+ _update_relation_endpoints ,
1965
+ ):
1966
+ # Test when the current unit is the departing unit.
1967
+ self .charm .unit .status = ActiveStatus ()
1968
+ event = Mock ()
1969
+ event .departing_unit = self .harness .charm .unit
1970
+ self .charm ._on_peer_relation_departed (event )
1971
+ _remove_raft_member .assert_not_called ()
1972
+ event .defer .assert_not_called ()
1973
+ _updated_synchronous_node_count .assert_not_called ()
1974
+ _get_ips_to_remove .assert_not_called ()
1975
+ _remove_from_members_ips .assert_not_called ()
1976
+ _update_config .assert_not_called ()
1977
+ _update_relation_endpoints .assert_not_called ()
1978
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
1979
+
1980
+ # Test when the current unit is not the departing unit, but removing
1981
+ # the member from the raft cluster fails.
1982
+ _remove_raft_member .side_effect = RemoveRaftMemberFailedError
1983
+ event .departing_unit = Unit (
1984
+ f"{ self .charm .app .name } /1" , None , self .harness .charm .app ._backend , {}
1985
+ )
1986
+ mock_ip_address = "1.1.1.1"
1987
+ _get_member_ip .return_value = mock_ip_address
1988
+ self .charm ._on_peer_relation_departed (event )
1989
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
1990
+ event .defer .assert_called_once ()
1991
+ _updated_synchronous_node_count .assert_not_called ()
1992
+ _get_ips_to_remove .assert_not_called ()
1993
+ _remove_from_members_ips .assert_not_called ()
1994
+ _update_config .assert_not_called ()
1995
+ _update_relation_endpoints .assert_not_called ()
1996
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
1997
+
1998
+ # Test when the member is successfully removed from the raft cluster,
1999
+ # but the unit is not the leader.
2000
+ _remove_raft_member .reset_mock ()
2001
+ event .defer .reset_mock ()
2002
+ _remove_raft_member .side_effect = None
2003
+ self .charm ._on_peer_relation_departed (event )
2004
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2005
+ event .defer .assert_not_called ()
2006
+ _updated_synchronous_node_count .assert_not_called ()
2007
+ _get_ips_to_remove .assert_not_called ()
2008
+ _remove_from_members_ips .assert_not_called ()
2009
+ _update_config .assert_not_called ()
2010
+ _update_relation_endpoints .assert_not_called ()
2011
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2012
+
2013
+ # Test when the unit is the leader, but the cluster hasn't initialized yet,
2014
+ # or it was unable to set synchronous_node_count.
2015
+ _remove_raft_member .reset_mock ()
2016
+ with self .harness .hooks_disabled ():
2017
+ self .harness .set_leader ()
2018
+ self .charm ._on_peer_relation_departed (event )
2019
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2020
+ event .defer .assert_called_once ()
2021
+ _updated_synchronous_node_count .assert_not_called ()
2022
+ _get_ips_to_remove .assert_not_called ()
2023
+ _remove_from_members_ips .assert_not_called ()
2024
+ _update_config .assert_not_called ()
2025
+ _update_relation_endpoints .assert_not_called ()
2026
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2027
+
2028
+ _remove_raft_member .reset_mock ()
2029
+ event .defer .reset_mock ()
2030
+ _updated_synchronous_node_count .return_value = False
2031
+ with self .harness .hooks_disabled ():
2032
+ self .harness .update_relation_data (
2033
+ self .rel_id , self .charm .app .name , {"cluster_initialised" : "True" }
2034
+ )
2035
+ self .charm ._on_peer_relation_departed (event )
2036
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2037
+ event .defer .assert_called_once ()
2038
+ _updated_synchronous_node_count .assert_called_once_with (1 )
2039
+ _get_ips_to_remove .assert_not_called ()
2040
+ _remove_from_members_ips .assert_not_called ()
2041
+ _update_config .assert_not_called ()
2042
+ _update_relation_endpoints .assert_not_called ()
2043
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2044
+
2045
+ # Test when there is more units in the cluster.
2046
+ _remove_raft_member .reset_mock ()
2047
+ event .defer .reset_mock ()
2048
+ _updated_synchronous_node_count .reset_mock ()
2049
+ self .harness .add_relation_unit (self .rel_id , f"{ self .charm .app .name } /2" )
2050
+ self .charm ._on_peer_relation_departed (event )
2051
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2052
+ event .defer .assert_called_once ()
2053
+ _updated_synchronous_node_count .assert_called_once_with (2 )
2054
+ _get_ips_to_remove .assert_not_called ()
2055
+ _remove_from_members_ips .assert_not_called ()
2056
+ _update_config .assert_not_called ()
2057
+ _update_relation_endpoints .assert_not_called ()
2058
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2059
+
2060
+ # Test when the cluster is initialised, and it could set synchronous_node_count,
2061
+ # but there is no IPs to be removed from the members list.
2062
+ _remove_raft_member .reset_mock ()
2063
+ event .defer .reset_mock ()
2064
+ _updated_synchronous_node_count .reset_mock ()
2065
+ _updated_synchronous_node_count .return_value = True
2066
+ self .charm ._on_peer_relation_departed (event )
2067
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2068
+ event .defer .assert_not_called ()
2069
+ _updated_synchronous_node_count .assert_called_once_with (2 )
2070
+ _get_ips_to_remove .assert_called_once ()
2071
+ _remove_from_members_ips .assert_not_called ()
2072
+ _update_config .assert_not_called ()
2073
+ _update_relation_endpoints .assert_not_called ()
2074
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2075
+
2076
+ # Test when there are IPs to be removed from the members list, but not all
2077
+ # the members are ready yet.
2078
+ _remove_raft_member .reset_mock ()
2079
+ _updated_synchronous_node_count .reset_mock ()
2080
+ _get_ips_to_remove .reset_mock ()
2081
+ ips_to_remove = ["2.2.2.2" , "3.3.3.3" ]
2082
+ _get_ips_to_remove .return_value = ips_to_remove
2083
+ _are_all_members_ready .return_value = False
2084
+ self .charm ._on_peer_relation_departed (event )
2085
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2086
+ event .defer .assert_called_once ()
2087
+ _updated_synchronous_node_count .assert_called_once_with (2 )
2088
+ _get_ips_to_remove .assert_called_once ()
2089
+ _remove_from_members_ips .assert_not_called ()
2090
+ _update_config .assert_not_called ()
2091
+ _update_relation_endpoints .assert_not_called ()
2092
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2093
+
2094
+ # Test when all members are ready.
2095
+ _remove_raft_member .reset_mock ()
2096
+ event .defer .reset_mock ()
2097
+ _updated_synchronous_node_count .reset_mock ()
2098
+ _get_ips_to_remove .reset_mock ()
2099
+ _are_all_members_ready .return_value = True
2100
+ self .charm ._on_peer_relation_departed (event )
2101
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2102
+ event .defer .assert_not_called ()
2103
+ _updated_synchronous_node_count .assert_called_once_with (2 )
2104
+ _get_ips_to_remove .assert_called_once ()
2105
+ _remove_from_members_ips .assert_has_calls ([call (ips_to_remove [0 ]), call (ips_to_remove [1 ])])
2106
+ self .assertEqual (_update_config .call_count , 2 )
2107
+ self .assertEqual (_update_relation_endpoints .call_count , 2 )
2108
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2109
+
2110
+ # Test when the primary is not reachable yet.
2111
+ _remove_raft_member .reset_mock ()
2112
+ event .defer .reset_mock ()
2113
+ _updated_synchronous_node_count .reset_mock ()
2114
+ _get_ips_to_remove .reset_mock ()
2115
+ _remove_from_members_ips .reset_mock ()
2116
+ _update_config .reset_mock ()
2117
+ _update_relation_endpoints .reset_mock ()
2118
+ _primary_endpoint .return_value = None
2119
+ self .charm ._on_peer_relation_departed (event )
2120
+ _remove_raft_member .assert_called_once_with (mock_ip_address )
2121
+ event .defer .assert_not_called ()
2122
+ _updated_synchronous_node_count .assert_called_once_with (2 )
2123
+ _get_ips_to_remove .assert_called_once ()
2124
+ _remove_from_members_ips .assert_called_once ()
2125
+ _update_config .assert_called_once ()
2126
+ _update_relation_endpoints .assert_not_called ()
2127
+ self .assertIsInstance (self .charm .unit .status , WaitingStatus )
2128
+
2129
+ @patch ("charm.PostgresqlOperatorCharm._update_relation_endpoints" )
2130
+ @patch ("charm.PostgresqlOperatorCharm.primary_endpoint" , new_callable = PropertyMock )
2131
+ def test_update_new_unit_status (self , _primary_endpoint , _update_relation_endpoints ):
2132
+ # Test when the charm is blocked.
2133
+ _primary_endpoint .return_value = "endpoint"
2134
+ self .charm .unit .status = BlockedStatus ("fake blocked status" )
2135
+ self .charm ._update_new_unit_status ()
2136
+ _update_relation_endpoints .assert_called_once ()
2137
+ self .assertIsInstance (self .charm .unit .status , BlockedStatus )
2138
+
2139
+ # Test when the charm is not blocked.
2140
+ _update_relation_endpoints .reset_mock ()
2141
+ self .charm .unit .status = WaitingStatus ()
2142
+ self .charm ._update_new_unit_status ()
2143
+ _update_relation_endpoints .assert_called_once ()
2144
+ self .assertIsInstance (self .charm .unit .status , ActiveStatus )
2145
+
2146
+ # Test when the primary endpoint is not reachable yet.
2147
+ _update_relation_endpoints .reset_mock ()
2148
+ _primary_endpoint .return_value = None
2149
+ self .charm ._update_new_unit_status ()
2150
+ _update_relation_endpoints .assert_not_called ()
2151
+ self .assertIsInstance (self .charm .unit .status , WaitingStatus )
0 commit comments