mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-08-25 14:53:37 +00:00
Enable pyright strict mode (#2652)
- added pyrightconfig.json with typeCheckingMode=strict - added type hints to all files except api.py - added type stubs for dns, etcd, consul, kazoo, pysyncobj and other modules - added type stubs for psycopg2 and urllib3 with some little fixes - fixes most of the issues reported by pyright - remaining issues will be addressed later, along with enabling CI linting task
This commit is contained in:
+4
-5
@@ -23,6 +23,7 @@ class MockResponse(object):
|
||||
|
||||
def __init__(self, status_code=200):
|
||||
self.status_code = status_code
|
||||
self.headers = {'content-type': 'json'}
|
||||
self.content = '{}'
|
||||
self.reason = 'Not Found'
|
||||
|
||||
@@ -38,10 +39,6 @@ class MockResponse(object):
|
||||
def getheader(*args):
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def getheaders():
|
||||
return {'content-type': 'json'}
|
||||
|
||||
|
||||
def requests_get(url, method='GET', endpoint=None, data='', **kwargs):
|
||||
members = '[{"id":14855829450254237642,"peerURLs":["http://localhost:2380","http://localhost:7001"],' +\
|
||||
@@ -85,6 +82,8 @@ class MockCursor(object):
|
||||
self.description = [Mock()]
|
||||
|
||||
def execute(self, sql, *params):
|
||||
if isinstance(sql, bytes):
|
||||
sql = sql.decode('utf-8')
|
||||
if sql.startswith('blabla'):
|
||||
raise psycopg.ProgrammingError()
|
||||
elif sql == 'CHECKPOINT' or sql.startswith('SELECT pg_catalog.pg_create_'):
|
||||
@@ -98,7 +97,7 @@ class MockCursor(object):
|
||||
elif sql.startswith('SELECT slot_name'):
|
||||
self.results = [('blabla', 'physical'), ('foobar', 'physical'), ('ls', 'logical', 'a', 'b', 5, 100, 500)]
|
||||
elif sql.startswith('WITH slots AS (SELECT slot_name, active'):
|
||||
self.results = [(False, True)]
|
||||
self.results = [(False, True)] if self.rowcount == 1 else [None]
|
||||
elif sql.startswith('SELECT CASE WHEN pg_catalog.pg_is_in_recovery()'):
|
||||
self.results = [(1, 2, 1, 0, False, 1, 1, None, None,
|
||||
[{"slot_name": "ls", "confirmed_flush_lsn": 12345}],
|
||||
|
||||
+1
-1
@@ -136,8 +136,8 @@ class TestCitus(BaseTestPostgresql):
|
||||
self.assertEqual(parameters['shared_preload_libraries'], 'citus,foo,bar')
|
||||
self.assertEqual(parameters['wal_level'], 'logical')
|
||||
|
||||
@patch.object(CitusHandler, 'is_enabled', Mock(return_value=False))
|
||||
def test_bootstrap(self):
|
||||
self.c._config = None
|
||||
self.c.bootstrap()
|
||||
|
||||
def test_ignore_replication_slot(self):
|
||||
|
||||
@@ -19,8 +19,9 @@ class TestConfig(unittest.TestCase):
|
||||
|
||||
def test_set_dynamic_configuration(self):
|
||||
with patch.object(Config, '_build_effective_configuration', Mock(side_effect=Exception)):
|
||||
self.assertIsNone(self.config.set_dynamic_configuration({'foo': 'bar'}))
|
||||
self.assertTrue(self.config.set_dynamic_configuration({'standby_cluster': {}}))
|
||||
self.assertFalse(self.config.set_dynamic_configuration({'foo': 'bar'}))
|
||||
self.assertTrue(self.config.set_dynamic_configuration({'standby_cluster': {}, 'postgresql': {
|
||||
'parameters': {'cluster_name': 1, 'wal_keep_size': 1, 'track_commit_timestamp': 1, 'wal_level': 1}}}))
|
||||
|
||||
def test_reload_local_configuration(self):
|
||||
os.environ.update({
|
||||
|
||||
@@ -195,6 +195,8 @@ class TestConsul(unittest.TestCase):
|
||||
@patch.object(consul.Consul.KV, 'delete', Mock(return_value=True))
|
||||
def test_delete_leader(self):
|
||||
self.c.delete_leader()
|
||||
self.c._name = 'other'
|
||||
self.c.delete_leader()
|
||||
|
||||
@patch.object(consul.Consul.KV, 'put', Mock(return_value=True))
|
||||
def test_initialize(self):
|
||||
|
||||
+5
-1
@@ -9,7 +9,7 @@ from mock import patch, Mock, PropertyMock
|
||||
from patroni.ctl import ctl, load_config, output_members, get_dcs, parse_dcs, \
|
||||
get_all_members, get_any_member, get_cursor, query_member, PatroniCtlException, apply_config_changes, \
|
||||
format_config_for_editing, show_diff, invoke_editor, format_pg_version, CONFIG_FILE_PATH, PatronictlPrettyTable
|
||||
from patroni.dcs.etcd import AbstractEtcdClientWithFailover, Failover
|
||||
from patroni.dcs.etcd import AbstractEtcdClientWithFailover, Cluster, Failover
|
||||
from patroni.psycopg import OperationalError
|
||||
from patroni.utils import tzutc
|
||||
from prettytable import PrettyTable, ALL
|
||||
@@ -639,6 +639,10 @@ class TestCtl(unittest.TestCase):
|
||||
self.runner.invoke(ctl, ['edit-config', 'dummy', '--force', '--apply', '-'], input='foo: bar')
|
||||
mock_get_dcs.return_value.set_config_value.return_value = True
|
||||
self.runner.invoke(ctl, ['edit-config', 'dummy', '--force', '--apply', '-'], input='foo: bar')
|
||||
mock_get_dcs.return_value.get_cluster = Mock(return_value=Cluster.empty())
|
||||
result = self.runner.invoke(ctl, ['edit-config', 'dummy'])
|
||||
assert result.exit_code == 1
|
||||
assert 'The config key does not exist in the cluster dummy' in result.output
|
||||
|
||||
@patch('patroni.ctl.get_dcs')
|
||||
def test_version(self, mock_get_dcs):
|
||||
|
||||
+3
-1
@@ -138,7 +138,9 @@ class TestClient(unittest.TestCase):
|
||||
@patch.object(EtcdClient, '_get_machines_list',
|
||||
Mock(return_value=['http://localhost:2379', 'http://localhost:4001']))
|
||||
def setUp(self):
|
||||
self.client = EtcdClient({'srv': 'test', 'retry_timeout': 3}, DnsCachingResolver())
|
||||
self.etcd = Etcd({'namespace': '/patroni/', 'ttl': 30, 'retry_timeout': 3,
|
||||
'srv': 'test', 'scope': 'test', 'name': 'foo'})
|
||||
self.client = self.etcd._client
|
||||
self.client.http.request = http_request
|
||||
self.client.http.request_encode_body = http_request
|
||||
|
||||
|
||||
+16
-5
@@ -3,7 +3,7 @@ import json
|
||||
import unittest
|
||||
import urllib3
|
||||
|
||||
from mock import Mock, patch
|
||||
from mock import Mock, PropertyMock, patch
|
||||
from patroni.dcs.etcd import DnsCachingResolver
|
||||
from patroni.dcs.etcd3 import PatroniEtcd3Client, Cluster, Etcd3Client, Etcd3Error, Etcd3ClientError, RetryFailedError,\
|
||||
InvalidAuthToken, Unavailable, Unknown, UnsupportedEtcdVersion, UserEmpty, AuthFailed, base64_encode, Etcd3
|
||||
@@ -85,6 +85,11 @@ class BaseTestEtcd3(unittest.TestCase):
|
||||
|
||||
class TestKVCache(BaseTestEtcd3):
|
||||
|
||||
@patch.object(urllib3.PoolManager, 'urlopen', mock_urlopen)
|
||||
@patch.object(Etcd3Client, 'watchprefix', Mock(return_value=urllib3.response.HTTPResponse()))
|
||||
def test__build_cache(self):
|
||||
self.kv_cache._build_cache()
|
||||
|
||||
def test__do_watch(self):
|
||||
self.client.watchprefix = Mock(return_value=False)
|
||||
self.assertRaises(AttributeError, self.kv_cache._do_watch, '1')
|
||||
@@ -94,14 +99,17 @@ class TestKVCache(BaseTestEtcd3):
|
||||
def test_run(self):
|
||||
self.assertRaises(SleepException, self.kv_cache.run)
|
||||
|
||||
@patch.object(urllib3.PoolManager, 'urlopen', mock_urlopen)
|
||||
@patch.object(urllib3.response.HTTPResponse, 'read_chunked',
|
||||
Mock(return_value=[b'{"error":{"grpc_code":14,"message":"","http_code":503}}']))
|
||||
@patch.object(Etcd3Client, 'watchprefix', Mock(return_value=urllib3.response.HTTPResponse()))
|
||||
def test_kill_stream(self):
|
||||
self.assertRaises(Unavailable, self.kv_cache._do_watch, '1')
|
||||
self.kv_cache.kill_stream()
|
||||
with patch.object(MockResponse, 'connection', create=True) as mock_conn:
|
||||
with patch.object(urllib3.response.HTTPResponse, 'connection') as mock_conn:
|
||||
self.kv_cache.kill_stream()
|
||||
mock_conn.sock.close.side_effect = Exception
|
||||
self.kv_cache.kill_stream()
|
||||
type(mock_conn).sock = PropertyMock(side_effect=Exception)
|
||||
self.kv_cache.kill_stream()
|
||||
|
||||
|
||||
class TestPatroniEtcd3Client(BaseTestEtcd3):
|
||||
@@ -180,7 +188,8 @@ class TestEtcd3(BaseTestEtcd3):
|
||||
@patch.object(urllib3.PoolManager, 'urlopen', mock_urlopen)
|
||||
def setUp(self):
|
||||
super(TestEtcd3, self).setUp()
|
||||
self.assertRaises(AttributeError, self.kv_cache._build_cache)
|
||||
# self.assertRaises(AttributeError, self.kv_cache._build_cache)
|
||||
self.kv_cache._build_cache()
|
||||
self.kv_cache._is_ready = True
|
||||
self.etcd3.get_cluster()
|
||||
|
||||
@@ -276,6 +285,8 @@ class TestEtcd3(BaseTestEtcd3):
|
||||
|
||||
def test_delete_leader(self):
|
||||
self.etcd3.delete_leader()
|
||||
self.etcd3._name = 'other'
|
||||
self.etcd3.delete_leader()
|
||||
|
||||
def test_delete_cluster(self):
|
||||
self.etcd3.delete_cluster()
|
||||
|
||||
+20
-29
@@ -244,7 +244,6 @@ class TestHa(PostgresInit):
|
||||
def test_bootstrap_as_standby_leader(self, initialize):
|
||||
self.p.data_directory_empty = true
|
||||
self.ha.cluster = get_cluster_not_initialized_without_leader(cluster_config=ClusterConfig(0, {}, 0))
|
||||
self.ha.cluster.is_unlocked = true
|
||||
self.ha.patroni.config._dynamic_configuration = {"standby_cluster": {"port": 5432}}
|
||||
self.assertEqual(self.ha.run_cycle(), 'trying to bootstrap a new standby leader')
|
||||
|
||||
@@ -261,7 +260,6 @@ class TestHa(PostgresInit):
|
||||
def test_start_as_cascade_replica_in_standby_cluster(self):
|
||||
self.p.data_directory_empty = true
|
||||
self.ha.cluster = get_standby_cluster_initialized_with_only_leader()
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.assertEqual(self.ha.run_cycle(), "trying to bootstrap from replica 'test'")
|
||||
|
||||
def test_recover_replica_failed(self):
|
||||
@@ -381,8 +379,8 @@ class TestHa(PostgresInit):
|
||||
self.ha._async_executor.schedule('promote')
|
||||
self.assertEqual(self.ha.run_cycle(), 'lost leader before promote')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_long_promote(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.p.is_leader = false
|
||||
self.p.set_role('primary')
|
||||
@@ -407,14 +405,13 @@ class TestHa(PostgresInit):
|
||||
self.p.is_leader = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'following a different leader because i am not the healthiest node')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_promote_because_have_lock(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.p.is_leader = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'promoted self to leader because I had the session lock')
|
||||
|
||||
def test_promote_without_watchdog(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.p.is_leader = true
|
||||
with patch.object(Watchdog, 'activate', Mock(return_value=False)):
|
||||
@@ -424,24 +421,22 @@ class TestHa(PostgresInit):
|
||||
|
||||
def test_leader_with_lock(self):
|
||||
self.ha.cluster = get_cluster_initialized_with_leader()
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), the leader with the lock')
|
||||
|
||||
def test_coordinator_leader_with_lock(self):
|
||||
self.ha.cluster = get_cluster_initialized_with_leader()
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), the leader with the lock')
|
||||
|
||||
@patch.object(Postgresql, '_wait_for_connection_close', Mock())
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_demote_because_not_having_lock(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
with patch.object(Watchdog, 'is_running', PropertyMock(return_value=True)):
|
||||
self.assertEqual(self.ha.run_cycle(), 'demoting self because I do not have the lock and I was a leader')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_demote_because_update_lock_failed(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.has_lock = true
|
||||
self.ha.update_lock = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'demoted self because failed to update leader lock in DCS')
|
||||
@@ -450,8 +445,8 @@ class TestHa(PostgresInit):
|
||||
self.p.is_leader = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'not promoting because failed to update leader lock in DCS')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_follow(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.p.is_leader = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), a secondary, and following a leader ()')
|
||||
self.ha.patroni.replicatefrom = "foo"
|
||||
@@ -465,8 +460,8 @@ class TestHa(PostgresInit):
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), a secondary, and following a leader ()')
|
||||
del self.ha.cluster.config.data['postgresql']['use_slots']
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_follow_in_pause(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.is_paused = true
|
||||
self.assertEqual(self.ha.run_cycle(), 'PAUSE: continue to run as primary without lock')
|
||||
self.p.is_leader = false
|
||||
@@ -659,10 +654,12 @@ class TestHa(PostgresInit):
|
||||
|
||||
self.ha.update_lock = false
|
||||
self.p.set_role('primary')
|
||||
with patch('patroni.async_executor.CriticalTask.cancel', Mock(return_value=False)):
|
||||
with patch('patroni.postgresql.Postgresql.terminate_starting_postmaster') as mock_terminate:
|
||||
self.assertEqual(self.ha.run_cycle(), 'lost leader lock during restart')
|
||||
mock_terminate.assert_called()
|
||||
with patch('patroni.async_executor.CriticalTask.cancel', Mock(return_value=False)),\
|
||||
patch('patroni.async_executor.CriticalTask.result',
|
||||
PropertyMock(return_value=PostmasterProcess(os.getpid())), create=True),\
|
||||
patch('patroni.postgresql.Postgresql.terminate_starting_postmaster') as mock_terminate:
|
||||
self.assertEqual(self.ha.run_cycle(), 'lost leader lock during restart')
|
||||
mock_terminate.assert_called()
|
||||
|
||||
self.ha.is_paused = true
|
||||
self.assertEqual(self.ha.run_cycle(), 'PAUSE: restart in progress')
|
||||
@@ -741,7 +738,6 @@ class TestHa(PostgresInit):
|
||||
def test_manual_failover_process_no_leader(self):
|
||||
self.p.is_leader = false
|
||||
self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', self.p.name, None))
|
||||
self.assertEqual(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock')
|
||||
self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', 'leader', None))
|
||||
self.p.set_role('replica')
|
||||
self.assertEqual(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock')
|
||||
@@ -847,10 +843,10 @@ class TestHa(PostgresInit):
|
||||
self.assertFalse(self.ha.is_healthiest_node())
|
||||
|
||||
def test__is_healthiest_node(self):
|
||||
self.p.is_leader = false
|
||||
self.ha.cluster = get_cluster_initialized_without_leader(sync=('postgresql1', self.p.name))
|
||||
self.ha.global_config = self.ha.patroni.config.get_global_config(self.ha.cluster)
|
||||
self.assertTrue(self.ha._is_healthiest_node(self.ha.old_cluster.members))
|
||||
self.p.is_leader = false
|
||||
self.ha.fetch_node_status = get_node_status() # accessible, in_recovery
|
||||
self.assertTrue(self.ha._is_healthiest_node(self.ha.old_cluster.members))
|
||||
self.ha.fetch_node_status = get_node_status(in_recovery=False) # accessible, not in_recovery
|
||||
@@ -864,6 +860,8 @@ class TestHa(PostgresInit):
|
||||
self.ha.global_config = self.ha.patroni.config.get_global_config(self.ha.cluster)
|
||||
with patch('patroni.postgresql.Postgresql.last_operation', return_value=1):
|
||||
self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members))
|
||||
with patch('patroni.postgresql.Postgresql.replica_cached_timeline', return_value=None):
|
||||
self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members))
|
||||
with patch('patroni.postgresql.Postgresql.replica_cached_timeline', return_value=1):
|
||||
self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members))
|
||||
self.ha.patroni.nofailover = True
|
||||
@@ -972,11 +970,11 @@ class TestHa(PostgresInit):
|
||||
with patch.object(Leader, 'conn_url', PropertyMock(return_value='')):
|
||||
self.assertEqual(self.ha.run_cycle(), 'continue following the old known standby leader')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=True))
|
||||
def test_process_unhealthy_standby_cluster_as_standby_leader(self):
|
||||
self.p.is_leader = false
|
||||
self.p.name = 'leader'
|
||||
self.ha.cluster = get_standby_cluster_initialized_with_only_leader()
|
||||
self.ha.cluster.is_unlocked = true
|
||||
self.ha.sysid_valid = true
|
||||
self.p._sysid = True
|
||||
self.assertEqual(self.ha.run_cycle(), 'promoted self to a standby leader by acquiring session lock')
|
||||
@@ -987,7 +985,6 @@ class TestHa(PostgresInit):
|
||||
self.p.is_leader = false
|
||||
self.p.name = 'replica'
|
||||
self.ha.cluster = get_standby_cluster_initialized_with_only_leader()
|
||||
self.ha.is_unlocked = true
|
||||
self.assertTrue(self.ha.run_cycle().startswith('running pg_rewind from remote_member:'))
|
||||
|
||||
def test_recover_unhealthy_leader_in_standby_cluster(self):
|
||||
@@ -998,13 +995,13 @@ class TestHa(PostgresInit):
|
||||
self.ha.cluster = get_standby_cluster_initialized_with_only_leader()
|
||||
self.assertEqual(self.ha.run_cycle(), 'starting as a standby leader because i had the session lock')
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=True))
|
||||
def test_recover_unhealthy_unlocked_standby_cluster(self):
|
||||
self.p.is_leader = false
|
||||
self.p.name = 'leader'
|
||||
self.p.is_running = false
|
||||
self.p.follow = false
|
||||
self.ha.cluster = get_standby_cluster_initialized_with_only_leader()
|
||||
self.ha.cluster.is_unlocked = true
|
||||
self.ha.has_lock = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'trying to follow a remote member because standby cluster is unhealthy')
|
||||
|
||||
@@ -1287,10 +1284,10 @@ class TestHa(PostgresInit):
|
||||
|
||||
@patch('patroni.postgresql.mtime', Mock(return_value=1588316884))
|
||||
@patch('builtins.open', Mock(side_effect=Exception))
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_restore_cluster_config(self):
|
||||
self.ha.cluster.config.data.clear()
|
||||
self.ha.has_lock = true
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), the leader with the lock')
|
||||
|
||||
def test_watch(self):
|
||||
@@ -1341,9 +1338,9 @@ class TestHa(PostgresInit):
|
||||
@patch('patroni.postgresql.mtime', Mock(return_value=1588316884))
|
||||
@patch('builtins.open', mock_open(read_data=('1\t0/40159C0\tno recovery target specified\n\n'
|
||||
'2\t1/40159C0\tno recovery target specified\n')))
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_update_cluster_history(self):
|
||||
self.ha.has_lock = true
|
||||
self.ha.cluster.is_unlocked = false
|
||||
for tl in (1, 3):
|
||||
self.p.get_primary_timeline = Mock(return_value=tl)
|
||||
self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), the leader with the lock')
|
||||
@@ -1355,9 +1352,9 @@ class TestHa(PostgresInit):
|
||||
self.ha.run_cycle()
|
||||
exit_mock.assert_called_once_with(1)
|
||||
|
||||
@patch.object(Cluster, 'is_unlocked', Mock(return_value=False))
|
||||
def test_after_pause(self):
|
||||
self.ha.has_lock = true
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.is_paused = true
|
||||
self.assertEqual(self.ha.run_cycle(), 'PAUSE: no action. I am (postgresql0), the leader with the lock')
|
||||
self.ha.is_paused = false
|
||||
@@ -1409,16 +1406,10 @@ class TestHa(PostgresInit):
|
||||
@patch.object(Postgresql, 'major_version', PropertyMock(return_value=130000))
|
||||
@patch.object(SlotsHandler, 'sync_replication_slots', Mock(return_value=['ls']))
|
||||
def test_follow_copy(self):
|
||||
self.ha.cluster.is_unlocked = false
|
||||
self.ha.cluster.config.data['slots'] = {'ls': {'database': 'a', 'plugin': 'b'}}
|
||||
self.p.is_leader = false
|
||||
self.assertTrue(self.ha.run_cycle().startswith('Copying logical slots'))
|
||||
|
||||
def test_is_failover_possible(self):
|
||||
self.ha.fetch_node_status = Mock(return_value=_MemberStatus(self.ha.cluster.members[0],
|
||||
True, True, 0, 2, None, {}, False))
|
||||
self.assertFalse(self.ha.is_failover_possible(self.ha.cluster.members))
|
||||
|
||||
def test_acquire_lock(self):
|
||||
self.ha.dcs.attempt_to_acquire_leader = Mock(side_effect=[DCSError('foo'), Exception])
|
||||
self.assertRaises(DCSError, self.ha.acquire_lock)
|
||||
|
||||
+21
-11
@@ -5,6 +5,7 @@ import mock
|
||||
import socket
|
||||
import time
|
||||
import unittest
|
||||
import urllib3
|
||||
|
||||
from mock import Mock, PropertyMock, mock_open, patch
|
||||
from patroni.dcs.kubernetes import Cluster, k8s_client, k8s_config, K8sConfig, K8sConnectionFailed,\
|
||||
@@ -34,6 +35,9 @@ def mock_list_namespaced_config_map(*args, **kwargs):
|
||||
metadata.update({'name': 'test-1-leader', 'labels': {Kubernetes._CITUS_LABEL: '1'},
|
||||
'annotations': {'leader': 'p-3', 'ttl': '30s'}})
|
||||
items.append(k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata)))
|
||||
metadata.update({'name': 'test-2-config', 'labels': {Kubernetes._CITUS_LABEL: '2'}, 'annotations': {}})
|
||||
items.append(k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata)))
|
||||
|
||||
metadata = k8s_client.V1ObjectMeta(resource_version='1')
|
||||
return k8s_client.V1ConfigMapList(metadata=metadata, items=items, kind='ConfigMapList')
|
||||
|
||||
@@ -403,13 +407,18 @@ class TestKubernetesEndpoints(BaseTestKubernetes):
|
||||
self.assertEqual(('create_config_service failed',), mock_logger_exception.call_args[0])
|
||||
|
||||
|
||||
def mock_watch(*args):
|
||||
return urllib3.HTTPResponse()
|
||||
|
||||
|
||||
class TestCacheBuilder(BaseTestKubernetes):
|
||||
|
||||
@patch.object(k8s_client.CoreV1Api, 'list_namespaced_config_map', mock_list_namespaced_config_map, create=True)
|
||||
@patch('patroni.dcs.kubernetes.ObjectCache._watch')
|
||||
def test__build_cache(self, mock_response):
|
||||
@patch('patroni.dcs.kubernetes.ObjectCache._watch', mock_watch)
|
||||
@patch.object(urllib3.HTTPResponse, 'read_chunked')
|
||||
def test__build_cache(self, mock_read_chunked):
|
||||
self.k._citus_group = '0'
|
||||
mock_response.return_value.read_chunked.return_value = [json.dumps(
|
||||
mock_read_chunked.return_value = [json.dumps(
|
||||
{'type': 'MODIFIED', 'object': {'metadata': {
|
||||
'name': self.k.config_path, 'resourceVersion': '2', 'annotations': {self.k._CONFIG: 'foo'}}}}
|
||||
).encode('utf-8'), ('\n' + json.dumps(
|
||||
@@ -435,12 +444,13 @@ class TestCacheBuilder(BaseTestKubernetes):
|
||||
self.assertRaises(AttributeError, self.k._kinds._do_watch, '1')
|
||||
|
||||
@patch.object(k8s_client.CoreV1Api, 'list_namespaced_config_map', mock_list_namespaced_config_map, create=True)
|
||||
@patch('patroni.dcs.kubernetes.ObjectCache._watch')
|
||||
def test_kill_stream(self, mock_watch):
|
||||
self.k._kinds.kill_stream()
|
||||
mock_watch.return_value.read_chunked.return_value = []
|
||||
mock_watch.return_value.connection.sock.close.side_effect = Exception
|
||||
self.k._kinds._do_watch('1')
|
||||
self.k._kinds.kill_stream()
|
||||
type(mock_watch.return_value).connection = PropertyMock(side_effect=Exception)
|
||||
@patch('patroni.dcs.kubernetes.ObjectCache._watch', mock_watch)
|
||||
@patch.object(urllib3.HTTPResponse, 'read_chunked', Mock(return_value=[]))
|
||||
def test_kill_stream(self):
|
||||
self.k._kinds.kill_stream()
|
||||
with patch.object(urllib3.HTTPResponse, 'connection') as mock_connection:
|
||||
mock_connection.sock.close.side_effect = Exception
|
||||
self.k._kinds._do_watch('1')
|
||||
self.k._kinds.kill_stream()
|
||||
with patch.object(urllib3.HTTPResponse, 'connection', PropertyMock(side_effect=Exception)):
|
||||
self.k._kinds.kill_stream()
|
||||
|
||||
@@ -369,6 +369,10 @@ class TestPostgresql(BaseTestPostgresql):
|
||||
def test_latest_checkpoint_location(self, mock_popen):
|
||||
mock_popen.return_value.communicate.return_value = (None, None)
|
||||
self.assertEqual(self.p.latest_checkpoint_location(), 28163096)
|
||||
with patch.object(Postgresql, 'controldata', Mock(return_value={'Database cluster state': 'shut down',
|
||||
'Latest checkpoint location': 'k/1ADBC18',
|
||||
"Latest checkpoint's TimeLineID": '1'})):
|
||||
self.assertIsNone(self.p.latest_checkpoint_location())
|
||||
# 9.3 and 9.4 format
|
||||
mock_popen.return_value.communicate.side_effect = [
|
||||
(b'rmgr: XLOG len (rec/tot): 72/ 104, tx: 0, lsn: 0/01ADBC18, prev 0/01ADBBB8, '
|
||||
@@ -518,10 +522,10 @@ class TestPostgresql(BaseTestPostgresql):
|
||||
|
||||
def test_can_create_replica_without_replication_connection(self):
|
||||
self.p.config._config['create_replica_method'] = []
|
||||
self.assertFalse(self.p.can_create_replica_without_replication_connection())
|
||||
self.assertFalse(self.p.can_create_replica_without_replication_connection(None))
|
||||
self.p.config._config['create_replica_method'] = ['wale', 'basebackup']
|
||||
self.p.config._config['wale'] = {'command': 'foo', 'no_leader': 1}
|
||||
self.assertTrue(self.p.can_create_replica_without_replication_connection())
|
||||
self.assertTrue(self.p.can_create_replica_without_replication_connection(None))
|
||||
|
||||
def test_replica_method_can_work_without_replication_connection(self):
|
||||
self.assertFalse(self.p.replica_method_can_work_without_replication_connection('basebackup'))
|
||||
|
||||
@@ -111,6 +111,8 @@ class TestSlotsHandler(BaseTestPostgresql):
|
||||
self.s.copy_logical_slots(self.cluster, ['ls'])
|
||||
with patch.object(MockCursor, 'execute', Mock(side_effect=psycopg.OperationalError)):
|
||||
self.s.copy_logical_slots(self.cluster, ['foo'])
|
||||
with patch.object(Cluster, 'leader', PropertyMock(return_value=None)):
|
||||
self.s.copy_logical_slots(self.cluster, ['foo'])
|
||||
|
||||
@patch.object(Postgresql, 'stop', Mock(return_value=True))
|
||||
@patch.object(Postgresql, 'start', Mock(return_value=True))
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class TestUtils(unittest.TestCase):
|
||||
|
||||
def test_enable_keepalive(self):
|
||||
with patch('socket.SIO_KEEPALIVE_VALS', 1, create=True):
|
||||
self.assertIsNotNone(enable_keepalive(Mock(), 10, 5))
|
||||
self.assertIsNone(enable_keepalive(Mock(), 10, 5))
|
||||
with patch('socket.SIO_KEEPALIVE_VALS', None, create=True):
|
||||
for platform in ('linux2', 'darwin', 'other'):
|
||||
with patch('sys.platform', platform):
|
||||
|
||||
@@ -123,7 +123,7 @@ class TestWALERestore(unittest.TestCase):
|
||||
with patch.object(WALERestore, 'run', Mock(return_value=1)), \
|
||||
patch('time.sleep', mock_sleep):
|
||||
self.assertEqual(_main(), 1)
|
||||
self.assertTrue(sleeps[0], WALE_TEST_RETRIES)
|
||||
self.assertEqual(sleeps[0], WALE_TEST_RETRIES)
|
||||
|
||||
@patch('os.path.isfile', Mock(return_value=True))
|
||||
def test_get_major_version(self):
|
||||
|
||||
@@ -110,6 +110,11 @@ class TestWatchdog(unittest.TestCase):
|
||||
watchdog.keepalive()
|
||||
self.assertEqual(len(device.writes), 1)
|
||||
|
||||
watchdog.impl._fd, fd = None, watchdog.impl._fd
|
||||
watchdog.keepalive()
|
||||
self.assertEqual(len(device.writes), 1)
|
||||
watchdog.impl._fd = fd
|
||||
|
||||
watchdog.disable()
|
||||
self.assertFalse(device.open)
|
||||
self.assertEqual(device.writes[-1], b'V')
|
||||
|
||||
@@ -13,6 +13,7 @@ from patroni.dcs.zookeeper import Cluster, Leader, PatroniKazooClient,\
|
||||
|
||||
class MockKazooClient(Mock):
|
||||
|
||||
handler = PatroniSequentialThreadingHandler(10)
|
||||
leader = False
|
||||
exists = True
|
||||
|
||||
@@ -154,11 +155,6 @@ class TestZooKeeper(unittest.TestCase):
|
||||
def test_session_listener(self):
|
||||
self.zk.session_listener(KazooState.SUSPENDED)
|
||||
|
||||
def test_members_watcher(self):
|
||||
self.zk._fetch_cluster = False
|
||||
self.zk.members_watcher(None)
|
||||
self.assertTrue(self.zk._fetch_cluster)
|
||||
|
||||
def test_reload_config(self):
|
||||
self.zk.reload_config({'ttl': 20, 'retry_timeout': 10, 'loop_wait': 10})
|
||||
self.zk.reload_config({'ttl': 20, 'retry_timeout': 10, 'loop_wait': 5})
|
||||
@@ -223,6 +219,8 @@ class TestZooKeeper(unittest.TestCase):
|
||||
|
||||
def test_cancel_initialization(self):
|
||||
self.zk.cancel_initialization()
|
||||
with patch.object(MockKazooClient, 'delete', Mock()):
|
||||
self.zk.cancel_initialization()
|
||||
|
||||
def test_touch_member(self):
|
||||
self.zk._name = 'buzz'
|
||||
|
||||
Reference in New Issue
Block a user