1#!/usr/bin/env python3
2#
3# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4#
5# This software may be used and distributed according to the terms of the
6# GNU General Public License version 2 or any later version.
7
8import argparse
9import pathlib
10import shutil
11import subprocess
12import sys
13
14
15def get_docker() -> str:
16    docker = shutil.which('docker.io') or shutil.which('docker')
17    if not docker:
18        print('could not find docker executable')
19        return 1
20
21    try:
22        out = subprocess.check_output([docker, '-h'], stderr=subprocess.STDOUT)
23
24        if b'Jansens' in out:
25            print(
26                '%s is the Docking System Tray; try installing docker.io'
27                % docker
28            )
29            sys.exit(1)
30    except subprocess.CalledProcessError as e:
31        print('error calling `%s -h`: %s' % (docker, e.output))
32        sys.exit(1)
33
34    out = subprocess.check_output([docker, 'version'], stderr=subprocess.STDOUT)
35
36    lines = out.splitlines()
37    if not any(l.startswith((b'Client:', b'Client version:')) for l in lines):
38        print('`%s version` does not look like Docker' % docker)
39        sys.exit(1)
40
41    if not any(l.startswith((b'Server:', b'Server version:')) for l in lines):
42        print('`%s version` does not look like Docker' % docker)
43        sys.exit(1)
44
45    return docker
46
47
48def get_dockerfile(path: pathlib.Path, args: list) -> bytes:
49    with path.open('rb') as fh:
50        df = fh.read()
51
52    for k, v in args:
53        df = df.replace(bytes('%%%s%%' % k.decode(), 'utf-8'), v)
54
55    return df
56
57
58def build_docker_image(dockerfile: pathlib.Path, params: list, tag: str):
59    """Build a Docker image from a templatized Dockerfile."""
60    docker = get_docker()
61
62    dockerfile_path = pathlib.Path(dockerfile)
63
64    dockerfile = get_dockerfile(dockerfile_path, params)
65
66    print('building Dockerfile:')
67    print(dockerfile.decode('utf-8', 'replace'))
68
69    args = [
70        docker,
71        'build',
72        '--build-arg',
73        'http_proxy',
74        '--build-arg',
75        'https_proxy',
76        '--tag',
77        tag,
78        '-',
79    ]
80
81    print('executing: %r' % args)
82    p = subprocess.Popen(args, stdin=subprocess.PIPE)
83    p.communicate(input=dockerfile)
84    if p.returncode:
85        raise subprocess.CalledProcessException(
86            p.returncode,
87            'failed to build docker image: %s %s' % (p.stdout, p.stderr),
88        )
89
90
91def command_build(args):
92    build_args = []
93    for arg in args.build_arg:
94        k, v = arg.split('=', 1)
95        build_args.append((k.encode('utf-8'), v.encode('utf-8')))
96
97    build_docker_image(pathlib.Path(args.dockerfile), build_args, args.tag)
98
99
100def command_docker(args):
101    print(get_docker())
102
103
104def main() -> int:
105    parser = argparse.ArgumentParser()
106
107    subparsers = parser.add_subparsers(title='subcommands')
108
109    build = subparsers.add_parser('build', help='Build a Docker image')
110    build.set_defaults(func=command_build)
111    build.add_argument(
112        '--build-arg',
113        action='append',
114        default=[],
115        help='Substitution to perform in Dockerfile; ' 'format: key=value',
116    )
117    build.add_argument('dockerfile', help='path to Dockerfile to use')
118    build.add_argument('tag', help='Tag to apply to created image')
119
120    docker = subparsers.add_parser('docker-path', help='Resolve path to Docker')
121    docker.set_defaults(func=command_docker)
122
123    args = parser.parse_args()
124
125    return args.func(args)
126
127
128if __name__ == '__main__':
129    sys.exit(main())
130