diff --git a/passbook/lib/management/__init__.py b/passbook/lib/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/passbook/lib/management/commands/__init__.py b/passbook/lib/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/passbook/lib/management/commands/bootstrap.py b/passbook/lib/management/commands/bootstrap.py new file mode 100644 index 000000000..aa77e213d --- /dev/null +++ b/passbook/lib/management/commands/bootstrap.py @@ -0,0 +1,66 @@ +"""passbook management command to bootstrap""" +from argparse import REMAINDER +from subprocess import Popen # nosec + +# pylint: disable=redefined-builtin +from sys import exit, stderr, stdin, stdout +from time import sleep + +from django.core.management.base import BaseCommand +from django.db import connection +from django.db.utils import OperationalError +from django_redis import get_redis_connection +from redis.exceptions import ConnectionError as RedisConnectionError +from structlog import get_logger + +LOGGER = get_logger() + + +class Command(BaseCommand): + """Bootstrap passbook, ensure Database and Cache are + reachable, and directories are writeable""" + + help = """Bootstrap passbook, ensure Database and Cache are + reachable, and directories are writeable""" + + def add_arguments(self, parser): + parser.add_argument("command", nargs=REMAINDER) + + def check_database(self) -> bool: + """Return true if database is reachable, false otherwise""" + try: + connection.cursor() + LOGGER.info("Database reachable") + return True + except OperationalError: + LOGGER.info("Database unreachable") + return False + + def check_cache(self) -> bool: + """Return true if cache is reachable, false otherwise""" + try: + con = get_redis_connection("default") + con.ping() + LOGGER.info("Cache reachable") + return True + except RedisConnectionError: + LOGGER.info("Cache unreachable") + return False + + def handle(self, *args, **options): + LOGGER.info("passbook bootstrapping...") + should_check = True + while should_check: + should_check = not (self.check_database() and self.check_cache()) + sleep(1) + LOGGER.info("Dependencies are up, starting command...") + proc = Popen( + args=options.get("command"), stdout=stdout, stderr=stderr, stdin=stdin + ) # nosec + try: + proc.wait() + exit(proc.returncode) + except KeyboardInterrupt: + LOGGER.info("Killing process") + proc.kill() + exit(254)