Skip to content

Commit 8a4e61c

Browse files
committed
[add] TLS support on OSS environments, CI, Unit Tests and Flow Tests kick off for RLTest
1 parent 3644e98 commit 8a4e61c

File tree

14 files changed

+717
-14
lines changed

14 files changed

+717
-14
lines changed

.github/workflows/ci.yml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
name: CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build-ubuntu:
7+
strategy:
8+
matrix:
9+
platform: [ubuntu-latest, ubuntu-16.04]
10+
runs-on: ${{ matrix.platform }}
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Install OpenSSL development libraries
14+
run: sudo apt-get install -y libssl-dev
15+
16+
- name: Setup Python
17+
uses: actions/setup-python@v1
18+
with:
19+
python-version: '3.6'
20+
21+
- name: Cache pip
22+
uses: actions/cache@v1
23+
with:
24+
path: ~/.cache/pip # This path is specific to Ubuntu
25+
# Look to see if there is a cache hit for the corresponding requirements file
26+
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
27+
restore-keys: |
28+
${{ runner.os }}-pip-
29+
${{ runner.os }}-
30+
- name: Install Python dependencies
31+
run: pip install -r requirements.txt
32+
33+
- name: Install Redis Server test dependencies
34+
run: |
35+
git clone git://github.com/antirez/redis.git --branch unstable
36+
cd redis
37+
make BUILD_TLS=yes
38+
./utils/gen-test-certs.sh
39+
./src/redis-server --version
40+
cd ..
41+
- name: Unit Test with pytest
42+
run: |
43+
cd RLTest
44+
pip install pytest
45+
pytest
46+
cd ..
47+
48+
- name: Install RLTest
49+
run: |
50+
pip3 install .
51+
52+
- name: Flow Test OSS TCP
53+
run: |
54+
cd tests
55+
python3 -m RLTest --env oss -v --clear-logs --oss-redis-path ./../redis/src/redis-server
56+
cd ..
57+
58+
- name: Flow Test OSS UNIX SOCKETS
59+
run: |
60+
cd tests
61+
python3 -m RLTest --env oss -v --clear-logs --oss-redis-path ./../redis/src/redis-server
62+
cd ..
63+
64+
- name: Flow Test OSS TCP SLAVES
65+
run: |
66+
cd tests
67+
python3 -m RLTest --env oss -v --unix --clear-logs --oss-redis-path ./../redis/src/redis-server
68+
cd ..
69+
70+
- name: Flow Test OSS-CLUSTER TCP
71+
run: |
72+
cd tests
73+
python3 -m RLTest --env oss-cluster -v --clear-logs --shards-count 3 --oss-redis-path ./../redis/src/redis-server
74+
cd ..
75+
76+
- name: Flow Test OSS TCP with TLS
77+
run: |
78+
cd tests
79+
python3 -m RLTest --env oss -v --clear-logs \
80+
--oss-redis-path ./../redis/src/redis-server \
81+
--tls-cert-file ./../redis/tests/tls/redis.crt \
82+
--tls-key-file ./../redis/tests/tls/redis.key \
83+
--tls-ca-cert-file ./../redis/tests/tls/ca.crt \
84+
--tls
85+
cd ..
86+
87+
- name: Flow Test OSS-CLUSTER with TLS
88+
run: |
89+
cd tests
90+
python3 -m RLTest --env oss-cluster --shards-count 3 -v --clear-logs \
91+
--oss-redis-path ./../redis/src/redis-server \
92+
--tls-cert-file ./../redis/tests/tls/redis.crt \
93+
--tls-key-file ./../redis/tests/tls/redis.key \
94+
--tls-ca-cert-file ./../redis/tests/tls/ca.crt \
95+
--tls
96+
cd ..
97+
98+
- name: Flow Test OSS-CLUSTER with SLAVES and TLS
99+
run: |
100+
cd tests
101+
python3 -m RLTest --env oss-cluster --shards-count 3 --use-slaves -v --clear-logs \
102+
--oss-redis-path ./../redis/src/redis-server \
103+
--tls-cert-file ./../redis/tests/tls/redis.crt \
104+
--tls-key-file ./../redis/tests/tls/redis.key \
105+
--tls-ca-cert-file ./../redis/tests/tls/ca.crt \
106+
--tls
107+
cd ..
108+
109+
- name: Generate coverage report
110+
run: |
111+
cd RLTest
112+
pip install pytest
113+
pip install pytest-cov
114+
pytest --cov=./ --cov-report=xml
115+
116+
- name: Upload coverage to Codecov
117+
uses: codecov/codecov-action@v1
118+
with:
119+
token: ${{ secrets.CODECOV_TOKEN }}
120+
file: ./RLTest/coverage.xml
121+
flags: unittests
122+
name: codecov-umbrella
123+
fail_ci_if_error: true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
106+
# Covers JetBrains PyCharm
107+
.idea

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# RLTest
2+
![CI](https://github.com/RedisLabsModules/RLTest/workflows/CI/badge.svg)
3+
24
Redis Labs Test Framework, allow to run tests on redis and modules on verity of environments.
35

46
Supported Environment: oss, oss-cluster, enterprise, enterprise-cluster
57

68
The framework allow you to write a test without environment specification and then run the test on all supported environment.
79

10+
811
# Install
912
```
1013
$ pip install git+https://github.com/RedisLabsModules/RLTest.git@master

RLTest/__main__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ def do_normal_conn(self, line):
250250
'--collect-only', action='store_true',
251251
help='Collect the tests and exit')
252252

253+
parser.add_argument('--tls', help='Enable TLS Support and disable the non-TLS port completely. TLS connections will be available at the default non-TLS ports.',
254+
default=False, action='store_true')
255+
256+
parser.add_argument(
257+
'--tls-cert-file', default=None, help='/path/to/redis.crt')
258+
259+
parser.add_argument(
260+
'--tls-key-file', default=None, help='/path/to/redis.key')
261+
262+
parser.add_argument(
263+
'--tls-ca-cert-file', default=None, help='/path/to/ca.crt')
253264

254265
class EnvScopeGuard:
255266
def __init__(self, runner):
@@ -354,6 +365,10 @@ def __init__(self):
354365
Defaults.external_addr = self.args.existing_env_addr
355366
Defaults.use_unix = self.args.unix
356367
Defaults.randomize_ports = self.args.randomize_ports
368+
Defaults.use_TLS = self.args.tls
369+
Defaults.tls_cert_file = self.args.tls_cert_file
370+
Defaults.tls_key_file = self.args.tls_key_file
371+
Defaults.tls_ca_cert_file = self.args.tls_ca_cert_file
357372
if Defaults.use_unix and Defaults.use_slaves:
358373
raise Exception('Cannot use unix sockets with slaves')
359374

RLTest/env.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ class Defaults:
107107
re_binary = None
108108
re_libdir = None
109109
use_aof = False
110+
use_TLS = False
111+
tls_cert_file = None
112+
tls_key_file = None
113+
tls_ca_cert_file = None
110114
debugger = None
111115
debug_print = False
112116
debug_pause = False
@@ -135,7 +139,7 @@ def compareEnvs(self, env):
135139

136140
def __init__(self, testName=None, testDescription=None, module=None,
137141
moduleArgs=None, env=None, useSlaves=None, shardsCount=None,
138-
useAof=None, forceTcp=False):
142+
useAof=None, forceTcp=False, useTLS=False, tlsCertFile=None, tlsKeyFile=None, tlsCaCertFile=None ):
139143
self.testName = testName if testName else '%s.%s' % (inspect.getmodule(inspect.currentframe().f_back).__name__, inspect.currentframe().f_back.f_code.co_name)
140144
self.testName = self.testName.replace(' ', '_')
141145

@@ -152,6 +156,10 @@ def __init__(self, testName=None, testDescription=None, module=None,
152156
self.logDir = Defaults.logdir
153157
self.forceTcp = forceTcp
154158
self.debugger = Defaults.debugger
159+
self.useTLS = useTLS if useTLS else Defaults.use_TLS
160+
self.tlsCertFile = tlsCertFile if tlsCertFile else Defaults.tls_cert_file
161+
self.tlsKeyFile = tlsKeyFile if tlsKeyFile else Defaults.tls_key_file
162+
self.tlsCaCertFile = tlsCaCertFile if tlsCaCertFile else Defaults.tls_ca_cert_file
155163

156164
self.assertionFailedSummary = []
157165

@@ -187,7 +195,11 @@ def getEnvByName(self):
187195
'dbDirPath': self.logDir,
188196
'debugger': Defaults.debugger,
189197
'noCatch': Defaults.no_capture_output,
190-
'verbose' : Defaults.verbose
198+
'verbose' : Defaults.verbose,
199+
'useTLS': self.useTLS,
200+
'tlsCertFile': self.tlsCertFile,
201+
'tlsKeyFile' : self.tlsKeyFile,
202+
'tlsCaCertFile' : self.tlsCaCertFile,
191203
}
192204

193205
single_args = {}

RLTest/redis_cluster.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import print_function
22
from .redis_std import StandardEnv
3-
import redis
43
import rediscluster
54
import time
65
from RLTest.utils import Colors
@@ -14,6 +13,7 @@ def __init__(self, **kwargs):
1413
self.moduleArgs = kwargs['moduleArgs']
1514
self.shardsCount = kwargs.pop('shardsCount')
1615
useSlaves = kwargs.get('useSlaves', False)
16+
self.useTLS = kwargs['useTLS']
1717
startPort = 20000
1818
totalRedises = self.shardsCount * (2 if useSlaves else 1)
1919
randomizePorts = kwargs.pop('randomizePorts', False)
@@ -97,8 +97,19 @@ def getConnection(self, shardId=1):
9797
return self.shards[shardId - 1].getConnection()
9898

9999
def getClusterConnection(self):
100-
return rediscluster.RedisCluster(startup_nodes=[{'host': 'localhost', 'port': self.shards[0].getMasterPort()}],
101-
decode_responses=True)
100+
if self.useTLS:
101+
return rediscluster.RedisCluster(startup_nodes=[{'host': 'localhost', 'port': self.shards[0].getMasterPort()}],
102+
decode_responses=True,
103+
ssl=self.useTLS,
104+
ssl_keyfile=self.shards[0].getTLSKeyFile(),
105+
ssl_certfile=self.shards[0].getTLSCertFile(),
106+
ssl_cert_reqs='required',
107+
ssl_ca_certs=self.shards[0].getTLSCACertFile(),
108+
)
109+
else:
110+
return rediscluster.RedisCluster(
111+
startup_nodes=[{'host': 'localhost', 'port': self.shards[0].getMasterPort()}],
112+
decode_responses=True )
102113

103114
def getSlaveConnection(self):
104115
raise Exception('unsupported')
@@ -152,6 +163,9 @@ def isUnixSocket(self):
152163
def isTcp(self):
153164
return True
154165

166+
def isTLS(self):
167+
return self.useTLS
168+
155169
def exists(self, val):
156170
return self.getClusterConnection().exists(val)
157171

RLTest/redis_std.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
class StandardEnv(object):
1616
def __init__(self, redisBinaryPath, port=6379, modulePath=None, moduleArgs=None, outputFilesFormat=None,
1717
dbDirPath=None, useSlaves=False, serverId=1, password=None, libPath=None, clusterEnabled=False,
18-
useAof=False, debugger=None, noCatch=False, unix=False, verbose=False):
18+
useAof=False, debugger=None, noCatch=False, unix=False, verbose=False, useTLS=False, tlsCertFile=None, tlsKeyFile=None, tlsCaCertFile=None ):
1919
self.uuid = uuid.uuid4().hex
2020
self.redisBinaryPath = os.path.expanduser(redisBinaryPath) if redisBinaryPath.startswith('~/') else redisBinaryPath
2121
self.modulePath = os.path.abspath(modulePath) if modulePath else None
@@ -38,6 +38,10 @@ def __init__(self, redisBinaryPath, port=6379, modulePath=None, moduleArgs=None,
3838
self.slaveExitCode = None
3939
self.verbose = verbose
4040
self.role = MASTER
41+
self.useTLS = useTLS
42+
self.tlsCertFile = tlsCertFile
43+
self.tlsKeyFile = tlsKeyFile
44+
self.tlsCaCertFile = tlsCaCertFile
4145

4246
if port > 0:
4347
self.port = port
@@ -57,6 +61,22 @@ def __init__(self, redisBinaryPath, port=6379, modulePath=None, moduleArgs=None,
5761
if self.has_interactive_debugger:
5862
assert self.noCatch and not self.useSlaves and not self.clusterEnabled
5963

64+
if self.useTLS:
65+
if self.useUnix:
66+
raise ValueError('Unix sockets cannot be used with TLS enabled mode')
67+
if self.tlsCertFile is None:
68+
raise ValueError('When useTLS option is True tlsCertFile must be defined')
69+
if os.path.isfile(self.tlsCertFile) is False:
70+
raise ValueError('When useTLS option is True tlsCertFile must exist. specified path {}'.format(self.tlsCertFile))
71+
if self.tlsKeyFile is None:
72+
raise ValueError('When useTLS option is True tlsKeyFile must be defined')
73+
if os.path.isfile(self.tlsKeyFile) is False:
74+
raise ValueError('When useTLS option is True tlsKeyFile must exist. specified path {}'.format(self.tlsKeyFile))
75+
if self.tlsCaCertFile is None:
76+
raise ValueError('When useTLS option is True tlsCaCertFile must be defined')
77+
if os.path.isfile(self.tlsCaCertFile) is False:
78+
raise ValueError('When useTLS option is True tlsCaCertFile must exist. specified path {}'.format(self.tlsCaCertFile))
79+
6080
if libPath:
6181
self.libPath = os.path.expanduser(libPath) if libPath.startswith('~/') else libPath
6282
else:
@@ -88,6 +108,15 @@ def getUnixPath(self, role):
88108
basename = '{}-{}.sock'.format(self.uuid, role)
89109
return os.path.abspath(os.path.join(self.dbDirPath, basename))
90110

111+
def getTLSCertFile(self):
112+
return os.path.abspath(self.tlsCertFile)
113+
114+
def getTLSKeyFile(self):
115+
return os.path.abspath(self.tlsKeyFile)
116+
117+
def getTLSCACertFile(self):
118+
return os.path.abspath(self.tlsCaCertFile)
119+
91120
@property
92121
def has_interactive_debugger(self):
93122
return self.debugger and self.debugger.is_interactive
@@ -98,8 +127,11 @@ def createCmdArgs(self, role):
98127
cmdArgs += self.debugger.generate_command(self._getVlgrindFilePath(role) if not self.noCatch else None)
99128

100129
cmdArgs += [self.redisBinaryPath]
101-
if self.port > -1:
102-
cmdArgs += ['--port', str(self.getPort(role))]
130+
if self.port > -1 :
131+
if self.useTLS:
132+
cmdArgs += ['--port', str(0), '--tls-port', str(self.getPort(role))]
133+
else:
134+
cmdArgs += ['--port', str(self.getPort(role))]
103135
else:
104136
cmdArgs += ['--port', str(0), '--unixsocket', self.getUnixPath(role)]
105137
if self.modulePath:
@@ -121,11 +153,18 @@ def createCmdArgs(self, role):
121153
if self.clusterEnabled and role is not SLAVE:
122154
cmdArgs += ['--cluster-enabled', 'yes', '--cluster-config-file', self._getFileName(role, '.cluster.conf'),
123155
'--cluster-node-timeout', '5000']
156+
if self.useTLS:
157+
cmdArgs += ['--tls-cluster', 'yes']
124158
if self.useAof:
125159
cmdArgs += ['--appendonly yes']
126160
cmdArgs += ['--appendfilename', self._getFileName(role, '.aof')]
127161
cmdArgs += ['--aof-use-rdb-preamble', 'yes']
128162

163+
if self.useTLS:
164+
cmdArgs += ['--tls-cert-file', self.getTLSCertFile()]
165+
cmdArgs += ['--tls-key-file', self.getTLSKeyFile()]
166+
cmdArgs += ['--tls-ca-cert-file', self.getTLSCACertFile()]
167+
129168
return cmdArgs
130169

131170
def waitForRedisToStart(self, con):
@@ -160,6 +199,10 @@ def _printEnvData(self, prefix='', role=MASTER):
160199
print(Colors.Yellow(prefix + 'db dir path: %s' % (self.dbDirPath)))
161200
if self.libPath:
162201
print(Colors.Yellow(prefix + 'library path: %s' % (self.libPath)))
202+
if self.useTLS:
203+
print(Colors.Yellow(prefix + 'TLS Cert File: %s' % (self.getTLSCertFile())))
204+
print(Colors.Yellow(prefix + 'TLS Key File: %s' % (self.getTLSKeyFile())))
205+
print(Colors.Yellow(prefix + 'TLS CA Cert File: %s' % (self.getTLSCACertFile())))
163206

164207
def printEnvData(self, prefix=''):
165208
print(Colors.Yellow(prefix + 'master:'))
@@ -258,6 +301,15 @@ def _getConnection(self, role):
258301
if self.useUnix:
259302
return redis.StrictRedis(unix_socket_path=self.getUnixPath(role),
260303
password=self.password)
304+
elif self.useTLS:
305+
return redis.StrictRedis('localhost', self.getPort(role),
306+
password=self.password,
307+
ssl=self.useTLS,
308+
ssl_keyfile=self.getTLSKeyFile(),
309+
ssl_certfile=self.getTLSCertFile(),
310+
ssl_cert_reqs='required',
311+
ssl_ca_certs=self.getTLSCACertFile(),
312+
)
261313
else:
262314
return redis.StrictRedis('localhost', self.getPort(role),
263315
password=self.password)
@@ -347,6 +399,9 @@ def isUnixSocket(self):
347399
def isTcp(self):
348400
return not(self.useUnix)
349401

402+
def isTLS(self):
403+
return self.useTLS
404+
350405
def exists(self, val):
351406
return self.getConnection().exists(val)
352407

0 commit comments

Comments
 (0)