From 44a20f12a4275ad0a3706c4740c6b11b4bd33949 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Mon, 14 Sep 2015 16:32:45 +0200 Subject: [PATCH 1/2] version field is znode is just version, not mzxid --- patroni/zookeeper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patroni/zookeeper.py b/patroni/zookeeper.py index 708cbafa..9e4ec8d8 100644 --- a/patroni/zookeeper.py +++ b/patroni/zookeeper.py @@ -115,7 +115,7 @@ class ZooKeeper(AbstractDCS): @staticmethod def member(name, value, znode): conn_url, api_url = parse_connection_string(value) - return Member(znode.mzxid, name, conn_url, api_url, None, None) + return Member(znode.version, name, conn_url, api_url, None, None) def get_children(self, key, watch=None): try: @@ -153,7 +153,7 @@ class ZooKeeper(AbstractDCS): if leader: member = Member(-1, leader[0], None, None, None, None) member = ([m for m in members if m.name == leader[0]] or [member])[0] - leader = Leader(leader[1].mzxid, None, None, member) + leader = Leader(leader[1].version, None, None, member) self.fetch_cluster = member.index == -1 # get last leader operation @@ -231,7 +231,7 @@ class ZooKeeper(AbstractDCS): def _cancel_initialization(self): node = self.get_node(self.initialize_path) if node and node[0] == self._name: - self.client.delete(self.initialize_path, version=node[1].mzxid) + self.client.delete(self.initialize_path, version=node[1].version) def cancel_initialization(self): try: From 90cfcf0c14d54163b249bf223f683445dcc163c2 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Mon, 14 Sep 2015 17:14:39 +0200 Subject: [PATCH 2/2] Make zookeeper module compatible with python3 --- patroni/zookeeper.py | 8 +++++--- tests/test_etcd.py | 2 +- tests/test_zookeeper.py | 29 ++++++++++++++++++++++------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/patroni/zookeeper.py b/patroni/zookeeper.py index 9e4ec8d8..eae810ed 100644 --- a/patroni/zookeeper.py +++ b/patroni/zookeeper.py @@ -108,7 +108,8 @@ class ZooKeeper(AbstractDCS): def get_node(self, key, watch=None): try: - return self.client.get(key, watch) + ret = self.client.get(key, watch) + return (ret[0].decode('utf-8'), ret[1]) except NoNodeError: return None @@ -176,7 +177,7 @@ class ZooKeeper(AbstractDCS): def _create(self, path, value, **kwargs): try: - self.client.retry(self.client.create, path, value, **kwargs) + self.client.retry(self.client.create, path, value.encode('utf-8'), **kwargs) return True except: return False @@ -193,6 +194,7 @@ class ZooKeeper(AbstractDCS): if self.cluster and any(m.name == self._name for m in self.cluster.members): return True path = self.member_path + connection_string = connection_string.encode('utf-8') try: self.client.retry(self.client.create, path, connection_string, makepath=True, ephemeral=True) return True @@ -209,7 +211,7 @@ class ZooKeeper(AbstractDCS): return self.attempt_to_acquire_leader() def update_leader(self, state_handler): - last_operation = state_handler.last_operation() + last_operation = state_handler.last_operation().encode('utf-8') if last_operation != self.last_leader_operation: self.last_leader_operation = last_operation path = self.leader_optime_path diff --git a/tests/test_etcd.py b/tests/test_etcd.py index 66261eeb..10a602db 100644 --- a/tests/test_etcd.py +++ b/tests/test_etcd.py @@ -44,7 +44,7 @@ class MockPostgresql: name = '' def last_operation(self): - return 0 + return '0' def requests_get(url, **kwargs): diff --git a/tests/test_zookeeper.py b/tests/test_zookeeper.py index 4b31bad2..b9defcb3 100644 --- a/tests/test_zookeeper.py +++ b/tests/test_zookeeper.py @@ -1,5 +1,6 @@ import patroni.zookeeper import requests +import six import unittest from patroni.dcs import Leader @@ -56,41 +57,55 @@ class MockKazooClient: func(*args, **kwargs) def get(self, path, watch=None): + if not isinstance(path, six.string_types): + raise TypeError("Invalid type for 'path' (string expected)") if path == '/no_node': raise NoNodeError elif '/members/' in path: return ( - 'postgres://repuser:rep-pass@localhost:5434/postgres?application_name=http://127.0.0.1:8009/patroni', + b'postgres://repuser:rep-pass@localhost:5434/postgres?application_name=http://127.0.0.1:8009/patroni', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) ) elif path.endswith('/optime/leader'): - return '1' + return (b'1', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) elif path.endswith('/leader'): if self.leader: - return ('foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0)) - return ('foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0)) + return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) elif path.endswith('/initialize'): - return ('foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) def get_children(self, path, watch=None, include_data=False): + if not isinstance(path, six.string_types): + raise TypeError("Invalid type for 'path' (string expected)") if path == '/no_node': raise NoNodeError elif path in ['/service/bla/', '/service/test/']: return ['initialize', 'leader', 'members', 'optime'] return ['foo', 'bar', 'buzz'] - def create(self, path, value="", acl=None, ephemeral=False, sequence=False, makepath=False): + def create(self, path, value=b"", acl=None, ephemeral=False, sequence=False, makepath=False): + if not isinstance(path, six.string_types): + raise TypeError("Invalid type for 'path' (string expected)") + if not isinstance(value, (six.binary_type,)): + raise TypeError("Invalid type for 'value' (must be a byte string)") if path.endswith('/initialize') or path == '/service/test/optime/leader': raise Exception - elif value == 'retry' or (value == 'exists' and self.exists): + elif value == b'retry' or (value == b'exists' and self.exists): raise NodeExistsError def set(self, path, value, version=-1): + if not isinstance(path, six.string_types): + raise TypeError("Invalid type for 'path' (string expected)") + if not isinstance(value, (six.binary_type,)): + raise TypeError("Invalid type for 'value' (must be a byte string)") if path == '/service/bla/optime/leader': raise Exception raise NoNodeError def delete(self, path, version=-1, recursive=False): + if not isinstance(path, six.string_types): + raise TypeError("Invalid type for 'path' (string expected)") self.exists = False if path == '/service/test/leader': if self.leader: