Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dockerize #1365

Merged
merged 70 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
7e28b23
WIP: All processes in a single container
Jul 8, 2020
afb4594
Use one container per process
Jul 8, 2020
08e99c6
Ignore venv and env directories
Jul 8, 2020
e7f9e3f
Skip flake8 checks on virtualenv dirs
Jul 8, 2020
bd1d793
Revert build_tools to Vagrant versions
Jul 9, 2020
58ca166
WIP on base image
Jul 9, 2020
c31ea12
Pass the master hostname to tests
Jul 9, 2020
a1e5a76
Fix flake8 errors
Jul 9, 2020
e0460b2
Use the existing --redis-url param to get master host
Jul 10, 2020
b5c2f2e
Don't shut down containers when building
Jul 10, 2020
61ece12
Add guide to contributing
Jul 13, 2020
6e31155
Merge branch 'dockerize' of github.com:abrookins/redis-py into dockerize
Jul 13, 2020
c5c9e81
Remove references to maling lists and IRC channels
Jul 13, 2020
af3771b
Attempt to use docker for travis testing
Jul 13, 2020
b4b1d97
WIP on travis config
Jul 13, 2020
867b28d
Install the version of compose that we need
Jul 13, 2020
e01cc2f
WIP
Jul 13, 2020
e4954ba
No need for Python in the test build anymore
Jul 13, 2020
27c4af4
Fix numbering
Jul 13, 2020
266e206
Format CLI commands correctly for RST
Jul 13, 2020
639a8dc
Update PHONY targets
Jul 13, 2020
a10751d
Improvements based on review feedback:
Jul 13, 2020
73b694d
Second try to add pypy and pypy3 to test runs
Jul 14, 2020
59bb043
Clean up cache files; delete root-owned .tox files...
Jul 14, 2020
7dfcd3b
Attempt to fix a timing bug
Jul 14, 2020
8fb3f97
Remove a rm command that would not work anyway
Jul 14, 2020
b2ef59b
Remove .tox from container side
Jul 14, 2020
df50cf6
Test all testenvs
Jul 14, 2020
aef6d4b
Move test Dockerfile into root, use COPY
Jul 14, 2020
55326ca
Include wait-for-it.sh copyright & license
Jul 15, 2020
273c8fe
remove unnecessary tox environments
andymccurdy Jul 15, 2020
a5508b1
force removal of docker images rather than ask the user if it's ok
andymccurdy Jul 15, 2020
0fe33ba
update PHONY targets
andymccurdy Jul 15, 2020
694f405
Clean up the wait-for-it.sh license
Jul 15, 2020
462fd34
master/slave configs for docker
andymccurdy Jul 15, 2020
26b611e
update configs to bind to explicit addresses. configure sentinel
andymccurdy Jul 15, 2020
fd32878
Merge branch 'dockerize' of github.com:abrookins/redis-py into dockerize
Jul 15, 2020
8c7a816
Check that we're subscribed to the right channels
Jul 15, 2020
54a8656
rename sentinel configs to sentinel.conf for clarity
andymccurdy Jul 15, 2020
1f5b2a6
no longer need to `make build`. `make test` will do everything
andymccurdy Jul 15, 2020
c8dc6a6
rename absurdly long test name
andymccurdy Jul 15, 2020
07abda7
Remove Vagrant files
Jul 15, 2020
6feed6e
Flesh out the docker env docs
Jul 15, 2020
adebdc3
Editing the contrib guide
Jul 15, 2020
61af925
Editing
Jul 15, 2020
344f589
Editing
Jul 15, 2020
f67ab19
Editing
Jul 15, 2020
5c32b23
Editing
Jul 15, 2020
11ef996
Editing docs
Jul 15, 2020
4cf1d83
restore codecov
andymccurdy Jul 15, 2020
5ab3a8d
Try running codecov in the test container
Jul 16, 2020
841d5ce
Try to pass the codecov token...
Jul 16, 2020
8c7d2da
testing docker-entry
andymccurdy Jul 16, 2020
59b460a
pass travis/codecov env vars to docker
andymccurdy Jul 16, 2020
07dd8e3
debug
andymccurdy Jul 16, 2020
d582494
debug
andymccurdy Jul 16, 2020
e5c7bfd
debug
andymccurdy Jul 16, 2020
cbf7b27
debug
andymccurdy Jul 16, 2020
2bff161
debug
andymccurdy Jul 16, 2020
fe89d31
debug
andymccurdy Jul 16, 2020
d878df7
debug
andymccurdy Jul 16, 2020
7da4b55
Try running codecov from tox
Jul 17, 2020
884c3d3
Attempt to combine coverage files
Jul 17, 2020
07c2675
Use the -a flag instead of "combine"
Jul 17, 2020
4c6cd16
Go back to "merge" -- -a failed
Jul 17, 2020
4565195
Remove unnecessary pytest-cov dep
Jul 17, 2020
a4ccbe4
add the covreport env to the list of default envs tox runs
andymccurdy Jul 20, 2020
f24b7f2
make the slowlog_get test more resilient to multiple clients being co…
andymccurdy Jul 20, 2020
458ded5
run the codecov env by default and disable when running outside Travis
andymccurdy Jul 20, 2020
1f988d8
changelog
andymccurdy Jul 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ vagrant/.vagrant
.eggs
.idea
.coverage
env
venv
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.PHONY: base

build:
docker build -t redis-py-base docker/base
docker-compose down
Copy link
Contributor Author

@abrookins abrookins Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this line is really necessary.

docker-compose build

dev:
docker-compose up -d

test: dev
find . -name "*.pyc" -exec rm -f {} \;
docker-compose run test tox -- --redis-url="redis://master:6379/9" --redis-master-host=master
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: "3.8"

services:
master:
build: docker/master
ports:
- "6379:6379"

slave:
build: docker/slave
ports:
- "6380:6380"

sentinel_1:
build: docker/sentinel_1
ports:
- "26379:26379"

sentinel_2:
build: docker/sentinel_2
ports:
- "26380:26380"

sentinel_3:
build: docker/sentinel_3
ports:
- "26381:26381"

test:
image: fkrull/multi-python:latest
working_dir: /redis-py
volumes:
- .:/redis-py
1 change: 1 addition & 0 deletions docker/base/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM redis:6.0.5-buster
7 changes: 7 additions & 0 deletions docker/master/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM redis-py-base:latest

COPY redis.conf /etc/conf/redis/redis.conf

EXPOSE 6379

CMD redis-server
9 changes: 9 additions & 0 deletions docker/master/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pidfile /var/run/redis-master.pid
bind *
port 6379
daemonize yes
unixsocket /tmp/redis_master.sock
unixsocketperm 777
dbfilename master.rdb
dir /var/lib/redis/backups
logfile /var/log/redis-master
7 changes: 7 additions & 0 deletions docker/sentinel_1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM redis-py-base:latest

COPY redis.conf /etc/conf/redis/redis.conf

EXPOSE 26379

CMD redis-server
7 changes: 7 additions & 0 deletions docker/sentinel_1/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pidfile /var/run/sentinel-1.pid
port 26379
daemonize yes
logfile /var/log/redis-sentinel-1

# short timeout for sentinel tests
sentinel down-after-milliseconds mymaster 500
7 changes: 7 additions & 0 deletions docker/sentinel_2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM redis-py-base:latest

COPY redis.conf /etc/conf/redis/redis.conf

EXPOSE 26380

CMD redis-server
7 changes: 7 additions & 0 deletions docker/sentinel_2/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pidfile /var/run/sentinel-2.pid
port 26380
daemonize yes
logfile /var/log/redis-sentinel-2

# short timeout for sentinel tests
sentinel down-after-milliseconds mymaster 500
7 changes: 7 additions & 0 deletions docker/sentinel_3/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM redis-py-base:latest

COPY redis.conf /etc/conf/redis/redis.conf

EXPOSE 26381

CMD redis-server
7 changes: 7 additions & 0 deletions docker/sentinel_3/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pidfile /var/run/sentinel-3.pid
port 26381
daemonize yes
logfile /var/log/redis-sentinel-3

# short timeout for sentinel tests
sentinel down-after-milliseconds mymaster 500
7 changes: 7 additions & 0 deletions docker/slave/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM redis-py-base:latest

COPY redis.conf /etc/conf/redis/redis.conf

EXPOSE 6380

CMD redis-server
11 changes: 11 additions & 0 deletions docker/slave/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pidfile /var/run/redis-slave.pid
bind *
port 6380
daemonize yes
unixsocket /tmp/redis-slave.sock
unixsocketperm 777
dbfilename slave.rdb
dir /var/lib/redis/backups
logfile /var/log/redis-slave

slaveof 127.0.0.1 6379
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
hiredis = hiredis>=0.1.3

[flake8]
exclude = .venv,.tox,dist,docs,build,*.egg,redis_install
exclude = .venv,.tox,dist,docs,build,*.egg,redis_install,env,venv

[bdist_wheel]
universal = 1
16 changes: 14 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@


REDIS_INFO = {}
default_redis_url = "redis://localhost:6379/9"
DEFAULT_REDIS_URL = "redis://localhost:6379/9"
DEFAULT_REDIS_MASTER_HOST = "localhost"



def pytest_addoption(parser):
parser.addoption('--redis-url', default=default_redis_url,
parser.addoption('--redis-url', default=DEFAULT_REDIS_URL,
action="store",
help="Redis connection string,"
" defaults to `%(default)s`")
parser.addoption('--redis-master-host', default=DEFAULT_REDIS_MASTER_HOST,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever a reason where redis-master-host would not point to the same host that's defined in the redis-url? I can't think of one.

Assuming there isn't a valid reason, we should infer the master hostname from the redis-url argument rather than introducing a new setting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not. Fixed!

action="store",
help="Redis master hostname,"
" defaults to `%(default)s`")



def _get_info(redis_url):
Expand Down Expand Up @@ -156,6 +163,11 @@ def mock_cluster_resp_slaves(request, **kwargs):
return _gen_cluster_mock_resp(r, response)


@pytest.fixture(scope="module")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the master_host value ever change from module to module? If not, scoping this to 'session' seems more appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't, no - updated!

def master_host(request):
yield request.config.getoption("--redis-master-host")


def wait_for_command(client, monitor, command):
# issue a command with a key name that's local to this process.
# if we find a command with our key before the command we're waiting
Expand Down
42 changes: 26 additions & 16 deletions tests/test_connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,25 @@ def test_connection_creation(self):
assert isinstance(connection, DummyConnection)
assert connection.kwargs == connection_kwargs

def test_multiple_connections(self):
pool = self.get_pool()
def test_multiple_connections(self, master_host):
connection_kwargs = {'host': master_host}
pool = self.get_pool(connection_kwargs=connection_kwargs)
c1 = pool.get_connection('_')
c2 = pool.get_connection('_')
assert c1 != c2

def test_max_connections(self):
pool = self.get_pool(max_connections=2)
def test_max_connections(self, master_host):
connection_kwargs = {'host': master_host}
pool = self.get_pool(max_connections=2,
connection_kwargs=connection_kwargs)
pool.get_connection('_')
pool.get_connection('_')
with pytest.raises(redis.ConnectionError):
pool.get_connection('_')

def test_reuse_previously_released_connection(self):
pool = self.get_pool()
def test_reuse_previously_released_connection(self, master_host):
connection_kwargs = {'host': master_host}
pool = self.get_pool(connection_kwargs=connection_kwargs)
c1 = pool.get_connection('_')
pool.release(c1)
c2 = pool.get_connection('_')
Expand Down Expand Up @@ -98,22 +102,25 @@ def get_pool(self, connection_kwargs=None, max_connections=10, timeout=20):
**connection_kwargs)
return pool

def test_connection_creation(self):
connection_kwargs = {'foo': 'bar', 'biz': 'baz'}
def test_connection_creation(self, master_host):
connection_kwargs = {'foo': 'bar', 'biz': 'baz', 'host': master_host}
pool = self.get_pool(connection_kwargs=connection_kwargs)
connection = pool.get_connection('_')
assert isinstance(connection, DummyConnection)
assert connection.kwargs == connection_kwargs

def test_multiple_connections(self):
pool = self.get_pool()
def test_multiple_connections(self, master_host):
connection_kwargs = {'host': master_host}
pool = self.get_pool(connection_kwargs=connection_kwargs)
c1 = pool.get_connection('_')
c2 = pool.get_connection('_')
assert c1 != c2

def test_connection_pool_blocks_until_timeout(self):
def test_connection_pool_blocks_until_timeout(self, master_host):
"When out of connections, block for timeout seconds, then raise"
pool = self.get_pool(max_connections=1, timeout=0.1)
connection_kwargs = {'host': master_host}
pool = self.get_pool(max_connections=1, timeout=0.1,
connection_kwargs=connection_kwargs)
pool.get_connection('_')

start = time.time()
Expand All @@ -122,12 +129,14 @@ def test_connection_pool_blocks_until_timeout(self):
# we should have waited at least 0.1 seconds
assert time.time() - start >= 0.1

def connection_pool_blocks_until_another_connection_released(self):
def test_connection_pool_blocks_until_another_connection_released(self, master_host):
"""
When out of connections, block until another connection is released
to the pool
"""
pool = self.get_pool(max_connections=1, timeout=2)
connection_kwargs = {'host': master_host}
pool = self.get_pool(max_connections=1, timeout=2,
connection_kwargs=connection_kwargs)
c1 = pool.get_connection('_')

def target():
Expand All @@ -139,8 +148,9 @@ def target():
pool.get_connection('_')
assert time.time() - start >= 0.1

def test_reuse_previously_released_connection(self):
pool = self.get_pool()
def test_reuse_previously_released_connection(self, master_host):
connection_kwargs = {'host': master_host}
pool = self.get_pool(connection_kwargs=connection_kwargs)
c1 = pool.get_connection('_')
pool.release(c1)
c2 = pool.get_connection('_')
Expand Down
16 changes: 8 additions & 8 deletions tests/test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ def r(self, request):
request=request,
single_connection_client=False)

def test_close_connection_in_child(self):
def test_close_connection_in_child(self, master_host):
"""
A connection owned by a parent and closed by a child doesn't
destroy the file descriptors so a parent can still use it.
"""
conn = Connection()
conn = Connection(host=master_host)
conn.send_command('ping')
assert conn.read_response() == b'PONG'

Expand All @@ -56,12 +56,12 @@ def target(conn):
conn.send_command('ping')
assert conn.read_response() == b'PONG'

def test_close_connection_in_parent(self):
def test_close_connection_in_parent(self, master_host):
"""
A connection owned by a parent is unusable by a child if the parent
(the owning process) closes the connection.
"""
conn = Connection()
conn = Connection(host=master_host)
conn.send_command('ping')
assert conn.read_response() == b'PONG'

Expand All @@ -84,12 +84,12 @@ def target(conn, ev):
assert proc.exitcode == 0

@pytest.mark.parametrize('max_connections', [1, 2, None])
def test_pool(self, max_connections):
def test_pool(self, max_connections, master_host):
"""
A child will create its own connections when using a pool created
by a parent.
"""
pool = ConnectionPool.from_url('redis://localhost',
pool = ConnectionPool.from_url('redis://{}'.format(master_host),
max_connections=max_connections)

conn = pool.get_connection('ping')
Expand Down Expand Up @@ -119,12 +119,12 @@ def target(pool):
assert conn.read_response() == b'PONG'

@pytest.mark.parametrize('max_connections', [1, 2, None])
def test_close_pool_in_main(self, max_connections):
def test_close_pool_in_main(self, max_connections, master_host):
"""
A child process that uses the same pool as its parent isn't affected
when the parent disconnects all connections within the pool.
"""
pool = ConnectionPool.from_url('redis://localhost',
pool = ConnectionPool.from_url('redis://{}'.format(master_host),
max_connections=max_connections)

conn = pool.get_connection('ping')
Expand Down
Loading