Skip to content

Commit 5281aa6

Browse files
committed
adding an entrypoint script
1 parent 568b529 commit 5281aa6

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

.dockerignore

+3
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ tests/results
1616
# test configurations
1717
/default.cfg
1818

19+
# git stuff
20+
.git
21+
1922
# pycharm ide
2023
.idea

Dockerfile

+4-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.1/main' >> /etc/apk/repositori
3232
libxslt-dev \
3333
'libxml2<2.9.4' \
3434
'libxml2-dev<2.9.4' \
35+
netcat-openbsd \
3536
wget \
3637
&& apk add --no-cache \
3738
--repository http://dl-3.alpinelinux.org/alpine/edge/testing/ \
@@ -54,10 +55,7 @@ USER pycsw
5455

5556
EXPOSE 8000
5657

57-
ENTRYPOINT [ \
58-
"gunicorn", \
59-
"--workers=2", \
60-
"--bind=0.0.0.0:8000", \
61-
"--access-logfile=-", \
62-
"pycsw.wsgi" \
58+
ENTRYPOINT [\
59+
"python3", \
60+
"/home/pycsw/bin/entrypoint.py" \
6361
]

bin/entrypoint.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""Entrypoint script for docker containers.
2+
3+
This module serves as the entrypoint for docker containers. Its main
4+
purpose is to set up the pycsw database so that newly generated
5+
containers may be useful soon after being launched, without requiring
6+
additional input.
7+
8+
"""
9+
10+
11+
import argparse
12+
from collections import namedtuple
13+
import logging
14+
import os
15+
from six.moves.configparser import SafeConfigParser
16+
from subprocess import call
17+
from subprocess import PIPE
18+
from time import sleep
19+
20+
from pycsw.core import admin
21+
22+
logger = logging.getLogger(__name__)
23+
24+
DatabaseParameters = namedtuple("DatabaseParameters", [
25+
"host",
26+
"port",
27+
"user",
28+
"password",
29+
"path"
30+
])
31+
32+
33+
def launch_pycsw(gunicorn_workers=2):
34+
logger.debug("Reading pycsw config...")
35+
config = SafeConfigParser()
36+
config.read("/etc/pycsw/pycsw.cfg")
37+
db_url = config.get("repository", "database")
38+
db = db_url.partition(":")[0].partition("+")[0]
39+
db_handler = {
40+
"sqlite": handle_sqlite_db,
41+
"postgres": handle_postgresql_db,
42+
}.get(db)
43+
logger.debug("Setting up pycsw's data repository...")
44+
logger.debug("Repository URL: {}".format(db_url))
45+
db_handler(db_url, config.get("repository", "table"))
46+
logger.debug("Launching pycsw...")
47+
pycsw_server_command = [
48+
"gunicorn",
49+
"--bind=0.0.0.0:8000",
50+
"--access-logfile=-",
51+
"--error-logfile=-",
52+
"--workers={}".format(gunicorn_workers)
53+
]
54+
pycsw_server_command.append("--workers={}".format(gunicorn_workers))
55+
pycsw_server_command.append("pycsw.wsgi")
56+
call(pycsw_server_command)
57+
58+
59+
def handle_sqlite_db(database_url, table_name):
60+
db_path = database_url.rpartition(":///")[-1]
61+
if not os.path.isfile(db_path):
62+
_create_pycsw_schema(database_url, table_name)
63+
64+
65+
def handle_postgresql_db(database_url, table_name):
66+
db_params = _extract_postgres_url_params(database_url)
67+
_wait_for_network_service(db_params.host, db_params.port)
68+
# - set up the db with pycsw-admin if needed
69+
_create_pycsw_schema(database_url, table_name)
70+
raise NotImplementedError
71+
72+
73+
def _extract_postgres_url_params(database_url):
74+
db_params = database_url.partition("://")[-1]
75+
rest, db_name = db_params.rpartition("/")[::2]
76+
user_params, host_params = rest.partition("@")[::2]
77+
user, password = user_params.partition(":")[::2]
78+
host, port = host_params.partition(":")[::2]
79+
port = port if port != "" else "5432"
80+
return DatabaseParameters(host, port, user, password, db_name)
81+
82+
83+
def _create_pycsw_schema(database_url, table):
84+
admin.setup_db(database=database_url, table=table, home="/home/pycsw")
85+
86+
87+
def _wait_for_network_service(host, port, max_tries=10, wait_seconds=3):
88+
logger.debug("Waiting for {!r}:{!r}...".format(host, port))
89+
current_try = 0
90+
while current_try < max_tries:
91+
return_code = call(
92+
["nc", "-z", str(host), str(port)], stdout=PIPE, stderr=PIPE)
93+
if int(return_code) == 0:
94+
logger.debug("{!r}:{!r} is already up!".format(host, port))
95+
break
96+
else:
97+
current_try += 1
98+
sleep(wait_seconds)
99+
else:
100+
raise RuntimeError("Could not find {}:{} after {} tries. "
101+
"Giving up".format(host, port, max_tries))
102+
103+
104+
105+
if __name__ == "__main__":
106+
parser = argparse.ArgumentParser()
107+
parser.add_argument("--verbose", action="store_true")
108+
parser.add_argument(
109+
"--workers",
110+
default=2,
111+
help="Number of workers to use by the gunicorn server. Defaults to 2."
112+
)
113+
args, remaining_args = parser.parse_known_args()
114+
logging.basicConfig(
115+
level=logging.DEBUG if args.verbose else logging.WARNING)
116+
launch_pycsw(gunicorn_workers=args.workers)

0 commit comments

Comments
 (0)