Skip to content

Commit c34cb62

Browse files
api: allow to require protocol version and features
Allow to set required_protocol_version and required_protocol_features on connection initialization to ensure that a Tarantool server provides expected features. The approach is similar to go-tarantool [1]. We do not check client protocol version and features, similar to the core Tarantool [1, 2]. 1. tarantool/go-tarantool#226 2. tarantool/tarantool#7953 Closes #267
1 parent 80c12dc commit c34cb62

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

Diff for: tarantool/connection.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
IPROTO_FEATURE_TRANSACTIONS,
6666
IPROTO_FEATURE_ERROR_EXTENSION,
6767
IPROTO_FEATURE_WATCHERS,
68+
IPROTO_FEATURE_PAGINATION,
69+
IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
70+
IPROTO_FEATURE_WATCH_ONCE,
6871
IPROTO_CHUNK,
6972
AUTH_TYPE_CHAP_SHA1,
7073
AUTH_TYPE_PAP_SHA256,
@@ -607,7 +610,9 @@ def __init__(self, host, port,
607610
packer_factory=default_packer_factory,
608611
unpacker_factory=default_unpacker_factory,
609612
auth_type=None,
610-
fetch_schema=True):
613+
fetch_schema=True,
614+
required_protocol_version=None,
615+
required_protocol_features=None):
611616
"""
612617
:param host: Server hostname or IP address. Use ``None`` for
613618
Unix sockets.
@@ -776,6 +781,14 @@ def __init__(self, host, port,
776781
:meth:`~tarantool.Connection.space`.
777782
:type fetch_schema: :obj:`bool`, optional
778783
784+
:param required_protocol_version: Minimal protocol version that
785+
should be supported by Tarantool server.
786+
:type required_protocol_version: :obj:`int` or :obj:`None`, optional
787+
788+
:param required_protocol_features: List of protocol features that
789+
should be supported by Tarantool server.
790+
:type required_protocol_version: :obj:`list` or :obj:`None`, optional
791+
779792
:raise: :exc:`~tarantool.error.ConfigurationError`,
780793
:meth:`~tarantool.Connection.connect` exceptions
781794
@@ -830,6 +843,9 @@ def __init__(self, host, port,
830843
IPROTO_FEATURE_TRANSACTIONS: False,
831844
IPROTO_FEATURE_ERROR_EXTENSION: False,
832845
IPROTO_FEATURE_WATCHERS: False,
846+
IPROTO_FEATURE_PAGINATION: False,
847+
IPROTO_FEATURE_SPACE_AND_INDEX_NAMES: False,
848+
IPROTO_FEATURE_WATCH_ONCE: False,
833849
}
834850
self._packer_factory_impl = packer_factory
835851
self._unpacker_factory_impl = unpacker_factory
@@ -838,6 +854,8 @@ def __init__(self, host, port,
838854
self.version_id = None
839855
self.uuid = None
840856
self._salt = None
857+
self.required_protocol_version = required_protocol_version
858+
self.required_protocol_features = required_protocol_features
841859

842860
if connect_now:
843861
self.connect()
@@ -2073,6 +2091,20 @@ def _check_features(self):
20732091
else:
20742092
raise exc
20752093

2094+
if self.required_protocol_version is not None:
2095+
if server_protocol_version is None or \
2096+
server_protocol_version < self.required_protocol_version:
2097+
raise ConfigurationError(f'Server protocol version is {server_protocol_version}, '
2098+
f'protocol version {self.required_protocol_version} '
2099+
'is required')
2100+
2101+
if self.required_protocol_features is not None:
2102+
failed_features = [val for val in self.required_protocol_features
2103+
if val not in server_features]
2104+
if len(failed_features) > 0:
2105+
str_features = ', '.join([str(v) for v in failed_features])
2106+
raise ConfigurationError(f'Server missing protocol features with id {str_features}')
2107+
20762108
if server_protocol_version is not None:
20772109
self._protocol_version = min(server_protocol_version,
20782110
CONNECTOR_IPROTO_VERSION)

Diff for: test/suites/lib/skip.py

+16
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,19 @@ def skip_or_run_constraints_test(func):
277277

278278
return skip_or_run_test_tarantool(func, '2.10.0',
279279
'does not support schema constraints')
280+
281+
282+
def skip_or_run_iproto_basic_features_test(func):
283+
"""
284+
Decorator to skip or run tests related to iproto ID requests,
285+
protocol version and features.
286+
287+
Tarantool supports iproto ID requests only since 2.10.0 version.
288+
Protocol version is 3 for Tarantool 2.10.0,
289+
IPROTO_FEATURE_STREAMS, IPROTO_FEATURE_TRANSACTIONS
290+
and IPROTO_FEATURE_ERROR_EXTENSION are supported in Tarantool 2.10.0.
291+
See https://github.com/tarantool/tarantool/issues/6253
292+
"""
293+
294+
return skip_or_run_test_tarantool(func, '2.10.0',
295+
'does not support iproto ID and iproto basic features')

Diff for: test/suites/test_protocol.py

+34
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
IPROTO_FEATURE_TRANSACTIONS,
1616
IPROTO_FEATURE_ERROR_EXTENSION,
1717
IPROTO_FEATURE_WATCHERS,
18+
IPROTO_FEATURE_PAGINATION,
19+
IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
20+
IPROTO_FEATURE_WATCH_ONCE,
1821
)
22+
from tarantool.error import NetworkError
1923
from tarantool.utils import greeting_decode, version_id
2024

2125
from .lib.tarantool_server import TarantoolServer
26+
from .lib.skip import skip_or_run_iproto_basic_features_test
2227

2328

2429
class TestSuiteProtocol(unittest.TestCase):
@@ -91,6 +96,35 @@ def test_04_protocol(self):
9196
self.assertEqual(self.con._features[IPROTO_FEATURE_STREAMS], False)
9297
self.assertEqual(self.con._features[IPROTO_FEATURE_TRANSACTIONS], False)
9398
self.assertEqual(self.con._features[IPROTO_FEATURE_WATCHERS], False)
99+
self.assertEqual(self.con._features[IPROTO_FEATURE_PAGINATION], False)
100+
self.assertEqual(self.con._features[IPROTO_FEATURE_SPACE_AND_INDEX_NAMES], False)
101+
self.assertEqual(self.con._features[IPROTO_FEATURE_WATCH_ONCE], False)
102+
103+
@skip_or_run_iproto_basic_features_test
104+
def test_protocol_requirement(self):
105+
try:
106+
c = tarantool.Connection(self.srv.host, self.srv.args['primary'],
107+
required_protocol_version=3,
108+
required_protocol_features=[IPROTO_FEATURE_STREAMS,
109+
IPROTO_FEATURE_TRANSACTIONS,
110+
IPROTO_FEATURE_ERROR_EXTENSION])
111+
c.close()
112+
except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except
113+
self.fail(f'Connection create have raised Exception: {repr(exc)}')
114+
115+
def test_protocol_version_requirement_fail(self):
116+
with self.assertRaisesRegex(NetworkError, # ConfigurationError is wrapped in NetworkError
117+
'protocol version 100500 is required'):
118+
c = tarantool.Connection(self.srv.host, self.srv.args['primary'],
119+
required_protocol_version=100500)
120+
c.close()
121+
122+
def test_protocol_features_requirement_fail(self):
123+
with self.assertRaisesRegex(NetworkError, # ConfigurationError is wrapped in NetworkError
124+
'Server missing protocol features with id 100500, 500100'):
125+
c = tarantool.Connection(self.srv.host, self.srv.args['primary'],
126+
required_protocol_features=[100500, 500100])
127+
c.close()
94128

95129
@classmethod
96130
def tearDownClass(cls):

0 commit comments

Comments
 (0)