From dcea427a6201e31308219313ef9539557062614b Mon Sep 17 00:00:00 2001 From: Dobroslaw Zybort Date: Wed, 25 Jul 2018 14:05:35 +0200 Subject: [PATCH] Implement MySQL check script for Docker Story: 2001694 Task: 23321 Change-Id: I103c864832cd85ec528080cb7d49393c09127c60 --- docker/Dockerfile | 4 +- docker/mysql_check.py | 88 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index fa001452..4a504bd5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,7 +5,7 @@ # https://github.com/pyca/cryptography/pull/4270 FROM python:3.5.5-alpine3.7 -COPY wait_for.sh kafka_wait_for_topics.py / +COPY wait_for.sh kafka_wait_for_topics.py mysql_check.py / COPY ashrc /root/.ashrc ENV \ @@ -23,7 +23,7 @@ LABEL org.opencontainers.image.revision="$BASE_GIT_COMMIT" LABEL org.opencontainers.image.licenses="Apache-2.0" RUN \ - chmod +x /wait_for.sh /kafka_wait_for_topics.py && \ + chmod +x /wait_for.sh /kafka_wait_for_topics.py /mysql_check.py && \ apk add --no-cache \ su-exec=0.2-r0 \ tini=0.16.1-r0 \ diff --git a/docker/mysql_check.py b/docker/mysql_check.py index 4a3455f6..cee54ce3 100644 --- a/docker/mysql_check.py +++ b/docker/mysql_check.py @@ -19,15 +19,34 @@ It's checking if requested database already exists. +For using this script you need to set some environment variables: +* `MYSQL_HOST` for connection string to MySQL. + Example: `mysql`, `192.168.10.6`. + Default: `mysql`. +* `MYSQL_PORT` for connection string to MySQL port. + Default: `3306`. +* `MYSQL_USER` for user that is cappable to connect to MySQL. + Default: `monapi`. +* `MYSQL_PASSWORD` for user password. + Default: `password`. +* `MYSQL_DB` for database that you need to have before starting service. + Default: `mon`. + After making sure that this environment variables are set you can simply execute this script in the following way: `python3 mysql_check.py && ./start_service.sh` `python3 mysql_check.py || exit 1` + +Additional environment variables available are: +* `LOG_LEVEL` - default to `INFO` +* `MYSQL_WAIT_RETRIES` - number of retries, default to `24` +* `MYSQL_WAIT_INTERVAL` - in seconds, default to `5` """ import logging import os import sys +import time import pymysql @@ -42,7 +61,7 @@ logging.basicConfig(level=LOG_LEVEL) logger = logging.getLogger(__name__) MYSQL_HOST = os.environ.get('MYSQL_HOST', 'mysql') -MYSQL_PORT = os.environ.get('MYSQL_HOST', 3306) +MYSQL_PORT = os.environ.get('MYSQL_PORT', 3306) MYSQL_USER = os.environ.get('MYSQL_USER', 'monapi') MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD', 'password') MYSQL_DB = os.environ.get('MYSQL_DB', 'mon') @@ -50,9 +69,64 @@ MYSQL_DB = os.environ.get('MYSQL_DB', 'mon') MYSQL_WAIT_RETRIES = int(os.environ.get('MYSQL_WAIT_RETRIES', '24')) MYSQL_WAIT_INTERVAL = int(os.environ.get('MYSQL_WAIT_INTERVAL', '5')) -# TODO(Dobroslaw): All checks and retry. -db = pymysql.connect( - host=MYSQL_HOST, port=MYSQL_PORT, - user=MYSQL_USER, passwd=MYSQL_PASSWORD, - db=MYSQL_DB -) + +def retry(retries=MYSQL_WAIT_RETRIES, delay=MYSQL_WAIT_INTERVAL, + check_exceptions=()): + """Retry decorator.""" + def decorator(func): + """Decorator.""" + def f_retry(*args, **kwargs): + """Retry running function on exception after delay.""" + for i in range(1, retries + 1): + try: + return func(*args, **kwargs) + # pylint: disable=W0703 + # We want to catch all exceptions here to retry. + except check_exceptions + (Exception,) as exc: + if i < retries: + logger.info('Connection attempt %d of %d failed', + i, retries) + if isinstance(exc, check_exceptions): + logger.debug('Caught known exception, retrying...', + exc_info=True) + else: + logger.warn( + 'Caught unknown exception, retrying...', + exc_info=True) + else: + logger.exception('Failed after %d attempts', retries) + + raise + + # No exception so wait before retrying + time.sleep(delay) + + return f_retry + return decorator + + +@retry(check_exceptions=(pymysql.err.OperationalError,)) +def connect_mysql(host, port, user, password, database): + """Connect to MySQL with retries.""" + return pymysql.connect( + host=host, port=port, + user=user, passwd=password, + db=database + ) + + +def main(): + """Start main part of the wait script.""" + logger.info('Waiting for database: `%s`', MYSQL_DB) + + connect_mysql( + host=MYSQL_HOST, port=MYSQL_PORT, + user=MYSQL_USER, password=MYSQL_PASSWORD, + database=MYSQL_DB + ) + + logger.info('Database `%s` found', MYSQL_DB) + + +if __name__ == '__main__': + main()