class TestPatroni(unittest.TestCase):

    def setUp(self):
        RestApiServer._BaseServer__is_shut_down = Mock()
        RestApiServer._BaseServer__shutdown_request = True
        RestApiServer.socket = 0
        with patch.object(etcd.Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            with open('postgres0.yml', 'r') as f:
                config = yaml.load(f)
                self.p = Patroni(config)

    @patch('time.sleep', Mock(side_effect=SleepException))
    @patch.object(etcd.Client, 'delete', Mock())
    @patch.object(etcd.Client, 'machines')
    def test_patroni_main(self, mock_machines):
        with patch('subprocess.call', Mock(return_value=1)):
            _main()
            sys.argv = ['patroni.py', 'postgres0.yml']

            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            with patch.object(Patroni, 'run', Mock(side_effect=SleepException)):
                self.assertRaises(SleepException, _main)
            with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())):
                _main()
            sys.argv = ['patroni.py']
            # read the content of the yaml configuration file into the environment variable
            # in order to test how does patroni handle the configuration passed from the environment.
            with open('postgres0.yml', 'r') as f:
                os.environ[Patroni.PATRONI_CONFIG_VARIABLE] = f.read()
            with patch.object(Patroni, 'run', Mock(side_effect=SleepException())):
                self.assertRaises(SleepException, _main)
            del os.environ[Patroni.PATRONI_CONFIG_VARIABLE]

    def test_run(self):
        self.p.ha.dcs.watch = Mock(side_effect=SleepException)
        self.p.api.start = Mock()
        self.assertRaises(SleepException, self.p.run)

    def test_schedule_next_run(self):
        self.p.ha.dcs.watch = Mock(return_value=True)
        self.p.schedule_next_run()
        self.p.next_run = time.time() - self.p.nap_time - 1
        self.p.schedule_next_run()

    def test_noloadbalance(self):
        self.p.tags['noloadbalance'] = True
        self.assertTrue(self.p.noloadbalance)

    def test_nofailover(self):
        self.p.tags['nofailover'] = True
        self.assertTrue(self.p.nofailover)
        self.p.tags['nofailover'] = None
        self.assertFalse(self.p.nofailover)

    def test_replicatefrom(self):
        self.assertIsNone(self.p.replicatefrom)
        self.p.tags['replicatefrom'] = 'foo'
        self.assertEqual(self.p.replicatefrom, 'foo')
class TestPatroni(unittest.TestCase):

    @patch.object(Client, 'machines')
    def setUp(self, mock_machines):
        mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
        self.touched = False
        self.init_cancelled = False
        RestApiServer._BaseServer__is_shut_down = Mock()
        RestApiServer._BaseServer__shutdown_request = True
        RestApiServer.socket = 0
        with open('postgres0.yml', 'r') as f:
            config = yaml.load(f)
            self.p = Patroni(config)
            self.p.ha.dcs.client.write = etcd_write
            self.p.ha.dcs.client.read = etcd_read

    @patch('patroni.zookeeper.KazooClient', MockKazooClient())
    def test_get_dcs(self):
        self.assertIsInstance(self.p.get_dcs('', {'zookeeper': {'scope': '', 'hosts': ''}}), ZooKeeper)
        self.assertRaises(Exception, self.p.get_dcs, '', {})

    @patch('time.sleep', Mock(side_effect=SleepException()))
    @patch.object(Etcd, 'delete_leader', Mock())
    @patch.object(Client, 'machines')
    def test_patroni_main(self, mock_machines):
        main()
        sys.argv = ['patroni.py', 'postgres0.yml']

        mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
        with patch.object(Patroni, 'run', Mock(side_effect=SleepException())):
            self.assertRaises(SleepException, main)
        with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())):
            main()

    @patch('time.sleep', Mock(side_effect=SleepException()))
    def test_run(self):
        self.p.ha.dcs.watch = time_sleep
        self.assertRaises(SleepException, self.p.run)

        self.p.ha.state_handler.is_leader = Mock(return_value=False)
        self.p.api.start = Mock()
        self.assertRaises(SleepException, self.p.run)

    def test_schedule_next_run(self):
        self.p.ha.dcs.watch = Mock(return_value=True)
        self.p.schedule_next_run()
        self.p.next_run = time.time() - self.p.nap_time - 1
        self.p.schedule_next_run()

    def test_nofailover(self):
        self.p.tags['nofailover'] = True
        self.assertTrue(self.p.nofailover)
        self.p.tags['nofailover'] = None
        self.assertFalse(self.p.nofailover)
Example #3
0
def get_dcs(config, scope):
    for k in set(DCS_DEFAULTS.keys()) & set(config.keys()):
        config[k].setdefault('scope', scope)
    try:
        return Patroni.get_dcs(scope, config)
    except PatroniException as e:
        raise PatroniCtlException(str(e))
Example #4
0
 def setUp(self):
     RestApiServer._BaseServer__is_shut_down = Mock()
     RestApiServer._BaseServer__shutdown_request = True
     RestApiServer.socket = 0
     with patch.object(Client, 'machines') as mock_machines:
         mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
         sys.argv = ['patroni.py', 'postgres0.yml']
         self.p = Patroni()
 def setUp(self):
     RestApiServer._BaseServer__is_shut_down = Mock()
     RestApiServer._BaseServer__shutdown_request = True
     RestApiServer.socket = 0
     with patch.object(etcd.Client, 'machines') as mock_machines:
         mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
         with open('postgres0.yml', 'r') as f:
             config = yaml.load(f)
             self.p = Patroni(config)
Example #6
0
 def setUp(self, mock_machines):
     mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
     self.touched = False
     self.init_cancelled = False
     RestApiServer._BaseServer__is_shut_down = Mock()
     RestApiServer._BaseServer__shutdown_request = True
     RestApiServer.socket = 0
     with open('postgres0.yml', 'r') as f:
         config = yaml.load(f)
         self.p = Patroni(config)
         self.p.ha.dcs.client.write = etcd_write
         self.p.ha.dcs.client.read = etcd_read
Example #7
0
 def set_up(self):
     self.touched = False
     subprocess.call = subprocess_call
     psycopg2.connect = psycopg2_connect
     self.time_sleep = time.sleep
     time.sleep = nop
     self.write_pg_hba = Postgresql.write_pg_hba
     self.write_recovery_conf = Postgresql.write_recovery_conf
     Postgresql.write_pg_hba = nop
     Postgresql.write_recovery_conf = nop
     BaseHTTPServer.HTTPServer.__init__ = nop
     RestApiServer._BaseServer__is_shut_down = Mock_BaseServer__is_shut_down()
     RestApiServer._BaseServer__shutdown_request = True
     RestApiServer.socket = 0
     with open('postgres0.yml', 'r') as f:
         config = yaml.load(f)
         with patch.object(Client, 'machines') as mock_machines:
             mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
             self.p = Patroni(config)
Example #8
0
class TestPatroni(unittest.TestCase):

    def __init__(self, method_name='runTest'):
        self.setUp = self.set_up
        self.tearDown = self.tear_down
        super(TestPatroni, self).__init__(method_name)

    def set_up(self):
        self.touched = False
        subprocess.call = subprocess_call
        psycopg2.connect = psycopg2_connect
        self.time_sleep = time.sleep
        time.sleep = nop
        self.write_pg_hba = Postgresql.write_pg_hba
        self.write_recovery_conf = Postgresql.write_recovery_conf
        Postgresql.write_pg_hba = nop
        Postgresql.write_recovery_conf = nop
        BaseHTTPServer.HTTPServer.__init__ = nop
        RestApiServer._BaseServer__is_shut_down = Mock_BaseServer__is_shut_down()
        RestApiServer._BaseServer__shutdown_request = True
        RestApiServer.socket = 0
        with open('postgres0.yml', 'r') as f:
            config = yaml.load(f)
            with patch.object(Client, 'machines') as mock_machines:
                mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
                self.p = Patroni(config)

    def tear_down(self):
        time.sleep = self.time_sleep
        Postgresql.write_pg_hba = self.write_pg_hba
        Postgresql.write_recovery_conf = self.write_recovery_conf

    def test_get_dcs(self):
        helpers.zookeeper.KazooClient = MockKazooClient
        self.assertIsInstance(self.p.get_dcs('', {'zookeeper': {'scope': '', 'hosts': ''}}), ZooKeeper)
        self.assertRaises(Exception, self.p.get_dcs, '', {})

    def test_patroni_main(self):
        main()
        sys.argv = ['patroni.py', 'postgres0.yml']
        time.sleep = time_sleep

        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            Patroni.initialize = nop
            touch_member = Patroni.touch_member
            run = Patroni.run

            Patroni.touch_member = self.touch_member
            Patroni.run = time_sleep

            Etcd.delete_leader = nop

            self.assertRaises(Exception, main)

            Patroni.run = run
            Patroni.touch_member = touch_member

    def test_patroni_run(self):
        time.sleep = time_sleep
        self.p.touch_member = self.touch_member
        self.p.ha.state_handler.sync_replication_slots = time_sleep
        self.p.ha.dcs.client.read = etcd_read
        self.assertRaises(Exception, self.p.run)
        self.p.ha.state_handler.is_leader = lambda: False
        self.p.api.start = nop
        self.assertRaises(Exception, self.p.run)

    def touch_member(self, ttl=None):
        if not self.touched:
            self.touched = True
            return False
        return True

    def test_touch_member(self):
        self.p.ha.dcs.client.write = etcd_write
        self.p.touch_member()
        now = datetime.datetime.utcnow()
        member = Member(0, self.p.postgresql.name, 'b', 'c', (now + datetime.timedelta(
            seconds=self.p.shutdown_member_ttl + 10)).strftime('%Y-%m-%dT%H:%M:%S.%fZ'), None)
        self.p.ha.cluster = Cluster(True, member, 0, [member])
        self.p.touch_member()

    def test_patroni_initialize(self):
        self.p.postgresql.should_use_s3_to_create_replica = false
        self.p.ha.dcs.client.write = etcd_write
        self.p.touch_member = self.touch_member
        self.p.postgresql.data_directory_empty = true
        self.p.ha.dcs.race = true
        self.p.initialize()

        self.p.ha.dcs.race = false
        time.sleep = time_sleep
        self.p.ha.dcs.client.read = etcd_read
        self.p.initialize()

        self.p.ha.dcs.current_leader = nop
        self.assertRaises(Exception, self.p.initialize)

        self.p.postgresql.data_directory_empty = false
        self.p.initialize()

    def test_schedule_next_run(self):
        self.p.next_run = time.time() - self.p.nap_time - 1
        self.p.schedule_next_run()
Example #9
0
class TestPatroni(unittest.TestCase):

    def setUp(self):
        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            self.touched = False
            self.init_cancelled = False
            RestApiServer._BaseServer__is_shut_down = Mock()
            RestApiServer._BaseServer__shutdown_request = True
            RestApiServer.socket = 0
            with open('postgres0.yml', 'r') as f:
                config = yaml.load(f)
                self.p = Patroni(config)

    @patch('patroni.zookeeper.KazooClient', MockKazooClient())
    @patch.object(Consul, 'create_or_restore_session', Mock())
    def test_get_dcs(self):
        self.assertIsInstance(self.p.get_dcs('', {'zookeeper': {'scope': '', 'hosts': ''}}), ZooKeeper)
        self.assertIsInstance(self.p.get_dcs('', {'consul': {'scope': '', 'hosts': '127.0.0.1:1'}}), Consul)
        self.assertRaises(PatroniException, self.p.get_dcs, '', {})

    @patch('time.sleep', Mock(side_effect=SleepException))
    @patch.object(Etcd, 'delete_leader', Mock())
    @patch.object(Client, 'machines')
    def test_patroni_main(self, mock_machines):
        _main()
        sys.argv = ['patroni.py', 'postgres0.yml']

        mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
        with patch.object(Patroni, 'run', Mock(side_effect=SleepException)):
            self.assertRaises(SleepException, _main)
        with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())):
            _main()
        sys.argv = ['patroni.py']
        # read the content of the yaml configuration file into the environment variable
        # in order to test how does patroni handle the configuration passed from the environment.
        with open('postgres0.yml', 'r') as f:
            os.environ[Patroni.PATRONI_CONFIG_VARIABLE] = f.read()
        with patch.object(Patroni, 'run', Mock(side_effect=SleepException())):
            self.assertRaises(SleepException, _main)
        del os.environ[Patroni.PATRONI_CONFIG_VARIABLE]

    def test_run(self):
        self.p.ha.dcs.watch = Mock(side_effect=SleepException)
        self.p.api.start = Mock()
        self.assertRaises(SleepException, self.p.run)

    def test_schedule_next_run(self):
        self.p.ha.dcs.watch = Mock(return_value=True)
        self.p.schedule_next_run()
        self.p.next_run = time.time() - self.p.nap_time - 1
        self.p.schedule_next_run()

    def test_noloadbalance(self):
        self.p.tags['noloadbalance'] = True
        self.assertTrue(self.p.noloadbalance)

    def test_nofailover(self):
        self.p.tags['nofailover'] = True
        self.assertTrue(self.p.nofailover)
        self.p.tags['nofailover'] = None
        self.assertFalse(self.p.nofailover)

    def test_replicatefrom(self):
        self.assertIsNone(self.p.replicatefrom)
        self.p.tags['replicatefrom'] = 'foo'
        self.assertEqual(self.p.replicatefrom, 'foo')
Example #10
0
class TestPatroni(unittest.TestCase):

    @patch.object(etcd.Client, 'read', etcd_read)
    def setUp(self):
        RestApiServer._BaseServer__is_shut_down = Mock()
        RestApiServer._BaseServer__shutdown_request = True
        RestApiServer.socket = 0
        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            sys.argv = ['patroni.py', 'postgres0.yml']
            self.p = Patroni()

    @patch('patroni.dcs.AbstractDCS.get_cluster', Mock(side_effect=[None, DCSError('foo'), None]))
    def test_load_dynamic_configuration(self):
        self.p.config._dynamic_configuration = {}
        self.p.load_dynamic_configuration()
        self.p.load_dynamic_configuration()

    @patch('time.sleep', Mock(side_effect=SleepException))
    @patch.object(etcd.Client, 'delete', Mock())
    @patch.object(Client, 'machines')
    def test_patroni_main(self, mock_machines):
        with patch('subprocess.call', Mock(return_value=1)):
            sys.argv = ['patroni.py', 'postgres0.yml']

            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            with patch.object(Patroni, 'run', Mock(side_effect=SleepException)):
                self.assertRaises(SleepException, _main)
            with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())):
                _main()

    @patch('patroni.config.Config.save_cache', Mock())
    @patch('patroni.config.Config.reload_local_configuration', Mock(return_value=True))
    def test_run(self):
        self.p.sighup_handler()
        self.p.ha.dcs.watch = Mock(side_effect=SleepException)
        self.p.api.start = Mock()
        self.p.config._dynamic_configuration = {}
        self.assertRaises(SleepException, self.p.run)
        with patch('patroni.config.Config.set_dynamic_configuration', Mock(return_value=True)):
            self.assertRaises(SleepException, self.p.run)
        with patch('patroni.postgresql.Postgresql.data_directory_empty', Mock(return_value=False)):
            self.assertRaises(SleepException, self.p.run)

    def test_sigterm_handler(self):
        self.assertRaises(SystemExit, self.p.sigterm_handler)

    def test_schedule_next_run(self):
        self.p.ha.dcs.watch = Mock(return_value=True)
        self.p.schedule_next_run()
        self.p.next_run = time.time() - self.p.dcs.loop_wait - 1
        self.p.schedule_next_run()

    def test_noloadbalance(self):
        self.p.tags['noloadbalance'] = True
        self.assertTrue(self.p.noloadbalance)

    def test_nofailover(self):
        self.p.tags['nofailover'] = True
        self.assertTrue(self.p.nofailover)
        self.p.tags['nofailover'] = None
        self.assertFalse(self.p.nofailover)

    def test_replicatefrom(self):
        self.assertIsNone(self.p.replicatefrom)
        self.p.tags['replicatefrom'] = 'foo'
        self.assertEqual(self.p.replicatefrom, 'foo')

    def test_reload_config(self):
        self.p.reload_config()
        self.p.get_tags = Mock(side_effect=Exception)
        self.p.reload_config()
Example #11
0
class TestPatroni(unittest.TestCase):

    @patch('pkgutil.get_importer', Mock(return_value=MockFrozenImporter()))
    @patch('sys.frozen', Mock(return_value=True), create=True)
    @patch.object(etcd.Client, 'read', etcd_read)
    def setUp(self):
        RestApiServer._BaseServer__is_shut_down = Mock()
        RestApiServer._BaseServer__shutdown_request = True
        RestApiServer.socket = 0
        with patch.object(Client, 'machines') as mock_machines:
            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            sys.argv = ['patroni.py', 'postgres0.yml']
            self.p = Patroni()

    @patch('patroni.dcs.AbstractDCS.get_cluster', Mock(side_effect=[None, DCSError('foo'), None]))
    def test_load_dynamic_configuration(self):
        self.p.config._dynamic_configuration = {}
        self.p.load_dynamic_configuration()
        self.p.load_dynamic_configuration()

    @patch('time.sleep', Mock(side_effect=SleepException))
    @patch.object(etcd.Client, 'delete', Mock())
    @patch.object(Client, 'machines')
    def test_patroni_patroni_main(self, mock_machines):
        with patch('subprocess.call', Mock(return_value=1)):
            sys.argv = ['patroni.py', 'postgres0.yml']

            mock_machines.__get__ = Mock(return_value=['http://remotehost:2379'])
            with patch.object(Patroni, 'run', Mock(side_effect=SleepException)):
                self.assertRaises(SleepException, patroni_main)
            with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())):
                with patch('patroni.ha.Ha.is_paused', Mock(return_value=True)):
                    patroni_main()

    @patch('os.getpid')
    @patch('subprocess.Popen', )
    @patch('patroni.patroni_main', Mock())
    def test_patroni_main(self, mock_popen, mock_getpid):
        mock_getpid.return_value = 2
        _main()

        with patch('sys.frozen', Mock(return_value=True), create=True):
            sys.argv = ['/patroni', 'pg_ctl_start', 'postgres', '-D', '/data', '--max_connections=100']
            _main()

        mock_getpid.return_value = 1

        def mock_signal(signo, handler):
            handler(signo, None)

        with patch('signal.signal', mock_signal):
            with patch('os.waitpid', Mock(side_effect=[(1, 0), (0, 0)])):
                _main()
            with patch('os.waitpid', Mock(side_effect=OSError)):
                _main()

        ref = {'passtochild': lambda signo, stack_frame: 0}

        def mock_sighup(signo, handler):
            if signo == signal.SIGHUP:
                ref['passtochild'] = handler

        def mock_wait():
            ref['passtochild'](0, None)

        mock_popen.return_value.wait = mock_wait
        with patch('signal.signal', mock_sighup), patch('os.kill', Mock()):
            self.assertIsNone(_main())

    @patch('patroni.config.Config.save_cache', Mock())
    @patch('patroni.config.Config.reload_local_configuration', Mock(return_value=True))
    @patch.object(Postgresql, 'state', PropertyMock(return_value='running'))
    @patch.object(Postgresql, 'data_directory_empty', Mock(return_value=False))
    def test_run(self):
        self.p.postgresql.set_role('replica')
        self.p.sighup_handler()
        self.p.ha.dcs.watch = Mock(side_effect=SleepException)
        self.p.api.start = Mock()
        self.p.config._dynamic_configuration = {}
        self.assertRaises(SleepException, self.p.run)
        with patch('patroni.config.Config.set_dynamic_configuration', Mock(return_value=True)):
            self.assertRaises(SleepException, self.p.run)
        with patch('patroni.postgresql.Postgresql.data_directory_empty', Mock(return_value=False)):
            self.assertRaises(SleepException, self.p.run)

    def test_sigterm_handler(self):
        self.assertRaises(SystemExit, self.p.sigterm_handler)

    def test_schedule_next_run(self):
        self.p.ha.dcs.watch = Mock(return_value=True)
        self.p.schedule_next_run()
        self.p.next_run = time.time() - self.p.dcs.loop_wait - 1
        self.p.schedule_next_run()

    def test_noloadbalance(self):
        self.p.tags['noloadbalance'] = True
        self.assertTrue(self.p.noloadbalance)

    def test_nofailover(self):
        self.p.tags['nofailover'] = True
        self.assertTrue(self.p.nofailover)
        self.p.tags['nofailover'] = None
        self.assertFalse(self.p.nofailover)

    def test_replicatefrom(self):
        self.assertIsNone(self.p.replicatefrom)
        self.p.tags['replicatefrom'] = 'foo'
        self.assertEqual(self.p.replicatefrom, 'foo')

    def test_reload_config(self):
        self.p.reload_config()
        self.p.get_tags = Mock(side_effect=Exception)
        self.p.reload_config()

    def test_nosync(self):
        self.p.tags['nosync'] = True
        self.assertTrue(self.p.nosync)
        self.p.tags['nosync'] = None
        self.assertFalse(self.p.nosync)