Merge "Add TLS support in etcd3 and etcd3gw drivers"

This commit is contained in:
Zuul 2020-04-01 18:27:03 +00:00 committed by Gerrit Code Review
commit 1880be2c00
7 changed files with 168 additions and 7 deletions

View File

@ -0,0 +1,6 @@
---
features:
- |
The etcd3 and etcd3gw drivers now support TLS, by adding the ability to
specify ca_cert, cert_key and cert_cert files. For the etcd3gw driver,
this is controlled by specifying "etcd3+https" in the coordination URL.

View File

@ -30,6 +30,7 @@ tooz.backends =
etcd = tooz.drivers.etcd:EtcdDriver etcd = tooz.drivers.etcd:EtcdDriver
etcd3 = tooz.drivers.etcd3:Etcd3Driver etcd3 = tooz.drivers.etcd3:Etcd3Driver
etcd3+http = tooz.drivers.etcd3gw:Etcd3Driver etcd3+http = tooz.drivers.etcd3gw:Etcd3Driver
etcd3+https = tooz.drivers.etcd3gw:Etcd3Driver
kazoo = tooz.drivers.zookeeper:KazooDriver kazoo = tooz.drivers.zookeeper:KazooDriver
zake = tooz.drivers.zake:ZakeDriver zake = tooz.drivers.zake:ZakeDriver
memcached = tooz.drivers.memcached:MemcachedDriver memcached = tooz.drivers.memcached:MemcachedDriver
@ -47,7 +48,7 @@ consul =
etcd = etcd =
requests>=2.10.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0
etcd3 = etcd3 =
etcd3>=0.6.2 # Apache-2.0 etcd3>=0.12.0 # Apache-2.0
grpcio>=1.18.0 grpcio>=1.18.0
etcd3gw = etcd3gw =
etcd3gw>=0.1.0 # Apache-2.0 etcd3gw>=0.1.0 # Apache-2.0

View File

@ -9,3 +9,4 @@ coverage>=3.6 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD fixtures>=3.0.0 # Apache-2.0/BSD
pifpaf>=0.10.0 # Apache-2.0 pifpaf>=0.10.0 # Apache-2.0
stestr>=2.0.0 stestr>=2.0.0
ddt>=1.2.1 # MIT

View File

@ -126,7 +126,9 @@ class Etcd3Driver(coordination.CoordinationDriverCachedRunWatchers,
================== ======= ================== =======
Name Default Name Default
================== ======= ================== =======
protocol http ca_cert None
cert_key None
cert_cert None
timeout 30 timeout 30
lock_timeout 30 lock_timeout 30
membership_timeout 30 membership_timeout 30
@ -147,8 +149,16 @@ class Etcd3Driver(coordination.CoordinationDriverCachedRunWatchers,
host = parsed_url.hostname or self.DEFAULT_HOST host = parsed_url.hostname or self.DEFAULT_HOST
port = parsed_url.port or self.DEFAULT_PORT port = parsed_url.port or self.DEFAULT_PORT
options = utils.collapse(options) options = utils.collapse(options)
ca_cert = options.get('ca_cert')
cert_key = options.get('cert_key')
cert_cert = options.get('cert_cert')
timeout = int(options.get('timeout', self.DEFAULT_TIMEOUT)) timeout = int(options.get('timeout', self.DEFAULT_TIMEOUT))
self.client = etcd3.client(host=host, port=port, timeout=timeout) self.client = etcd3.client(host=host,
port=port,
ca_cert=ca_cert,
cert_key=cert_key,
cert_cert=cert_cert,
timeout=timeout)
self.lock_timeout = int(options.get('lock_timeout', timeout)) self.lock_timeout = int(options.get('lock_timeout', timeout))
self.membership_timeout = int(options.get( self.membership_timeout = int(options.get(
'membership_timeout', timeout)) 'membership_timeout', timeout))

View File

@ -169,15 +169,18 @@ class Etcd3Driver(coordination.CoordinationDriverWithExecutor):
The Etcd driver connection URI should look like:: The Etcd driver connection URI should look like::
etcd3+http://[HOST[:PORT]][?OPTION1=VALUE1[&OPTION2=VALUE2[&...]]] etcd3+PROTOCOL://[HOST[:PORT]][?OPTION1=VALUE1[&OPTION2=VALUE2[&...]]]
If not specified, HOST defaults to localhost and PORT defaults to 2379. The PROTOCOL can be http or https. If not specified, HOST defaults to
localhost and PORT defaults to 2379.
Available options are: Available options are:
================== ======= ================== =======
Name Default Name Default
================== ======= ================== =======
protocol http ca_cert None
cert_key None
cert_cert None
timeout 30 timeout 30
lock_timeout 30 lock_timeout 30
membership_timeout 30 membership_timeout 30
@ -197,11 +200,21 @@ class Etcd3Driver(coordination.CoordinationDriverWithExecutor):
def __init__(self, member_id, parsed_url, options): def __init__(self, member_id, parsed_url, options):
super(Etcd3Driver, self).__init__(member_id, parsed_url, options) super(Etcd3Driver, self).__init__(member_id, parsed_url, options)
protocol = 'https' if parsed_url.scheme.endswith('https') else 'http'
host = parsed_url.hostname or self.DEFAULT_HOST host = parsed_url.hostname or self.DEFAULT_HOST
port = parsed_url.port or self.DEFAULT_PORT port = parsed_url.port or self.DEFAULT_PORT
options = utils.collapse(options) options = utils.collapse(options)
ca_cert = options.get('ca_cert')
cert_key = options.get('cert_key')
cert_cert = options.get('cert_cert')
timeout = int(options.get('timeout', self.DEFAULT_TIMEOUT)) timeout = int(options.get('timeout', self.DEFAULT_TIMEOUT))
self.client = etcd3gw.client(host=host, port=port, timeout=timeout) self.client = etcd3gw.client(host=host,
port=port,
protocol=protocol,
ca_cert=ca_cert,
cert_key=cert_key,
cert_cert=cert_cert,
timeout=timeout)
self.lock_timeout = int(options.get('lock_timeout', timeout)) self.lock_timeout = int(options.get('lock_timeout', timeout))
self.membership_timeout = int(options.get( self.membership_timeout = int(options.get(
'membership_timeout', timeout)) 'membership_timeout', timeout))

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
#
# Copyright 2020 Red Hat, Inc.
#
# 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 ddt
from testtools import testcase
from unittest import mock
import tooz.coordination
import tooz.drivers.etcd3 as etcd3_driver
import tooz.tests
@ddt.ddt
class TestEtcd3(testcase.TestCase):
FAKE_MEMBER_ID = tooz.tests.get_random_uuid()
@ddt.data({'coord_url': 'etcd3://',
'host': etcd3_driver.Etcd3Driver.DEFAULT_HOST,
'port': etcd3_driver.Etcd3Driver.DEFAULT_PORT,
'ca_cert': None,
'cert_key': None,
'cert_cert': None,
'timeout': etcd3_driver.Etcd3Driver.DEFAULT_TIMEOUT},
{'coord_url': ('etcd3://my_host:666?ca_cert=/my/ca_cert&'
'cert_key=/my/cert_key&cert_cert=/my/cert_cert&'
'timeout=42'),
'host': 'my_host',
'port': 666,
'ca_cert': '/my/ca_cert',
'cert_key': '/my/cert_key',
'cert_cert': '/my/cert_cert',
'timeout': 42})
@ddt.unpack
@mock.patch('etcd3.client')
def test_etcd3_client_init(self,
mock_etcd3_client,
coord_url,
host,
port,
ca_cert,
cert_key,
cert_cert,
timeout):
tooz.coordination.get_coordinator(coord_url, self.FAKE_MEMBER_ID)
mock_etcd3_client.assert_called_with(host=host,
port=port,
ca_cert=ca_cert,
cert_key=cert_key,
cert_cert=cert_cert,
timeout=timeout)

View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
#
# Copyright 2020 Red Hat, Inc.
#
# 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 ddt
from testtools import testcase
from unittest import mock
import tooz.coordination
import tooz.drivers.etcd3gw as etcd3gw_driver
import tooz.tests
@ddt.ddt
class TestEtcd3Gw(testcase.TestCase):
FAKE_MEMBER_ID = tooz.tests.get_random_uuid()
@ddt.data({'coord_url': 'etcd3+http://',
'protocol': 'http',
'host': etcd3gw_driver.Etcd3Driver.DEFAULT_HOST,
'port': etcd3gw_driver.Etcd3Driver.DEFAULT_PORT,
'ca_cert': None,
'cert_key': None,
'cert_cert': None,
'timeout': etcd3gw_driver.Etcd3Driver.DEFAULT_TIMEOUT},
{'coord_url': ('etcd3+https://my_host:666?ca_cert=/my/ca_cert&'
'cert_key=/my/cert_key&cert_cert=/my/cert_cert&'
'timeout=42'),
'protocol': 'https',
'host': 'my_host',
'port': 666,
'ca_cert': '/my/ca_cert',
'cert_key': '/my/cert_key',
'cert_cert': '/my/cert_cert',
'timeout': 42})
@ddt.unpack
@mock.patch('etcd3gw.client')
def test_etcd3gw_client_init(self,
mock_etcd3gw_client,
coord_url,
protocol,
host,
port,
ca_cert,
cert_key,
cert_cert,
timeout):
tooz.coordination.get_coordinator(coord_url, self.FAKE_MEMBER_ID)
mock_etcd3gw_client.assert_called_with(host=host,
port=port,
protocol=protocol,
ca_cert=ca_cert,
cert_key=cert_key,
cert_cert=cert_cert,
timeout=timeout)