Пример #1
0
    def test_pause_cluster(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader

        with patch('requests.patch', Mock(return_value=MockResponse(500))):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Failed' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(200))),\
                patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Cluster is already paused' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(200))):
            result = self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
            assert "'pause' request sent" in result.output
            mock_get_dcs.return_value.get_cluster = Mock(side_effect=[
                get_cluster_initialized_with_leader(),
                get_cluster(None, None, [], None, None)
            ])
            self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
            member = Member(1, 'other', 28, {})
            mock_get_dcs.return_value.get_cluster = Mock(side_effect=[
                get_cluster_initialized_with_leader(),
                get_cluster(None, None, [member], None, None)
            ])
            self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
    def test_get_cursor(self):
        self.assertIsNone(get_cursor(get_cluster_initialized_without_leader(), role='master'))

        self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), role='master'))

        # MockCursor returns pg_is_in_recovery as false
        self.assertIsNone(get_cursor(get_cluster_initialized_with_leader(), role='replica'))

        self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), role='any'))
Пример #3
0
    def test_get_cursor(self):
        self.assertIsNone(get_cursor(get_cluster_initialized_without_leader(), {}, role='master'))

        self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), {}, role='master'))

        # MockCursor returns pg_is_in_recovery as false
        self.assertIsNone(get_cursor(get_cluster_initialized_with_leader(), {}, role='replica'))

        self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), {'database': 'foo'}, role='any'))
Пример #4
0
    def test_get_all_members(self):
        self.assertEqual(list(get_all_members(get_cluster_initialized_without_leader(), role='master')), [])

        r = list(get_all_members(get_cluster_initialized_with_leader(), role='master'))
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].name, 'leader')

        r = list(get_all_members(get_cluster_initialized_with_leader(), role='replica'))
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].name, 'other')

        self.assertEqual(len(list(get_all_members(get_cluster_initialized_without_leader(), role='replica'))), 2)
    def test_get_all_members(self):
        self.assertEquals(list(get_all_members(get_cluster_initialized_without_leader(), role='master')), [])

        r = list(get_all_members(get_cluster_initialized_with_leader(), role='master'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'leader')

        r = list(get_all_members(get_cluster_initialized_with_leader(), role='replica'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'other')

        self.assertEquals(len(list(get_all_members(get_cluster_initialized_without_leader(), role='replica'))), 2)
Пример #6
0
    def test_get_cursor(self):
        c = get_cursor(get_cluster_initialized_without_leader(), role="master")
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role="master")
        assert c is not None

        c = get_cursor(get_cluster_initialized_with_leader(), role="replica")
        # # MockCursor returns pg_is_in_recovery as false
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role="any")
        assert c is not None
Пример #7
0
    def test_get_cursor(self):
        c = get_cursor(get_cluster_initialized_without_leader(), role='master')
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role='master')
        assert c is not None

        c = get_cursor(get_cluster_initialized_with_leader(), role='replica')
        # # MockCursor returns pg_is_in_recovery as false
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role='any')
        assert c is not None
Пример #8
0
    def test_get_all_members(self):
        r = list(get_all_members(get_cluster_initialized_without_leader(), role="master"))
        assert len(r) == 0

        r = list(get_all_members(get_cluster_initialized_with_leader(), role="master"))
        assert len(r) == 1
        assert r[0].name == "leader"

        r = list(get_all_members(get_cluster_initialized_with_leader(), role="replica"))
        assert len(r) == 1
        assert r[0].name == "other"

        r = list(get_all_members(get_cluster_initialized_without_leader(), role="replica"))
        assert len(r) == 2
Пример #9
0
 def test_output_members(self):
     scheduled_at = datetime.now(tzutc) + timedelta(seconds=600)
     cluster = get_cluster_initialized_with_leader(Failover(1, 'foo', 'bar', scheduled_at))
     self.assertIsNone(output_members(cluster, name='abc', fmt='pretty'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='yaml'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))
Пример #10
0
 def test_output_members(self):
     scheduled_at = datetime.now(tzutc) + timedelta(seconds=600)
     cluster = get_cluster_initialized_with_leader(Failover(1, 'foo', 'bar', scheduled_at))
     self.assertIsNone(output_members(cluster, name='abc', fmt='pretty'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='yaml'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))
Пример #11
0
    def test_list_extended(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        cluster = get_cluster_initialized_with_leader(sync=('leader', 'other'))
        mock_get_dcs.return_value.get_cluster = Mock(return_value=cluster)

        result = self.runner.invoke(ctl, ['list', 'dummy', '--extended', '--timestamp'])
        assert '2100' in result.output
        assert 'Scheduled restart' in result.output
Пример #12
0
    def test_get_any_member(self):
        m = get_any_member(get_cluster_initialized_without_leader(),
                           role='master')
        assert m is None

        m = get_any_member(get_cluster_initialized_with_leader(),
                           role='master')
        assert m.name == 'leader'
Пример #13
0
    def test_request_patroni(self, mock_context):
        member = get_cluster_initialized_with_leader().leader.member

        mock_context.return_value.obj = {'ctl': {'cacert': 'cert.pem'}}
        self.assertRaises(requests.exceptions.ConnectionError, request_patroni, member, 'post', 'dummy', {})

        mock_context.return_value.obj = {'ctl': {'insecure': True}}
        self.assertRaises(requests.exceptions.ConnectionError, request_patroni, member, 'post', 'dummy', {})
Пример #14
0
    def test_get_any_member(self):
        self.assertIsNone(
            get_any_member(get_cluster_initialized_without_leader(),
                           role='master'))

        m = get_any_member(get_cluster_initialized_with_leader(),
                           role='master')
        self.assertEquals(m.name, 'leader')
Пример #15
0
    def test_list_extended(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        cluster = get_cluster_initialized_with_leader(sync=('leader', 'other'))
        mock_get_dcs.return_value.get_cluster = Mock(return_value=cluster)

        result = self.runner.invoke(ctl, ['list', 'dummy', '--extended', '--timestamp'])
        assert '2100' in result.output
        assert 'Scheduled restart' in result.output
Пример #16
0
    def test_get_all_members(self):
        r = list(
            get_all_members(get_cluster_initialized_without_leader(),
                            role='master'))
        assert len(r) == 0

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='master'))
        assert len(r) == 1
        assert r[0].name == 'leader'

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='replica'))
        assert len(r) == 1
        assert r[0].name == 'other'

        r = list(
            get_all_members(get_cluster_initialized_without_leader(),
                            role='replica'))
        assert len(r) == 2
Пример #17
0
    def test_pause_cluster(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader

        with patch('requests.patch', Mock(return_value=MockResponse(500))):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Failed' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(200))),\
                patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Cluster is already paused' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(200))):
            result = self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
            assert "'pause' request sent" in result.output
            mock_get_dcs.return_value.get_cluster = Mock(side_effect=[get_cluster_initialized_with_leader(),
                                                                      get_cluster(None, None, [], None, None)])
            self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
            member = Member(1, 'other', 28, {})
            mock_get_dcs.return_value.get_cluster = Mock(side_effect=[get_cluster_initialized_with_leader(),
                                                                      get_cluster(None, None, [member], None, None)])
            self.runner.invoke(ctl, ['pause', 'dummy', '--wait'])
Пример #18
0
 def test_output_members(self):
     cluster = get_cluster_initialized_with_leader()
     output_members(cluster, name='abc', format='pretty')
     output_members(cluster, name='abc', format='json')
     output_members(cluster, name='abc', format='tsv')
Пример #19
0
 def test_output_members(self):
     cluster = get_cluster_initialized_with_leader()
     output_members(cluster, name="abc", format="pretty")
     output_members(cluster, name="abc", format="json")
     output_members(cluster, name="abc", format="tsv")
Пример #20
0
class TestCtl(unittest.TestCase):
    @patch('socket.getaddrinfo', socket_getaddrinfo)
    @patch.object(Client, 'machines')
    def setUp(self, mock_machines):
        mock_machines.__get__ = Mock(return_value=['http://*****:*****@patch('psycopg2.connect', psycopg2_connect)
    def test_get_cursor(self):
        c = get_cursor(get_cluster_initialized_without_leader(), role='master')
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role='master')
        assert c is not None

        c = get_cursor(get_cluster_initialized_with_leader(), role='replica')
        # # MockCursor returns pg_is_in_recovery as false
        assert c is None

        c = get_cursor(get_cluster_initialized_with_leader(), role='any')
        assert c is not None

    def test_output_members(self):
        cluster = get_cluster_initialized_with_leader()
        output_members(cluster, name='abc', format='pretty')
        output_members(cluster, name='abc', format='json')
        output_members(cluster, name='abc', format='tsv')

    @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))
    @patch('patroni.etcd.Etcd.set_failover_value', Mock(return_value=None))
    @patch('patroni.ctl.wait_for_leader',
           Mock(return_value=get_cluster_initialized_with_leader()))
    @patch('requests.get', requests_get)
    @patch('requests.post', requests_get)
    @patch('patroni.ctl.post_patroni', Mock(return_value=MockResponse()))
    def test_failover(self):
        runner = CliRunner()

        with patch('patroni.etcd.Etcd.get_cluster',
                   Mock(return_value=get_cluster_initialized_with_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'Failing over to new leader' in result.output

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
N''')
            assert 'Aborting failover' in str(result.exception)

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
leader
y''')
            assert 'target and source are the same' in str(result.exception)

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
Reality
y''')
            assert 'Reality does not exist' in str(result.exception)

            result = runner.invoke(ctl, ['failover', 'dummy', '--force'])
            assert 'Failing over to new leader' in result.output

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='dummy')
            assert 'is not the leader of cluster' in str(result.exception)

        with patch(
                'patroni.etcd.Etcd.get_cluster',
                Mock(return_value=get_cluster_initialized_with_only_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'No candidates found to failover to' in str(
                result.exception)

        with patch(
                'patroni.etcd.Etcd.get_cluster',
                Mock(return_value=get_cluster_initialized_without_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'This cluster has no master' in str(result.exception)

        with patch('patroni.ctl.post_patroni', Mock(side_effect=Exception())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'falling back to DCS' in result.output
            assert 'Failover failed' in result.output

        mocked = Mock()
        mocked.return_value.status_code = 500
        with patch('patroni.ctl.post_patroni', Mock(return_value=mocked)):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'Failover failed, details' in result.output

#        with patch('patroni.dcs.AbstractDCS.get_cluster', Mock(return_value=get_cluster_initialized_with_leader())):
#            result = runner.invoke(ctl, ['failover', 'alpha', '--dcs', '8.8.8.8'], input='nonsense')
#            assert 'is not the leader of cluster' in str(result.exception)

# result = runner.invoke(ctl, ['failover', 'alpha', '--dcs', '8.8.8.8', '--master', 'nonsense'])
# assert 'is not the leader of cluster' in str(result.exception)

# result = runner.invoke(ctl, ['failover', 'alpha', '--dcs', '8.8.8.8'], input='leader\nother\nn')
# assert 'Aborting failover' in str(result.exception)

# with patch('patroni.ctl.wait_for_leader', Mock(return_value = get_cluster_initialized_with_leader())):
# result = runner.invoke(ctl, ['failover', 'alpha', '--dcs', '8.8.8.8'], input='leader\nother\nY')
# assert 'master did not change after' in result.output

# result = runner.invoke(ctl, ['failover', 'alpha', '--dcs', '8.8.8.8'], input='leader\nother\nY')
# assert 'Failover failed' in result.output

    def test_(self):
        self.assertRaises(patroni.exceptions.PatroniCtlException, get_dcs,
                          {'scheme': 'dummy'}, 'dummy')

    @patch('psycopg2.connect', psycopg2_connect)
    @patch('patroni.ctl.query_member',
           Mock(return_value=([['mock column']], None)))
    def test_query(self):
        runner = CliRunner()

        with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
            result = runner.invoke(ctl, [
                'query',
                'alpha',
                '--member',
                'abc',
                '--role',
                'master',
            ])
            assert 'mutually exclusive' in str(result.exception)

            with runner.isolated_filesystem():
                dummy_file = open('dummy', 'w')
                dummy_file.write('SELECT 1')
                dummy_file.close()

                result = runner.invoke(ctl, [
                    'query',
                    'alpha',
                    '--file',
                    'dummy',
                    '--command',
                    'dummy',
                ])
                assert 'mutually exclusive' in str(result.exception)

                result = runner.invoke(ctl,
                                       ['query', 'alpha', '--file', 'dummy'])

                os.remove('dummy')

            result = runner.invoke(ctl,
                                   ['query', 'alpha', '--command', 'SELECT 1'])
            assert 'mock column' in result.output

    @patch('patroni.ctl.get_cursor', Mock(return_value=MockConnect().cursor()))
    def test_query_member(self):
        rows = query_member(None, None, None, 'master',
                            'SELECT pg_is_in_recovery()')
        assert 'False' in str(rows)

        rows = query_member(None, None, None, 'replica',
                            'SELECT pg_is_in_recovery()')
        assert rows == (None, None)

        with patch('patroni.ctl.get_cursor', Mock(return_value=None)):
            rows = query_member(None, None, None, None,
                                'SELECT pg_is_in_recovery()')
            assert 'No connection to' in str(rows)

            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')
            assert 'No connection to' in str(rows)

        with patch('patroni.ctl.get_cursor',
                   Mock(side_effect=psycopg2.OperationalError('bla'))):
            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')

        with patch('test_postgresql.MockCursor.execute',
                   Mock(side_effect=psycopg2.OperationalError('bla'))):
            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')

    @patch('patroni.dcs.AbstractDCS.get_cluster',
           Mock(return_value=get_cluster_initialized_with_leader()))
    def test_dsn(self):
        runner = CliRunner()

        with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
            result = runner.invoke(ctl, ['dsn', 'alpha', '--dcs', '8.8.8.8'])
            assert 'host=127.0.0.1 port=5435' in result.output

            result = runner.invoke(ctl, [
                'dsn',
                'alpha',
                '--role',
                'master',
                '--member',
                'dummy',
            ])
            assert 'mutually exclusive' in str(result.exception)

            result = runner.invoke(ctl, ['dsn', 'alpha', '--member', 'dummy'])
            assert 'Can not find' in str(result.exception)

        # result = runner.invoke(ctl, ['dsn', 'alpha', '--dcs', '8.8.8.8', '--role', 'replica'])
        # assert 'host=127.0.0.1 port=5436' in result.output

    @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))
    @patch('requests.get', requests_get)
    @patch('requests.post', requests_get)
    def test_restart_reinit(self):
        runner = CliRunner()

        result = runner.invoke(ctl, ['restart', 'alpha', '--dcs', '8.8.8.8'],
                               input='y')
        result = runner.invoke(ctl, ['reinit', 'alpha', '--dcs', '8.8.8.8'],
                               input='y')

        result = runner.invoke(ctl, ['restart', 'alpha', '--dcs', '8.8.8.8'],
                               input='N')
        result = runner.invoke(ctl, [
            'restart',
            'alpha',
            '--dcs',
            '8.8.8.8',
            'dummy',
            '--any',
        ],
                               input='y')
        assert 'not a member' in str(result.exception)

        with patch('requests.post', Mock(return_value=MockResponse())):
            result = runner.invoke(ctl,
                                   ['restart', 'alpha', '--dcs', '8.8.8.8'],
                                   input='y')

    @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_remove(self):
        runner = CliRunner()

        result = runner.invoke(ctl, ['remove', 'alpha', '--dcs', '8.8.8.8'],
                               input='alpha\nslave')
        assert 'Please confirm' in result.output
        assert 'You are about to remove all' in result.output
        assert 'You did not exactly type' in str(result.exception)

        result = runner.invoke(ctl, ['remove', 'alpha', '--dcs', '8.8.8.8'],
                               input='''alpha
Yes I am aware
slave''')
        assert 'You did not specify the current master of the cluster' in str(
            result.exception)

        result = runner.invoke(ctl, ['remove', 'alpha', '--dcs', '8.8.8.8'],
                               input='beta\nleader')
        assert 'Cluster names specified do not match' in str(result.exception)

        with patch('patroni.etcd.Etcd.get_cluster',
                   get_cluster_initialized_with_leader):
            result = runner.invoke(ctl,
                                   ['remove', 'alpha', '--dcs', '8.8.8.8'],
                                   input='''alpha
Yes I am aware
leader''')
            assert 'object has no attribute' in str(result.exception)

        with patch('patroni.ctl.get_dcs', Mock(return_value=Mock())):
            result = runner.invoke(ctl,
                                   ['remove', 'alpha', '--dcs', '8.8.8.8'],
                                   input='''alpha
Yes I am aware
leader''')
            assert 'We have not implemented this for DCS of type' in str(
                result.exception)

    @patch('patroni.etcd.Etcd.watch', Mock(return_value=None))
    @patch('patroni.etcd.Etcd.get_cluster',
           Mock(return_value=get_cluster_initialized_with_leader()))
    def test_wait_for_leader(self):
        dcs = self.e
        self.assertRaises(patroni.exceptions.PatroniCtlException,
                          wait_for_leader, dcs, 0)

        cluster = wait_for_leader(dcs=dcs, timeout=2)
        assert cluster.leader.member.name == 'leader'

    def test_post_patroni(self):
        member = get_cluster_initialized_with_leader().leader.member
        self.assertRaises(requests.exceptions.ConnectionError, post_patroni,
                          member, 'dummy', {})

    def test_ctl(self):
        runner = CliRunner()

        runner.invoke(ctl, ['list'])

        result = runner.invoke(ctl, ['--help'])
        assert 'Usage:' in result.output

    def test_get_any_member(self):
        m = get_any_member(get_cluster_initialized_without_leader(),
                           role='master')
        assert m is None

        m = get_any_member(get_cluster_initialized_with_leader(),
                           role='master')
        assert m.name == 'leader'

    def test_get_all_members(self):
        r = list(
            get_all_members(get_cluster_initialized_without_leader(),
                            role='master'))
        assert len(r) == 0

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='master'))
        assert len(r) == 1
        assert r[0].name == 'leader'

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='replica'))
        assert len(r) == 1
        assert r[0].name == 'other'

        r = list(
            get_all_members(get_cluster_initialized_without_leader(),
                            role='replica'))
        assert len(r) == 2

    @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))
    @patch('requests.get', requests_get)
    @patch('requests.post', requests_get)
    def test_members(self):
        runner = CliRunner()

        result = runner.invoke(members, ['alpha'])
        assert result.exit_code == 0

    def test_configure(self):
        runner = CliRunner()

        result = runner.invoke(configure, [
            '--dcs',
            'abc',
            '-c',
            'dummy',
            '-n',
            'bla',
        ])

        assert result.exit_code == 0
 def test_post_patroni(self):
     member = get_cluster_initialized_with_leader().leader.member
     self.assertRaises(requests.exceptions.ConnectionError, post_patroni, member, 'dummy', {})
Пример #22
0
    def test_failover(self):
        runner = CliRunner()

        with patch('patroni.etcd.Etcd.get_cluster',
                   Mock(return_value=get_cluster_initialized_with_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'Failing over to new leader' in result.output

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
N''')
            assert 'Aborting failover' in str(result.exception)

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
leader
y''')
            assert 'target and source are the same' in str(result.exception)

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
Reality
y''')
            assert 'Reality does not exist' in str(result.exception)

            result = runner.invoke(ctl, ['failover', 'dummy', '--force'])
            assert 'Failing over to new leader' in result.output

            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='dummy')
            assert 'is not the leader of cluster' in str(result.exception)

        with patch(
                'patroni.etcd.Etcd.get_cluster',
                Mock(return_value=get_cluster_initialized_with_only_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'No candidates found to failover to' in str(
                result.exception)

        with patch(
                'patroni.etcd.Etcd.get_cluster',
                Mock(return_value=get_cluster_initialized_without_leader())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'This cluster has no master' in str(result.exception)

        with patch('patroni.ctl.post_patroni', Mock(side_effect=Exception())):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'falling back to DCS' in result.output
            assert 'Failover failed' in result.output

        mocked = Mock()
        mocked.return_value.status_code = 500
        with patch('patroni.ctl.post_patroni', Mock(return_value=mocked)):
            result = runner.invoke(ctl,
                                   ['failover', 'dummy', '--dcs', '8.8.8.8'],
                                   input='''leader
other
y''')
            assert 'Failover failed, details' in result.output
Пример #23
0
    def test_failover(self):
        runner = CliRunner()

        with patch("patroni.etcd.Etcd.get_cluster", Mock(return_value=get_cluster_initialized_with_leader())):
            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
y""",
            )
            assert "Failing over to new leader" in result.output

            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
N""",
            )
            assert "Aborting failover" in str(result.exception)

            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
leader
y""",
            )
            assert "target and source are the same" in str(result.exception)

            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
Reality
y""",
            )
            assert "Reality does not exist" in str(result.exception)

            result = runner.invoke(ctl, ["failover", "dummy", "--force"])
            assert "Failing over to new leader" in result.output

            result = runner.invoke(ctl, ["failover", "dummy", "--dcs", "8.8.8.8"], input="dummy")
            assert "is not the leader of cluster" in str(result.exception)

        with patch("patroni.etcd.Etcd.get_cluster", Mock(return_value=get_cluster_initialized_with_only_leader())):
            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
y""",
            )
            assert "No candidates found to failover to" in str(result.exception)

        with patch("patroni.etcd.Etcd.get_cluster", Mock(return_value=get_cluster_initialized_without_leader())):
            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
y""",
            )
            assert "This cluster has no master" in str(result.exception)

        with patch("patroni.ctl.post_patroni", Mock(side_effect=Exception())):
            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
y""",
            )
            assert "falling back to DCS" in result.output
            assert "Failover failed" in result.output

        mocked = Mock()
        mocked.return_value.status_code = 500
        with patch("patroni.ctl.post_patroni", Mock(return_value=mocked)):
            result = runner.invoke(
                ctl,
                ["failover", "dummy", "--dcs", "8.8.8.8"],
                input="""leader
other
y""",
            )
            assert "Failover failed, details" in result.output
Пример #24
0
    def test_get_any_member(self):
        m = get_any_member(get_cluster_initialized_without_leader(), role="master")
        assert m is None

        m = get_any_member(get_cluster_initialized_with_leader(), role="master")
        assert m.name == "leader"
 def test_output_members(self):
     cluster = get_cluster_initialized_with_leader()
     self.assertIsNone(output_members(cluster, name='abc', fmt='pretty'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))
Пример #26
0
 def test_post_patroni(self):
     member = get_cluster_initialized_with_leader().leader.member
     self.assertRaises(requests.exceptions.ConnectionError, post_patroni,
                       member, 'dummy', {})
Пример #27
0
 def test_output_members(self):
     cluster = get_cluster_initialized_with_leader()
     self.assertIsNone(output_members(cluster, name='abc', fmt='pretty'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
     self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))
    def test_get_any_member(self):
        self.assertIsNone(get_any_member(get_cluster_initialized_without_leader(), role='master'))

        m = get_any_member(get_cluster_initialized_with_leader(), role='master')
        self.assertEquals(m.name, 'leader')
Пример #29
0
class TestCtl(unittest.TestCase):
    @patch('socket.getaddrinfo', socket_getaddrinfo)
    def setUp(self):
        self.runner = CliRunner()
        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(
                return_value=['http://*****:*****@patch('psycopg2.connect', psycopg2_connect)
    def test_get_cursor(self):
        self.assertIsNone(
            get_cursor(get_cluster_initialized_without_leader(),
                       role='master'))

        self.assertIsNotNone(
            get_cursor(get_cluster_initialized_with_leader(), role='master'))

        # MockCursor returns pg_is_in_recovery as false
        self.assertIsNone(
            get_cursor(get_cluster_initialized_with_leader(), role='replica'))

        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'))
        self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
        self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))

    @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))
    @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
            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, {'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)))
    @patch.object(etcd.Client, 'read', etcd_read)
    def test_query(self):
        with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
            # Mutually exclusive
            result = self.runner.invoke(
                ctl, ['query', 'alpha', '--member', 'abc', '--role', 'master'])
            assert result.exit_code == 1

            with self.runner.isolated_filesystem():
                with open('dummy', 'w') as dummy_file:
                    dummy_file.write('SELECT 1')

                # Mutually exclusive
                result = self.runner.invoke(ctl, [
                    'query', 'alpha', '--file', 'dummy', '--command', 'dummy'
                ])
                assert result.exit_code == 1

                result = self.runner.invoke(
                    ctl, ['query', 'alpha', '--file', 'dummy'])
                assert result.exit_code == 0

                os.remove('dummy')

            result = self.runner.invoke(
                ctl, ['query', 'alpha', '--command', 'SELECT 1'])
            assert 'mock column' in result.output

            # --command or --file is mandatory
            result = self.runner.invoke(ctl, ['query', 'alpha'])
            assert result.exit_code == 1

            result = self.runner.invoke(ctl, [
                'query', 'alpha', '--command', 'SELECT 1', '--username',
                'root', '--password', '--dbname', 'postgres'
            ],
                                        input='ab\nab')
            assert 'mock column' in result.output

    def test_query_member(self):
        with patch('patroni.ctl.get_cursor',
                   Mock(return_value=MockConnect().cursor())):
            rows = query_member(None, None, None, 'master',
                                'SELECT pg_is_in_recovery()')
            self.assertTrue('False' in str(rows))

            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')
            self.assertEquals(rows, (None, None))

            with patch('test_postgresql.MockCursor.execute',
                       Mock(side_effect=OperationalError('bla'))):
                rows = query_member(None, None, None, 'replica',
                                    'SELECT pg_is_in_recovery()')

        with patch('patroni.ctl.get_cursor', Mock(return_value=None)):
            rows = query_member(None, None, None, None,
                                'SELECT pg_is_in_recovery()')
            self.assertTrue('No connection to' in str(rows))

            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')
            self.assertTrue('No connection to' in str(rows))

        with patch('patroni.ctl.get_cursor',
                   Mock(side_effect=OperationalError('bla'))):
            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()')

    @patch('patroni.dcs.AbstractDCS.get_cluster',
           Mock(return_value=get_cluster_initialized_with_leader()))
    def test_dsn(self):
        with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
            result = self.runner.invoke(ctl, ['dsn', 'alpha'])
            assert 'host=127.0.0.1 port=5435' in result.output

            # Mutually exclusive options
            result = self.runner.invoke(
                ctl, ['dsn', 'alpha', '--role', 'master', '--member', 'dummy'])
            assert result.exit_code == 1

            # Non-existing member
            result = self.runner.invoke(ctl,
                                        ['dsn', 'alpha', '--member', 'dummy'])
            assert result.exit_code == 1

    @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))
    @patch('requests.post', requests_get)
    def test_restart_reinit(self):
        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):
        with patch('patroni.ctl.get_dcs', Mock(return_value=self.e)):
            result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                        input='alpha\nslave')
            assert 'Please confirm' in result.output
            assert 'You are about to remove all' in result.output
            # Not typing an exact confirmation
            assert result.exit_code == 1

            # master specified does not match master of cluster
            result = self.runner.invoke(
                ctl, ['remove', 'alpha'],
                input='''alpha\nYes I am aware\nslave''')
            assert result.exit_code == 1

            # cluster specified on cmdline does not match verification prompt
            result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                        input='beta\nleader')
            assert result.exit_code == 1

            result = self.runner.invoke(
                ctl, ['remove', 'alpha'],
                input='''alpha\nYes I am aware\nleader''')
            assert result.exit_code == 0

    @patch('patroni.etcd.Etcd.watch', Mock(return_value=None))
    @patch('patroni.etcd.Etcd.get_cluster',
           Mock(return_value=get_cluster_initialized_with_leader()))
    def test_wait_for_leader(self):
        self.assertRaises(PatroniCtlException, wait_for_leader, self.e, 0)

        cluster = wait_for_leader(self.e, timeout=2)
        assert cluster.leader.member.name == 'leader'

    @patch('requests.post',
           Mock(side_effect=requests.exceptions.ConnectionError('foo')))
    def test_post_patroni(self):
        member = get_cluster_initialized_with_leader().leader.member
        self.assertRaises(requests.exceptions.ConnectionError, post_patroni,
                          member, 'dummy', {})

    def test_ctl(self):
        self.runner.invoke(ctl, ['list'])

        result = self.runner.invoke(ctl, ['--help'])
        assert 'Usage:' in result.output

    def test_get_any_member(self):
        self.assertIsNone(
            get_any_member(get_cluster_initialized_without_leader(),
                           role='master'))

        m = get_any_member(get_cluster_initialized_with_leader(),
                           role='master')
        self.assertEquals(m.name, 'leader')

    def test_get_all_members(self):
        self.assertEquals(
            list(
                get_all_members(get_cluster_initialized_without_leader(),
                                role='master')), [])

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='master'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'leader')

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='replica'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'other')

        self.assertEquals(
            len(
                list(
                    get_all_members(get_cluster_initialized_without_leader(),
                                    role='replica'))), 2)

    @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 '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'])
        assert result.exit_code == 0
Пример #30
0
class TestCtl(unittest.TestCase):
    @patch('socket.getaddrinfo', socket_getaddrinfo)
    def setUp(self):
        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(
                return_value=['http://*****:*****@patch('psycopg2.connect', psycopg2_connect)
    def test_get_cursor(self):
        self.assertIsNone(
            get_cursor(get_cluster_initialized_without_leader(), {},
                       role='master'))

        self.assertIsNotNone(
            get_cursor(get_cluster_initialized_with_leader(), {},
                       role='master'))

        # MockCursor returns pg_is_in_recovery as false
        self.assertIsNone(
            get_cursor(get_cluster_initialized_with_leader(), {},
                       role='replica'))

        self.assertIsNotNone(
            get_cursor(get_cluster_initialized_with_leader(),
                       {'database': 'foo'},
                       role='any'))

    def test_parse_dcs(self):
        assert parse_dcs(None) is None
        assert parse_dcs('localhost') == {'etcd': {'host': 'localhost:2379'}}
        assert parse_dcs('') == {'etcd': {'host': 'localhost:2379'}}
        assert parse_dcs('localhost:8500') == {
            'consul': {
                'host': 'localhost:8500'
            }
        }
        assert parse_dcs('zookeeper://localhost') == {
            'zookeeper': {
                'hosts': ['localhost:2181']
            }
        }
        assert parse_dcs('exhibitor://dummy') == {
            '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'))
        self.assertIsNone(output_members(cluster, name='abc', fmt='json'))
        self.assertIsNone(output_members(cluster, name='abc', fmt='tsv'))

    @patch('patroni.ctl.get_dcs')
    @patch('patroni.ctl.request_patroni', Mock(return_value=MockResponse()))
    def test_failover(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        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\n2300-01-01T12:23:00\ny')
        assert result.exit_code == 0

        with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            result = self.runner.invoke(ctl, [
                'failover', 'dummy', '--force', '--scheduled',
                '2015-01-01T12:00:00'
            ])
            assert result.exit_code == 1

        # 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.ctl.request_patroni', Mock(side_effect=Exception)):
            # Non-responding patroni
            result = self.runner.invoke(
                ctl, ['failover', 'dummy'],
                input='leader\nother\n2300-01-01T12:23:00\ny')
            assert 'falling back to DCS' in result.output

        with patch('patroni.ctl.request_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

        # No members available
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_only_leader
        result = self.runner.invoke(ctl, ['failover', 'dummy'],
                                    input='leader\nother\n\ny')
        assert result.exit_code == 1

        # No master available
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_without_leader
        result = self.runner.invoke(ctl, ['failover', 'dummy'],
                                    input='leader\nother\n\ny')
        assert result.exit_code == 1

    def test_get_dcs(self):
        self.assertRaises(PatroniCtlException, get_dcs, {'dummy': {}}, 'dummy')

    @patch('psycopg2.connect', psycopg2_connect)
    @patch('patroni.ctl.query_member',
           Mock(return_value=([['mock column']], None)))
    @patch('patroni.ctl.get_dcs')
    @patch.object(etcd.Client, 'read', etcd_read)
    def test_query(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        # Mutually exclusive
        result = self.runner.invoke(
            ctl, ['query', 'alpha', '--member', 'abc', '--role', 'master'])
        assert result.exit_code == 1

        with self.runner.isolated_filesystem():
            with open('dummy', 'w') as dummy_file:
                dummy_file.write('SELECT 1')

            # Mutually exclusive
            result = self.runner.invoke(
                ctl,
                ['query', 'alpha', '--file', 'dummy', '--command', 'dummy'])
            assert result.exit_code == 1

            result = self.runner.invoke(ctl,
                                        ['query', 'alpha', '--file', 'dummy'])
            assert result.exit_code == 0

            os.remove('dummy')

        result = self.runner.invoke(
            ctl, ['query', 'alpha', '--command', 'SELECT 1'])
        assert 'mock column' in result.output

        # --command or --file is mandatory
        result = self.runner.invoke(ctl, ['query', 'alpha'])
        assert result.exit_code == 1

        result = self.runner.invoke(ctl, [
            'query', 'alpha', '--command', 'SELECT 1', '--username', 'root',
            '--password', '--dbname', 'postgres'
        ],
                                    input='ab\nab')
        assert 'mock column' in result.output

    def test_query_member(self):
        with patch('patroni.ctl.get_cursor',
                   Mock(return_value=MockConnect().cursor())):
            rows = query_member(None, None, None, 'master',
                                'SELECT pg_is_in_recovery()', {})
            self.assertTrue('False' in str(rows))

            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()', {})
            self.assertEquals(rows, (None, None))

            with patch('test_postgresql.MockCursor.execute',
                       Mock(side_effect=OperationalError('bla'))):
                rows = query_member(None, None, None, 'replica',
                                    'SELECT pg_is_in_recovery()', {})

        with patch('patroni.ctl.get_cursor', Mock(return_value=None)):
            rows = query_member(None, None, None, None,
                                'SELECT pg_is_in_recovery()', {})
            self.assertTrue('No connection to' in str(rows))

            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()', {})
            self.assertTrue('No connection to' in str(rows))

        with patch('patroni.ctl.get_cursor',
                   Mock(side_effect=OperationalError('bla'))):
            rows = query_member(None, None, None, 'replica',
                                'SELECT pg_is_in_recovery()', {})

    @patch('patroni.ctl.get_dcs')
    def test_dsn(self, mock_get_dcs):
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        result = self.runner.invoke(ctl, ['dsn', 'alpha'])
        assert 'host=127.0.0.1 port=5435' in result.output

        # Mutually exclusive options
        result = self.runner.invoke(
            ctl, ['dsn', 'alpha', '--role', 'master', '--member', 'dummy'])
        assert result.exit_code == 1

        # Non-existing member
        result = self.runner.invoke(ctl, ['dsn', 'alpha', '--member', 'dummy'])
        assert result.exit_code == 1

    @patch('requests.post', requests_get)
    @patch('patroni.ctl.get_dcs')
    def test_restart_reinit(self, mock_get_dcs):
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        result = self.runner.invoke(ctl, ['restart', 'alpha'],
                                    input='y\n\nnow')
        assert 'Failed: restart for' in result.output
        assert result.exit_code == 0

        result = self.runner.invoke(ctl, ['reinit', 'alpha'], input='y')
        assert result.exit_code == 1

        # successful reinit
        result = self.runner.invoke(ctl, ['reinit', 'alpha', 'other'],
                                    input='y')
        assert result.exit_code == 0

        # Aborted restart
        result = self.runner.invoke(ctl, ['restart', 'alpha'], input='N')
        assert result.exit_code == 1

        result = self.runner.invoke(
            ctl, ['restart', 'alpha', '--pending', '--force'])
        assert result.exit_code == 0

        # Not a member
        result = self.runner.invoke(ctl,
                                    ['restart', 'alpha', 'dummy', '--any'],
                                    input='y')
        assert result.exit_code == 1

        # Wrong pg version
        result = self.runner.invoke(
            ctl, ['restart', 'alpha', '--any', '--pg-version', '9.1'],
            input='y')
        assert 'Error: PostgreSQL version' in result.output
        assert result.exit_code == 1

        with patch('requests.delete', Mock(return_value=MockResponse(500))):
            # normal restart, the schedule is actually parsed, but not validated in patronictl
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', 'other', '--force', '--scheduled',
                '2300-10-01T14:30'
            ])
            assert 'Failed: flush scheduled restart' in result.output

        with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', 'other', '--force', '--scheduled',
                '2300-10-01T14:30'
            ])
            assert result.exit_code == 1

        with patch('requests.post', Mock(return_value=MockResponse())):
            # normal restart, the schedule is actually parsed, but not validated in patronictl
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', '--pg-version', '42.0.0', '--scheduled',
                '2300-10-01T14:30'
            ],
                                        input='y')
            assert result.exit_code == 0

        with patch('requests.post', Mock(return_value=MockResponse(204))):
            # get restart with the non-200 return code
            # normal restart, the schedule is actually parsed, but not validated in patronictl
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', '--pg-version', '42.0.0', '--scheduled',
                '2300-10-01T14:30'
            ],
                                        input='y')
            assert result.exit_code == 0

        # force restart with restart already present
        with patch('patroni.ctl.request_patroni',
                   Mock(return_value=MockResponse(204))):
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', 'other', '--force', '--scheduled',
                '2300-10-01T14:30'
            ])
            assert result.exit_code == 0

        with patch('requests.post', Mock(return_value=MockResponse(202))):
            # get restart with the non-200 return code
            # normal restart, the schedule is actually parsed, but not validated in patronictl
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', '--pg-version', '99.0.0', '--scheduled',
                '2300-10-01T14:30'
            ],
                                        input='y')
            assert 'Success: restart scheduled' in result.output
            assert result.exit_code == 0

        with patch('requests.post', Mock(return_value=MockResponse(409))):
            # get restart with the non-200 return code
            # normal restart, the schedule is actually parsed, but not validated in patronictl
            result = self.runner.invoke(ctl, [
                'restart', 'alpha', '--pg-version', '99.0.0', '--scheduled',
                '2300-10-01T14:30'
            ],
                                        input='y')
            assert 'Failed: another restart is already' in result.output
            assert result.exit_code == 0

    @patch('patroni.ctl.get_dcs')
    def test_remove(self, mock_get_dcs):
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                    input='alpha\nslave')
        assert 'Please confirm' in result.output
        assert 'You are about to remove all' in result.output
        # Not typing an exact confirmation
        assert result.exit_code == 1

        # master specified does not match master of cluster
        result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                    input='alpha\nYes I am aware\nslave')
        assert result.exit_code == 1

        # cluster specified on cmdline does not match verification prompt
        result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                    input='beta\nleader')
        assert result.exit_code == 1

        result = self.runner.invoke(ctl, ['remove', 'alpha'],
                                    input='alpha\nYes I am aware\nleader')
        assert result.exit_code == 0

    @patch('patroni.dcs.AbstractDCS.watch', Mock(return_value=None))
    @patch('patroni.dcs.AbstractDCS.get_cluster',
           Mock(return_value=get_cluster_initialized_with_leader()))
    def test_wait_for_leader(self):
        self.assertRaises(PatroniCtlException, wait_for_leader, self.e, 0)

        cluster = wait_for_leader(self.e, timeout=2)
        assert cluster.leader.member.name == 'leader'

    @patch('requests.post',
           Mock(side_effect=requests.exceptions.ConnectionError('foo')))
    def test_request_patroni(self):
        member = get_cluster_initialized_with_leader().leader.member
        self.assertRaises(requests.exceptions.ConnectionError, request_patroni,
                          member, 'post', 'dummy', {})

    def test_ctl(self):
        self.runner.invoke(ctl, ['list'])

        result = self.runner.invoke(ctl, ['--help'])
        assert 'Usage:' in result.output

    def test_get_any_member(self):
        self.assertIsNone(
            get_any_member(get_cluster_initialized_without_leader(),
                           role='master'))

        m = get_any_member(get_cluster_initialized_with_leader(),
                           role='master')
        self.assertEquals(m.name, 'leader')

    def test_get_all_members(self):
        self.assertEquals(
            list(
                get_all_members(get_cluster_initialized_without_leader(),
                                role='master')), [])

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='master'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'leader')

        r = list(
            get_all_members(get_cluster_initialized_with_leader(),
                            role='replica'))
        self.assertEquals(len(r), 1)
        self.assertEquals(r[0].name, 'other')

        self.assertEquals(
            len(
                list(
                    get_all_members(get_cluster_initialized_without_leader(),
                                    role='replica'))), 2)

    @patch('patroni.ctl.get_dcs')
    def test_members(self, mock_get_dcs):
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        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'])
        assert result.exit_code == 0

    @patch('patroni.ctl.get_dcs')
    def test_scaffold(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_not_initialized_without_leader
        mock_get_dcs.return_value.initialize = Mock(return_value=True)
        mock_get_dcs.return_value.touch_member = Mock(return_value=True)
        mock_get_dcs.return_value.attempt_to_acquire_leader = Mock(
            return_value=True)

        with patch.object(self.e, 'initialize', return_value=False):
            result = self.runner.invoke(ctl, ['scaffold', 'alpha'])
            assert result.exception

        with patch.object(mock_get_dcs.return_value, 'touch_member',
                          Mock(return_value=False)):
            result = self.runner.invoke(ctl, ['scaffold', 'alpha'])
            assert result.exception

        result = self.runner.invoke(ctl, ['scaffold', 'alpha'])
        assert result.exit_code == 0

        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader
        result = self.runner.invoke(ctl, ['scaffold', 'alpha'])
        assert result.exception

    @patch('patroni.ctl.get_dcs')
    def test_list_extended(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        cluster = get_cluster_initialized_with_leader(sync=('leader', 'other'))
        mock_get_dcs.return_value.get_cluster = Mock(return_value=cluster)

        result = self.runner.invoke(ctl, ['list', 'dummy', '--extended'])
        assert '2100' in result.output
        assert 'Scheduled restart' in result.output

    @patch('patroni.ctl.get_dcs')
    @patch('requests.delete', Mock(return_value=MockResponse()))
    def test_flush(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader

        result = self.runner.invoke(
            ctl, ['flush', 'dummy', 'restart', '-r', 'master'], input='y')
        assert 'No scheduled restart' in result.output

        result = self.runner.invoke(ctl,
                                    ['flush', 'dummy', 'restart', '--force'])
        assert 'Success: flush scheduled restart' in result.output
        with patch.object(requests, 'delete', return_value=MockResponse(404)):
            result = self.runner.invoke(
                ctl, ['flush', 'dummy', 'restart', '--force'])
            assert 'Failed: flush scheduled restart' in result.output

    @patch('patroni.ctl.get_dcs')
    def test_pause_cluster(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader

        with patch('requests.patch', Mock(return_value=MockResponse(200))):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Success' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(500))):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Failed' in result.output

        with patch('requests.patch', Mock(return_value=MockResponse(200))),\
                patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            result = self.runner.invoke(ctl, ['pause', 'dummy'])
            assert 'Cluster is already paused' in result.output

    @patch('patroni.ctl.get_dcs')
    def test_resume_cluster(self, mock_get_dcs):
        mock_get_dcs.return_value = self.e
        mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader

        with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)):
            with patch('requests.patch', Mock(return_value=MockResponse(200))):
                result = self.runner.invoke(ctl, ['resume', 'dummy'])
                assert 'Success' in result.output

            with patch('requests.patch', Mock(return_value=MockResponse(500))):
                result = self.runner.invoke(ctl, ['resume', 'dummy'])
                assert 'Failed' in result.output

            with patch('requests.patch', Mock(return_value=MockResponse(200))),\
                    patch('patroni.dcs.Cluster.is_paused', Mock(return_value=False)):
                result = self.runner.invoke(ctl, ['resume', 'dummy'])
                assert 'Cluster is not paused' in result.output