mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-09-02 17:49:34 +00:00
Merge pull request #184 from zalando/feature/unify_patronictl_config_with_patroni
Unify Patroni and Patronictl configuration
This commit is contained in:
+91
-71
@@ -6,7 +6,7 @@ import unittest
|
||||
|
||||
from click.testing import CliRunner
|
||||
from mock import patch, Mock
|
||||
from patroni.ctl import ctl, members, store_config, load_config, output_members, post_patroni, get_dcs, \
|
||||
from patroni.ctl import ctl, members, store_config, load_config, output_members, post_patroni, get_dcs, parse_dcs, \
|
||||
wait_for_leader, get_all_members, get_any_member, get_cursor, query_member, configure, PatroniCtlException
|
||||
from patroni.etcd import Etcd, Client
|
||||
from psycopg2 import OperationalError
|
||||
@@ -40,6 +40,8 @@ def test_rw_config():
|
||||
load_config(CONFIG_FILE_PATH, None)
|
||||
load_config(CONFIG_FILE_PATH, '0.0.0.0')
|
||||
|
||||
store_config({'dcs_api': None}, CONFIG_FILE_PATH)
|
||||
load_config(CONFIG_FILE_PATH, None)
|
||||
|
||||
@patch('patroni.ctl.load_config', Mock(return_value={'dcs': {'etcd': {'host': 'localhost:4001'}}}))
|
||||
class TestCtl(unittest.TestCase):
|
||||
@@ -62,6 +64,17 @@ class TestCtl(unittest.TestCase):
|
||||
|
||||
self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), role='any'))
|
||||
|
||||
def test_parse_dcs(self):
|
||||
assert parse_dcs(None) is None
|
||||
assert parse_dcs('localhost') == {'etcd': {'host': 'localhost:4001'}}
|
||||
assert parse_dcs('') == {'etcd': {'host': 'localhost:4001'}}
|
||||
assert parse_dcs('localhost:8500') == {'consul': {'host': 'localhost:8500'}}
|
||||
assert parse_dcs('zookeeper://localhost') == {'zookeeper': {'hosts': ['localhost:2181']}}
|
||||
assert parse_dcs('exhibitor://dummy') == {'zookeeper': {'exhibitor': {'hosts': ['dummy'], 'port': 8181}}}
|
||||
assert parse_dcs('consul://localhost') == {'consul': {'host': 'localhost:8500'}}
|
||||
self.assertRaises(PatroniCtlException, parse_dcs, 'invalid://test')
|
||||
|
||||
|
||||
def test_output_members(self):
|
||||
cluster = get_cluster_initialized_with_leader()
|
||||
self.assertIsNone(output_members(cluster, name='abc', fmt='pretty'))
|
||||
@@ -72,67 +85,70 @@ class TestCtl(unittest.TestCase):
|
||||
@patch('patroni.etcd.Etcd.get_etcd_client', Mock(return_value=None))
|
||||
@patch('patroni.ctl.post_patroni', Mock(return_value=MockResponse()))
|
||||
def test_failover(self):
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'leader' in result.output
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n2100-01-01T12:23:00\ny''')
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n2030-01-01T12:23:00\ny''')
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Aborting failover,as we anser NO to the confirmation
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\nN''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Target and source are equal
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nleader\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Reality is not part of this cluster
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nReality\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force'])
|
||||
assert 'Member' in result.output
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', '2015-01-01T12:00:00+01:00'])
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Invalid timestamp
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', 'invalid'])
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Invalid timestamp
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', '2115-02-30T12:00:00+01:00'])
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Specifying wrong leader
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='dummy')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_with_only_leader())):
|
||||
# No members available
|
||||
with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'leader' in result.output
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n2100-01-01T12:23:00\ny''')
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n2030-01-01T12:23:00\ny''')
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Aborting failover,as we anser NO to the confirmation
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\nN''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_without_leader())):
|
||||
# No master available
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
# Target and source are equal
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nleader\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.ctl.post_patroni', Mock(side_effect=Exception)):
|
||||
# Non-responding patroni
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'falling back to DCS' in result.output
|
||||
# Reality is not part of this cluster
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nReality\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.ctl.post_patroni') as mocked:
|
||||
mocked.return_value.status_code = 500
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'Failover failed' in result.output
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force'])
|
||||
assert 'Member' in result.output
|
||||
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', '2015-01-01T12:00:00+01:00'])
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Invalid timestamp
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', 'invalid'])
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Invalid timestamp
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy', '--force', '--scheduled', '2115-02-30T12:00:00+01:00'])
|
||||
assert result.exit_code != 0
|
||||
|
||||
# Specifying wrong leader
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='dummy')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_with_only_leader())):
|
||||
# No members available
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_without_leader())):
|
||||
# No master available
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('patroni.ctl.post_patroni', Mock(side_effect=Exception)):
|
||||
# Non-responding patroni
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'falling back to DCS' in result.output
|
||||
|
||||
with patch('patroni.ctl.post_patroni') as mocked:
|
||||
mocked.return_value.status_code = 500
|
||||
result = self.runner.invoke(ctl, ['failover', 'dummy'], input='''leader\nother\n\ny''')
|
||||
assert 'Failover failed' in result.output
|
||||
|
||||
def test_get_dcs(self):
|
||||
self.assertRaises(PatroniCtlException, get_dcs, {'dcs': {'dummy': {}}}, 'dummy')
|
||||
self.assertRaises(PatroniCtlException, get_dcs, {'dummy': {}}, 'dummy')
|
||||
with patch('patroni.Patroni.get_dcs', Mock(return_value=self.e)):
|
||||
assert get_dcs({'etcd': {'host':'none'}}, 'dummy').client_path('') == '/service/test/'
|
||||
|
||||
@patch('psycopg2.connect', psycopg2_connect)
|
||||
@patch('patroni.ctl.query_member', Mock(return_value=([['mock column']], None)))
|
||||
@@ -206,24 +222,26 @@ class TestCtl(unittest.TestCase):
|
||||
@patch('patroni.etcd.Etcd.get_etcd_client', Mock(return_value=None))
|
||||
@patch('requests.post', requests_get)
|
||||
def test_restart_reinit(self):
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha'], input='y')
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = self.runner.invoke(ctl, ['reinit', 'alpha'], input='y')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Aborted restart
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha'], input='N')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Not a member
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha', 'dummy', '--any'], input='y')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('requests.post', Mock(return_value=MockResponse())):
|
||||
with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha'], input='y')
|
||||
assert 'restart failed for' in result.output
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = self.runner.invoke(ctl, ['reinit', 'alpha'], input='y')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Aborted restart
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha'], input='N')
|
||||
assert result.exit_code == 1
|
||||
|
||||
# Not a member
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha', 'dummy', '--any'], input='y')
|
||||
assert result.exit_code == 1
|
||||
|
||||
with patch('requests.post', Mock(return_value=MockResponse())):
|
||||
result = self.runner.invoke(ctl, ['restart', 'alpha'], input='y')
|
||||
assert result.exit_code == 0
|
||||
|
||||
@patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_with_leader()))
|
||||
@patch.object(etcd.Client, 'delete', Mock(side_effect=etcd.EtcdException))
|
||||
def test_remove(self):
|
||||
@@ -286,8 +304,10 @@ class TestCtl(unittest.TestCase):
|
||||
@patch('patroni.etcd.Etcd.get_cluster', Mock(return_value=get_cluster_initialized_with_leader()))
|
||||
@patch('patroni.etcd.Etcd.get_etcd_client', Mock(return_value=None))
|
||||
def test_members(self):
|
||||
result = self.runner.invoke(members, ['alpha'])
|
||||
assert result.exit_code == 0
|
||||
with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
|
||||
result = self.runner.invoke(members, ['alpha'])
|
||||
assert '127.0.0.1' in result.output
|
||||
assert result.exit_code == 0
|
||||
|
||||
def test_configure(self):
|
||||
result = self.runner.invoke(configure, ['--dcs', 'abc', '-c', 'dummy', '-n', 'bla'])
|
||||
|
||||
Reference in New Issue
Block a user