Message ID | 20190822134725.32479-3-marcandre.lureau@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [PULL,1/6] docker.py: add --run-as-current-user | expand |
On 8/22/19 3:47 PM, Marc-André Lureau wrote: > Add a --engine option to select either docker, podman or auto. > > Among other advantages, podman allows to run rootless & daemonless > containers, fortunately sharing compatible CLI with docker. > > With current podman, we have to use a uidmap trick in order to be able > to rw-share the ccache directory with the container user. > > With a user 1000, the default mapping is: 1000 (host) -> 0 (container). > So write access to /var/tmp/ccache ends will end with permission > denied error. > > With "--uidmap 1000:0:1 --uidmap 0:1:1000", the mapping is: > 1000 (host) -> 0 (container, 1st namespace) -> 1000 (container, 2nd namespace). > (the rest is mumbo jumbo to avoid holes in the range of UIDs) > > A future podman version may have an option such as --userns-keep-uid. > Thanks to Debarshi Ray <rishi@redhat.com> for the help! > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > Acked-by: Alex Bennée <alex.bennee@linaro.org> > Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> > --- > tests/docker/docker.py | 48 +++++++++++++++++++++++++++++++++++++----- > 1 file changed, 43 insertions(+), 5 deletions(-) > > diff --git a/tests/docker/docker.py b/tests/docker/docker.py > index f15545aeea..ac5baab4ca 100755 > --- a/tests/docker/docker.py > +++ b/tests/docker/docker.py > @@ -20,6 +20,7 @@ import hashlib > import atexit > import uuid > import argparse > +import enum This broke Shippable: https://app.shippable.com/github/qemu/qemu/runs/1897/summary/console Traceback (most recent call last): File "./tests/docker/docker.py", line 23, in <module> import enum ImportError: No module named enum > import tempfile > import re > import signal > @@ -38,6 +39,26 @@ FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] > > DEVNULL = open(os.devnull, 'wb') > > +class EngineEnum(enum.IntEnum): > + AUTO = 1 > + DOCKER = 2 > + PODMAN = 3 > + > + def __str__(self): > + return self.name.lower() > + > + def __repr__(self): > + return str(self) > + > + @staticmethod > + def argparse(s): > + try: > + return EngineEnum[s.upper()] > + except KeyError: > + return s > + > + > +USE_ENGINE = EngineEnum.AUTO > > def _text_checksum(text): > """Calculate a digest string unique to the text content""" > @@ -48,9 +69,14 @@ def _file_checksum(filename): > return _text_checksum(open(filename, 'rb').read()) > > > -def _guess_docker_command(): > - """ Guess a working docker command or raise exception if not found""" > - commands = [["docker"], ["sudo", "-n", "docker"]] > +def _guess_engine_command(): > + """ Guess a working engine command or raise exception if not found""" > + commands = [] > + > + if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.PODMAN]: > + commands += [["podman"]] > + if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.DOCKER]: > + commands += [["docker"], ["sudo", "-n", "docker"]] > for cmd in commands: > try: > # docker version will return the client details in stdout > @@ -61,7 +87,7 @@ def _guess_docker_command(): > except OSError: > pass > commands_txt = "\n".join([" " + " ".join(x) for x in commands]) > - raise Exception("Cannot find working docker command. Tried:\n%s" % > + raise Exception("Cannot find working engine command. Tried:\n%s" % > commands_txt) > > > @@ -190,7 +216,7 @@ def _dockerfile_preprocess(df): > class Docker(object): > """ Running Docker commands """ > def __init__(self): > - self._command = _guess_docker_command() > + self._command = _guess_engine_command() > self._instances = [] > atexit.register(self._kill_instances) > signal.signal(signal.SIGTERM, self._kill_instances) > @@ -340,6 +366,11 @@ class RunCommand(SubCommand): > if args.run_as_current_user: > uid = os.getuid() > argv = [ "-u", str(uid) ] + argv > + docker = Docker() > + if docker._command[0] == "podman": > + argv = [ "--uidmap", "%d:0:1" % uid, > + "--uidmap", "0:1:%d" % uid, > + "--uidmap", "%d:%d:64536" % (uid + 1, uid + 1)] + argv > return Docker().run(argv, args.keep, quiet=args.quiet) > > > @@ -507,6 +538,8 @@ class ProbeCommand(SubCommand): > print("yes") > elif docker._command[0] == "sudo": > print("sudo") > + elif docker._command[0] == "podman": > + print("podman") > except Exception: > print("no") > > @@ -602,9 +635,13 @@ class CheckCommand(SubCommand): > > > def main(): > + global USE_ENGINE > + > parser = argparse.ArgumentParser(description="A Docker helper", > usage="%s <subcommand> ..." % > os.path.basename(sys.argv[0])) > + parser.add_argument("--engine", type=EngineEnum.argparse, choices=list(EngineEnum), > + help="specify which container engine to use") > subparsers = parser.add_subparsers(title="subcommands", help=None) > for cls in SubCommand.__subclasses__(): > cmd = cls() > @@ -613,6 +650,7 @@ def main(): > cmd.args(subp) > subp.set_defaults(cmdobj=cmd) > args, argv = parser.parse_known_args() > + USE_ENGINE = args.engine > return args.cmdobj.run(args, argv) > > >
Philippe Mathieu-Daudé <philmd@redhat.com> writes: > On 8/22/19 3:47 PM, Marc-André Lureau wrote: >> Add a --engine option to select either docker, podman or auto. >> >> Among other advantages, podman allows to run rootless & daemonless >> containers, fortunately sharing compatible CLI with docker. >> >> With current podman, we have to use a uidmap trick in order to be able >> to rw-share the ccache directory with the container user. >> >> With a user 1000, the default mapping is: 1000 (host) -> 0 (container). >> So write access to /var/tmp/ccache ends will end with permission >> denied error. >> >> With "--uidmap 1000:0:1 --uidmap 0:1:1000", the mapping is: >> 1000 (host) -> 0 (container, 1st namespace) -> 1000 (container, 2nd namespace). >> (the rest is mumbo jumbo to avoid holes in the range of UIDs) >> >> A future podman version may have an option such as --userns-keep-uid. >> Thanks to Debarshi Ray <rishi@redhat.com> for the help! >> >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> >> Acked-by: Alex Bennée <alex.bennee@linaro.org> >> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> >> --- >> tests/docker/docker.py | 48 +++++++++++++++++++++++++++++++++++++----- >> 1 file changed, 43 insertions(+), 5 deletions(-) >> >> diff --git a/tests/docker/docker.py b/tests/docker/docker.py >> index f15545aeea..ac5baab4ca 100755 >> --- a/tests/docker/docker.py >> +++ b/tests/docker/docker.py >> @@ -20,6 +20,7 @@ import hashlib >> import atexit >> import uuid >> import argparse >> +import enum > > This broke Shippable: > https://app.shippable.com/github/qemu/qemu/runs/1897/summary/console The patch to fix it is in my PR although there are more fixes for the fall-out coming in a new series. -- Alex Bennée
diff --git a/tests/docker/docker.py b/tests/docker/docker.py index f15545aeea..ac5baab4ca 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -20,6 +20,7 @@ import hashlib import atexit import uuid import argparse +import enum import tempfile import re import signal @@ -38,6 +39,26 @@ FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] DEVNULL = open(os.devnull, 'wb') +class EngineEnum(enum.IntEnum): + AUTO = 1 + DOCKER = 2 + PODMAN = 3 + + def __str__(self): + return self.name.lower() + + def __repr__(self): + return str(self) + + @staticmethod + def argparse(s): + try: + return EngineEnum[s.upper()] + except KeyError: + return s + + +USE_ENGINE = EngineEnum.AUTO def _text_checksum(text): """Calculate a digest string unique to the text content""" @@ -48,9 +69,14 @@ def _file_checksum(filename): return _text_checksum(open(filename, 'rb').read()) -def _guess_docker_command(): - """ Guess a working docker command or raise exception if not found""" - commands = [["docker"], ["sudo", "-n", "docker"]] +def _guess_engine_command(): + """ Guess a working engine command or raise exception if not found""" + commands = [] + + if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.PODMAN]: + commands += [["podman"]] + if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.DOCKER]: + commands += [["docker"], ["sudo", "-n", "docker"]] for cmd in commands: try: # docker version will return the client details in stdout @@ -61,7 +87,7 @@ def _guess_docker_command(): except OSError: pass commands_txt = "\n".join([" " + " ".join(x) for x in commands]) - raise Exception("Cannot find working docker command. Tried:\n%s" % + raise Exception("Cannot find working engine command. Tried:\n%s" % commands_txt) @@ -190,7 +216,7 @@ def _dockerfile_preprocess(df): class Docker(object): """ Running Docker commands """ def __init__(self): - self._command = _guess_docker_command() + self._command = _guess_engine_command() self._instances = [] atexit.register(self._kill_instances) signal.signal(signal.SIGTERM, self._kill_instances) @@ -340,6 +366,11 @@ class RunCommand(SubCommand): if args.run_as_current_user: uid = os.getuid() argv = [ "-u", str(uid) ] + argv + docker = Docker() + if docker._command[0] == "podman": + argv = [ "--uidmap", "%d:0:1" % uid, + "--uidmap", "0:1:%d" % uid, + "--uidmap", "%d:%d:64536" % (uid + 1, uid + 1)] + argv return Docker().run(argv, args.keep, quiet=args.quiet) @@ -507,6 +538,8 @@ class ProbeCommand(SubCommand): print("yes") elif docker._command[0] == "sudo": print("sudo") + elif docker._command[0] == "podman": + print("podman") except Exception: print("no") @@ -602,9 +635,13 @@ class CheckCommand(SubCommand): def main(): + global USE_ENGINE + parser = argparse.ArgumentParser(description="A Docker helper", usage="%s <subcommand> ..." % os.path.basename(sys.argv[0])) + parser.add_argument("--engine", type=EngineEnum.argparse, choices=list(EngineEnum), + help="specify which container engine to use") subparsers = parser.add_subparsers(title="subcommands", help=None) for cls in SubCommand.__subclasses__(): cmd = cls() @@ -613,6 +650,7 @@ def main(): cmd.args(subp) subp.set_defaults(cmdobj=cmd) args, argv = parser.parse_known_args() + USE_ENGINE = args.engine return args.cmdobj.run(args, argv)