#!/usr/bin/env python # vim: set shiftwidth=4 softtabstop=4: """Script to locally build a docker image """ # Version 2.17 import argparse import json import logging import os import signal from subprocess import call import sys from six.moves import configparser import docker # apt python-docker (1.9) or pip install docker if docker.__version__ > '2.5.0': from docker import APIClient as docker_api else: from docker import Client as docker_api _logger = logging.getLogger(__name__) __version__ = '0.1.1' __date__ = '2018-04-04' __updated__ = '2018-04-24' def main(argv=None): # IGNORE:C0111 """Parse arguments and docker build """ program_version = __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % ( program_version, program_build_date) program_shortdesc = __doc__.split(".")[0] program_license = '''%s Created by Vincent Hatakeyama on %s. Copyright 2018 XCG Consulting. All rights reserved. Licensed under the MIT License Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) # TODO the script assume it is launched from the parent super project project_path = os.path.realpath('.') project_name = os.path.basename(project_path) if project_name == 'odoo_scripts': logging.fatal( "You must run this script from the super project" " (./odoo_scripts/docker_build.py)") return setup_path = 'setup.cfg' # TODO add a way to store configuration options in a project file # Argument parsing parser = argparse.ArgumentParser( description=program_license, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '-V', '--version', action='version', version=program_version_message) parser.add_argument( '-v', '--verbose', dest='verbose', action='count', help="set verbosity level [default: %(default)s]") # TODO add tag option, and maybe force tag option parser.add_argument( '--ensureconf', help="ensureconf [default: %(default)s]", action='store_true', ) parser.add_argument( '--push', help="Push image [default: %(default)s]", action='store_true', ) parser.add_argument( '--dev', help="add dev feature to generated image [default: %(default)s]", action='store_true', ) # TODO (maybe) add argument for other build arg # TODO detect that user is member of docker group nmspc = parser.parse_args(argv) verbose = nmspc.verbose if not verbose: logging.basicConfig(level=logging.WARN) if verbose == 1: logging.basicConfig(level=logging.INFO) if verbose and verbose > 1: logging.basicConfig(level=logging.DEBUG) ensureconf = nmspc.ensureconf dev = nmspc.dev push = nmspc.push c = configparser.ConfigParser() if not os.path.exists(setup_path): logging.fatal('Missing %s', setup_path) return 12 c.read(setup_path) registry = ( c.has_section('odoo_scripts') and c.has_option('odoo_scripts', 'registry') and c.get('odoo_scripts', 'registry')) or 'dockerhub.xcg.io' project = ( c.has_section('odoo_scripts') and c.has_option('odoo_scripts', 'image') and c.get('odoo_scripts', 'image')) or os.path.basename(project_path) odoo_type = ( c.has_section('odoo_scripts') and c.has_option('odoo_scripts', 'odoo_type') and c.get('odoo_scripts', 'odoo_type')) or 'odoo7' image = "%s/%s:latest" % (registry, project) logging.debug("Docker image: %s", image) # TODO ensureconf if ensureconf: raise NotImplementedError # call build copy cmd = ['./odoo_scripts/docker_build_copy'] logging.debug(' '.join(cmd)) call(cmd) # clean on exit def signal_handler(code, frame): cmd = ['./odoo_scripts/docker_build_clean'] logging.debug(' '.join(cmd)) call(cmd) # XXX needed? # sys.exit(0) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # TODO docker build buildargs = {} dockerfile = 'Dockerfile' if dev: debug_dockerfile = 'Dockerfile.debug' call(['cp', dockerfile, debug_dockerfile]) with open(debug_dockerfile, 'a') as myfile: myfile.write('\n# Developer helpers\n' 'RUN apt-get update -qq\n') myfile.write( 'RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ') if odoo_type == 'odoo11': myfile.write( 'python3-watchdog python3-ipdb\n') elif odoo_type in ('odoo10', ): myfile.write( 'python-ipdb\n') myfile.write('RUN pip install watchdog') elif odoo_type in ('odoo7', 'odoo8'): myfile.write( 'python-ipdb\n') myfile.write('RUN pip install pyinotify') dockerfile = debug_dockerfile # TODO remove temp image docker_client = docker_api(base_url='unix://var/run/docker.sock') builder = docker_client.build( path='.', rm=True, pull=True, buildargs=buildargs, tag=image, dockerfile=dockerfile) # this is for python docker 1.8-1.9 # TODO add compatibility with newer python docker for line in builder: d = json.loads(line) if 'stream' in d: logging.info(d['stream']) if 'errorDetail' in d: logging.fatal(d['errorDetail']) return 1 if dev: call(['rm', dockerfile]) # TODO exit if build failed # TODO docker tag with tags/bookmarks (unused so maybe no need) # TODO docker push (only when asked for) if push: raise NotImplementedError # XXX call cleanup more intelligently signal_handler(0, None) return 0 if __name__ == "__main__": return_code = main(sys.argv[1:]) if return_code: exit(return_code)