diff --git a/patroni/__init__.py b/patroni/__init__.py index 9badd6f7..72e11145 100644 --- a/patroni/__init__.py +++ b/patroni/__init__.py @@ -176,13 +176,13 @@ def main(): if pid: os.kill(pid, signo) - signal.signal(signal.SIGCHLD, sigchld_handler) if os.name != 'nt': + signal.signal(signal.SIGCHLD, sigchld_handler) signal.signal(signal.SIGHUP, passtochild) signal.signal(signal.SIGQUIT, passtochild) + signal.signal(signal.SIGUSR1, passtochild) + signal.signal(signal.SIGUSR2, passtochild) signal.signal(signal.SIGINT, passtochild) - signal.signal(signal.SIGUSR1, passtochild) - signal.signal(signal.SIGUSR2, passtochild) signal.signal(signal.SIGABRT, passtochild) signal.signal(signal.SIGTERM, passtochild) diff --git a/patroni/ctl.py b/patroni/ctl.py index 7a2fc2d8..d1068916 100644 --- a/patroni/ctl.py +++ b/patroni/ctl.py @@ -63,8 +63,8 @@ def parse_dcs(dcs): elif scheme not in DCS_DEFAULTS: raise PatroniCtlException('Unknown dcs scheme: {}'.format(scheme)) - dcs_info = DCS_DEFAULTS[scheme] - return yaml.load(dcs_info['template'].format(host=parsed.hostname or 'localhost', port=port or dcs_info['port'])) + default = DCS_DEFAULTS[scheme] + return yaml.safe_load(default['template'].format(host=parsed.hostname or 'localhost', port=port or default['port'])) def load_config(path, dcs): diff --git a/patroni/watchdog/linux.py b/patroni/watchdog/linux.py index 311dcd19..fcb363b2 100644 --- a/patroni/watchdog/linux.py +++ b/patroni/watchdog/linux.py @@ -1,6 +1,5 @@ import collections import ctypes -import fcntl import os import platform from patroni.watchdog.base import WatchdogBase, WatchdogError @@ -162,7 +161,9 @@ class LinuxWatchdogDevice(WatchdogBase): Raises OSError or IOError (Python 2) when the ioctl fails.""" if self._fd is None: raise WatchdogError("Watchdog device is closed") - fcntl.ioctl(self._fd, func, arg, True) + if os.name != 'nt': + import fcntl + fcntl.ioctl(self._fd, func, arg, True) def get_support(self): if self._support_cache is None: diff --git a/tests/test_config.py b/tests/test_config.py index f99f8e2a..357a227b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -52,7 +52,7 @@ class TestConfig(unittest.TestCase): 'PATRONI_ETCD_KEY': '/key', 'PATRONI_CONSUL_HOST': '127.0.0.1:8500', 'PATRONI_CONSUL_REGISTER_SERVICE': 'on', - 'PATRONI_KUBERNETES_LABELS': 'a:b:c', + 'PATRONI_KUBERNETES_LABELS': 'a: b: c', 'PATRONI_KUBERNETES_SCOPE_LABEL': 'a', 'PATRONI_KUBERNETES_PORTS': '[{"name": "postgresql"}]', 'PATRONI_ZOOKEEPER_HOSTS': "'host1:2181','host2:2181'", diff --git a/tests/test_ctl.py b/tests/test_ctl.py index 297a1f7f..52681ffc 100644 --- a/tests/test_ctl.py +++ b/tests/test_ctl.py @@ -533,7 +533,8 @@ class TestCtl(unittest.TestCase): show_diff(b"foo:\n bar: \xc3\xb6\xc3\xb6\n".decode('utf-8'), b"foo:\n bar: \xc3\xbc\xc3\xbc\n".decode('utf-8')) - def test_invoke_editor(self): + @patch('subprocess.call', return_value=1) + def test_invoke_editor(self, mock_subprocess_call): for e in ('', 'false'): os.environ['EDITOR'] = e self.assertRaises(PatroniCtlException, invoke_editor, 'foo: bar\n', 'test') diff --git a/tests/test_patroni.py b/tests/test_patroni.py index d2d061a9..2406279d 100644 --- a/tests/test_patroni.py +++ b/tests/test_patroni.py @@ -87,7 +87,7 @@ class TestPatroni(unittest.TestCase): ref = {'passtochild': lambda signo, stack_frame: 0} def mock_sighup(signo, handler): - if signo == signal.SIGHUP: + if hasattr(signal, 'SIGHUP') and signo == signal.SIGHUP: ref['passtochild'] = handler def mock_join(): diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index 8a94d727..e0288c04 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -15,6 +15,7 @@ from patroni.postmaster import PostmasterProcess from patroni.utils import RetryFailedError from six.moves import builtins from threading import Thread, current_thread +from tempfile import gettempdir class MockCursor(object): @@ -189,7 +190,8 @@ class TestPostgresql(unittest.TestCase): if not os.path.exists(self.data_dir): os.makedirs(self.data_dir) self.p = Postgresql({'name': 'test0', 'scope': 'batman', 'data_dir': self.data_dir, - 'config_dir': self.config_dir, 'retry_timeout': 10, 'pgpass': '/tmp/pgpass0', + 'config_dir': self.config_dir, 'retry_timeout': 10, + 'pgpass': os.path.join(gettempdir(), 'pgpass0'), 'listen': '127.0.0.2, 127.0.0.3:5432', 'connect_address': '127.0.0.2:5432', 'authentication': {'superuser': {'username': 'test', 'password': 'test'}, 'replication': {'username': 'replicator', 'password': 'rep-pass'}}, @@ -721,12 +723,18 @@ class TestPostgresql(unittest.TestCase): self.assertEqual(self.p.get_postgres_role_from_data_directory(), 'replica') def test_remove_data_directory(self): + def _symlink(src, dst): + try: + os.symlink(src, dst) + except OSError: + if os.name == 'nt': # os.symlink under Windows needs admin rights skip it + pass os.makedirs(os.path.join(self.data_dir, 'foo')) - os.symlink('foo', os.path.join(self.data_dir, 'pg_wal')) + _symlink('foo', os.path.join(self.data_dir, 'pg_wal')) self.p.remove_data_directory() open(self.data_dir, 'w').close() self.p.remove_data_directory() - os.symlink('unexisting', self.data_dir) + _symlink('unexisting', self.data_dir) with patch('os.unlink', Mock(side_effect=OSError)): self.p.remove_data_directory() self.p.remove_data_directory() diff --git a/tests/test_postmaster.py b/tests/test_postmaster.py index c8e42363..6929f55e 100644 --- a/tests/test_postmaster.py +++ b/tests/test_postmaster.py @@ -6,6 +6,18 @@ from patroni.postmaster import PostmasterProcess from six.moves import builtins +class MockProcess(object): + def __init__(self, target, args): + self.target = target + self.args = args + + def start(self): + self.target(*self.args) + + def join(self): + pass + + class TestPostmasterProcess(unittest.TestCase): @patch('psutil.Process.__init__', Mock()) def test_init(self): @@ -82,6 +94,8 @@ class TestPostmasterProcess(unittest.TestCase): self.assertIsNone(proc.wait_for_user_backends_to_close()) @patch('subprocess.Popen') + @patch('os.setsid', Mock(), create=True) + @patch('multiprocessing.Process', MockProcess) @patch.object(PostmasterProcess, 'from_pid') @patch.object(PostmasterProcess, '_from_pidfile') def test_start(self, mock_frompidfile, mock_frompid, mock_popen): diff --git a/tests/test_watchdog.py b/tests/test_watchdog.py index 493e148d..e70213c5 100644 --- a/tests/test_watchdog.py +++ b/tests/test_watchdog.py @@ -2,6 +2,7 @@ import ctypes import patroni.watchdog.linux as linuxwd import sys import unittest +import os from mock import patch, Mock, PropertyMock from patroni.watchdog import Watchdog, WatchdogError @@ -61,6 +62,7 @@ def mock_close(fd): mock_devices[fd].open = False +@unittest.skipIf(os.name == 'nt', "Windows not supported") @patch('os.open', mock_open) @patch('os.write', mock_write) @patch('os.close', mock_close) @@ -174,6 +176,7 @@ class TestNullWatchdog(unittest.TestCase): self.assertIsInstance(NullWatchdog.from_config({}), NullWatchdog) +@unittest.skipIf(os.name == 'nt', "Windows not supported") class TestLinuxWatchdogDevice(unittest.TestCase): def setUp(self):