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