Add a PostgreSQL driver
Change-Id: Id6a6ef4c1ec11c1f8cf5aa440c4f425213837ffc
This commit is contained in:
parent
b6242610fc
commit
b5f87e4fc5
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
. functions.sh
|
||||||
|
|
||||||
|
function clean_exit() {
|
||||||
|
local error_code="$?"
|
||||||
|
${PGSQL_PATH}/pg_ctl -w -D ${PGSQL_DATA} -o "-p $PGSQL_PORT" stop
|
||||||
|
rm -rf ${PGSQL_DATA}
|
||||||
|
return $error_code
|
||||||
|
}
|
||||||
|
|
||||||
|
trap "clean_exit" EXIT
|
||||||
|
|
||||||
|
# Start PostgreSQL process for tests
|
||||||
|
PGSQL_DATA=`mktemp -d /tmp/tooz-pgsql-XXXXX`
|
||||||
|
PGSQL_PATH=`pg_config --bindir`
|
||||||
|
PGSQL_PORT=9825
|
||||||
|
${PGSQL_PATH}/initdb ${PGSQL_DATA}
|
||||||
|
${PGSQL_PATH}/pg_ctl -w -D ${PGSQL_DATA} -o "-k ${PGSQL_DATA} -p ${PGSQL_PORT}" start
|
||||||
|
# Wait for PostgreSQL to start listening to connections
|
||||||
|
export TOOZ_TEST_PGSQL_URL="postgresql:///?host=${PGSQL_DATA}&port=${PGSQL_PORT}&dbname=template1"
|
||||||
|
|
||||||
|
# Yield execution to venv command
|
||||||
|
$*
|
|
@ -30,6 +30,7 @@ tooz.backends =
|
||||||
memcached = tooz.drivers.memcached:MemcachedDriver
|
memcached = tooz.drivers.memcached:MemcachedDriver
|
||||||
ipc = tooz.drivers.ipc:IPCDriver
|
ipc = tooz.drivers.ipc:IPCDriver
|
||||||
redis = tooz.drivers.redis:RedisDriver
|
redis = tooz.drivers.redis:RedisDriver
|
||||||
|
postgresql = tooz.drivers.pgsql:PostgresDriver
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
|
|
|
@ -9,3 +9,4 @@ testtools>=0.9.32
|
||||||
testscenarios>=0.4
|
testscenarios>=0.4
|
||||||
coverage>=3.6
|
coverage>=3.6
|
||||||
sysv_ipc>=0.6.8
|
sysv_ipc>=0.6.8
|
||||||
|
psycopg2
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright © 2014 eNovance
|
||||||
|
#
|
||||||
|
# Author: Julien Danjou <julien@danjou.info>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
import six
|
||||||
|
|
||||||
|
import tooz
|
||||||
|
from tooz import coordination
|
||||||
|
from tooz import locking
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresLock(locking.Lock):
|
||||||
|
"""A PostgreSQL based lock."""
|
||||||
|
|
||||||
|
def __init__(self, name, connection):
|
||||||
|
super(PostgresLock, self).__init__(name)
|
||||||
|
self._conn = connection
|
||||||
|
h = hashlib.md5()
|
||||||
|
h.update(name)
|
||||||
|
if six.PY2:
|
||||||
|
self.key = list(map(ord, h.digest()[0:2]))
|
||||||
|
else:
|
||||||
|
self.key = h.digest()[0:2]
|
||||||
|
|
||||||
|
def acquire(self, blocking=True):
|
||||||
|
if blocking is not True:
|
||||||
|
raise tooz.NotImplemented(
|
||||||
|
"This driver does not support non blocking locks")
|
||||||
|
with self._conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT pg_advisory_lock(%s, %s);", self.key)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
with self._conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT pg_advisory_unlock(%s, %s);", self.key)
|
||||||
|
return cur.fetchone()[0]
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresDriver(coordination.CoordinationDriver):
|
||||||
|
|
||||||
|
def __init__(self, member_id, parsed_url, options):
|
||||||
|
"""Initialize the PostgreSQL driver."""
|
||||||
|
super(PostgresDriver, self).__init__()
|
||||||
|
self._host = options.get("host", [None])[-1]
|
||||||
|
self._port = parsed_url.port or options.get("port", [None])[-1]
|
||||||
|
self._dbname = parsed_url.path[1:] or options.get("dbname", [None])[-1]
|
||||||
|
self._username = parsed_url.username
|
||||||
|
self._password = parsed_url.password
|
||||||
|
|
||||||
|
def _start(self):
|
||||||
|
self._conn = psycopg2.connect(host=self._host,
|
||||||
|
port=self._port,
|
||||||
|
user=self._username,
|
||||||
|
password=self._password,
|
||||||
|
database=self._dbname)
|
||||||
|
|
||||||
|
def _stop(self):
|
||||||
|
self._conn.close()
|
||||||
|
|
||||||
|
def get_lock(self, name):
|
||||||
|
return PostgresLock(name, self._conn)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def watch_join_group(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unwatch_join_group(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def watch_leave_group(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unwatch_leave_group(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def watch_elected_as_leader(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unwatch_elected_as_leader(group_id, callback):
|
||||||
|
raise tooz.NotImplemented
|
|
@ -13,6 +13,7 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ class TestAPI(testscenarios.TestWithScenarios,
|
||||||
('memcached', {'url': 'memcached://?timeout=5'}),
|
('memcached', {'url': 'memcached://?timeout=5'}),
|
||||||
('ipc', {'url': 'ipc://'}),
|
('ipc', {'url': 'ipc://'}),
|
||||||
('redis', {'url': 'redis://localhost:6379?timeout=5'}),
|
('redis', {'url': 'redis://localhost:6379?timeout=5'}),
|
||||||
|
('postgresql', {'url': os.getenv("TOOZ_TEST_PGSQL_URL")}),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Only certain drivers have the tested support for timeouts that we test
|
# Only certain drivers have the tested support for timeouts that we test
|
||||||
|
@ -61,6 +63,8 @@ class TestAPI(testscenarios.TestWithScenarios,
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestAPI, self).setUp()
|
super(TestAPI, self).setUp()
|
||||||
|
if self.url is None:
|
||||||
|
self.skipTest("No URL set for this driver")
|
||||||
self.group_id = self._get_random_uuid()
|
self.group_id = self._get_random_uuid()
|
||||||
self.member_id = self._get_random_uuid()
|
self.member_id = self._get_random_uuid()
|
||||||
self._coord = tooz.coordination.get_coordinator(self.url,
|
self._coord = tooz.coordination.get_coordinator(self.url,
|
||||||
|
|
13
tox.ini
13
tox.ini
|
@ -69,6 +69,19 @@ deps = {[testenv:py34]deps}
|
||||||
basepython = python3.4
|
basepython = python3.4
|
||||||
commands = {toxinidir}/setup-memcached-env.sh python setup.py testr --slowest --testr-args="{posargs}"
|
commands = {toxinidir}/setup-memcached-env.sh python setup.py testr --slowest --testr-args="{posargs}"
|
||||||
|
|
||||||
|
[testenv:py27-postgresql]
|
||||||
|
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
|
||||||
|
|
||||||
|
[testenv:py33-postgresql]
|
||||||
|
deps = {[testenv:py33]deps}
|
||||||
|
basepython = python3.3
|
||||||
|
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
|
||||||
|
|
||||||
|
[testenv:py34-postgresql]
|
||||||
|
deps = {[testenv:py34]deps}
|
||||||
|
basepython = python3.4
|
||||||
|
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
commands = python setup.py testr --slowest --coverage --testr-args="{posargs}"
|
commands = python setup.py testr --slowest --coverage --testr-args="{posargs}"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue