Skip to content

Commit e4758ba

Browse files
symatanmolnar
authored andcommitted
ZOOKEEPER-3567: add SSL support for zkpython
This PR is about adding SSL support for zkPython, based on the C-binding. I also fixed the zkpython ant build to work with the current maven top-level build. I also added a new python test case to try to connect to ZooKeeper with SSL. You can test this patch in the following way: ``` # cleanup everything, just to be on the safe side: git clean -xdf # on ubuntu 16.4 make sure you have the following packages installed apt-get install -y libcppunit-dev openssl libssl-dev python-setuptools python2.7 python2.7-dev # make a full build (incl. C-client) mvn clean install -DskipTests -Pfull-build # we only support python2, so e.g. on ubuntu 18.4 you need to switch to python2 update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 # compile and test zkpython cd zookeeper-contrib/zookeeper-contrib-zkpython/ ant compile ant test ``` Author: Mate Szalay-Beko <[email protected]> Reviewers: [email protected] Closes apache#1121 from symat/ZOOKEEPER-3567 and squashes the following commits: a5839cb [Mate Szalay-Beko] Merge remote-tracking branch 'apache/master' into ZOOKEEPER-3567 d25d610 [Mate Szalay-Beko] ZOOKEEPER-3567: fix build issues after top-level ant removal a8869c9 [Mate Szalay-Beko] Merge remote-tracking branch 'apache/master' into HEAD b92f686 [Mate Szalay-Beko] ZOOKEEPER-3567: fix license check issue 0150986 [Mate Szalay-Beko] ZOOKEEPER-3567: removing code duplication: re-use test SSL certificate generator from C-client tests 7d91359 [Mate Szalay-Beko] ZOOKEEPER-3567: add SSL support for zkpython
1 parent 7c1251d commit e4758ba

14 files changed

+217
-48
lines changed

README_packaging.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ yum install openssl openssl-devel
2020
yum install cyrus-sasl-md5 cyrus-sasl-gssapi cyrus-sasl-devel
2121
```
2222

23-
On Ubuntu:
23+
On Ubuntu (in case of 16.4+):
2424

2525
```
26-
apt-get install cppunit
27-
apt-get install python-setuptools
26+
apt-get install libcppunit-dev
27+
apt-get install python-setuptools python2.7-dev
2828
apt-get install openssl libssl-dev
2929
apt-get install libsasl2-modules-gssapi-mit libsasl2-modules libsasl2-dev
3030
```

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,7 @@
855855
<exclude>zookeeper-contrib-fatjar/src/main/resources/mainClasses</exclude>
856856
<exclude>zookeeper-contrib-zkperl/Changes</exclude>
857857
<exclude>zookeeper-contrib-zkperl/MANIFEST</exclude>
858+
<exclude>zookeeper-contrib-zkpython/src/test/zoo.cfg</exclude>
858859
<exclude>zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/*</exclude>
859860
<exclude>src/main/resources/webapp/org/apache/zookeeper/graph/resources/*</exclude>
860861
<exclude>src/main/java/com/nitido/utils/toaster/Toaster.java</exclude>

zookeeper-contrib/build-contrib.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
<property name="lib.dir" location="${zk.root}/zookeeper-server/src/main/resources/lib"/>
3434

35-
<property name="build.dir" location="${zk.root}/build/zookeeper-contrib/zookeeper-contrib-${name}"/>
35+
<property name="build.dir" location="${zk.root}/zookeeper-contrib/zookeeper-contrib-${name}/target"/>
3636
<property name="build.classes" location="${build.dir}/classes"/>
3737
<property name="build.test" location="${build.dir}/test"/>
3838

@@ -47,7 +47,7 @@
4747
<property name="ivy.home" value="${user.home}/.ant" />
4848
<property name="ivy.lib" value="${build.dir}/lib"/>
4949
<property name="ivy.test.lib" value="${build.test}/lib"/>
50-
<property name="ivysettings.xml" value="${zk.root}/ivysettings.xml"/>
50+
<property name="ivysettings.xml" value="${zk.root}/zookeeper-contrib/ivysettings.xml"/>
5151

5252
<!-- to be overridden by sub-projects -->
5353
<target name="check-contrib"/>
@@ -179,7 +179,7 @@
179179
<!-- we can't use id=classpath, because available fails if fileset directory
180180
doesn't exist -->
181181
<classpath>
182-
<pathelement location="${zk.root}/build/classes"/>
182+
<pathelement location="${zk.root}/zookeeper-server/target/classes"/>
183183
</classpath>
184184
</available>
185185
</target>
@@ -190,7 +190,7 @@
190190

191191

192192
<target name="checkMainTestIsAvailable">
193-
<available file="${zk.root}/build/test/classes/org/apache/zookeeper/test/ClientBase.class"
193+
<available file="${zk.root}/zookeeper-server/target/test-classes/org/apache/zookeeper/test/ClientBase.class"
194194
property="mainTestIsCompiled">
195195
</available>
196196
</target>

zookeeper-contrib/ivysettings.xml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<ivysettings>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one or more
5+
contributor license agreements. See the NOTICE file distributed with
6+
this work for additional information regarding copyright ownership.
7+
The ASF licenses this file to You under the Apache License, Version 2.0
8+
(the "License"); you may not use this file except in compliance with
9+
the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-->
19+
20+
<property name="repo.maven.org"
21+
value="https://repo1.maven.org/maven2/" override="false"/>
22+
<property name="repo.jboss.org"
23+
value="https://repository.jboss.org/nexus/content/groups/public/" override="false"/>
24+
<property name="maven2.pattern"
25+
value="[organisation]/[module]/[revision]/[module]-[revision]"/>
26+
<property name="maven2.pattern.ext" value="${maven2.pattern}.[ext]"/>
27+
<include url="${ivy.default.conf.dir}/ivyconf-local.xml"/>
28+
<settings defaultResolver="default"/>
29+
<resolvers>
30+
<ibiblio name="maven2" root="${repo.maven.org}"
31+
pattern="${maven2.pattern.ext}" m2compatible="true"/>
32+
<ibiblio name="jboss-maven2" root="${repo.jboss.org}"
33+
pattern="${maven2.pattern.ext}" m2compatible="true"/>
34+
35+
<chain name="default" dual="true">
36+
<resolver ref="maven2"/>
37+
<resolver ref="jboss-maven2"/>
38+
</chain>
39+
40+
</resolvers>
41+
</ivysettings>

zookeeper-contrib/zookeeper-contrib-zkpython/README

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ DEPENDENCIES:
77

88
This has only been tested against SVN (i.e. 3.2.0 in development) but should work against 3.1.1.
99

10-
You will need the Python development headers installed to build the module - on many package-management systems, these can be found in python-devel.
10+
You will need the Python development headers installed to build the module - on many package-management systems, these can be found in python-devel. (On ubuntu 18.4, install python2.7 and python2.7-dev.)
1111

1212
Python >= 2.6 is required. We have tested against 2.6. We have not tested against 3.x.
1313

14+
E.g. setting up tpyhon and python devel on ubuntu 18.4:
15+
sudo apt-get install python2.7 python2.7-dev
16+
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1
17+
1418
BUILD AND INSTALL:
1519
-------------------
1620

17-
To install, make sure that the C client has been built and that the libraries are installed in /usr/local/lib (or change this directory in setup.py). Then run:
21+
To install, make sure that the C client has been built (use `mvn clean install -DskipTests -Pfull-build` in the root folder of zookeeper) or that the zookeeper C libraries are installed in /usr/local/lib (or change this directory in setup.py). Then run:
1822

1923
ant install
2024

zookeeper-contrib/zookeeper-contrib-zkpython/build.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
<project name="zkpython" default="install">
2121
<import file="../build-contrib.xml"/>
2222
<property name="python.src.dir" value="src/python"/>
23-
<property name="test.build.dir" value="build/test/" />
2423
<property name="test.src.dir" value="src/test"/>
2524
<property name="test.log.dir" value="${build.test}/logs" />
2625
<property name="test.output" value="no" />
26+
<property name="test.output" value="no" />
2727
<property name="test.timeout" value="900000" />
2828

2929
<target name="test"

zookeeper-contrib/zookeeper-contrib-zkpython/src/c/pyzk_docstrings.h

+27
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,33 @@ static const char pyzk_init_doc[] =
568568
" an integer handle. If it fails to create \n"
569569
" a new zhandle the function throws an exception.\n";
570570

571+
static const char pyzk_init_ssl_doc[] =
572+
"This method creates a new handle and a zookeeper SSL session that corresponds\n"
573+
"to that handle. Session establishment is asynchronous, meaning that the\n"
574+
"session should not be considered established until (and unless) an\n"
575+
"event of state CONNECTED_STATE is received.\n"
576+
"PARAMETERS:\n"
577+
" host: comma separated host:port pairs, each corresponding to a zk\n"
578+
" server. e.g. '127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002'\n"
579+
" cert_str: SSL certificate string e.g. 'server.cert,client.cert,client-priv-key.pom,passwd'\n"
580+
"\n"
581+
"(subsequent parameters are optional)\n"
582+
" fn: the global watcher callback function. When notifications are\n"
583+
" triggered this function will be invoked.\n"
584+
" recv_timeout: \n"
585+
" (clientid, passwd)\n"
586+
" clientid the id of a previously established session that this\n"
587+
" client will be reconnecting to. Clients can access the session id of an established, valid,\n"
588+
" connection by calling zoo_client_id. If\n"
589+
" the specified clientid has expired, or if the clientid is invalid for \n"
590+
" any reason, the returned zhandle_t will be invalid -- the zhandle_t \n"
591+
" state will indicate the reason for failure (typically\n"
592+
" EXPIRED_SESSION_STATE).\n"
593+
"\n"
594+
"RETURNS:\n"
595+
" an integer handle. If it fails to create \n"
596+
" a new zhandle the function throws an exception.\n";
597+
571598
static const char pyzk_get_doc[] =
572599
" gets the data associated with a node synchronously.\n"
573600
"\n"

zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c

+33-11
Original file line numberDiff line numberDiff line change
@@ -599,12 +599,13 @@ void acl_completion_dispatch(int rc, struct ACL_vector *acl, struct Stat *stat,
599599
/* ZOOKEEPER API IMPLEMENTATION */
600600
/* -------------------------------------------------------------------------- */
601601

602-
static PyObject *pyzookeeper_init(PyObject *self, PyObject *args)
603-
{
602+
603+
static PyObject *pyzookeeper_init_optional_ssl(PyObject *self, PyObject *args, int ssl) {
604604
const char *host;
605+
const char *cert_str;
605606
PyObject *watcherfn = Py_None;
607+
zhandle_t *zh = NULL;
606608
int recv_timeout = 10000;
607-
// int clientid = -1;
608609
clientid_t cid;
609610
cid.client_id = -1;
610611
const char *passwd;
@@ -621,9 +622,14 @@ static PyObject *pyzookeeper_init(PyObject *self, PyObject *args)
621622
return NULL;
622623
}
623624

624-
if (!PyArg_ParseTuple(args, "s|Oi(Ls)", &host, &watcherfn, &recv_timeout, &cid.client_id, &passwd))
625-
return NULL;
626-
625+
if (ssl) {
626+
if (!PyArg_ParseTuple(args, "ss|Oi(Ls)", &host, &cert_str, &watcherfn, &recv_timeout, &cid.client_id, &passwd))
627+
return NULL;
628+
} else {
629+
if (!PyArg_ParseTuple(args, "s|Oi(Ls)", &host, &watcherfn, &recv_timeout, &cid.client_id, &passwd))
630+
return NULL;
631+
}
632+
627633
if (cid.client_id != -1) {
628634
strncpy(cid.passwd, passwd, 16*sizeof(char));
629635
}
@@ -635,21 +641,36 @@ static PyObject *pyzookeeper_init(PyObject *self, PyObject *args)
635641
}
636642
}
637643
watchers[handle] = pyw;
638-
zhandle_t *zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL,
639-
recv_timeout, cid.client_id == -1 ? 0 : &cid,
640-
pyw,
641-
0 );
644+
645+
if (ssl) {
646+
zh = zookeeper_init_ssl( host, cert_str, watcherfn != Py_None ? watcher_dispatch : NULL,
647+
recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 );
648+
} else {
649+
zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL,
650+
recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 );
651+
}
642652

643653
if (zh == NULL)
644654
{
645-
PyErr_SetString( ZooKeeperException, "Could not internally obtain zookeeper handle" );
655+
PyErr_SetString( ZooKeeperException, "Could not internally obtain SSL zookeeper handle" );
646656
return NULL;
647657
}
648658

649659
zhandles[handle] = zh;
650660
return Py_BuildValue( "i", handle);
651661
}
652662

663+
static PyObject *pyzookeeper_init(PyObject *self, PyObject *args)
664+
{
665+
return pyzookeeper_init_optional_ssl(self, args, 0);
666+
}
667+
668+
669+
static PyObject *pyzookeeper_init_ssl(PyObject *self, PyObject *args)
670+
{
671+
return pyzookeeper_init_optional_ssl(self, args, 1);
672+
}
673+
653674

654675
/* -------------------------------------------------------------------------- */
655676
/* Asynchronous API implementation */
@@ -1497,6 +1518,7 @@ PyObject *pyzoo_deterministic_conn_order(PyObject *self, PyObject *args)
14971518

14981519
static PyMethodDef ZooKeeperMethods[] = {
14991520
{"init", pyzookeeper_init, METH_VARARGS, pyzk_init_doc },
1521+
{"init_ssl", pyzookeeper_init_ssl, METH_VARARGS, pyzk_init_ssl_doc },
15001522
{"create",pyzoo_create, METH_VARARGS, pyzk_create_doc },
15011523
{"delete",pyzoo_delete, METH_VARARGS, pyzk_delete_doc },
15021524
{"get_children", pyzoo_get_children, METH_VARARGS, pyzk_get_children_doc },

zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
zookeepermodule = Extension("zookeeper",
2222
sources=["src/c/zookeeper.c"],
2323
include_dirs=[zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/include",
24-
zookeeper_basedir + "/build/c",
24+
zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/target/c",
2525
zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/generated"],
2626
libraries=["zookeeper_mt"],
2727
library_dirs=[zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/.libs/",
28-
zookeeper_basedir + "/build/c/.libs/",
28+
zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/target/c/.libs/",
2929
zookeeper_basedir + "/build/test/test-cppunit/.libs",
3030
"/usr/local/lib"
3131
])

zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py

+31
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,37 @@ def connection_watcher(handle, type, state, path):
5858
self.handle,
5959
"/")
6060

61+
62+
def testsslconnection(self):
63+
cv = threading.Condition()
64+
self.connected = False
65+
def connection_watcher(handle, type, state, path):
66+
cv.acquire()
67+
self.connected = True
68+
self.assertEqual(zookeeper.CONNECTED_STATE, state)
69+
self.handle = handle
70+
cv.notify()
71+
cv.release()
72+
73+
cv.acquire()
74+
ret = zookeeper.init_ssl(self.sslhost, self.sslcert, connection_watcher)
75+
cv.wait(15.0)
76+
cv.release()
77+
self.assertEqual(self.connected, True, "SSL Connection timed out to " + self.host)
78+
self.assertEqual(zookeeper.CONNECTED_STATE, zookeeper.state(self.handle))
79+
80+
self.assertEqual(zookeeper.close(self.handle), zookeeper.OK)
81+
# Trying to close the same handle twice is an error, and the C library will segfault on it
82+
# so make sure this is caught at the Python module layer
83+
self.assertRaises(zookeeper.ZooKeeperException,
84+
zookeeper.close,
85+
self.handle)
86+
87+
self.assertRaises(zookeeper.ZooKeeperException,
88+
zookeeper.get,
89+
self.handle,
90+
"/")
91+
6192
def testhandlereuse(self):
6293
"""
6394
Test a) multiple concurrent connections b) reuse of closed handles

zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ else
3030
fi
3131

3232
# Find the build directory containing zookeeper.so
33-
SO_PATH=`find ../../build/ -name "zookeeper.so" | head -1`
33+
SO_PATH=`find ./target/ -name "zookeeper.so" | head -1`
3434
PYTHONPATH=`dirname $SO_PATH`
35-
LIB_PATH=../../build/c/.libs/:../../build/test/test-cppunit/.libs
35+
LIB_PATH=../../zookeeper-client/zookeeper-client-c/target/c/.libs
3636
for test in `ls $1/*_test.py`;
3737
do
3838
echo "Running $test"
39+
echo "Running LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test"
3940
LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test
4041
done

0 commit comments

Comments
 (0)