Newer
Older
#!/usr/bin/env python
# vim: set shiftwidth=4 softtabstop=4:
"""Script to locally build a docker image
isort:skip_file

Vincent Hatakeyama
committed
import datetime
import json
import logging
import os
import signal

Vincent Hatakeyama
committed
from subprocess import call, check_output
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__)

Vincent Hatakeyama
committed
__version__ = '0.1.3'
__updated__ = '2018-11-19'
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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',
)
parser.add_argument(
'--build-arg',

Vincent Hatakeyama
committed
help="build arg for the image, formated like FOO=BAR "
"[default: %(default)s]",

Vincent Hatakeyama
committed
nargs='*',
parser.add_argument(
'--no-pull',

Vincent Hatakeyama
committed
help="indicate to docker to not pull the base image "
"[default: %(default)s]",
action='store_true',
)
# TODO (maybe) add argument for other build arg
# TODO detect that user is member of docker group

Vincent Hatakeyama
committed
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

Vincent Hatakeyama
committed
buildargs = dict()
if os.path.exists('.hg'):
buildargs['REVISION'] = check_output(
'hg identify -i'.split()).split()[0].decode('utf-8')

Vincent Hatakeyama
committed
buildargs['CREATED'] = datetime.datetime.now().isoformat()
if nmspc.build_arg:
for arg in nmspc.build_arg:
a = arg.split('=')
buildargs[a[0]] = a[1]
logging.debug("Build args: %s", 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', ):
'python-ipdb\n')
myfile.write(
'RUN pip install watchdog --disable-pip-version-check '
'--system --no-cache-dir --only-binary wheel')
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')
pull = not nmspc.no_pull
logging.debug("Docker Pull %s", pull)
path='.', rm=True, pull=pull, 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.decode('utf-8'))
if 'stream' in d:
logging.info(d['stream'])

Vincent Hatakeyama
committed
if 'errorDetail' in d:
logging.fatal(d['errorDetail'])
return 1
# 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__":

Vincent Hatakeyama
committed
return_code = main(sys.argv[1:])
if return_code:
exit(return_code)