xref: /qemu/tests/docker/docker.py (revision f03868bd5653265e97b253102d77d83ea85efdea)
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
14*f03868bdSEduardo 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
296e733da6SAlex Bennéefrom StringIO import StringIO
30a9f8d038SAlex Bennéefrom shutil import copy, rmtree
31414a8ce5SAlex Bennéefrom pwd import getpwuid
324485b04bSFam Zheng
33c9772570SSascha Silbe
3406cc3551SPhilippe Mathieu-DaudéFILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy']
3506cc3551SPhilippe Mathieu-Daudé
3606cc3551SPhilippe Mathieu-Daudé
37c9772570SSascha SilbeDEVNULL = open(os.devnull, 'wb')
38c9772570SSascha Silbe
39c9772570SSascha Silbe
404485b04bSFam Zhengdef _text_checksum(text):
414485b04bSFam Zheng    """Calculate a digest string unique to the text content"""
424485b04bSFam Zheng    return hashlib.sha1(text).hexdigest()
434485b04bSFam Zheng
44438d1168SPhilippe Mathieu-Daudédef _file_checksum(filename):
45438d1168SPhilippe Mathieu-Daudé    return _text_checksum(open(filename, 'rb').read())
46438d1168SPhilippe Mathieu-Daudé
474485b04bSFam Zhengdef _guess_docker_command():
484485b04bSFam Zheng    """ Guess a working docker command or raise exception if not found"""
494485b04bSFam Zheng    commands = [["docker"], ["sudo", "-n", "docker"]]
504485b04bSFam Zheng    for cmd in commands:
510679f98bSEduardo Habkost        try:
524485b04bSFam Zheng            if subprocess.call(cmd + ["images"],
53c9772570SSascha Silbe                               stdout=DEVNULL, stderr=DEVNULL) == 0:
544485b04bSFam Zheng                return cmd
550679f98bSEduardo Habkost        except OSError:
560679f98bSEduardo Habkost            pass
574485b04bSFam Zheng    commands_txt = "\n".join(["  " + " ".join(x) for x in commands])
584485b04bSFam Zheng    raise Exception("Cannot find working docker command. Tried:\n%s" % \
594485b04bSFam Zheng                    commands_txt)
604485b04bSFam Zheng
612499ee9fSPhilippe Mathieu-Daudédef _copy_with_mkdir(src, root_dir, sub_path='.'):
62504ca3c2SAlex Bennée    """Copy src into root_dir, creating sub_path as needed."""
63504ca3c2SAlex Bennée    dest_dir = os.path.normpath("%s/%s" % (root_dir, sub_path))
64504ca3c2SAlex Bennée    try:
65504ca3c2SAlex Bennée        os.makedirs(dest_dir)
66504ca3c2SAlex Bennée    except OSError:
67504ca3c2SAlex Bennée        # we can safely ignore already created directories
68504ca3c2SAlex Bennée        pass
69504ca3c2SAlex Bennée
70504ca3c2SAlex Bennée    dest_file = "%s/%s" % (dest_dir, os.path.basename(src))
71504ca3c2SAlex Bennée    copy(src, dest_file)
72504ca3c2SAlex Bennée
73504ca3c2SAlex Bennée
74504ca3c2SAlex Bennéedef _get_so_libs(executable):
75504ca3c2SAlex Bennée    """Return a list of libraries associated with an executable.
76504ca3c2SAlex Bennée
77504ca3c2SAlex Bennée    The paths may be symbolic links which would need to be resolved to
78504ca3c2SAlex Bennée    ensure theright data is copied."""
79504ca3c2SAlex Bennée
80504ca3c2SAlex Bennée    libs = []
81504ca3c2SAlex Bennée    ldd_re = re.compile(r"(/.*/)(\S*)")
82504ca3c2SAlex Bennée    try:
83504ca3c2SAlex Bennée        ldd_output = subprocess.check_output(["ldd", executable])
84504ca3c2SAlex Bennée        for line in ldd_output.split("\n"):
85504ca3c2SAlex Bennée            search = ldd_re.search(line)
86504ca3c2SAlex Bennée            if search and len(search.groups()) == 2:
87504ca3c2SAlex Bennée                so_path = search.groups()[0]
88504ca3c2SAlex Bennée                so_lib = search.groups()[1]
89504ca3c2SAlex Bennée                libs.append("%s/%s" % (so_path, so_lib))
90504ca3c2SAlex Bennée    except subprocess.CalledProcessError:
91*f03868bdSEduardo Habkost        print("%s had no associated libraries (static build?)" % (executable))
92504ca3c2SAlex Bennée
93504ca3c2SAlex Bennée    return libs
94504ca3c2SAlex Bennée
95504ca3c2SAlex Bennéedef _copy_binary_with_libs(src, dest_dir):
96504ca3c2SAlex Bennée    """Copy a binary executable and all its dependant libraries.
97504ca3c2SAlex Bennée
98504ca3c2SAlex Bennée    This does rely on the host file-system being fairly multi-arch
99504ca3c2SAlex Bennée    aware so the file don't clash with the guests layout."""
100504ca3c2SAlex Bennée
101504ca3c2SAlex Bennée    _copy_with_mkdir(src, dest_dir, "/usr/bin")
102504ca3c2SAlex Bennée
103504ca3c2SAlex Bennée    libs = _get_so_libs(src)
104504ca3c2SAlex Bennée    if libs:
105504ca3c2SAlex Bennée        for l in libs:
106504ca3c2SAlex Bennée            so_path = os.path.dirname(l)
107504ca3c2SAlex Bennée            _copy_with_mkdir(l , dest_dir, so_path)
108504ca3c2SAlex Bennée
109c1958e9dSFam Zhengdef _read_qemu_dockerfile(img_name):
110c1958e9dSFam Zheng    df = os.path.join(os.path.dirname(__file__), "dockerfiles",
111c1958e9dSFam Zheng                      img_name + ".docker")
112c1958e9dSFam Zheng    return open(df, "r").read()
113c1958e9dSFam Zheng
114c1958e9dSFam Zhengdef _dockerfile_preprocess(df):
115c1958e9dSFam Zheng    out = ""
116c1958e9dSFam Zheng    for l in df.splitlines():
117c1958e9dSFam Zheng        if len(l.strip()) == 0 or l.startswith("#"):
118c1958e9dSFam Zheng            continue
119c1958e9dSFam Zheng        from_pref = "FROM qemu:"
120c1958e9dSFam Zheng        if l.startswith(from_pref):
121c1958e9dSFam Zheng            # TODO: Alternatively we could replace this line with "FROM $ID"
122c1958e9dSFam Zheng            # where $ID is the image's hex id obtained with
123c1958e9dSFam Zheng            #    $ docker images $IMAGE --format="{{.Id}}"
124c1958e9dSFam Zheng            # but unfortunately that's not supported by RHEL 7.
125c1958e9dSFam Zheng            inlining = _read_qemu_dockerfile(l[len(from_pref):])
126c1958e9dSFam Zheng            out += _dockerfile_preprocess(inlining)
127c1958e9dSFam Zheng            continue
128c1958e9dSFam Zheng        out += l + "\n"
129c1958e9dSFam Zheng    return out
130c1958e9dSFam Zheng
1314485b04bSFam Zhengclass Docker(object):
1324485b04bSFam Zheng    """ Running Docker commands """
1334485b04bSFam Zheng    def __init__(self):
1344485b04bSFam Zheng        self._command = _guess_docker_command()
1354485b04bSFam Zheng        self._instances = []
1364485b04bSFam Zheng        atexit.register(self._kill_instances)
13797cba1a1SFam Zheng        signal.signal(signal.SIGTERM, self._kill_instances)
13897cba1a1SFam Zheng        signal.signal(signal.SIGHUP, self._kill_instances)
1394485b04bSFam Zheng
14058bf7b6dSFam Zheng    def _do(self, cmd, quiet=True, **kwargs):
1414485b04bSFam Zheng        if quiet:
142c9772570SSascha Silbe            kwargs["stdout"] = DEVNULL
1434485b04bSFam Zheng        return subprocess.call(self._command + cmd, **kwargs)
1444485b04bSFam Zheng
1450b95ff72SFam Zheng    def _do_check(self, cmd, quiet=True, **kwargs):
1460b95ff72SFam Zheng        if quiet:
1470b95ff72SFam Zheng            kwargs["stdout"] = DEVNULL
1480b95ff72SFam Zheng        return subprocess.check_call(self._command + cmd, **kwargs)
1490b95ff72SFam Zheng
1504485b04bSFam Zheng    def _do_kill_instances(self, only_known, only_active=True):
1514485b04bSFam Zheng        cmd = ["ps", "-q"]
1524485b04bSFam Zheng        if not only_active:
1534485b04bSFam Zheng            cmd.append("-a")
1544485b04bSFam Zheng        for i in self._output(cmd).split():
1554485b04bSFam Zheng            resp = self._output(["inspect", i])
1564485b04bSFam Zheng            labels = json.loads(resp)[0]["Config"]["Labels"]
1574485b04bSFam Zheng            active = json.loads(resp)[0]["State"]["Running"]
1584485b04bSFam Zheng            if not labels:
1594485b04bSFam Zheng                continue
1604485b04bSFam Zheng            instance_uuid = labels.get("com.qemu.instance.uuid", None)
1614485b04bSFam Zheng            if not instance_uuid:
1624485b04bSFam Zheng                continue
1634485b04bSFam Zheng            if only_known and instance_uuid not in self._instances:
1644485b04bSFam Zheng                continue
165*f03868bdSEduardo Habkost            print("Terminating", i)
1664485b04bSFam Zheng            if active:
1674485b04bSFam Zheng                self._do(["kill", i])
1684485b04bSFam Zheng            self._do(["rm", i])
1694485b04bSFam Zheng
1704485b04bSFam Zheng    def clean(self):
1714485b04bSFam Zheng        self._do_kill_instances(False, False)
1724485b04bSFam Zheng        return 0
1734485b04bSFam Zheng
17497cba1a1SFam Zheng    def _kill_instances(self, *args, **kwargs):
1754485b04bSFam Zheng        return self._do_kill_instances(True)
1764485b04bSFam Zheng
1774485b04bSFam Zheng    def _output(self, cmd, **kwargs):
1784485b04bSFam Zheng        return subprocess.check_output(self._command + cmd,
1794485b04bSFam Zheng                                       stderr=subprocess.STDOUT,
1804485b04bSFam Zheng                                       **kwargs)
1814485b04bSFam Zheng
1824485b04bSFam Zheng    def get_image_dockerfile_checksum(self, tag):
1834485b04bSFam Zheng        resp = self._output(["inspect", tag])
1844485b04bSFam Zheng        labels = json.loads(resp)[0]["Config"].get("Labels", {})
1854485b04bSFam Zheng        return labels.get("com.qemu.dockerfile-checksum", "")
1864485b04bSFam Zheng
187414a8ce5SAlex Bennée    def build_image(self, tag, docker_dir, dockerfile,
188438d1168SPhilippe Mathieu-Daudé                    quiet=True, user=False, argv=None, extra_files_cksum=[]):
1894485b04bSFam Zheng        if argv == None:
1904485b04bSFam Zheng            argv = []
1914485b04bSFam Zheng
192a9f8d038SAlex Bennée        tmp_df = tempfile.NamedTemporaryFile(dir=docker_dir, suffix=".docker")
1934485b04bSFam Zheng        tmp_df.write(dockerfile)
1944485b04bSFam Zheng
195414a8ce5SAlex Bennée        if user:
196414a8ce5SAlex Bennée            uid = os.getuid()
197414a8ce5SAlex Bennée            uname = getpwuid(uid).pw_name
198414a8ce5SAlex Bennée            tmp_df.write("\n")
199414a8ce5SAlex Bennée            tmp_df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" %
200414a8ce5SAlex Bennée                         (uname, uid, uname))
201414a8ce5SAlex Bennée
2024485b04bSFam Zheng        tmp_df.write("\n")
2034485b04bSFam Zheng        tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" %
204438d1168SPhilippe Mathieu-Daudé                     _text_checksum("\n".join([dockerfile] +
205438d1168SPhilippe Mathieu-Daudé                                    extra_files_cksum)))
2064485b04bSFam Zheng        tmp_df.flush()
207a9f8d038SAlex Bennée
2080b95ff72SFam Zheng        self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + \
209a9f8d038SAlex Bennée                       [docker_dir],
2104485b04bSFam Zheng                       quiet=quiet)
2114485b04bSFam Zheng
2126e733da6SAlex Bennée    def update_image(self, tag, tarball, quiet=True):
2136e733da6SAlex Bennée        "Update a tagged image using "
2146e733da6SAlex Bennée
2150b95ff72SFam Zheng        self._do_check(["build", "-t", tag, "-"], quiet=quiet, stdin=tarball)
2166e733da6SAlex Bennée
2174485b04bSFam Zheng    def image_matches_dockerfile(self, tag, dockerfile):
2184485b04bSFam Zheng        try:
2194485b04bSFam Zheng            checksum = self.get_image_dockerfile_checksum(tag)
2204485b04bSFam Zheng        except Exception:
2214485b04bSFam Zheng            return False
222c1958e9dSFam Zheng        return checksum == _text_checksum(_dockerfile_preprocess(dockerfile))
2234485b04bSFam Zheng
2244485b04bSFam Zheng    def run(self, cmd, keep, quiet):
2254485b04bSFam Zheng        label = uuid.uuid1().hex
2264485b04bSFam Zheng        if not keep:
2274485b04bSFam Zheng            self._instances.append(label)
2280b95ff72SFam Zheng        ret = self._do_check(["run", "--label",
2294485b04bSFam Zheng                             "com.qemu.instance.uuid=" + label] + cmd,
2304485b04bSFam Zheng                             quiet=quiet)
2314485b04bSFam Zheng        if not keep:
2324485b04bSFam Zheng            self._instances.remove(label)
2334485b04bSFam Zheng        return ret
2344485b04bSFam Zheng
2354b08af60SFam Zheng    def command(self, cmd, argv, quiet):
2364b08af60SFam Zheng        return self._do([cmd] + argv, quiet=quiet)
2374b08af60SFam Zheng
2384485b04bSFam Zhengclass SubCommand(object):
2394485b04bSFam Zheng    """A SubCommand template base class"""
2404485b04bSFam Zheng    name = None # Subcommand name
2414485b04bSFam Zheng    def shared_args(self, parser):
2424485b04bSFam Zheng        parser.add_argument("--quiet", action="store_true",
2434485b04bSFam Zheng                            help="Run quietly unless an error occured")
2444485b04bSFam Zheng
2454485b04bSFam Zheng    def args(self, parser):
2464485b04bSFam Zheng        """Setup argument parser"""
2474485b04bSFam Zheng        pass
2484485b04bSFam Zheng    def run(self, args, argv):
2494485b04bSFam Zheng        """Run command.
2504485b04bSFam Zheng        args: parsed argument by argument parser.
2514485b04bSFam Zheng        argv: remaining arguments from sys.argv.
2524485b04bSFam Zheng        """
2534485b04bSFam Zheng        pass
2544485b04bSFam Zheng
2554485b04bSFam Zhengclass RunCommand(SubCommand):
2564485b04bSFam Zheng    """Invoke docker run and take care of cleaning up"""
2574485b04bSFam Zheng    name = "run"
2584485b04bSFam Zheng    def args(self, parser):
2594485b04bSFam Zheng        parser.add_argument("--keep", action="store_true",
2604485b04bSFam Zheng                            help="Don't remove image when command completes")
2614485b04bSFam Zheng    def run(self, args, argv):
2624485b04bSFam Zheng        return Docker().run(argv, args.keep, quiet=args.quiet)
2634485b04bSFam Zheng
2644485b04bSFam Zhengclass BuildCommand(SubCommand):
2654485b04bSFam Zheng    """ Build docker image out of a dockerfile. Arguments: <tag> <dockerfile>"""
2664485b04bSFam Zheng    name = "build"
2674485b04bSFam Zheng    def args(self, parser):
268504ca3c2SAlex Bennée        parser.add_argument("--include-executable", "-e",
269504ca3c2SAlex Bennée                            help="""Specify a binary that will be copied to the
270504ca3c2SAlex Bennée                            container together with all its dependent
271504ca3c2SAlex Bennée                            libraries""")
2724c84f662SPhilippe Mathieu-Daudé        parser.add_argument("--extra-files", "-f", nargs='*',
2734c84f662SPhilippe Mathieu-Daudé                            help="""Specify files that will be copied in the
2744c84f662SPhilippe Mathieu-Daudé                            Docker image, fulfilling the ADD directive from the
2754c84f662SPhilippe Mathieu-Daudé                            Dockerfile""")
276414a8ce5SAlex Bennée        parser.add_argument("--add-current-user", "-u", dest="user",
277414a8ce5SAlex Bennée                            action="store_true",
278414a8ce5SAlex Bennée                            help="Add the current user to image's passwd")
2794485b04bSFam Zheng        parser.add_argument("tag",
2804485b04bSFam Zheng                            help="Image Tag")
2814485b04bSFam Zheng        parser.add_argument("dockerfile",
2824485b04bSFam Zheng                            help="Dockerfile name")
2834485b04bSFam Zheng
2844485b04bSFam Zheng    def run(self, args, argv):
2854485b04bSFam Zheng        dockerfile = open(args.dockerfile, "rb").read()
2864485b04bSFam Zheng        tag = args.tag
2874485b04bSFam Zheng
2884485b04bSFam Zheng        dkr = Docker()
2896fe3ae3fSAlex Bennée        if "--no-cache" not in argv and \
2906fe3ae3fSAlex Bennée           dkr.image_matches_dockerfile(tag, dockerfile):
2914485b04bSFam Zheng            if not args.quiet:
292*f03868bdSEduardo Habkost                print("Image is up to date.")
293a9f8d038SAlex Bennée        else:
294a9f8d038SAlex Bennée            # Create a docker context directory for the build
295a9f8d038SAlex Bennée            docker_dir = tempfile.mkdtemp(prefix="docker_build")
2964485b04bSFam Zheng
297920776eaSAlex Bennée            # Is there a .pre file to run in the build context?
298920776eaSAlex Bennée            docker_pre = os.path.splitext(args.dockerfile)[0]+".pre"
299920776eaSAlex Bennée            if os.path.exists(docker_pre):
300f8042deaSSascha Silbe                stdout = DEVNULL if args.quiet else None
301920776eaSAlex Bennée                rc = subprocess.call(os.path.realpath(docker_pre),
302f8042deaSSascha Silbe                                     cwd=docker_dir, stdout=stdout)
303920776eaSAlex Bennée                if rc == 3:
304*f03868bdSEduardo Habkost                    print("Skip")
305920776eaSAlex Bennée                    return 0
306920776eaSAlex Bennée                elif rc != 0:
307*f03868bdSEduardo Habkost                    print("%s exited with code %d" % (docker_pre, rc))
308920776eaSAlex Bennée                    return 1
309920776eaSAlex Bennée
3104c84f662SPhilippe Mathieu-Daudé            # Copy any extra files into the Docker context. These can be
3114c84f662SPhilippe Mathieu-Daudé            # included by the use of the ADD directive in the Dockerfile.
312438d1168SPhilippe Mathieu-Daudé            cksum = []
313504ca3c2SAlex Bennée            if args.include_executable:
314438d1168SPhilippe Mathieu-Daudé                # FIXME: there is no checksum of this executable and the linked
315438d1168SPhilippe Mathieu-Daudé                # libraries, once the image built any change of this executable
316438d1168SPhilippe Mathieu-Daudé                # or any library won't trigger another build.
3174c84f662SPhilippe Mathieu-Daudé                _copy_binary_with_libs(args.include_executable, docker_dir)
3184c84f662SPhilippe Mathieu-Daudé            for filename in args.extra_files or []:
3194c84f662SPhilippe Mathieu-Daudé                _copy_with_mkdir(filename, docker_dir)
320438d1168SPhilippe Mathieu-Daudé                cksum += [_file_checksum(filename)]
321504ca3c2SAlex Bennée
32206cc3551SPhilippe Mathieu-Daudé            argv += ["--build-arg=" + k.lower() + "=" + v
32306cc3551SPhilippe Mathieu-Daudé                        for k, v in os.environ.iteritems()
32406cc3551SPhilippe Mathieu-Daudé                        if k.lower() in FILTERED_ENV_NAMES]
325a9f8d038SAlex Bennée            dkr.build_image(tag, docker_dir, dockerfile,
326438d1168SPhilippe Mathieu-Daudé                            quiet=args.quiet, user=args.user, argv=argv,
327438d1168SPhilippe Mathieu-Daudé                            extra_files_cksum=cksum)
328a9f8d038SAlex Bennée
329a9f8d038SAlex Bennée            rmtree(docker_dir)
330a9f8d038SAlex Bennée
3314485b04bSFam Zheng        return 0
3324485b04bSFam Zheng
3336e733da6SAlex Bennéeclass UpdateCommand(SubCommand):
3346e733da6SAlex Bennée    """ Update a docker image with new executables. Arguments: <tag> <executable>"""
3356e733da6SAlex Bennée    name = "update"
3366e733da6SAlex Bennée    def args(self, parser):
3376e733da6SAlex Bennée        parser.add_argument("tag",
3386e733da6SAlex Bennée                            help="Image Tag")
3396e733da6SAlex Bennée        parser.add_argument("executable",
3406e733da6SAlex Bennée                            help="Executable to copy")
3416e733da6SAlex Bennée
3426e733da6SAlex Bennée    def run(self, args, argv):
3436e733da6SAlex Bennée        # Create a temporary tarball with our whole build context and
3446e733da6SAlex Bennée        # dockerfile for the update
3456e733da6SAlex Bennée        tmp = tempfile.NamedTemporaryFile(suffix="dckr.tar.gz")
3466e733da6SAlex Bennée        tmp_tar = TarFile(fileobj=tmp, mode='w')
3476e733da6SAlex Bennée
3486e733da6SAlex Bennée        # Add the executable to the tarball
3496e733da6SAlex Bennée        bn = os.path.basename(args.executable)
3506e733da6SAlex Bennée        ff = "/usr/bin/%s" % bn
3516e733da6SAlex Bennée        tmp_tar.add(args.executable, arcname=ff)
3526e733da6SAlex Bennée
3536e733da6SAlex Bennée        # Add any associated libraries
3546e733da6SAlex Bennée        libs = _get_so_libs(args.executable)
3556e733da6SAlex Bennée        if libs:
3566e733da6SAlex Bennée            for l in libs:
3576e733da6SAlex Bennée                tmp_tar.add(os.path.realpath(l), arcname=l)
3586e733da6SAlex Bennée
3596e733da6SAlex Bennée        # Create a Docker buildfile
3606e733da6SAlex Bennée        df = StringIO()
3616e733da6SAlex Bennée        df.write("FROM %s\n" % args.tag)
3626e733da6SAlex Bennée        df.write("ADD . /\n")
3636e733da6SAlex Bennée        df.seek(0)
3646e733da6SAlex Bennée
3656e733da6SAlex Bennée        df_tar = TarInfo(name="Dockerfile")
3666e733da6SAlex Bennée        df_tar.size = len(df.buf)
3676e733da6SAlex Bennée        tmp_tar.addfile(df_tar, fileobj=df)
3686e733da6SAlex Bennée
3696e733da6SAlex Bennée        tmp_tar.close()
3706e733da6SAlex Bennée
3716e733da6SAlex Bennée        # reset the file pointers
3726e733da6SAlex Bennée        tmp.flush()
3736e733da6SAlex Bennée        tmp.seek(0)
3746e733da6SAlex Bennée
3756e733da6SAlex Bennée        # Run the build with our tarball context
3766e733da6SAlex Bennée        dkr = Docker()
3776e733da6SAlex Bennée        dkr.update_image(args.tag, tmp, quiet=args.quiet)
3786e733da6SAlex Bennée
3796e733da6SAlex Bennée        return 0
3806e733da6SAlex Bennée
3814485b04bSFam Zhengclass CleanCommand(SubCommand):
3824485b04bSFam Zheng    """Clean up docker instances"""
3834485b04bSFam Zheng    name = "clean"
3844485b04bSFam Zheng    def run(self, args, argv):
3854485b04bSFam Zheng        Docker().clean()
3864485b04bSFam Zheng        return 0
3874485b04bSFam Zheng
3884b08af60SFam Zhengclass ImagesCommand(SubCommand):
3894b08af60SFam Zheng    """Run "docker images" command"""
3904b08af60SFam Zheng    name = "images"
3914b08af60SFam Zheng    def run(self, args, argv):
3924b08af60SFam Zheng        return Docker().command("images", argv, args.quiet)
3934b08af60SFam Zheng
39415df9d37SAlex Bennée
39515df9d37SAlex Bennéeclass ProbeCommand(SubCommand):
39615df9d37SAlex Bennée    """Probe if we can run docker automatically"""
39715df9d37SAlex Bennée    name = "probe"
39815df9d37SAlex Bennée
39915df9d37SAlex Bennée    def run(self, args, argv):
40015df9d37SAlex Bennée        try:
40115df9d37SAlex Bennée            docker = Docker()
40215df9d37SAlex Bennée            if docker._command[0] == "docker":
403*f03868bdSEduardo Habkost                print("yes")
40415df9d37SAlex Bennée            elif docker._command[0] == "sudo":
405*f03868bdSEduardo Habkost                print("sudo")
40615df9d37SAlex Bennée        except Exception:
407*f03868bdSEduardo Habkost            print("no")
40815df9d37SAlex Bennée
40915df9d37SAlex Bennée        return
41015df9d37SAlex Bennée
41115df9d37SAlex Bennée
4124485b04bSFam Zhengdef main():
4134485b04bSFam Zheng    parser = argparse.ArgumentParser(description="A Docker helper",
4144485b04bSFam Zheng            usage="%s <subcommand> ..." % os.path.basename(sys.argv[0]))
4154485b04bSFam Zheng    subparsers = parser.add_subparsers(title="subcommands", help=None)
4164485b04bSFam Zheng    for cls in SubCommand.__subclasses__():
4174485b04bSFam Zheng        cmd = cls()
4184485b04bSFam Zheng        subp = subparsers.add_parser(cmd.name, help=cmd.__doc__)
4194485b04bSFam Zheng        cmd.shared_args(subp)
4204485b04bSFam Zheng        cmd.args(subp)
4214485b04bSFam Zheng        subp.set_defaults(cmdobj=cmd)
4224485b04bSFam Zheng    args, argv = parser.parse_known_args()
4234485b04bSFam Zheng    return args.cmdobj.run(args, argv)
4244485b04bSFam Zheng
4254485b04bSFam Zhengif __name__ == "__main__":
4264485b04bSFam Zheng    sys.exit(main())
427