Commit 61146e9c authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/elmarco/tags/podman-pull-request' into staging



tests/docker: add podman support

# gpg: Signature made Thu 22 Aug 2019 14:46:51 BST
# gpg:                using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg:                issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276  F62D DAE8 E109 7596 9CE5

* remotes/elmarco/tags/podman-pull-request:
  test: skip tests if socket_check_protocol_support() failed
  test-char: skip tcp tests if ipv4 check failed
  tests: specify the address family when checking bind
  tests/docker: add podman support
  docker.py: add podman support
  docker.py: add --run-as-current-user

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 3590b27c a4eb74a6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1157,7 +1157,7 @@ endif
	@echo  ''
	@echo  'Test targets:'
	@echo  '  check           - Run all tests (check-help for details)'
	@echo  '  docker          - Help about targets running tests inside Docker containers'
	@echo  '  docker          - Help about targets running tests inside containers'
	@echo  '  vm-help         - Help about targets running tests inside VM'
	@echo  ''
	@echo  'Documentation targets:'
+1 −1
Original line number Diff line number Diff line
@@ -525,7 +525,7 @@ tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)

tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y)
tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y) tests/socket-helpers.o
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
+7 −3
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@ DOCKER_TESTS := $(notdir $(shell \

DOCKER_TOOLS := travis

DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py
ENGINE := auto

DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(ENGINE)

TESTS ?= %
IMAGES ?= %
@@ -146,7 +148,7 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPR
)

docker:
	@echo 'Build QEMU and run tests inside Docker containers'
	@echo 'Build QEMU and run tests inside Docker or Podman containers'
	@echo
	@echo 'Available targets:'
	@echo
@@ -193,6 +195,8 @@ endif
	@echo '    EXECUTABLE=<path>    Include executable in image.'
	@echo '    EXTRA_FILES="<path> [... <path>]"'
	@echo '                         Include extra files in image.'
	@echo '    ENGINE=auto/docker/podman'
	@echo '                         Specify which container engine to run.'

# This rule if for directly running against an arbitrary docker target.
# It is called by the expanded docker targets (e.g. make
@@ -212,7 +216,7 @@ docker-run: docker-qemu-src
			"  COPYING $(EXECUTABLE) to $(IMAGE)"))
	$(call quiet-command,						\
		$(DOCKER_SCRIPT) run 					\
			$(if $(NOUSER),,-u $(shell id -u)) 		\
			$(if $(NOUSER),,--run-as-current-user) 		\
			--security-opt seccomp=unconfined		\
			$(if $V,,--rm) 					\
			$(if $(DEBUG),-ti,)				\
+48 −5
Original line number Diff line number Diff line
@@ -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)
@@ -333,8 +359,18 @@ class RunCommand(SubCommand):
    def args(self, parser):
        parser.add_argument("--keep", action="store_true",
                            help="Don't remove image when command completes")
        parser.add_argument("--run-as-current-user", action="store_true",
                            help="Run container using the current user's uid")

    def run(self, args, argv):
        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)


@@ -502,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")

@@ -597,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()
@@ -608,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)


+13 −4
Original line number Diff line number Diff line
@@ -30,7 +30,16 @@
# define EAI_ADDRFAMILY 0
#endif

int socket_can_bind_connect(const char *hostname)
/*
 * @hostname: a DNS name or numeric IP address
 *
 * Check whether it is possible to bind & connect to ports
 * on the DNS name or IP address @hostname. If an IP address
 * is used, it must not be a wildcard address.
 *
 * Returns 0 on success, -1 on error with errno set
 */
static int socket_can_bind_connect(const char *hostname, int family)
{
    int lfd = -1, cfd = -1, afd = -1;
    struct addrinfo ai, *res = NULL;
@@ -44,7 +53,7 @@ int socket_can_bind_connect(const char *hostname)

    memset(&ai, 0, sizeof(ai));
    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
    ai.ai_family = AF_UNSPEC;
    ai.ai_family = family;
    ai.ai_socktype = SOCK_STREAM;

    /* lookup */
@@ -129,7 +138,7 @@ int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
{
    *has_ipv4 = *has_ipv6 = false;

    if (socket_can_bind_connect("127.0.0.1") < 0) {
    if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
        if (errno != EADDRNOTAVAIL) {
            return -1;
        }
@@ -137,7 +146,7 @@ int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
        *has_ipv4 = true;
    }

    if (socket_can_bind_connect("::1") < 0) {
    if (socket_can_bind_connect("::1", PF_INET6) < 0) {
        if (errno != EADDRNOTAVAIL) {
            return -1;
        }
Loading