14485b04bSFam Zheng#!/usr/bin/env python2 24485b04bSFam Zheng# 34485b04bSFam Zheng# Docker controlling module 44485b04bSFam Zheng# 54485b04bSFam Zheng# Copyright (c) 2016 Red Hat Inc. 64485b04bSFam Zheng# 74485b04bSFam Zheng# Authors: 84485b04bSFam Zheng# Fam Zheng <famz@redhat.com> 94485b04bSFam Zheng# 104485b04bSFam Zheng# This work is licensed under the terms of the GNU GPL, version 2 114485b04bSFam Zheng# or (at your option) any later version. See the COPYING file in 124485b04bSFam Zheng# the top-level directory. 134485b04bSFam Zheng 14f03868bdSEduardo Habkostfrom __future__ import print_function 154485b04bSFam Zhengimport os 164485b04bSFam Zhengimport sys 17c2d31896SStefan Hajnoczisys.path.append(os.path.join(os.path.dirname(__file__), 18c2d31896SStefan Hajnoczi '..', '..', 'scripts')) 19c2d31896SStefan Hajnocziimport argparse 204485b04bSFam Zhengimport subprocess 214485b04bSFam Zhengimport json 224485b04bSFam Zhengimport hashlib 234485b04bSFam Zhengimport atexit 244485b04bSFam Zhengimport uuid 254485b04bSFam Zhengimport tempfile 26504ca3c2SAlex Bennéeimport re 2797cba1a1SFam Zhengimport signal 286e733da6SAlex Bennéefrom tarfile import TarFile, TarInfo 297a5d936bSAlex Bennéetry: 306e733da6SAlex Bennée from StringIO import StringIO 317a5d936bSAlex Bennéeexcept ImportError: 327a5d936bSAlex Bennée from io import StringIO 33a9f8d038SAlex Bennéefrom shutil import copy, rmtree 34414a8ce5SAlex Bennéefrom pwd import getpwuid 354485b04bSFam Zheng 36c9772570SSascha Silbe 3706cc3551SPhilippe Mathieu-DaudéFILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] 3806cc3551SPhilippe Mathieu-Daudé 3906cc3551SPhilippe Mathieu-Daudé 40c9772570SSascha SilbeDEVNULL = open(os.devnull, 'wb') 41c9772570SSascha Silbe 42c9772570SSascha Silbe 434485b04bSFam Zhengdef _text_checksum(text): 444485b04bSFam Zheng """Calculate a digest string unique to the text content""" 454485b04bSFam Zheng return hashlib.sha1(text).hexdigest() 464485b04bSFam Zheng 47438d1168SPhilippe Mathieu-Daudédef _file_checksum(filename): 48438d1168SPhilippe Mathieu-Daudé return _text_checksum(open(filename, 'rb').read()) 49438d1168SPhilippe Mathieu-Daudé 504485b04bSFam Zhengdef _guess_docker_command(): 514485b04bSFam Zheng """ Guess a working docker command or raise exception if not found""" 524485b04bSFam Zheng commands = [["docker"], ["sudo", "-n", "docker"]] 534485b04bSFam Zheng for cmd in commands: 540679f98bSEduardo Habkost try: 554485b04bSFam Zheng if subprocess.call(cmd + ["images"], 56c9772570SSascha Silbe stdout=DEVNULL, stderr=DEVNULL) == 0: 574485b04bSFam Zheng return cmd 580679f98bSEduardo Habkost except OSError: 590679f98bSEduardo Habkost pass 604485b04bSFam Zheng commands_txt = "\n".join([" " + " ".join(x) for x in commands]) 614485b04bSFam Zheng raise Exception("Cannot find working docker command. Tried:\n%s" % \ 624485b04bSFam Zheng commands_txt) 634485b04bSFam Zheng 642499ee9fSPhilippe Mathieu-Daudédef _copy_with_mkdir(src, root_dir, sub_path='.'): 65504ca3c2SAlex Bennée """Copy src into root_dir, creating sub_path as needed.""" 66504ca3c2SAlex Bennée dest_dir = os.path.normpath("%s/%s" % (root_dir, sub_path)) 67504ca3c2SAlex Bennée try: 68504ca3c2SAlex Bennée os.makedirs(dest_dir) 69504ca3c2SAlex Bennée except OSError: 70504ca3c2SAlex Bennée # we can safely ignore already created directories 71504ca3c2SAlex Bennée pass 72504ca3c2SAlex Bennée 73504ca3c2SAlex Bennée dest_file = "%s/%s" % (dest_dir, os.path.basename(src)) 74504ca3c2SAlex Bennée copy(src, dest_file) 75504ca3c2SAlex Bennée 76504ca3c2SAlex Bennée 77504ca3c2SAlex Bennéedef _get_so_libs(executable): 78504ca3c2SAlex Bennée """Return a list of libraries associated with an executable. 79504ca3c2SAlex Bennée 80504ca3c2SAlex Bennée The paths may be symbolic links which would need to be resolved to 81504ca3c2SAlex Bennée ensure theright data is copied.""" 82504ca3c2SAlex Bennée 83504ca3c2SAlex Bennée libs = [] 84504ca3c2SAlex Bennée ldd_re = re.compile(r"(/.*/)(\S*)") 85504ca3c2SAlex Bennée try: 86504ca3c2SAlex Bennée ldd_output = subprocess.check_output(["ldd", executable]) 87504ca3c2SAlex Bennée for line in ldd_output.split("\n"): 88504ca3c2SAlex Bennée search = ldd_re.search(line) 89504ca3c2SAlex Bennée if search and len(search.groups()) == 2: 90504ca3c2SAlex Bennée so_path = search.groups()[0] 91504ca3c2SAlex Bennée so_lib = search.groups()[1] 92504ca3c2SAlex Bennée libs.append("%s/%s" % (so_path, so_lib)) 93504ca3c2SAlex Bennée except subprocess.CalledProcessError: 94f03868bdSEduardo Habkost print("%s had no associated libraries (static build?)" % (executable)) 95504ca3c2SAlex Bennée 96504ca3c2SAlex Bennée return libs 97504ca3c2SAlex Bennée 98504ca3c2SAlex Bennéedef _copy_binary_with_libs(src, dest_dir): 99504ca3c2SAlex Bennée """Copy a binary executable and all its dependant libraries. 100504ca3c2SAlex Bennée 101504ca3c2SAlex Bennée This does rely on the host file-system being fairly multi-arch 102504ca3c2SAlex Bennée aware so the file don't clash with the guests layout.""" 103504ca3c2SAlex Bennée 104504ca3c2SAlex Bennée _copy_with_mkdir(src, dest_dir, "/usr/bin") 105504ca3c2SAlex Bennée 106504ca3c2SAlex Bennée libs = _get_so_libs(src) 107504ca3c2SAlex Bennée if libs: 108504ca3c2SAlex Bennée for l in libs: 109504ca3c2SAlex Bennée so_path = os.path.dirname(l) 110504ca3c2SAlex Bennée _copy_with_mkdir(l , dest_dir, so_path) 111504ca3c2SAlex Bennée 112c1958e9dSFam Zhengdef _read_qemu_dockerfile(img_name): 113c1958e9dSFam Zheng df = os.path.join(os.path.dirname(__file__), "dockerfiles", 114c1958e9dSFam Zheng img_name + ".docker") 115c1958e9dSFam Zheng return open(df, "r").read() 116c1958e9dSFam Zheng 117c1958e9dSFam Zhengdef _dockerfile_preprocess(df): 118c1958e9dSFam Zheng out = "" 119c1958e9dSFam Zheng for l in df.splitlines(): 120c1958e9dSFam Zheng if len(l.strip()) == 0 or l.startswith("#"): 121c1958e9dSFam Zheng continue 122c1958e9dSFam Zheng from_pref = "FROM qemu:" 123c1958e9dSFam Zheng if l.startswith(from_pref): 124c1958e9dSFam Zheng # TODO: Alternatively we could replace this line with "FROM $ID" 125c1958e9dSFam Zheng # where $ID is the image's hex id obtained with 126c1958e9dSFam Zheng # $ docker images $IMAGE --format="{{.Id}}" 127c1958e9dSFam Zheng # but unfortunately that's not supported by RHEL 7. 128c1958e9dSFam Zheng inlining = _read_qemu_dockerfile(l[len(from_pref):]) 129c1958e9dSFam Zheng out += _dockerfile_preprocess(inlining) 130c1958e9dSFam Zheng continue 131c1958e9dSFam Zheng out += l + "\n" 132c1958e9dSFam Zheng return out 133c1958e9dSFam Zheng 1344485b04bSFam Zhengclass Docker(object): 1354485b04bSFam Zheng """ Running Docker commands """ 1364485b04bSFam Zheng def __init__(self): 1374485b04bSFam Zheng self._command = _guess_docker_command() 1384485b04bSFam Zheng self._instances = [] 1394485b04bSFam Zheng atexit.register(self._kill_instances) 14097cba1a1SFam Zheng signal.signal(signal.SIGTERM, self._kill_instances) 14197cba1a1SFam Zheng signal.signal(signal.SIGHUP, self._kill_instances) 1424485b04bSFam Zheng 14358bf7b6dSFam Zheng def _do(self, cmd, quiet=True, **kwargs): 1444485b04bSFam Zheng if quiet: 145c9772570SSascha Silbe kwargs["stdout"] = DEVNULL 1464485b04bSFam Zheng return subprocess.call(self._command + cmd, **kwargs) 1474485b04bSFam Zheng 1480b95ff72SFam Zheng def _do_check(self, cmd, quiet=True, **kwargs): 1490b95ff72SFam Zheng if quiet: 1500b95ff72SFam Zheng kwargs["stdout"] = DEVNULL 1510b95ff72SFam Zheng return subprocess.check_call(self._command + cmd, **kwargs) 1520b95ff72SFam Zheng 1534485b04bSFam Zheng def _do_kill_instances(self, only_known, only_active=True): 1544485b04bSFam Zheng cmd = ["ps", "-q"] 1554485b04bSFam Zheng if not only_active: 1564485b04bSFam Zheng cmd.append("-a") 1574485b04bSFam Zheng for i in self._output(cmd).split(): 1584485b04bSFam Zheng resp = self._output(["inspect", i]) 1594485b04bSFam Zheng labels = json.loads(resp)[0]["Config"]["Labels"] 1604485b04bSFam Zheng active = json.loads(resp)[0]["State"]["Running"] 1614485b04bSFam Zheng if not labels: 1624485b04bSFam Zheng continue 1634485b04bSFam Zheng instance_uuid = labels.get("com.qemu.instance.uuid", None) 1644485b04bSFam Zheng if not instance_uuid: 1654485b04bSFam Zheng continue 1664485b04bSFam Zheng if only_known and instance_uuid not in self._instances: 1674485b04bSFam Zheng continue 168f03868bdSEduardo Habkost print("Terminating", i) 1694485b04bSFam Zheng if active: 1704485b04bSFam Zheng self._do(["kill", i]) 1714485b04bSFam Zheng self._do(["rm", i]) 1724485b04bSFam Zheng 1734485b04bSFam Zheng def clean(self): 1744485b04bSFam Zheng self._do_kill_instances(False, False) 1754485b04bSFam Zheng return 0 1764485b04bSFam Zheng 17797cba1a1SFam Zheng def _kill_instances(self, *args, **kwargs): 1784485b04bSFam Zheng return self._do_kill_instances(True) 1794485b04bSFam Zheng 1804485b04bSFam Zheng def _output(self, cmd, **kwargs): 1814485b04bSFam Zheng return subprocess.check_output(self._command + cmd, 1824485b04bSFam Zheng stderr=subprocess.STDOUT, 1834485b04bSFam Zheng **kwargs) 1844485b04bSFam Zheng 1854485b04bSFam Zheng def get_image_dockerfile_checksum(self, tag): 1864485b04bSFam Zheng resp = self._output(["inspect", tag]) 1874485b04bSFam Zheng labels = json.loads(resp)[0]["Config"].get("Labels", {}) 1884485b04bSFam Zheng return labels.get("com.qemu.dockerfile-checksum", "") 1894485b04bSFam Zheng 190414a8ce5SAlex Bennée def build_image(self, tag, docker_dir, dockerfile, 191438d1168SPhilippe Mathieu-Daudé quiet=True, user=False, argv=None, extra_files_cksum=[]): 1924485b04bSFam Zheng if argv == None: 1934485b04bSFam Zheng argv = [] 1944485b04bSFam Zheng 195a9f8d038SAlex Bennée tmp_df = tempfile.NamedTemporaryFile(dir=docker_dir, suffix=".docker") 1964485b04bSFam Zheng tmp_df.write(dockerfile) 1974485b04bSFam Zheng 198414a8ce5SAlex Bennée if user: 199414a8ce5SAlex Bennée uid = os.getuid() 200414a8ce5SAlex Bennée uname = getpwuid(uid).pw_name 201414a8ce5SAlex Bennée tmp_df.write("\n") 202414a8ce5SAlex Bennée tmp_df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % 203414a8ce5SAlex Bennée (uname, uid, uname)) 204414a8ce5SAlex Bennée 2054485b04bSFam Zheng tmp_df.write("\n") 2064485b04bSFam Zheng tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" % 207438d1168SPhilippe Mathieu-Daudé _text_checksum("\n".join([dockerfile] + 208438d1168SPhilippe Mathieu-Daudé extra_files_cksum))) 2094485b04bSFam Zheng tmp_df.flush() 210a9f8d038SAlex Bennée 2110b95ff72SFam Zheng self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + \ 212a9f8d038SAlex Bennée [docker_dir], 2134485b04bSFam Zheng quiet=quiet) 2144485b04bSFam Zheng 2156e733da6SAlex Bennée def update_image(self, tag, tarball, quiet=True): 2166e733da6SAlex Bennée "Update a tagged image using " 2176e733da6SAlex Bennée 2180b95ff72SFam Zheng self._do_check(["build", "-t", tag, "-"], quiet=quiet, stdin=tarball) 2196e733da6SAlex Bennée 2204485b04bSFam Zheng def image_matches_dockerfile(self, tag, dockerfile): 2214485b04bSFam Zheng try: 2224485b04bSFam Zheng checksum = self.get_image_dockerfile_checksum(tag) 2234485b04bSFam Zheng except Exception: 2244485b04bSFam Zheng return False 225c1958e9dSFam Zheng return checksum == _text_checksum(_dockerfile_preprocess(dockerfile)) 2264485b04bSFam Zheng 2274485b04bSFam Zheng def run(self, cmd, keep, quiet): 2284485b04bSFam Zheng label = uuid.uuid1().hex 2294485b04bSFam Zheng if not keep: 2304485b04bSFam Zheng self._instances.append(label) 2310b95ff72SFam Zheng ret = self._do_check(["run", "--label", 2324485b04bSFam Zheng "com.qemu.instance.uuid=" + label] + cmd, 2334485b04bSFam Zheng quiet=quiet) 2344485b04bSFam Zheng if not keep: 2354485b04bSFam Zheng self._instances.remove(label) 2364485b04bSFam Zheng return ret 2374485b04bSFam Zheng 2384b08af60SFam Zheng def command(self, cmd, argv, quiet): 2394b08af60SFam Zheng return self._do([cmd] + argv, quiet=quiet) 2404b08af60SFam Zheng 2414485b04bSFam Zhengclass SubCommand(object): 2424485b04bSFam Zheng """A SubCommand template base class""" 2434485b04bSFam Zheng name = None # Subcommand name 2444485b04bSFam Zheng def shared_args(self, parser): 2454485b04bSFam Zheng parser.add_argument("--quiet", action="store_true", 2464485b04bSFam Zheng help="Run quietly unless an error occured") 2474485b04bSFam Zheng 2484485b04bSFam Zheng def args(self, parser): 2494485b04bSFam Zheng """Setup argument parser""" 2504485b04bSFam Zheng pass 2514485b04bSFam Zheng def run(self, args, argv): 2524485b04bSFam Zheng """Run command. 2534485b04bSFam Zheng args: parsed argument by argument parser. 2544485b04bSFam Zheng argv: remaining arguments from sys.argv. 2554485b04bSFam Zheng """ 2564485b04bSFam Zheng pass 2574485b04bSFam Zheng 2584485b04bSFam Zhengclass RunCommand(SubCommand): 2594485b04bSFam Zheng """Invoke docker run and take care of cleaning up""" 2604485b04bSFam Zheng name = "run" 2614485b04bSFam Zheng def args(self, parser): 2624485b04bSFam Zheng parser.add_argument("--keep", action="store_true", 2634485b04bSFam Zheng help="Don't remove image when command completes") 2644485b04bSFam Zheng def run(self, args, argv): 2654485b04bSFam Zheng return Docker().run(argv, args.keep, quiet=args.quiet) 2664485b04bSFam Zheng 2674485b04bSFam Zhengclass BuildCommand(SubCommand): 2684485b04bSFam Zheng """ Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>""" 2694485b04bSFam Zheng name = "build" 2704485b04bSFam Zheng def args(self, parser): 271504ca3c2SAlex Bennée parser.add_argument("--include-executable", "-e", 272504ca3c2SAlex Bennée help="""Specify a binary that will be copied to the 273504ca3c2SAlex Bennée container together with all its dependent 274504ca3c2SAlex Bennée libraries""") 2754c84f662SPhilippe Mathieu-Daudé parser.add_argument("--extra-files", "-f", nargs='*', 2764c84f662SPhilippe Mathieu-Daudé help="""Specify files that will be copied in the 2774c84f662SPhilippe Mathieu-Daudé Docker image, fulfilling the ADD directive from the 2784c84f662SPhilippe Mathieu-Daudé Dockerfile""") 279414a8ce5SAlex Bennée parser.add_argument("--add-current-user", "-u", dest="user", 280414a8ce5SAlex Bennée action="store_true", 281414a8ce5SAlex Bennée help="Add the current user to image's passwd") 2824485b04bSFam Zheng parser.add_argument("tag", 2834485b04bSFam Zheng help="Image Tag") 2844485b04bSFam Zheng parser.add_argument("dockerfile", 2854485b04bSFam Zheng help="Dockerfile name") 2864485b04bSFam Zheng 2874485b04bSFam Zheng def run(self, args, argv): 2884485b04bSFam Zheng dockerfile = open(args.dockerfile, "rb").read() 2894485b04bSFam Zheng tag = args.tag 2904485b04bSFam Zheng 2914485b04bSFam Zheng dkr = Docker() 2926fe3ae3fSAlex Bennée if "--no-cache" not in argv and \ 2936fe3ae3fSAlex Bennée dkr.image_matches_dockerfile(tag, dockerfile): 2944485b04bSFam Zheng if not args.quiet: 295f03868bdSEduardo Habkost print("Image is up to date.") 296a9f8d038SAlex Bennée else: 297a9f8d038SAlex Bennée # Create a docker context directory for the build 298a9f8d038SAlex Bennée docker_dir = tempfile.mkdtemp(prefix="docker_build") 2994485b04bSFam Zheng 300920776eaSAlex Bennée # Is there a .pre file to run in the build context? 301920776eaSAlex Bennée docker_pre = os.path.splitext(args.dockerfile)[0]+".pre" 302920776eaSAlex Bennée if os.path.exists(docker_pre): 303f8042deaSSascha Silbe stdout = DEVNULL if args.quiet else None 304920776eaSAlex Bennée rc = subprocess.call(os.path.realpath(docker_pre), 305f8042deaSSascha Silbe cwd=docker_dir, stdout=stdout) 306920776eaSAlex Bennée if rc == 3: 307f03868bdSEduardo Habkost print("Skip") 308920776eaSAlex Bennée return 0 309920776eaSAlex Bennée elif rc != 0: 310f03868bdSEduardo Habkost print("%s exited with code %d" % (docker_pre, rc)) 311920776eaSAlex Bennée return 1 312920776eaSAlex Bennée 3134c84f662SPhilippe Mathieu-Daudé # Copy any extra files into the Docker context. These can be 3144c84f662SPhilippe Mathieu-Daudé # included by the use of the ADD directive in the Dockerfile. 315438d1168SPhilippe Mathieu-Daudé cksum = [] 316504ca3c2SAlex Bennée if args.include_executable: 317438d1168SPhilippe Mathieu-Daudé # FIXME: there is no checksum of this executable and the linked 318438d1168SPhilippe Mathieu-Daudé # libraries, once the image built any change of this executable 319438d1168SPhilippe Mathieu-Daudé # or any library won't trigger another build. 3204c84f662SPhilippe Mathieu-Daudé _copy_binary_with_libs(args.include_executable, docker_dir) 3214c84f662SPhilippe Mathieu-Daudé for filename in args.extra_files or []: 3224c84f662SPhilippe Mathieu-Daudé _copy_with_mkdir(filename, docker_dir) 323438d1168SPhilippe Mathieu-Daudé cksum += [_file_checksum(filename)] 324504ca3c2SAlex Bennée 32506cc3551SPhilippe Mathieu-Daudé argv += ["--build-arg=" + k.lower() + "=" + v 32606cc3551SPhilippe Mathieu-Daudé for k, v in os.environ.iteritems() 32706cc3551SPhilippe Mathieu-Daudé if k.lower() in FILTERED_ENV_NAMES] 328a9f8d038SAlex Bennée dkr.build_image(tag, docker_dir, dockerfile, 329438d1168SPhilippe Mathieu-Daudé quiet=args.quiet, user=args.user, argv=argv, 330438d1168SPhilippe Mathieu-Daudé extra_files_cksum=cksum) 331a9f8d038SAlex Bennée 332a9f8d038SAlex Bennée rmtree(docker_dir) 333a9f8d038SAlex Bennée 3344485b04bSFam Zheng return 0 3354485b04bSFam Zheng 3366e733da6SAlex Bennéeclass UpdateCommand(SubCommand): 3376e733da6SAlex Bennée """ Update a docker image with new executables. Arguments: <tag> <executable>""" 3386e733da6SAlex Bennée name = "update" 3396e733da6SAlex Bennée def args(self, parser): 3406e733da6SAlex Bennée parser.add_argument("tag", 3416e733da6SAlex Bennée help="Image Tag") 3426e733da6SAlex Bennée parser.add_argument("executable", 3436e733da6SAlex Bennée help="Executable to copy") 3446e733da6SAlex Bennée 3456e733da6SAlex Bennée def run(self, args, argv): 3466e733da6SAlex Bennée # Create a temporary tarball with our whole build context and 3476e733da6SAlex Bennée # dockerfile for the update 3486e733da6SAlex Bennée tmp = tempfile.NamedTemporaryFile(suffix="dckr.tar.gz") 3496e733da6SAlex Bennée tmp_tar = TarFile(fileobj=tmp, mode='w') 3506e733da6SAlex Bennée 3516e733da6SAlex Bennée # Add the executable to the tarball 3526e733da6SAlex Bennée bn = os.path.basename(args.executable) 3536e733da6SAlex Bennée ff = "/usr/bin/%s" % bn 3546e733da6SAlex Bennée tmp_tar.add(args.executable, arcname=ff) 3556e733da6SAlex Bennée 3566e733da6SAlex Bennée # Add any associated libraries 3576e733da6SAlex Bennée libs = _get_so_libs(args.executable) 3586e733da6SAlex Bennée if libs: 3596e733da6SAlex Bennée for l in libs: 3606e733da6SAlex Bennée tmp_tar.add(os.path.realpath(l), arcname=l) 3616e733da6SAlex Bennée 3626e733da6SAlex Bennée # Create a Docker buildfile 3636e733da6SAlex Bennée df = StringIO() 3646e733da6SAlex Bennée df.write("FROM %s\n" % args.tag) 3656e733da6SAlex Bennée df.write("ADD . /\n") 3666e733da6SAlex Bennée df.seek(0) 3676e733da6SAlex Bennée 3686e733da6SAlex Bennée df_tar = TarInfo(name="Dockerfile") 3696e733da6SAlex Bennée df_tar.size = len(df.buf) 3706e733da6SAlex Bennée tmp_tar.addfile(df_tar, fileobj=df) 3716e733da6SAlex Bennée 3726e733da6SAlex Bennée tmp_tar.close() 3736e733da6SAlex Bennée 3746e733da6SAlex Bennée # reset the file pointers 3756e733da6SAlex Bennée tmp.flush() 3766e733da6SAlex Bennée tmp.seek(0) 3776e733da6SAlex Bennée 3786e733da6SAlex Bennée # Run the build with our tarball context 3796e733da6SAlex Bennée dkr = Docker() 3806e733da6SAlex Bennée dkr.update_image(args.tag, tmp, quiet=args.quiet) 3816e733da6SAlex Bennée 3826e733da6SAlex Bennée return 0 3836e733da6SAlex Bennée 3844485b04bSFam Zhengclass CleanCommand(SubCommand): 3854485b04bSFam Zheng """Clean up docker instances""" 3864485b04bSFam Zheng name = "clean" 3874485b04bSFam Zheng def run(self, args, argv): 3884485b04bSFam Zheng Docker().clean() 3894485b04bSFam Zheng return 0 3904485b04bSFam Zheng 3914b08af60SFam Zhengclass ImagesCommand(SubCommand): 3924b08af60SFam Zheng """Run "docker images" command""" 3934b08af60SFam Zheng name = "images" 3944b08af60SFam Zheng def run(self, args, argv): 3954b08af60SFam Zheng return Docker().command("images", argv, args.quiet) 3964b08af60SFam Zheng 39715df9d37SAlex Bennée 39815df9d37SAlex Bennéeclass ProbeCommand(SubCommand): 39915df9d37SAlex Bennée """Probe if we can run docker automatically""" 40015df9d37SAlex Bennée name = "probe" 40115df9d37SAlex Bennée 40215df9d37SAlex Bennée def run(self, args, argv): 40315df9d37SAlex Bennée try: 40415df9d37SAlex Bennée docker = Docker() 40515df9d37SAlex Bennée if docker._command[0] == "docker": 406f03868bdSEduardo Habkost print("yes") 40715df9d37SAlex Bennée elif docker._command[0] == "sudo": 408f03868bdSEduardo Habkost print("sudo") 40915df9d37SAlex Bennée except Exception: 410f03868bdSEduardo Habkost print("no") 41115df9d37SAlex Bennée 41215df9d37SAlex Bennée return 41315df9d37SAlex Bennée 41415df9d37SAlex Bennée 415*5e03c2d8SAlex Bennéeclass CcCommand(SubCommand): 416*5e03c2d8SAlex Bennée """Compile sources with cc in images""" 417*5e03c2d8SAlex Bennée name = "cc" 418*5e03c2d8SAlex Bennée 419*5e03c2d8SAlex Bennée def args(self, parser): 420*5e03c2d8SAlex Bennée parser.add_argument("--image", "-i", required=True, 421*5e03c2d8SAlex Bennée help="The docker image in which to run cc") 422*5e03c2d8SAlex Bennée parser.add_argument("--source-path", "-s", nargs="*", dest="paths", 423*5e03c2d8SAlex Bennée help="""Extra paths to (ro) mount into container for 424*5e03c2d8SAlex Bennée reading sources""") 425*5e03c2d8SAlex Bennée 426*5e03c2d8SAlex Bennée def run(self, args, argv): 427*5e03c2d8SAlex Bennée if argv and argv[0] == "--": 428*5e03c2d8SAlex Bennée argv = argv[1:] 429*5e03c2d8SAlex Bennée cwd = os.getcwd() 430*5e03c2d8SAlex Bennée cmd = ["--rm", "-w", cwd, 431*5e03c2d8SAlex Bennée "-v", "%s:%s:rw" % (cwd, cwd)] 432*5e03c2d8SAlex Bennée if args.paths: 433*5e03c2d8SAlex Bennée for p in args.paths: 434*5e03c2d8SAlex Bennée cmd += ["-v", "%s:%s:ro,z" % (p, p)] 435*5e03c2d8SAlex Bennée cmd += [args.image, "cc"] 436*5e03c2d8SAlex Bennée cmd += argv 437*5e03c2d8SAlex Bennée return Docker().command("run", cmd, args.quiet) 438*5e03c2d8SAlex Bennée 439*5e03c2d8SAlex Bennée 4404485b04bSFam Zhengdef main(): 4414485b04bSFam Zheng parser = argparse.ArgumentParser(description="A Docker helper", 4424485b04bSFam Zheng usage="%s <subcommand> ..." % os.path.basename(sys.argv[0])) 4434485b04bSFam Zheng subparsers = parser.add_subparsers(title="subcommands", help=None) 4444485b04bSFam Zheng for cls in SubCommand.__subclasses__(): 4454485b04bSFam Zheng cmd = cls() 4464485b04bSFam Zheng subp = subparsers.add_parser(cmd.name, help=cmd.__doc__) 4474485b04bSFam Zheng cmd.shared_args(subp) 4484485b04bSFam Zheng cmd.args(subp) 4494485b04bSFam Zheng subp.set_defaults(cmdobj=cmd) 4504485b04bSFam Zheng args, argv = parser.parse_known_args() 4514485b04bSFam Zheng return args.cmdobj.run(args, argv) 4524485b04bSFam Zheng 4534485b04bSFam Zhengif __name__ == "__main__": 4544485b04bSFam Zheng sys.exit(main()) 455