From 8e647fe9748c60f877dc0f5821f2147b4fbdc748 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 7 Jun 2017 14:19:01 +0100 Subject: [PATCH 1/3] vendor: Update hyperkit go bindings The update allows redirecting the VMs output. Signed-off-by: Rolf Neugebauer --- vendor.conf | 2 +- .../github.com/moby/hyperkit/go/hyperkit.go | 53 +++++++++++--- .../moby/hyperkit/go/pty_util_darwin.go | 70 +++++++++++++++++++ .../moby/hyperkit/go/pty_util_fallback.go | 24 +++++++ .../github.com/moby/hyperkit/go/vendor.conf | 1 + 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 vendor/github.com/moby/hyperkit/go/pty_util_darwin.go create mode 100644 vendor/github.com/moby/hyperkit/go/pty_util_fallback.go diff --git a/vendor.conf b/vendor.conf index 80e8627d4..3f331692f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,7 @@ github.com/docker/infrakit cb420e3e50ea60afe58538b1d3cab1cb14059433 github.com/golang/protobuf c9c7427a2a70d2eb3bafa0ab2dc163e45f143317 github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751 github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 -github.com/moby/hyperkit 70205a6d5143340299a679af259f70dfcd7cf8a4 +github.com/moby/hyperkit ffbde436bf43219808ebe24dc8f6aacdb0ab57bd github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091 github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506 github.com/surma/gocpio fcb68777e7dc4ea43ffce871b552c0d073c17495 diff --git a/vendor/github.com/moby/hyperkit/go/hyperkit.go b/vendor/github.com/moby/hyperkit/go/hyperkit.go index 1b67e0046..9fd8ea64f 100644 --- a/vendor/github.com/moby/hyperkit/go/hyperkit.go +++ b/vendor/github.com/moby/hyperkit/go/hyperkit.go @@ -35,6 +35,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/mitchellh/go-ps" ) @@ -212,6 +213,9 @@ func (h *HyperKit) execute(cmdline string) error { if h.Console == ConsoleFile && h.StateDir == "" { return fmt.Errorf("If ConsoleFile is set, StateDir must be specified") } + if h.Console == ConsoleStdio && !isTerminal(os.Stdout) && h.StateDir == "" { + return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + } if h.ISOImage != "" { if _, err = os.Stat(h.ISOImage); os.IsNotExist(err) { return fmt.Errorf("ISO %s does not exist", h.ISOImage) @@ -420,10 +424,10 @@ func (h *HyperKit) buildArgs(cmdline string) { nextSlot++ } - if h.Console == ConsoleFile { - a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) - } else { + if h.Console == ConsoleStdio && isTerminal(os.Stdout) { a = append(a, "-l", "com1,stdio") + } else if h.StateDir != "" { + a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) } kernArgs := fmt.Sprintf("kexec,%s,%s,earlyprintk=serial %s", h.Kernel, h.Initrd, cmdline) @@ -439,14 +443,43 @@ func (h *HyperKit) execHyperKit() error { cmd := exec.Command(h.HyperKit, h.Arguments...) cmd.Env = os.Environ() - // Plumb in stdin/stdout/stderr. If ConsoleStdio is configured - // plumb them to the system streams. If a logger is specified, - // use it for stdout/stderr logging. Otherwise use the default - // /dev/null. + // Plumb in stdin/stdout/stderr. + // + // If ConsoleStdio is configured and we are on a terminal, + // just plugin stdio. If we are not on a terminal we have a + // StateDir (as per checks above) and have configured HyperKit + // to use a PTY in the statedir. In this case, we just open + // the PTY slave and copy it to stdout (and ignore + // stdin). This allows for redirecting of the VM output, used + // for testing. + // + // If a logger is specified, use it for stdout/stderr + // logging. Otherwise use the default /dev/null. if h.Console == ConsoleStdio { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + if isTerminal(os.Stdout) { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } else { + go func() { + ttyPath := fmt.Sprintf("%s/tty", h.StateDir) + var tty *os.File + var err error + for { + tty, err = os.OpenFile(ttyPath, os.O_RDONLY, 0) + if err != nil { + time.Sleep(10 * 1000 * 1000 * time.Nanosecond) + continue + } else { + break + } + } + saneTerminal(tty) + setRaw(tty) + io.Copy(os.Stdout, tty) + tty.Close() + }() + } } else if h.log != nil { stdoutChan := make(chan string) stderrChan := make(chan string) diff --git a/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go b/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go new file mode 100644 index 000000000..559e865c8 --- /dev/null +++ b/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go @@ -0,0 +1,70 @@ +package hyperkit + +/* +Most of this code was copied and adjusted from: +https://github.com/containerd/console +which is under Apache License Version 2.0, January 2004 +*/ +import ( + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr, p *unix.Termios) error { + return ioctl(fd, unix.TIOCGETA, uintptr(unsafe.Pointer(p))) +} + +func tcset(fd uintptr, p *unix.Termios) error { + return ioctl(fd, unix.TIOCSETA, uintptr(unsafe.Pointer(p))) +} + +func ioctl(fd, flag, data uintptr) error { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +func saneTerminal(f *os.File) error { + // Go doesn't have a wrapper for any of the termios ioctls. + var termios unix.Termios + if err := tcget(f.Fd(), &termios); err != nil { + return err + } + // Set -onlcr so we don't have to deal with \r. + termios.Oflag &^= unix.ONLCR + return tcset(f.Fd(), &termios) +} + +func setRaw(f *os.File) error { + var termios unix.Termios + if err := tcget(f.Fd(), &termios); err != nil { + return err + } + termios = cfmakeraw(termios) + termios.Oflag = termios.Oflag | unix.OPOST + return tcset(f.Fd(), &termios) +} + +// isTerminal checks if the provided file is a terminal +func isTerminal(f *os.File) bool { + var termios unix.Termios + if tcget(f.Fd(), &termios) != nil { + return false + } + return true +} + +func cfmakeraw(t unix.Termios) unix.Termios { + t.Iflag = uint64(uint32(t.Iflag) & ^uint32((unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON))) + t.Oflag = uint64(uint32(t.Oflag) & ^uint32(unix.OPOST)) + t.Lflag = uint64(uint32(t.Lflag) & ^(uint32(unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN))) + t.Cflag = uint64(uint32(t.Cflag) & ^(uint32(unix.CSIZE | unix.PARENB))) + t.Cflag = t.Cflag | unix.CS8 + t.Cc[unix.VMIN] = 1 + t.Cc[unix.VTIME] = 0 + + return t +} diff --git a/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go b/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go new file mode 100644 index 000000000..aff274cc5 --- /dev/null +++ b/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go @@ -0,0 +1,24 @@ +// +build !darwin + +package hyperkit + +import ( + "log" + "os" +) + +func saneTerminal(f *os.File) error { + log.Fatal("Function not supported on your OS") + return nil +} + +func setRaw(f *os.File) error { + log.Fatal("Function not supported on your OS") + return nil +} + +// isTerminal checks if the provided file is a terminal +func isTerminal(f *os.File) bool { + log.Fatal("Function not supported on your OS") + return false +} diff --git a/vendor/github.com/moby/hyperkit/go/vendor.conf b/vendor/github.com/moby/hyperkit/go/vendor.conf index 0e9e15ee7..7ae581711 100644 --- a/vendor/github.com/moby/hyperkit/go/vendor.conf +++ b/vendor/github.com/moby/hyperkit/go/vendor.conf @@ -1 +1,2 @@ github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 +golang.org/x/sys b90f89a1e7a9c1f6b918820b3daa7f08488c8594 From d449ef6901f037310c1e59797ae600887a2acb1b Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 7 Jun 2017 15:53:40 +0100 Subject: [PATCH 2/3] tests: Run kernel tests on HyperKit when running on OSX With the updated HyperKit go bindings we can redirect the VM output and check for test results. Use this for all kernel tests as this speeds up running the tests on OSX. Also enable 'set -x' so we see the commands being executed and don't fail the test if the cleanup failed. Signed-off-by: Rolf Neugebauer --- test/cases/020_kernel/000_config_4.4.x/test.sh | 4 ++-- test/cases/020_kernel/001_config_4.9.x/test.sh | 4 ++-- test/cases/020_kernel/003_config_4.11.x/test.sh | 4 ++-- test/cases/020_kernel/010_kmod_4.9.x/test.sh | 11 +++++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/test/cases/020_kernel/000_config_4.4.x/test.sh b/test/cases/020_kernel/000_config_4.4.x/test.sh index f05fc86d5..2ecd67ed8 100644 --- a/test/cases/020_kernel/000_config_4.4.x/test.sh +++ b/test/cases/020_kernel/000_config_4.4.x/test.sh @@ -10,13 +10,13 @@ set -e . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-kernel-config.yml -RESULT="$(linuxkit run qemu -kernel test-kernel-config)" +RESULT="$(linuxkit run test-kernel-config)" echo "${RESULT}" | grep -q "suite PASSED" exit 0 diff --git a/test/cases/020_kernel/001_config_4.9.x/test.sh b/test/cases/020_kernel/001_config_4.9.x/test.sh index 1c047cae1..2ecd67ed8 100644 --- a/test/cases/020_kernel/001_config_4.9.x/test.sh +++ b/test/cases/020_kernel/001_config_4.9.x/test.sh @@ -10,13 +10,13 @@ set -e . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-kernel-config.yml -RESULT="$(linuxkit run qemu test-kernel-config)" +RESULT="$(linuxkit run test-kernel-config)" echo "${RESULT}" | grep -q "suite PASSED" exit 0 diff --git a/test/cases/020_kernel/003_config_4.11.x/test.sh b/test/cases/020_kernel/003_config_4.11.x/test.sh index 1c047cae1..2ecd67ed8 100644 --- a/test/cases/020_kernel/003_config_4.11.x/test.sh +++ b/test/cases/020_kernel/003_config_4.11.x/test.sh @@ -10,13 +10,13 @@ set -e . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-kernel-config*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-kernel-config.yml -RESULT="$(linuxkit run qemu test-kernel-config)" +RESULT="$(linuxkit run test-kernel-config)" echo "${RESULT}" | grep -q "suite PASSED" exit 0 diff --git a/test/cases/020_kernel/010_kmod_4.9.x/test.sh b/test/cases/020_kernel/010_kmod_4.9.x/test.sh index 50f6079ca..9a08df964 100644 --- a/test/cases/020_kernel/010_kmod_4.9.x/test.sh +++ b/test/cases/020_kernel/010_kmod_4.9.x/test.sh @@ -13,7 +13,7 @@ IMAGE_NAME="kmod-test" clean_up() { docker rmi ${IMAGE_NAME} || true - find . -iname "kmod*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "kmod*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT @@ -21,7 +21,10 @@ trap clean_up EXIT docker pull linuxkit/kernel:4.9.x # Build a package docker build -t ${IMAGE_NAME} . -# Build a LinuxKit image with kernel module (and test script) + +# Build and run a LinuxKit image with kernel module (and test script) moby build -output kernel+initrd kmod.yml -# Run it -linuxkit run qemu kmod | grep -q "Hello LinuxKit" +RESULT="$(linuxkit run kmod)" +echo "${RESULT}" | grep -q "Hello LinuxKit" + +exit 0 From 4195ff85536b66a2036da9c75ea98c7ba5340f00 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 7 Jun 2017 16:18:43 +0100 Subject: [PATCH 3/3] tests: Run the package tests on HyperKit when running on OSX With the updated HyperKit go bindings we can redirect the VM output and check for test results. Use this for all kernel tests as this speeds up running the tests on OSX. Also use 'set -x' instead of 'set -v' for consistency and don't fail when the clean up code fails. The mkimage package test currently doesn't work on HyperKit as it requires support for multiple disks to be added. Signed-off-by: Rolf Neugebauer --- test/cases/040_packages/002_binfmt/test.sh | 5 ++--- test/cases/040_packages/003_ca-certificates/test.sh | 5 ++--- test/cases/040_packages/004_dhcpcd/test.sh | 5 ++--- test/cases/040_packages/019_sysctl/test.sh | 5 ++--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/test/cases/040_packages/002_binfmt/test.sh b/test/cases/040_packages/002_binfmt/test.sh index 78641aa60..7cd5cc0e0 100644 --- a/test/cases/040_packages/002_binfmt/test.sh +++ b/test/cases/040_packages/002_binfmt/test.sh @@ -4,20 +4,19 @@ # REPEAT: set -e -set -v # Source libraries. Uncomment if needed/defined #. "${RT_LIB}" . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-binfmt*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-binfmt*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-binfmt.yml -RESULT="$(linuxkit run qemu -kernel test-binfmt)" +RESULT="$(linuxkit run test-binfmt)" echo "${RESULT}" echo "${RESULT}" | grep -q "suite PASSED" diff --git a/test/cases/040_packages/003_ca-certificates/test.sh b/test/cases/040_packages/003_ca-certificates/test.sh index 5e634a646..6ba5bebf7 100644 --- a/test/cases/040_packages/003_ca-certificates/test.sh +++ b/test/cases/040_packages/003_ca-certificates/test.sh @@ -4,20 +4,19 @@ # REPEAT: set -e -set -v # Source libraries. Uncomment if needed/defined #. "${RT_LIB}" . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-ca-certificates*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-ca-certificates*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-ca-certificates.yml -RESULT="$(linuxkit run qemu -kernel test-ca-certificates)" +RESULT="$(linuxkit run test-ca-certificates)" echo "${RESULT}" echo "${RESULT}" | grep -q "suite PASSED" diff --git a/test/cases/040_packages/004_dhcpcd/test.sh b/test/cases/040_packages/004_dhcpcd/test.sh index e4bd6104a..ac1d42816 100644 --- a/test/cases/040_packages/004_dhcpcd/test.sh +++ b/test/cases/040_packages/004_dhcpcd/test.sh @@ -4,20 +4,19 @@ # REPEAT: set -e -set -v # Source libraries. Uncomment if needed/defined #. "${RT_LIB}" . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-dhcpcd*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-dhcpcd*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-dhcpcd.yml -RESULT="$(linuxkit run qemu -kernel test-dhcpcd)" +RESULT="$(linuxkit run test-dhcpcd)" echo "${RESULT}" echo "${RESULT}" | grep -q "suite PASSED" diff --git a/test/cases/040_packages/019_sysctl/test.sh b/test/cases/040_packages/019_sysctl/test.sh index 732c7be5c..47e652a9c 100644 --- a/test/cases/040_packages/019_sysctl/test.sh +++ b/test/cases/040_packages/019_sysctl/test.sh @@ -4,20 +4,19 @@ # REPEAT: set -e -set -v # Source libraries. Uncomment if needed/defined #. "${RT_LIB}" . "${RT_PROJECT_ROOT}/_lib/lib.sh" clean_up() { - find . -iname "test-sysctl*" -not -iname "*.yml" -exec rm -rf {} \; + find . -iname "test-sysctl*" -not -iname "*.yml" -exec rm -rf {} \; || true } trap clean_up EXIT # Test code goes here moby build -output kernel+initrd test-sysctl.yml -RESULT="$(linuxkit run qemu -kernel test-sysctl)" +RESULT="$(linuxkit run test-sysctl)" echo "${RESULT}" echo "${RESULT}" | grep -q "suite PASSED"