1+ import datetime
12import json
23import os
34import sys
1213from patroni import config , dcs
1314from patroni import multisite
1415from patroni .multisite import MultisiteController
15- from patroni .dcs import Member
16+ from patroni .dcs import Cluster , ClusterConfig , Failover , Leader , Member , Status , SyncState , TimelineHistory
1617from patroni .dcs .etcd import AbstractEtcdClientWithFailover
18+ from patroni .postgresql .misc import PostgresqlRole
1719
1820from tests .test_ha import Config
1921from .test_etcd import etcd_read , etcd_write , socket_getaddrinfo
2022from .test_ha import get_cluster_initialized_with_leader
2123
24+ SYSID = '12345678901'
25+
2226# def get_cluster_config():
2327# return Config({"name": "", "scope": "alpha", "namespace": "batman", "loop_wait": 10, "ttl": 30, "retry_timeout": 10,
2428# "multisite": {"ttl": 90}})
@@ -63,16 +67,43 @@ def get_member_with_wrong_data():
6367 with patch .dict (STANDBY_CONFIG , c ):
6468 return Member .from_node (1 , 'other_dc' , 1 , json .dumps (c ))
6569
70+ def get_cluster (initialize , leader , members , failover , cluster_config = None ):
71+ t = datetime .datetime .now ().isoformat ()
72+ history = TimelineHistory (1 , '[[1,67197376,"no recovery target specified","' + t + '","foo"]]' ,
73+ [(1 , 67197376 , 'no recovery target specified' , t , 'foo' )])
74+ cluster_config = cluster_config or ClusterConfig (1 , {'check_timeline' : True , 'member_slots_ttl' : 0 }, 1 )
75+ s = SyncState .empty ()
76+ return Cluster (initialize , cluster_config , leader , Status (10 , None , []), members , failover , s , history , False )
77+
78+ def get_cluster_initialized_without_leader (leader = False , failover = None , cluster_config = None , failsafe = False ):
79+ m1 = Member (
0 ,
'leader' ,
28 , {
'conn_url' :
'postgres://replicator:[email protected] :5435/postgres' ,
80+ 'api_url' : 'http://127.0.0.1:8008/patroni' , 'xlog_location' : 4 ,
81+ 'role' : PostgresqlRole .PRIMARY , 'state' : 'running' })
82+ leader = Leader (0 , 0 , m1 if leader else Member (0 , '' , 28 , {}))
83+ m2 = Member (
0 ,
'other' ,
28 , {
'conn_url' :
'postgres://replicator:[email protected] :5436/postgres' ,
84+ 'api_url' : 'http://127.0.0.1:8011/patroni' ,
85+ 'state' : 'running' ,
86+ 'pause' : True ,
87+ 'tags' : {'clonefrom' : True },
88+ 'scheduled_restart' : {'schedule' : "2100-01-01 10:53:07.560445+00:00" ,
89+ 'postgres_version' : '99.0.0' }})
90+ failsafe = {m .name : m .api_url for m in (m1 , m2 )} if failsafe else None
91+ return get_cluster (SYSID , leader , [m1 , m2 ], failover , cluster_config )
92+
93+
94+ def get_cluster_initialized_with_leader (failover = None ):
95+ return get_cluster_initialized_without_leader (leader = True , failover = failover )
96+
6697# def get_clusterleader():
6798
6899
69- @patch .object (etcd .Client , 'write' , etcd_write )
100+ # @patch.object(etcd.Client, 'write', etcd_write)
70101# @patch.object(etcd.Client, 'read', etcd_read)
71- @patch .object (etcd .Client , 'delete' , Mock (side_effect = etcd .EtcdException ))
102+ # @patch.object(etcd.Client, 'delete', Mock(side_effect=etcd.EtcdException))
72103class TestMultisite (unittest .TestCase ):
73104
74- @patch ('patroni.dcs.dcs_modules' , Mock (return_value = ['patroni.dcs.etcd' ]))
75- @patch .object (etcd .Client , 'read' , etcd_read )
105+ # @patch('patroni.dcs.dcs_modules', Mock(return_value=['patroni.dcs.etcd']))
106+ # @patch.object(etcd.Client, 'read', etcd_read)
76107 def setUp (self ):
77108 super (TestMultisite , self ).setUp ()
78109 os .environ [Config .PATRONI_CONFIG_VARIABLE ] = PATRONI_CONFIG
@@ -184,7 +215,6 @@ def test_set_standby_config(self):
184215 self .assertEqual (r , True )
185216
186217 def test_check_transition (self ):
187- # with patch.object(MultisiteController, 'on_change', Mock()) as o:
188218 on_change = Mock ()
189219 os .environ [Config .PATRONI_CONFIG_VARIABLE ] = PATRONI_CONFIG .replace ('name: mstest' , 'name: leader' )
190220 self .config = Config (None )
@@ -195,6 +225,40 @@ def test_check_transition(self):
195225 self .m .on_change .assert_called_once ()
196226 self .assertEqual (self .m ._has_leader , True )
197227
228+ # TODO: state_handler case
229+
230+ def test_resolve_multisite_leader (self ):
231+ pass
232+
233+ def test_observe_leader (self ):
234+ pass
235+
236+ def test_update_history (self ):
237+ pass
238+
239+ def test_check_for_failover (self ):
240+ self .multisite .dcs .manual_failover = Mock ()
241+
242+ c = get_cluster_initialized_with_leader (Failover (0 , '' , 'foo' , None , 'mstest' ))
243+ self .multisite ._check_for_failover (c )
244+ self .multisite .dcs .manual_failover .assert_called_with ('' , '' )
245+
246+ c = get_cluster_initialized_with_leader (failover = None )
247+ self .multisite ._check_for_failover (c )
248+ self .assertIsNone (self .multisite ._failover_target )
249+ self .assertIsNone (self .multisite ._failover_timeout )
250+
251+ c = get_cluster_initialized_with_leader (Failover (0 , '' , 'foo' , None , 'other' ))
252+ self .multisite ._check_for_failover (c )
253+ self .assertIsNotNone (self .multisite ._failover_timeout )
254+ self .assertEqual (self .multisite ._failover_target , 'other' )
255+
256+ def test_touch_member (self ):
257+ self .multisite .dcs .touch_member = Mock ()
258+ self .multisite .touch_member ()
259+ self .multisite .dcs .touch_member .assert_called_with ({'host' : '127.0.0.1' , 'port' : 5432 })
260+
261+
198262 # def test_get_dcs_config_exception(self):
199263 # print(self.config.local_configuration)
200264 # with patch(self.config.local_configuration, new_callable=PropertyMock) as local_config:
0 commit comments