Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.

Commit bf3360a

Browse files
committed
replacing run.sh with a "docker-registry" console script
1 parent 17931fd commit bf3360a

File tree

7 files changed

+106
-22
lines changed

7 files changed

+106
-22
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ ADD ./config/boto.cfg /etc/boto.cfg
2020

2121
RUN pip install /docker-registry/
2222

23-
RUN cp --no-clobber /docker-registry/config/config_sample.yml /docker-registry/config/config.yml
23+
ENV DOCKER_REGISTRY_CONFIG /docker-registry/config/config_sample.yml
2424

2525
EXPOSE 5000
2626

27-
CMD cd /docker-registry && ./setup-configs.sh && exec ./run.sh
27+
CMD cd /docker-registry && ./setup-configs.sh && exec docker-registry

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ sudo python-pip install .
298298
#### Run it
299299
300300
```
301-
gunicorn --access-logfile - --debug -k gevent -b 0.0.0.0:5000 -w 1 wsgi:application
301+
gunicorn --access-logfile - --debug -k gevent -b 0.0.0.0:5000 -w 1 docker_registry.wsgi:application
302302
```
303303
304304
### How do I setup user accounts?
@@ -315,7 +315,7 @@ You could use for instance supervisord to spawn the registry with 8 workers
315315
using this command:
316316
317317
```
318-
gunicorn -k gevent --max-requests 100 --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 wsgi:application
318+
gunicorn -k gevent --max-requests 100 --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 docker_registry.wsgi:application
319319
```
320320
321321
Note that when using multiple workers, the secret_key for the Flask session

docker_registry/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
# -*- coding: utf-8 -*-
22
# flake8: noqa
33

4+
from __future__ import print_function
5+
6+
# this must happen before anything else
7+
import gevent.monkey
8+
gevent.monkey.patch_all()
9+
10+
from argparse import ArgumentParser
11+
from argparse import RawTextHelpFormatter
12+
import distutils.spawn
13+
import os
14+
import sys
15+
416
from .app import app
517
from .tags import *
618
from .images import *
@@ -11,3 +23,42 @@
1123
if cfg.standalone is not False:
1224
# If standalone mode is enabled (default), load the fake Index routes
1325
from .index import *
26+
27+
28+
DESCRIPTION = """run the docker-registry with gunicorn, honoring the following environment variables:
29+
30+
GUNICORN_WORKERS: number of worker processes gunicorn should start
31+
REGISTRY_PORT: TCP port to bind to on all ipv4 addresses; default is 5000
32+
GUNICORN_GRACEFUL_TIMEOUT: timeout in seconds for graceful worker restart
33+
GUNiCORN_SILENT_TIMEOUT: timeout in seconds for restarting silent workers
34+
"""
35+
36+
37+
def run_gunicorn():
38+
"""
39+
Exec gunicorn with our wsgi app, taking settings from environment
40+
variables as listed in the help text. This is intended to be called as a
41+
console_script entry point.
42+
"""
43+
44+
# this only exists to provide help/usage text
45+
parser = ArgumentParser(description=DESCRIPTION,
46+
formatter_class=RawTextHelpFormatter)
47+
parser.parse_args()
48+
49+
workers = os.environ.get('GUNICORN_WORKERS', '4')
50+
port = os.environ.get('REGISTRY_PORT', '5000')
51+
graceful_timeout = os.environ.get('GUNICORN_GRACEFUL_TIMEOUT', '3600')
52+
silent_timeout = os.environ.get('GUNICORN_SILENT_TIMEOUT', '3600')
53+
54+
address = '0.0.0.0:{0}'.format(port)
55+
56+
gunicorn_path = distutils.spawn.find_executable('gunicorn')
57+
if gunicorn_path is None:
58+
print('error: gunicorn executable not found', file=sys.stderr)
59+
sys.exit(1)
60+
61+
os.execl(gunicorn_path, 'gunicorn', '--access-logfile', '-', '--debug',
62+
'--max-requests', '100', '--graceful-timeout', graceful_timeout,
63+
'-t', silent_timeout, '-k', 'gevent', '-b', address, '-w', workers,
64+
'docker_registry.wsgi:application')

wsgi.py renamed to docker_registry/wsgi.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,24 @@
44
import logging
55
import os
66

7-
import docker_registry
7+
from . import app
88

99

1010
if __name__ == '__main__':
1111
# Bind to PORT if defined, otherwise default to 5000.
1212
port = int(os.environ.get('PORT_WWW', 5000))
13-
docker_registry.app.debug = True
14-
docker_registry.app.run(host='0.0.0.0', port=port)
13+
app.debug = True
14+
app.run(host='0.0.0.0', port=port)
1515
# Or you can run:
1616
# gunicorn --access-logfile - --log-level debug --debug -b 0.0.0.0:5000 \
1717
# -w 1 wsgi:application
1818
else:
1919
# For uwsgi
20-
docker_registry.app.logger.setLevel(logging.INFO)
20+
app.logger.setLevel(logging.INFO)
2121
stderr_logger = logging.StreamHandler()
2222
stderr_logger.setLevel(logging.INFO)
2323
stderr_logger.setFormatter(
2424
logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
25-
docker_registry.app.logger.addHandler(stderr_logger)
26-
application = docker_registry.app
27-
# uwsgi
28-
app = application
25+
app.logger.addHandler(stderr_logger)
26+
27+
application = app

run.sh

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

setup.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,10 @@
2929
'License :: OSI Approved :: Apache Software License',
3030
'Programming Language :: Python',
3131
'Programming Language :: Python :: 2',
32-
)
32+
),
33+
entry_points={
34+
'console_scripts': [
35+
'docker-registry = docker_registry:run_gunicorn'
36+
]
37+
}
3338
)

test/test_run_gunicorn.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import unittest
2+
3+
import mock
4+
5+
import docker_registry
6+
7+
8+
class TestRunGunicorn(unittest.TestCase):
9+
@mock.patch('argparse.ArgumentParser.parse_args')
10+
@mock.patch('os.execl')
11+
def test_exec_gunicorn(self, mock_execl, mock_parse_args):
12+
docker_registry.run_gunicorn()
13+
14+
self.assertEqual(mock_execl.call_count, 1)
15+
# ensure that the executable's path ends with 'gunicorn', so we have
16+
# some confidence that it called the correct executable
17+
self.assertTrue(mock_execl.call_args[0][0].endswith('gunicorn'))
18+
19+
@mock.patch('argparse.ArgumentParser.parse_args')
20+
@mock.patch('os.execl')
21+
def test_parses_args(self, mock_execl, mock_parse_args):
22+
docker_registry.run_gunicorn()
23+
24+
# ensure that argument parsing is happening
25+
mock_parse_args.assert_called_once_with()
26+
27+
@mock.patch('sys.exit')
28+
@mock.patch('distutils.spawn.find_executable', autospec=True)
29+
@mock.patch('argparse.ArgumentParser.parse_args')
30+
@mock.patch('os.execl')
31+
def test_gunicorn_not_found(self, mock_execl, mock_parse_args,
32+
mock_find_exec, mock_exit):
33+
mock_find_exec.return_value = None
34+
35+
docker_registry.run_gunicorn()
36+
37+
# ensure that sys.exit was called
38+
mock_exit.assert_called_once_with(1)

0 commit comments

Comments
 (0)