# HG changeset patch # User Vincent Hatakeyama <vincent.hatakeyama@xcg-consulting.fr> # Date 1503938725 -7200 # Mon Aug 28 18:45:25 2017 +0200 # Node ID 425ed2d0d7dee881731bd4d56dc3aa04ad8e5181 # Parent 2546f9b90bc99ab55f7d4b019b707b60dad52078 :sparkles: option to run start postgresql docker, also uses docker-py instead of subprocess.call diff --git a/docker_dev_start.py b/docker_dev_start.py --- a/docker_dev_start.py +++ b/docker_dev_start.py @@ -6,14 +6,17 @@ import argparse import logging import pwd +import signal from subprocess import call import sys +from threading import Thread import os import ConfigParser -import docker # apt python-docker (1.9) -from psycopg2 import connect, OperationalError # apt python-psycopg2 +import docker # apt python-docker (1.9) +from psycopg2 import connect, OperationalError # apt python-psycopg2 +from requests.exceptions import ConnectionError # TODO auto create list of module @@ -136,6 +139,11 @@ help="Append the module_list from setup.cfg to the modules to install [default: %(default)s]", action='store_true', ) + parser.add_argument( + '--start-postgresql', + help="Start a Postgresql docker", + action='store_true', + ) network_group = parser.add_mutually_exclusive_group() network_group.add_argument( '--host-network', @@ -164,6 +172,7 @@ db_user = nmspc.db_user db_password = nmspc.db_password use_host_network = nmspc.host_network + start_postgresql = nmspc.start_postgresql if project_name == 'odoo_scripts': logging.fatal( @@ -192,6 +201,10 @@ c.has_option('odoo_scripts', 'odoo_type') and c.get('odoo_scripts', 'odoo_type')) or 'odoo7' image = "%s/%s:latest" % (registry, project) + postgresql_version = ( + c.has_section('odoo_scripts') and + c.has_option('odoo_scripts', 'postgresql_version') and + c.get('odoo_scripts', 'postgresql_version')) or '9.6' logging.debug("Docker image: %s", image) # detect if docker image already exists docker_client = docker.Client(base_url='unix://var/run/docker.sock') @@ -201,15 +214,7 @@ else: logging.info("Test image found") - options = [ - '--name', - project_name, - '--rm', - '--publish', - '8069:8069', - '--tty', - '--interactive', - ] + binds = [] arg = [ 'start', ] @@ -249,8 +254,6 @@ # auto detect local ip if use_host_network: local_ip = '127.0.0.1' - options.append('--network') - options.append('host') else: local_ip = None try: @@ -275,8 +278,7 @@ password = db_password if os.path.isfile(local_conf_path): logging.info('Local configuration file found: %s' % local_conf_path) - options.append('--volume') - options.append('%s:/opt/odoo/etc/odoo.conf' % os.path.join( + binds.append('%s:/opt/odoo/etc/odoo.conf' % os.path.join( project_path, local_conf_path)) cp_local = ConfigParser.ConfigParser() cp_local.read(local_conf_path) @@ -287,36 +289,57 @@ # data volume handling if odoo_type != 'odoo7': - options.append('--mount') data_volume_name = '{}_data'.format(project_name) logging.debug('Using data volume %s', data_volume_name) - volumes = docker_client.volumes(filters={'name': data_volume_name}) - if volumes['Volumes']: - logging.debug('Volume %s already exist', data_volume_name) - volume = volumes['Volumes'][0] - else: - logging.debug('Creating volume %s', data_volume_name) - volume = docker_client.create_volume(name=data_volume_name) - mount_opts = 'source={},target=/mnt/data'.format(data_volume_name) + volume = getVolume(docker_client, data_volume_name) # make sure the permission in the volume are correct # TODO replace by something cleaner if possible call(['docker', 'run', '--rm', '--mount', mount_opts, 'busybox', 'chmod', '777', '/mnt/data']) - options.append(mount_opts) + binds.append('{}:/mnt/data'.format(data_volume_name)) arg.append('--data-dir /mnt/data') else: logging.debug('No data volume for this odoo version') + if start_postgresql: + pg_repository = 'xcgd/postgresql' + pg_image = '{}:{}'.format(pg_repository, postgresql_version) + name = 'pg{}'.format(postgresql_version) + docker_client.pull(repository=pg_repository, tag=postgresql_version) + pg_data_volume_name = 'postgresql_{}-{}'.format(postgresql_version, project_name) + host_pg_port = 5433 + volume = getVolume(docker_client, pg_data_volume_name) + host_config = docker_client.create_host_config( + binds=['{}:/var/lib/postgresql'.format(pg_data_volume_name)], + port_bindings={5432: host_pg_port}, + ) + logging.debug('Creating postgresql container') + pg = docker_client.create_container(image=pg_image, host_config=host_config, name=name) + logging.debug('Starting postgresql container') + docker_client.start(pg.get('Id')) + # give pg the time to start up + import time + time.sleep(5) + arg.append('--db_port') + arg.append(str(host_pg_port)) # Check that connection can be done, try to create user if asked to + # TODO handle the case where the database is still starting up (and remove the sleep) try: - test_connection = connect(user=user, password=password, database='postgres', host=local_ip) + if local_ip or start_postgresql: + port = host_pg_port if start_postgresql else None + test_connection = connect(user=user, password=password, database='postgres', host=local_ip, port=port) + else: + test_connection = connect(user=user, password=password, database='postgres') except OperationalError as exception: if nmspc.create_user: logging.info('Cannot connect to database with user %s', user) logging.info(exception) logging.info('Creating user %s', user) - loginname = pwd.getpwuid(os.getuid())[0] - connection = connect(user=loginname, database='postgres') + if start_postgresql: + connection = connect(user='pg', database='postgres', host=local_ip, port=host_pg_port) + else: + loginname = pwd.getpwuid(os.getuid())[0] + connection = connect(user=loginname, database='postgres') with connection.cursor() as cursor: # not injection safe but you are on your own machine # with already full access to db @@ -342,15 +365,61 @@ arg.append('--addons-path') arg.append(','.join(all_addons_dir)) - cmd = ['docker', 'run'] - cmd.extend(options) - cmd.append(image) - cmd.extend(arg) - logging.debug(cmd) - # TODO directly use docker-py instead - call(cmd) + # add volumes + odoo_host_config = docker_client.create_host_config( + binds=binds, + port_bindings={8069: 8069}, + network_mode='host' if use_host_network else 'bridge', + ) + logging.debug('Creating odoo container') + odoo = docker_client.create_container( + name=project_name, host_config=odoo_host_config, + image=image, command=arg, tty=True) + logging.debug('Starting odoo container') + docker_client.start(odoo.get('Id')) - # TODO add handling of signal to reload + def signal_handler(code, frame): + if code == signal.SIGINT: + logging.debug('You pressed Ctrl+C!') + logging.info('Stopping odoo') + docker_client.stop(odoo.get('Id')) + logging.info('Removing container odoo') + docker_client.remove_container(odoo.get('Id')) + if start_postgresql: + logging.info('Stopping postgresql') + docker_client.stop(pg.get('Id')) + logging.info('Removing container postgresql') + docker_client.remove_container(pg.get('Id')) + sys.exit(0) + # TODO add a kill of pg when crashing + signal.signal(signal.SIGINT, signal_handler) + logging.info('Press Ctrl+C to quit') + + # print docker logs of odoo + stream = docker_client.logs(odoo.get('Id'), stream=True, follow=True) + while True: + try: + for log in stream: + sys.stdout.write(log) + except ConnectionError: + # If there is no log for some time requests throw some errors + # we ignore them + pass + + # Clean up, just in case + # signal_handler(signal.SIGINT, None) + # TODO add handling of signal to restart odoo + +def getVolume(docker_client, data_volume_name): + """Return the volume passed in parameter, creating it if it does not exists + """ + volumes = docker_client.volumes(filters={'name': data_volume_name}) + if volumes['Volumes']: + logging.debug('Volume %s already exist', data_volume_name) + return volumes['Volumes'][0] + else: + logging.debug('Creating volume %s', data_volume_name) + return docker_client.create_volume(name=data_volume_name) if __name__ == "__main__": return_code = main()