Skip to content

Commit b2030be

Browse files
ivandaschisapego
authored andcommitted
IGNITE-14240 Re-factor tests
Handle authentication error. Fix infinite recursion on failed connection on handshake. Skip affinity test if server doesn't support protocol. Remove travis. This closes #19
1 parent 672a767 commit b2030be

32 files changed

Lines changed: 472 additions & 494 deletions

.travis.yml

Lines changed: 0 additions & 48 deletions
This file was deleted.

pyignite/connection/connection.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
from pyignite.constants import *
3636
from pyignite.exceptions import (
37-
HandshakeError, ParameterError, SocketError, connection_errors,
37+
HandshakeError, ParameterError, SocketError, connection_errors, AuthenticationError,
3838
)
3939
from pyignite.datatypes import Byte, Int, Short, String, UUIDObject
4040
from pyignite.datatypes.internal import Struct
@@ -43,6 +43,8 @@
4343
from .ssl import wrap
4444
from ..stream import BinaryStream, READ_BACKWARD
4545

46+
CLIENT_STATUS_AUTH_FAILURE = 2000
47+
4648

4749
class Connection:
4850
"""
@@ -180,7 +182,7 @@ def read_response(self) -> Union[dict, OrderedDict]:
180182
('length', Int),
181183
('op_code', Byte),
182184
])
183-
with BinaryStream(self, self.recv()) as stream:
185+
with BinaryStream(self, self.recv(reconnect=False)) as stream:
184186
start_class = response_start.parse(stream)
185187
start = stream.read_ctype(start_class, direction=READ_BACKWARD)
186188
data = response_start.to_python(start)
@@ -191,6 +193,7 @@ def read_response(self) -> Union[dict, OrderedDict]:
191193
('version_minor', Short),
192194
('version_patch', Short),
193195
('message', String),
196+
('client_status', Int)
194197
])
195198
elif self.get_protocol_version() >= (1, 4, 0):
196199
response_end = Struct([
@@ -267,7 +270,7 @@ def _connect_version(
267270

268271
with BinaryStream(self) as stream:
269272
hs_request.from_python(stream)
270-
self.send(stream.getbuffer())
273+
self.send(stream.getbuffer(), reconnect=False)
271274

272275
hs_response = self.read_response()
273276
if hs_response['op_code'] == 0:
@@ -291,6 +294,8 @@ def _connect_version(
291294
client_patch=protocol_version[2],
292295
**hs_response
293296
)
297+
elif hs_response['client_status'] == CLIENT_STATUS_AUTH_FAILURE:
298+
raise AuthenticationError(error_text)
294299
raise HandshakeError((
295300
hs_response['version_major'],
296301
hs_response['version_minor'],
@@ -313,12 +318,13 @@ def reconnect(self):
313318
except connection_errors:
314319
pass
315320

316-
def send(self, data: Union[bytes, bytearray, memoryview], flags=None):
321+
def send(self, data: Union[bytes, bytearray, memoryview], flags=None, reconnect=True):
317322
"""
318323
Send data down the socket.
319324
320325
:param data: bytes to send,
321326
:param flags: (optional) OS-specific flags.
327+
:param reconnect: (optional) reconnect on failure, default True.
322328
"""
323329
if self.closed:
324330
raise SocketError('Attempt to use closed connection.')
@@ -334,7 +340,13 @@ def send(self, data: Union[bytes, bytearray, memoryview], flags=None):
334340
self.reconnect()
335341
raise
336342

337-
def recv(self, flags=None) -> bytearray:
343+
def recv(self, flags=None, reconnect=True) -> bytearray:
344+
"""
345+
Receive data from the socket.
346+
347+
:param flags: (optional) OS-specific flags.
348+
:param reconnect: (optional) reconnect on failure, default True.
349+
"""
338350
def _recv(buffer, num_bytes):
339351
bytes_to_receive = num_bytes
340352
while bytes_to_receive > 0:
@@ -344,7 +356,8 @@ def _recv(buffer, num_bytes):
344356
raise SocketError('Connection broken.')
345357
except connection_errors:
346358
self.failed = True
347-
self.reconnect()
359+
if reconnect:
360+
self.reconnect()
348361
raise
349362

350363
buffer = buffer[bytes_rcvd:]

pyignite/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
PROTOCOL_STRING_ENCODING = 'utf-8'
5050
PROTOCOL_CHAR_ENCODING = 'utf-16le'
5151

52-
SSL_DEFAULT_VERSION = ssl.PROTOCOL_TLSv1_1
52+
SSL_DEFAULT_VERSION = ssl.PROTOCOL_TLSv1_2
5353
SSL_DEFAULT_CIPHERS = ssl._DEFAULT_CIPHERS
5454

5555
FNV1_OFFSET_BASIS = 0x811c9dc5

pyignite/exceptions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ class ParseError(Exception):
2525
pass
2626

2727

28+
class AuthenticationError(Exception):
29+
"""
30+
This exception is raised on authentication failure.
31+
"""
32+
33+
def __init__(self, message: str):
34+
self.message = message
35+
36+
2837
class HandshakeError(SocketError):
2938
"""
3039
This exception is raised on Ignite binary protocol handshake failure,

requirements/install.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# these pip packages are necessary for the pyignite to run
22

3-
attrs==18.1.0
3+
attrs==20.3.0

requirements/setup.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# additional package for integrating pytest in setuptools
22

3-
pytest-runner==4.2
3+
pytest-runner==5.3.0

requirements/tests.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# these packages are used for testing
22

3-
pytest==3.6.1
4-
pytest-cov==2.5.1
5-
teamcity-messages==1.21
6-
psutil==5.6.5
3+
pytest==6.2.2
4+
pytest-cov==2.11.1
5+
teamcity-messages==1.28
6+
psutil==5.8.0
77
jinja2==2.11.3

tests/affinity/conftest.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import pytest
17+
18+
from pyignite import Client
19+
from pyignite.api import cache_create, cache_destroy
20+
from tests.util import start_ignite_gen
21+
22+
23+
@pytest.fixture(scope='module', autouse=True)
24+
def server1():
25+
yield from start_ignite_gen(1)
26+
27+
28+
@pytest.fixture(scope='module', autouse=True)
29+
def server2():
30+
yield from start_ignite_gen(2)
31+
32+
33+
@pytest.fixture(scope='module', autouse=True)
34+
def server3():
35+
yield from start_ignite_gen(3)
36+
37+
38+
@pytest.fixture
39+
def client():
40+
client = Client(partition_aware=True)
41+
42+
client.connect([('127.0.0.1', 10800 + i) for i in range(1, 4)])
43+
44+
yield client
45+
46+
client.close()
47+
48+
49+
@pytest.fixture
50+
def client_not_connected():
51+
client = Client(partition_aware=True)
52+
yield client
53+
client.close()
54+
55+
56+
@pytest.fixture
57+
def cache(connected_client):
58+
cache_name = 'my_bucket'
59+
conn = connected_client.random_node
60+
61+
cache_create(conn, cache_name)
62+
yield cache_name
63+
cache_destroy(conn, cache_name)
64+
65+
66+
@pytest.fixture(scope='module', autouse=True)
67+
def skip_if_no_affinity(request, server1):
68+
client = Client(partition_aware=True)
69+
client.connect('127.0.0.1', 10801)
70+
71+
if not client.partition_awareness_supported_by_protocol:
72+
pytest.skip(f'skipped {request.node.name}, partition awareness is not supported.')
Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@
2727
from pyignite.datatypes.prop_codes import *
2828

2929

30-
def test_get_node_partitions(client_partition_aware):
30+
def test_get_node_partitions(client):
31+
conn = client.random_node
3132

32-
conn = client_partition_aware.random_node
33-
34-
cache_1 = client_partition_aware.get_or_create_cache('test_cache_1')
35-
cache_2 = client_partition_aware.get_or_create_cache({
33+
cache_1 = client.get_or_create_cache('test_cache_1')
34+
cache_2 = client.get_or_create_cache({
3635
PROP_NAME: 'test_cache_2',
3736
PROP_CACHE_KEY_CONFIGURATION: [
3837
{
@@ -41,9 +40,9 @@ def test_get_node_partitions(client_partition_aware):
4140
}
4241
],
4342
})
44-
cache_3 = client_partition_aware.get_or_create_cache('test_cache_3')
45-
cache_4 = client_partition_aware.get_or_create_cache('test_cache_4')
46-
cache_5 = client_partition_aware.get_or_create_cache('test_cache_5')
43+
client.get_or_create_cache('test_cache_3')
44+
client.get_or_create_cache('test_cache_4')
45+
client.get_or_create_cache('test_cache_5')
4746

4847
result = cache_get_node_partitions(
4948
conn,
@@ -115,9 +114,8 @@ def test_get_node_partitions(client_partition_aware):
115114
116115
],
117116
)
118-
def test_affinity(client_partition_aware, key, key_hint):
119-
120-
cache_1 = client_partition_aware.get_or_create_cache({
117+
def test_affinity(client, key, key_hint):
118+
cache_1 = client.get_or_create_cache({
121119
PROP_NAME: 'test_cache_1',
122120
PROP_CACHE_MODE: CacheMode.PARTITIONED,
123121
})
@@ -126,7 +124,7 @@ def test_affinity(client_partition_aware, key, key_hint):
126124

127125
best_node = cache_1.get_best_node(key, key_hint=key_hint)
128126

129-
for node in filter(lambda n: n.alive, client_partition_aware._nodes):
127+
for node in filter(lambda n: n.alive, client._nodes):
130128
result = cache_local_peek(
131129
node, cache_1.cache_id, key, key_hint=key_hint,
132130
)
@@ -142,9 +140,8 @@ def test_affinity(client_partition_aware, key, key_hint):
142140
cache_1.destroy()
143141

144142

145-
def test_affinity_for_generic_object(client_partition_aware):
146-
147-
cache_1 = client_partition_aware.get_or_create_cache({
143+
def test_affinity_for_generic_object(client):
144+
cache_1 = client.get_or_create_cache({
148145
PROP_NAME: 'test_cache_1',
149146
PROP_CACHE_MODE: CacheMode.PARTITIONED,
150147
})
@@ -166,7 +163,7 @@ class KeyClass(
166163

167164
best_node = cache_1.get_best_node(key, key_hint=BinaryObject)
168165

169-
for node in filter(lambda n: n.alive, client_partition_aware._nodes):
166+
for node in filter(lambda n: n.alive, client._nodes):
170167
result = cache_local_peek(
171168
node, cache_1.cache_id, key, key_hint=BinaryObject,
172169
)
@@ -182,16 +179,8 @@ class KeyClass(
182179
cache_1.destroy()
183180

184181

185-
def test_affinity_for_generic_object_without_type_hints(client_partition_aware):
186-
187-
if not client_partition_aware.partition_awareness_supported_by_protocol:
188-
pytest.skip(
189-
'Best effort affinity is not supported by the protocol {}.'.format(
190-
client_partition_aware.protocol_version
191-
)
192-
)
193-
194-
cache_1 = client_partition_aware.get_or_create_cache({
182+
def test_affinity_for_generic_object_without_type_hints(client):
183+
cache_1 = client.get_or_create_cache({
195184
PROP_NAME: 'test_cache_1',
196185
PROP_CACHE_MODE: CacheMode.PARTITIONED,
197186
})
@@ -213,7 +202,7 @@ class KeyClass(
213202

214203
best_node = cache_1.get_best_node(key)
215204

216-
for node in filter(lambda n: n.alive, client_partition_aware._nodes):
205+
for node in filter(lambda n: n.alive, client._nodes):
217206
result = cache_local_peek(
218207
node, cache_1.cache_id, key
219208
)

0 commit comments

Comments
 (0)