summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/cmd/test_app/BUILD21
-rw-r--r--test/cmd/test_app/fds.go185
-rw-r--r--test/cmd/test_app/test_app.go394
-rw-r--r--test/e2e/BUILD4
-rw-r--r--test/e2e/exec_test.go193
-rw-r--r--test/e2e/integration_test.go233
-rw-r--r--test/e2e/regression_test.go18
-rw-r--r--test/image/BUILD4
-rw-r--r--test/image/image_test.go195
-rwxr-xr-x[-rw-r--r--]test/image/ruby.sh0
-rw-r--r--test/iptables/BUILD8
-rw-r--r--test/iptables/README.md2
-rw-r--r--test/iptables/iptables.go7
-rw-r--r--test/iptables/iptables_test.go271
-rw-r--r--test/iptables/iptables_util.go2
-rw-r--r--test/iptables/runner/BUILD17
-rw-r--r--test/iptables/runner/main.go3
-rwxr-xr-xtest/packetdrill/packetdrill_test.sh25
-rw-r--r--test/packetimpact/testbench/BUILD6
-rw-r--r--test/packetimpact/testbench/connections.go40
-rw-r--r--test/packetimpact/testbench/dut.go2
-rw-r--r--test/packetimpact/testbench/layers.go44
-rw-r--r--test/packetimpact/testbench/layers_test.go159
-rw-r--r--test/packetimpact/tests/defs.bzl35
-rwxr-xr-xtest/packetimpact/tests/test_runner.sh48
-rw-r--r--test/root/BUILD8
-rw-r--r--test/root/cgroup_test.go114
-rw-r--r--test/root/chroot_test.go20
-rw-r--r--test/root/crictl_test.go192
-rw-r--r--test/root/main_test.go2
-rw-r--r--test/root/oom_score_adj_test.go78
-rw-r--r--test/root/runsc_test.go2
-rw-r--r--test/root/testdata/BUILD18
-rw-r--r--test/root/testdata/busybox.go32
-rw-r--r--test/root/testdata/containerd_config.go39
-rw-r--r--test/root/testdata/httpd.go32
-rw-r--r--test/root/testdata/httpd_mount_paths.go53
-rw-r--r--test/root/testdata/sandbox.go30
-rw-r--r--test/root/testdata/simple.go41
-rw-r--r--test/runner/BUILD2
-rw-r--r--test/runner/runner.go12
-rw-r--r--test/runtimes/BUILD22
-rw-r--r--test/runtimes/README.md56
-rw-r--r--test/runtimes/build_defs.bzl75
-rw-r--r--test/runtimes/defs.bzl79
-rw-r--r--test/runtimes/proctor/BUILD (renamed from test/runtimes/images/proctor/BUILD)5
-rw-r--r--test/runtimes/proctor/go.go (renamed from test/runtimes/images/proctor/go.go)0
-rw-r--r--test/runtimes/proctor/java.go (renamed from test/runtimes/images/proctor/java.go)0
-rw-r--r--test/runtimes/proctor/nodejs.go (renamed from test/runtimes/images/proctor/nodejs.go)0
-rw-r--r--test/runtimes/proctor/php.go (renamed from test/runtimes/images/proctor/php.go)0
-rw-r--r--test/runtimes/proctor/proctor.go (renamed from test/runtimes/images/proctor/proctor.go)0
-rw-r--r--test/runtimes/proctor/proctor_test.go (renamed from test/runtimes/images/proctor/proctor_test.go)14
-rw-r--r--test/runtimes/proctor/python.go (renamed from test/runtimes/images/proctor/python.go)0
-rwxr-xr-xtest/runtimes/runner.sh35
-rw-r--r--test/runtimes/runner/BUILD21
-rw-r--r--test/runtimes/runner/blacklist_test.go (renamed from test/runtimes/blacklist_test.go)0
-rw-r--r--test/runtimes/runner/main.go (renamed from test/runtimes/runner.go)37
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc43
58 files changed, 1730 insertions, 1248 deletions
diff --git a/test/cmd/test_app/BUILD b/test/cmd/test_app/BUILD
new file mode 100644
index 000000000..98ba5a3d9
--- /dev/null
+++ b/test/cmd/test_app/BUILD
@@ -0,0 +1,21 @@
+load("//tools:defs.bzl", "go_binary")
+
+package(licenses = ["notice"])
+
+go_binary(
+ name = "test_app",
+ testonly = 1,
+ srcs = [
+ "fds.go",
+ "test_app.go",
+ ],
+ pure = True,
+ visibility = ["//runsc/container:__pkg__"],
+ deps = [
+ "//pkg/test/testutil",
+ "//pkg/unet",
+ "//runsc/flag",
+ "@com_github_google_subcommands//:go_default_library",
+ "@com_github_kr_pty//:go_default_library",
+ ],
+)
diff --git a/test/cmd/test_app/fds.go b/test/cmd/test_app/fds.go
new file mode 100644
index 000000000..a7658eefd
--- /dev/null
+++ b/test/cmd/test_app/fds.go
@@ -0,0 +1,185 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "context"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "github.com/google/subcommands"
+ "gvisor.dev/gvisor/pkg/test/testutil"
+ "gvisor.dev/gvisor/pkg/unet"
+ "gvisor.dev/gvisor/runsc/flag"
+)
+
+const fileContents = "foobarbaz"
+
+// fdSender will open a file and send the FD over a unix domain socket.
+type fdSender struct {
+ socketPath string
+}
+
+// Name implements subcommands.Command.Name.
+func (*fdSender) Name() string {
+ return "fd_sender"
+}
+
+// Synopsis implements subcommands.Command.Synopsys.
+func (*fdSender) Synopsis() string {
+ return "creates a file and sends the FD over the socket"
+}
+
+// Usage implements subcommands.Command.Usage.
+func (*fdSender) Usage() string {
+ return "fd_sender <flags>"
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (fds *fdSender) SetFlags(f *flag.FlagSet) {
+ f.StringVar(&fds.socketPath, "socket", "", "path to socket")
+}
+
+// Execute implements subcommands.Command.Execute.
+func (fds *fdSender) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ if fds.socketPath == "" {
+ log.Fatalf("socket flag must be set")
+ }
+
+ dir, err := ioutil.TempDir("", "")
+ if err != nil {
+ log.Fatalf("TempDir failed: %v", err)
+ }
+
+ fileToSend, err := ioutil.TempFile(dir, "")
+ if err != nil {
+ log.Fatalf("TempFile failed: %v", err)
+ }
+ defer fileToSend.Close()
+
+ if _, err := fileToSend.WriteString(fileContents); err != nil {
+ log.Fatalf("Write(%q) failed: %v", fileContents, err)
+ }
+
+ // Receiver may not be started yet, so try connecting in a poll loop.
+ var s *unet.Socket
+ if err := testutil.Poll(func() error {
+ var err error
+ s, err = unet.Connect(fds.socketPath, true /* SEQPACKET, so we can send empty message with FD */)
+ return err
+ }, 10*time.Second); err != nil {
+ log.Fatalf("Error connecting to socket %q: %v", fds.socketPath, err)
+ }
+ defer s.Close()
+
+ w := s.Writer(true)
+ w.ControlMessage.PackFDs(int(fileToSend.Fd()))
+ if _, err := w.WriteVec([][]byte{[]byte{'a'}}); err != nil {
+ log.Fatalf("Error sending FD %q over socket %q: %v", fileToSend.Fd(), fds.socketPath, err)
+ }
+
+ log.Print("FD SENDER exiting successfully")
+ return subcommands.ExitSuccess
+}
+
+// fdReceiver receives an FD from a unix domain socket and does things to it.
+type fdReceiver struct {
+ socketPath string
+}
+
+// Name implements subcommands.Command.Name.
+func (*fdReceiver) Name() string {
+ return "fd_receiver"
+}
+
+// Synopsis implements subcommands.Command.Synopsys.
+func (*fdReceiver) Synopsis() string {
+ return "reads an FD from a unix socket, and then does things to it"
+}
+
+// Usage implements subcommands.Command.Usage.
+func (*fdReceiver) Usage() string {
+ return "fd_receiver <flags>"
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (fdr *fdReceiver) SetFlags(f *flag.FlagSet) {
+ f.StringVar(&fdr.socketPath, "socket", "", "path to socket")
+}
+
+// Execute implements subcommands.Command.Execute.
+func (fdr *fdReceiver) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ if fdr.socketPath == "" {
+ log.Fatalf("Flags cannot be empty, given: socket: %q", fdr.socketPath)
+ }
+
+ ss, err := unet.BindAndListen(fdr.socketPath, true /* packet */)
+ if err != nil {
+ log.Fatalf("BindAndListen(%q) failed: %v", fdr.socketPath, err)
+ }
+ defer ss.Close()
+
+ var s *unet.Socket
+ c := make(chan error, 1)
+ go func() {
+ var err error
+ s, err = ss.Accept()
+ c <- err
+ }()
+
+ select {
+ case err := <-c:
+ if err != nil {
+ log.Fatalf("Accept() failed: %v", err)
+ }
+ case <-time.After(10 * time.Second):
+ log.Fatalf("Timeout waiting for accept")
+ }
+
+ r := s.Reader(true)
+ r.EnableFDs(1)
+ b := [][]byte{{'a'}}
+ if n, err := r.ReadVec(b); n != 1 || err != nil {
+ log.Fatalf("ReadVec got n=%d err %v (wanted 0, nil)", n, err)
+ }
+
+ fds, err := r.ExtractFDs()
+ if err != nil {
+ log.Fatalf("ExtractFD() got err %v", err)
+ }
+ if len(fds) != 1 {
+ log.Fatalf("ExtractFD() got %d FDs, wanted 1", len(fds))
+ }
+ fd := fds[0]
+
+ file := os.NewFile(uintptr(fd), "received file")
+ defer file.Close()
+ if _, err := file.Seek(0, os.SEEK_SET); err != nil {
+ log.Fatalf("Seek(0, 0) failed: %v", err)
+ }
+
+ got, err := ioutil.ReadAll(file)
+ if err != nil {
+ log.Fatalf("ReadAll failed: %v", err)
+ }
+ if string(got) != fileContents {
+ log.Fatalf("ReadAll got %q want %q", string(got), fileContents)
+ }
+
+ log.Print("FD RECEIVER exiting successfully")
+ return subcommands.ExitSuccess
+}
diff --git a/test/cmd/test_app/test_app.go b/test/cmd/test_app/test_app.go
new file mode 100644
index 000000000..3ba4f38f8
--- /dev/null
+++ b/test/cmd/test_app/test_app.go
@@ -0,0 +1,394 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Binary test_app is like a swiss knife for tests that need to run anything
+// inside the sandbox. New functionality can be added with new commands.
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+ "os/exec"
+ "regexp"
+ "strconv"
+ sys "syscall"
+ "time"
+
+ "github.com/google/subcommands"
+ "github.com/kr/pty"
+ "gvisor.dev/gvisor/pkg/test/testutil"
+ "gvisor.dev/gvisor/runsc/flag"
+)
+
+func main() {
+ subcommands.Register(subcommands.HelpCommand(), "")
+ subcommands.Register(subcommands.FlagsCommand(), "")
+ subcommands.Register(new(capability), "")
+ subcommands.Register(new(fdReceiver), "")
+ subcommands.Register(new(fdSender), "")
+ subcommands.Register(new(forkBomb), "")
+ subcommands.Register(new(ptyRunner), "")
+ subcommands.Register(new(reaper), "")
+ subcommands.Register(new(syscall), "")
+ subcommands.Register(new(taskTree), "")
+ subcommands.Register(new(uds), "")
+
+ flag.Parse()
+
+ exitCode := subcommands.Execute(context.Background())
+ os.Exit(int(exitCode))
+}
+
+type uds struct {
+ fileName string
+ socketPath string
+}
+
+// Name implements subcommands.Command.Name.
+func (*uds) Name() string {
+ return "uds"
+}
+
+// Synopsis implements subcommands.Command.Synopsys.
+func (*uds) Synopsis() string {
+ return "creates unix domain socket client and server. Client sends a contant flow of sequential numbers. Server prints them to --file"
+}
+
+// Usage implements subcommands.Command.Usage.
+func (*uds) Usage() string {
+ return "uds <flags>"
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (c *uds) SetFlags(f *flag.FlagSet) {
+ f.StringVar(&c.fileName, "file", "", "name of output file")
+ f.StringVar(&c.socketPath, "socket", "", "path to socket")
+}
+
+// Execute implements subcommands.Command.Execute.
+func (c *uds) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ if c.fileName == "" || c.socketPath == "" {
+ log.Fatalf("Flags cannot be empty, given: fileName: %q, socketPath: %q", c.fileName, c.socketPath)
+ return subcommands.ExitFailure
+ }
+ outputFile, err := os.OpenFile(c.fileName, os.O_WRONLY|os.O_CREATE, 0666)
+ if err != nil {
+ log.Fatal("error opening output file:", err)
+ }
+
+ defer os.Remove(c.socketPath)
+
+ listener, err := net.Listen("unix", c.socketPath)
+ if err != nil {
+ log.Fatalf("error listening on socket %q: %v", c.socketPath, err)
+ }
+
+ go server(listener, outputFile)
+ for i := 0; ; i++ {
+ conn, err := net.Dial("unix", c.socketPath)
+ if err != nil {
+ log.Fatal("error dialing:", err)
+ }
+ if _, err := conn.Write([]byte(strconv.Itoa(i))); err != nil {
+ log.Fatal("error writing:", err)
+ }
+ conn.Close()
+ time.Sleep(100 * time.Millisecond)
+ }
+}
+
+func server(listener net.Listener, out *os.File) {
+ buf := make([]byte, 16)
+
+ for {
+ c, err := listener.Accept()
+ if err != nil {
+ log.Fatal("error accepting connection:", err)
+ }
+ nr, err := c.Read(buf)
+ if err != nil {
+ log.Fatal("error reading from buf:", err)
+ }
+ data := buf[0:nr]
+ fmt.Fprint(out, string(data)+"\n")
+ }
+}
+
+type taskTree struct {
+ depth int
+ width int
+ pause bool
+}
+
+// Name implements subcommands.Command.
+func (*taskTree) Name() string {
+ return "task-tree"
+}
+
+// Synopsis implements subcommands.Command.
+func (*taskTree) Synopsis() string {
+ return "creates a tree of tasks"
+}
+
+// Usage implements subcommands.Command.
+func (*taskTree) Usage() string {
+ return "task-tree <flags>"
+}
+
+// SetFlags implements subcommands.Command.
+func (c *taskTree) SetFlags(f *flag.FlagSet) {
+ f.IntVar(&c.depth, "depth", 1, "number of levels to create")
+ f.IntVar(&c.width, "width", 1, "number of tasks at each level")
+ f.BoolVar(&c.pause, "pause", false, "whether the tasks should pause perpetually")
+}
+
+// Execute implements subcommands.Command.
+func (c *taskTree) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ stop := testutil.StartReaper()
+ defer stop()
+
+ if c.depth == 0 {
+ log.Printf("Child sleeping, PID: %d\n", os.Getpid())
+ select {}
+ }
+ log.Printf("Parent %d sleeping, PID: %d\n", c.depth, os.Getpid())
+
+ var cmds []*exec.Cmd
+ for i := 0; i < c.width; i++ {
+ cmd := exec.Command(
+ "/proc/self/exe", c.Name(),
+ "--depth", strconv.Itoa(c.depth-1),
+ "--width", strconv.Itoa(c.width),
+ "--pause", strconv.FormatBool(c.pause))
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Start(); err != nil {
+ log.Fatal("failed to call self:", err)
+ }
+ cmds = append(cmds, cmd)
+ }
+
+ for _, c := range cmds {
+ c.Wait()
+ }
+
+ if c.pause {
+ select {}
+ }
+
+ return subcommands.ExitSuccess
+}
+
+type forkBomb struct {
+ delay time.Duration
+}
+
+// Name implements subcommands.Command.
+func (*forkBomb) Name() string {
+ return "fork-bomb"
+}
+
+// Synopsis implements subcommands.Command.
+func (*forkBomb) Synopsis() string {
+ return "creates child process until the end of times"
+}
+
+// Usage implements subcommands.Command.
+func (*forkBomb) Usage() string {
+ return "fork-bomb <flags>"
+}
+
+// SetFlags implements subcommands.Command.
+func (c *forkBomb) SetFlags(f *flag.FlagSet) {
+ f.DurationVar(&c.delay, "delay", 100*time.Millisecond, "amount of time to delay creation of child")
+}
+
+// Execute implements subcommands.Command.
+func (c *forkBomb) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ time.Sleep(c.delay)
+
+ cmd := exec.Command("/proc/self/exe", c.Name())
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Fatal("failed to call self:", err)
+ }
+ return subcommands.ExitSuccess
+}
+
+type reaper struct{}
+
+// Name implements subcommands.Command.
+func (*reaper) Name() string {
+ return "reaper"
+}
+
+// Synopsis implements subcommands.Command.
+func (*reaper) Synopsis() string {
+ return "reaps all children in a loop"
+}
+
+// Usage implements subcommands.Command.
+func (*reaper) Usage() string {
+ return "reaper <flags>"
+}
+
+// SetFlags implements subcommands.Command.
+func (*reaper) SetFlags(*flag.FlagSet) {}
+
+// Execute implements subcommands.Command.
+func (c *reaper) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ stop := testutil.StartReaper()
+ defer stop()
+ select {}
+}
+
+type syscall struct {
+ sysno uint64
+}
+
+// Name implements subcommands.Command.
+func (*syscall) Name() string {
+ return "syscall"
+}
+
+// Synopsis implements subcommands.Command.
+func (*syscall) Synopsis() string {
+ return "syscall makes a syscall"
+}
+
+// Usage implements subcommands.Command.
+func (*syscall) Usage() string {
+ return "syscall <flags>"
+}
+
+// SetFlags implements subcommands.Command.
+func (s *syscall) SetFlags(f *flag.FlagSet) {
+ f.Uint64Var(&s.sysno, "syscall", 0, "syscall to call")
+}
+
+// Execute implements subcommands.Command.
+func (s *syscall) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ if _, _, errno := sys.Syscall(uintptr(s.sysno), 0, 0, 0); errno != 0 {
+ fmt.Printf("syscall(%d, 0, 0...) failed: %v\n", s.sysno, errno)
+ } else {
+ fmt.Printf("syscall(%d, 0, 0...) success\n", s.sysno)
+ }
+ return subcommands.ExitSuccess
+}
+
+type capability struct {
+ enabled uint64
+ disabled uint64
+}
+
+// Name implements subcommands.Command.
+func (*capability) Name() string {
+ return "capability"
+}
+
+// Synopsis implements subcommands.Command.
+func (*capability) Synopsis() string {
+ return "checks if effective capabilities are set/unset"
+}
+
+// Usage implements subcommands.Command.
+func (*capability) Usage() string {
+ return "capability [--enabled=number] [--disabled=number]"
+}
+
+// SetFlags implements subcommands.Command.
+func (c *capability) SetFlags(f *flag.FlagSet) {
+ f.Uint64Var(&c.enabled, "enabled", 0, "")
+ f.Uint64Var(&c.disabled, "disabled", 0, "")
+}
+
+// Execute implements subcommands.Command.
+func (c *capability) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ if c.enabled == 0 && c.disabled == 0 {
+ fmt.Println("One of the flags must be set")
+ return subcommands.ExitUsageError
+ }
+
+ status, err := ioutil.ReadFile("/proc/self/status")
+ if err != nil {
+ fmt.Printf("Error reading %q: %v\n", "proc/self/status", err)
+ return subcommands.ExitFailure
+ }
+ re := regexp.MustCompile("CapEff:\t([0-9a-f]+)\n")
+ matches := re.FindStringSubmatch(string(status))
+ if matches == nil || len(matches) != 2 {
+ fmt.Printf("Effective capabilities not found in\n%s\n", status)
+ return subcommands.ExitFailure
+ }
+ caps, err := strconv.ParseUint(matches[1], 16, 64)
+ if err != nil {
+ fmt.Printf("failed to convert capabilities %q: %v\n", matches[1], err)
+ return subcommands.ExitFailure
+ }
+
+ if c.enabled != 0 && (caps&c.enabled) != c.enabled {
+ fmt.Printf("Missing capabilities, want: %#x: got: %#x\n", c.enabled, caps)
+ return subcommands.ExitFailure
+ }
+ if c.disabled != 0 && (caps&c.disabled) != 0 {
+ fmt.Printf("Extra capabilities found, dont_want: %#x: got: %#x\n", c.disabled, caps)
+ return subcommands.ExitFailure
+ }
+
+ return subcommands.ExitSuccess
+}
+
+type ptyRunner struct{}
+
+// Name implements subcommands.Command.
+func (*ptyRunner) Name() string {
+ return "pty-runner"
+}
+
+// Synopsis implements subcommands.Command.
+func (*ptyRunner) Synopsis() string {
+ return "runs the given command with an open pty terminal"
+}
+
+// Usage implements subcommands.Command.
+func (*ptyRunner) Usage() string {
+ return "pty-runner [command]"
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (*ptyRunner) SetFlags(f *flag.FlagSet) {}
+
+// Execute implements subcommands.Command.
+func (*ptyRunner) Execute(_ context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
+ c := exec.Command(fs.Args()[0], fs.Args()[1:]...)
+ f, err := pty.Start(c)
+ if err != nil {
+ fmt.Printf("pty.Start failed: %v", err)
+ return subcommands.ExitFailure
+ }
+ defer f.Close()
+
+ // Copy stdout from the command to keep this process alive until the
+ // subprocess exits.
+ io.Copy(os.Stdout, f)
+
+ return subcommands.ExitSuccess
+}
diff --git a/test/e2e/BUILD b/test/e2e/BUILD
index 76e04f878..44cce0e3b 100644
--- a/test/e2e/BUILD
+++ b/test/e2e/BUILD
@@ -20,9 +20,9 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/bits",
- "//runsc/dockerutil",
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
"//runsc/specutils",
- "//runsc/testutil",
],
)
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index 594c8e752..6a63b1232 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -23,6 +23,8 @@ package integration
import (
"fmt"
+ "os"
+ "os/exec"
"strconv"
"strings"
"syscall"
@@ -31,23 +33,23 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
- "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
"gvisor.dev/gvisor/runsc/specutils"
)
// Test that exec uses the exact same capability set as the container.
func TestExecCapabilities(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-capabilities-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container.
- if err := d.Run("alpine", "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+ // Check that capability.
matches, err := d.WaitForOutputSubmatch("CapEff:\t([0-9a-f]+)\n", 5*time.Second)
if err != nil {
t.Fatalf("WaitForOutputSubmatch() timeout: %v", err)
@@ -59,7 +61,7 @@ func TestExecCapabilities(t *testing.T) {
t.Log("Root capabilities:", want)
// Now check that exec'd process capabilities match the root.
- got, err := d.Exec("grep", "CapEff:", "/proc/self/status")
+ got, err := d.Exec(dockerutil.RunOpts{}, "grep", "CapEff:", "/proc/self/status")
if err != nil {
t.Fatalf("docker exec failed: %v", err)
}
@@ -72,16 +74,16 @@ func TestExecCapabilities(t *testing.T) {
// Test that 'exec --privileged' adds all capabilities, except for CAP_NET_RAW
// which is removed from the container when --net-raw=false.
func TestExecPrivileged(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-privileged-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container with all capabilities dropped.
- if err := d.Run("--cap-drop=all", "alpine", "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ CapDrop: []string{"all"},
+ }, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Check that all capabilities where dropped from container.
matches, err := d.WaitForOutputSubmatch("CapEff:\t([0-9a-f]+)\n", 5*time.Second)
@@ -100,9 +102,11 @@ func TestExecPrivileged(t *testing.T) {
t.Fatalf("Container should have no capabilities: %x", containerCaps)
}
- // Check that 'exec --privileged' adds all capabilities, except
- // for CAP_NET_RAW.
- got, err := d.ExecWithFlags([]string{"--privileged"}, "grep", "CapEff:", "/proc/self/status")
+ // Check that 'exec --privileged' adds all capabilities, except for
+ // CAP_NET_RAW.
+ got, err := d.Exec(dockerutil.RunOpts{
+ Privileged: true,
+ }, "grep", "CapEff:", "/proc/self/status")
if err != nil {
t.Fatalf("docker exec failed: %v", err)
}
@@ -114,97 +118,99 @@ func TestExecPrivileged(t *testing.T) {
}
func TestExecJobControl(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-job-control-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container.
- if err := d.Run("alpine", "sleep", "1000"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sleep", "1000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Exec 'sh' with an attached pty.
- cmd, ptmx, err := d.ExecWithTerminal("sh")
- if err != nil {
+ if _, err := d.Exec(dockerutil.RunOpts{
+ Pty: func(cmd *exec.Cmd, ptmx *os.File) {
+ // Call "sleep 100 | cat" in the shell. We pipe to cat
+ // so that there will be two processes in the
+ // foreground process group.
+ if _, err := ptmx.Write([]byte("sleep 100 | cat\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // Give shell a few seconds to start executing the sleep.
+ time.Sleep(2 * time.Second)
+
+ // Send a ^C to the pty, which should kill sleep and
+ // cat, but not the shell. \x03 is ASCII "end of
+ // text", which is the same as ^C.
+ if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // The shell should still be alive at this point. Sleep
+ // should have exited with code 2+128=130. We'll exit
+ // with 10 plus that number, so that we can be sure
+ // that the shell did not get signalled.
+ if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // Exec process should exit with code 10+130=140.
+ ps, err := cmd.Process.Wait()
+ if err != nil {
+ t.Fatalf("error waiting for exec process: %v", err)
+ }
+ ws := ps.Sys().(syscall.WaitStatus)
+ if !ws.Exited() {
+ t.Errorf("ws.Exited got false, want true")
+ }
+ if got, want := ws.ExitStatus(), 140; got != want {
+ t.Errorf("ws.ExitedStatus got %d, want %d", got, want)
+ }
+ },
+ }, "sh"); err != nil {
t.Fatalf("docker exec failed: %v", err)
}
- defer ptmx.Close()
-
- // Call "sleep 100 | cat" in the shell. We pipe to cat so that there
- // will be two processes in the foreground process group.
- if _, err := ptmx.Write([]byte("sleep 100 | cat\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Give shell a few seconds to start executing the sleep.
- time.Sleep(2 * time.Second)
-
- // Send a ^C to the pty, which should kill sleep and cat, but not the
- // shell. \x03 is ASCII "end of text", which is the same as ^C.
- if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // The shell should still be alive at this point. Sleep should have
- // exited with code 2+128=130. We'll exit with 10 plus that number, so
- // that we can be sure that the shell did not get signalled.
- if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Exec process should exit with code 10+130=140.
- ps, err := cmd.Process.Wait()
- if err != nil {
- t.Fatalf("error waiting for exec process: %v", err)
- }
- ws := ps.Sys().(syscall.WaitStatus)
- if !ws.Exited() {
- t.Errorf("ws.Exited got false, want true")
- }
- if got, want := ws.ExitStatus(), 140; got != want {
- t.Errorf("ws.ExitedStatus got %d, want %d", got, want)
- }
}
// Test that failure to exec returns proper error message.
func TestExecError(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-error-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container.
- if err := d.Run("alpine", "sleep", "1000"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sleep", "1000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
- _, err := d.Exec("no_can_find")
+ // Attempt to exec a binary that doesn't exist.
+ out, err := d.Exec(dockerutil.RunOpts{}, "no_can_find")
if err == nil {
t.Fatalf("docker exec didn't fail")
}
- if want := `error finding executable "no_can_find" in PATH`; !strings.Contains(err.Error(), want) {
- t.Fatalf("docker exec wrong error, got: %s, want: .*%s.*", err.Error(), want)
+ if want := `error finding executable "no_can_find" in PATH`; !strings.Contains(out, want) {
+ t.Fatalf("docker exec wrong error, got: %s, want: .*%s.*", out, want)
}
}
// Test that exec inherits environment from run.
func TestExecEnv(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-env-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container with env FOO=BAR.
- if err := d.Run("-e", "FOO=BAR", "alpine", "sleep", "1000"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Env: []string{"FOO=BAR"},
+ }, "sleep", "1000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Exec "echo $FOO".
- got, err := d.Exec("/bin/sh", "-c", "echo $FOO")
+ got, err := d.Exec(dockerutil.RunOpts{}, "/bin/sh", "-c", "echo $FOO")
if err != nil {
t.Fatalf("docker exec failed: %v", err)
}
@@ -216,17 +222,19 @@ func TestExecEnv(t *testing.T) {
// TestRunEnvHasHome tests that run always has HOME environment set.
func TestRunEnvHasHome(t *testing.T) {
// Base alpine image does not have any environment variables set.
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("run-env-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Exec "echo $HOME". The 'bin' user's home dir is '/bin'.
- got, err := d.RunFg("--user", "bin", "alpine", "/bin/sh", "-c", "echo $HOME")
+ got, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ User: "bin",
+ }, "/bin/sh", "-c", "echo $HOME")
if err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+
+ // Check that the directory matches.
if got, want := strings.TrimSpace(got), "/bin"; got != want {
t.Errorf("bad output from 'docker run'. Got %q; Want %q.", got, want)
}
@@ -235,18 +243,17 @@ func TestRunEnvHasHome(t *testing.T) {
// Test that exec always has HOME environment set, even when not set in run.
func TestExecEnvHasHome(t *testing.T) {
// Base alpine image does not have any environment variables set.
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("exec-env-home-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
- if err := d.Run("alpine", "sleep", "1000"); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sleep", "1000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Exec "echo $HOME", and expect to see "/root".
- got, err := d.Exec("/bin/sh", "-c", "echo $HOME")
+ got, err := d.Exec(dockerutil.RunOpts{}, "/bin/sh", "-c", "echo $HOME")
if err != nil {
t.Fatalf("docker exec failed: %v", err)
}
@@ -258,12 +265,14 @@ func TestExecEnvHasHome(t *testing.T) {
newUID := 1234
newHome := "/foo/bar"
cmd := fmt.Sprintf("mkdir -p -m 777 %q && adduser foo -D -u %d -h %q", newHome, newUID, newHome)
- if _, err := d.Exec("/bin/sh", "-c", cmd); err != nil {
+ if _, err := d.Exec(dockerutil.RunOpts{}, "/bin/sh", "-c", cmd); err != nil {
t.Fatalf("docker exec failed: %v", err)
}
// Execute the same as the new user and expect newHome.
- got, err = d.ExecAsUser(strconv.Itoa(newUID), "/bin/sh", "-c", "echo $HOME")
+ got, err = d.Exec(dockerutil.RunOpts{
+ User: strconv.Itoa(newUID),
+ }, "/bin/sh", "-c", "echo $HOME")
if err != nil {
t.Fatalf("docker exec failed: %v", err)
}
diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go
index cc4fbbaed..404e37689 100644
--- a/test/e2e/integration_test.go
+++ b/test/e2e/integration_test.go
@@ -27,14 +27,15 @@ import (
"net"
"net/http"
"os"
+ "os/exec"
"strconv"
"strings"
"syscall"
"testing"
"time"
- "gvisor.dev/gvisor/runsc/dockerutil"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
// httpRequestSucceeds sends a request to a given url and checks that the status is OK.
@@ -53,65 +54,66 @@ func httpRequestSucceeds(client http.Client, server string, port int) error {
// TestLifeCycle tests a basic Create/Start/Stop docker container life cycle.
func TestLifeCycle(t *testing.T) {
- if err := dockerutil.Pull("nginx"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("lifecycle-test")
- if err := d.Create("-p", "80", "nginx"); err != nil {
- t.Fatal("docker create failed:", err)
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Start the container.
+ if err := d.Create(dockerutil.RunOpts{
+ Image: "basic/nginx",
+ Ports: []int{80},
+ }); err != nil {
+ t.Fatalf("docker create failed: %v", err)
}
if err := d.Start(); err != nil {
- d.CleanUp()
- t.Fatal("docker start failed:", err)
+ t.Fatalf("docker start failed: %v", err)
}
- // Test that container is working
+ // Test that container is working.
port, err := d.FindPort(80)
if err != nil {
- t.Fatal("docker.FindPort(80) failed: ", err)
+ t.Fatalf("docker.FindPort(80) failed: %v", err)
}
if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
}
client := http.Client{Timeout: time.Duration(2 * time.Second)}
if err := httpRequestSucceeds(client, "localhost", port); err != nil {
- t.Error("http request failed:", err)
+ t.Errorf("http request failed: %v", err)
}
if err := d.Stop(); err != nil {
- d.CleanUp()
- t.Fatal("docker stop failed:", err)
+ t.Fatalf("docker stop failed: %v", err)
}
if err := d.Remove(); err != nil {
- t.Fatal("docker rm failed:", err)
+ t.Fatalf("docker rm failed: %v", err)
}
}
func TestPauseResume(t *testing.T) {
- const img = "gcr.io/gvisor-presubmit/python-hello"
if !testutil.IsCheckpointSupported() {
- t.Log("Checkpoint is not supported, skipping test.")
- return
+ t.Skip("Checkpoint is not supported.")
}
- if err := dockerutil.Pull(img); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("pause-resume-test")
- if err := d.Run("-p", "8080", img); err != nil {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Start the container.
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/python",
+ Ports: []int{8080}, // See Dockerfile.
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Find where port 8080 is mapped to.
port, err := d.FindPort(8080)
if err != nil {
- t.Fatal("docker.FindPort(8080) failed:", err)
+ t.Fatalf("docker.FindPort(8080) failed: %v", err)
}
// Wait until it's up and running.
if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
}
// Check that container is working.
@@ -121,7 +123,7 @@ func TestPauseResume(t *testing.T) {
}
if err := d.Pause(); err != nil {
- t.Fatal("docker pause failed:", err)
+ t.Fatalf("docker pause failed: %v", err)
}
// Check if container is paused.
@@ -137,12 +139,12 @@ func TestPauseResume(t *testing.T) {
}
if err := d.Unpause(); err != nil {
- t.Fatal("docker unpause failed:", err)
+ t.Fatalf("docker unpause failed: %v", err)
}
// Wait until it's up and running.
if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
}
// Check if container is working again.
@@ -152,43 +154,43 @@ func TestPauseResume(t *testing.T) {
}
func TestCheckpointRestore(t *testing.T) {
- const img = "gcr.io/gvisor-presubmit/python-hello"
if !testutil.IsCheckpointSupported() {
- t.Log("Pause/resume is not supported, skipping test.")
- return
+ t.Skip("Pause/resume is not supported.")
}
- if err := dockerutil.Pull(img); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("save-restore-test")
- if err := d.Run("-p", "8080", img); err != nil {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Start the container.
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/python",
+ Ports: []int{8080}, // See Dockerfile.
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+ // Create a snapshot.
if err := d.Checkpoint("test"); err != nil {
- t.Fatal("docker checkpoint failed:", err)
+ t.Fatalf("docker checkpoint failed: %v", err)
}
-
if _, err := d.Wait(30 * time.Second); err != nil {
- t.Fatal(err)
+ t.Fatalf("wait failed: %v", err)
}
// TODO(b/143498576): Remove Poll after github.com/moby/moby/issues/38963 is fixed.
if err := testutil.Poll(func() error { return d.Restore("test") }, 15*time.Second); err != nil {
- t.Fatal("docker restore failed:", err)
+ t.Fatalf("docker restore failed: %v", err)
}
// Find where port 8080 is mapped to.
port, err := d.FindPort(8080)
if err != nil {
- t.Fatal("docker.FindPort(8080) failed:", err)
+ t.Fatalf("docker.FindPort(8080) failed: %v", err)
}
// Wait until it's up and running.
if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
}
// Check if container is working again.
@@ -200,26 +202,28 @@ func TestCheckpointRestore(t *testing.T) {
// Create client and server that talk to each other using the local IP.
func TestConnectToSelf(t *testing.T) {
- d := dockerutil.MakeDocker("connect-to-self-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Creates server that replies "server" and exists. Sleeps at the end because
// 'docker exec' gets killed if the init process exists before it can finish.
- if err := d.Run("ubuntu:trusty", "/bin/sh", "-c", "echo server | nc -l -p 8080 && sleep 1"); err != nil {
- t.Fatal("docker run failed:", err)
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/ubuntu",
+ }, "/bin/sh", "-c", "echo server | nc -l -p 8080 && sleep 1"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Finds IP address for host.
- ip, err := d.Exec("/bin/sh", "-c", "cat /etc/hosts | grep ${HOSTNAME} | awk '{print $1}'")
+ ip, err := d.Exec(dockerutil.RunOpts{}, "/bin/sh", "-c", "cat /etc/hosts | grep ${HOSTNAME} | awk '{print $1}'")
if err != nil {
- t.Fatal("docker exec failed:", err)
+ t.Fatalf("docker exec failed: %v", err)
}
ip = strings.TrimRight(ip, "\n")
// Runs client that sends "client" to the server and exits.
- reply, err := d.Exec("/bin/sh", "-c", fmt.Sprintf("echo client | nc %s 8080", ip))
+ reply, err := d.Exec(dockerutil.RunOpts{}, "/bin/sh", "-c", fmt.Sprintf("echo client | nc %s 8080", ip))
if err != nil {
- t.Fatal("docker exec failed:", err)
+ t.Fatalf("docker exec failed: %v", err)
}
// Ensure both client and server got the message from each other.
@@ -227,21 +231,22 @@ func TestConnectToSelf(t *testing.T) {
t.Errorf("Error on server, want: %q, got: %q", want, reply)
}
if _, err := d.WaitForOutput("^client\n$", 1*time.Second); err != nil {
- t.Fatal("docker.WaitForOutput(client) timeout:", err)
+ t.Fatalf("docker.WaitForOutput(client) timeout: %v", err)
}
}
func TestMemLimit(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("cgroup-test")
- cmd := "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'"
- out, err := d.RunFg("--memory=500MB", "alpine", "sh", "-c", cmd)
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ allocMemory := 500 * 1024
+ out, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Memory: allocMemory, // In kB.
+ }, "sh", "-c", "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'")
if err != nil {
- t.Fatal("docker run failed:", err)
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Remove warning message that swap isn't present.
if strings.HasPrefix(out, "WARNING") {
@@ -252,27 +257,30 @@ func TestMemLimit(t *testing.T) {
out = lines[1]
}
+ // Ensure the memory matches what we want.
got, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64)
if err != nil {
t.Fatalf("failed to parse %q: %v", out, err)
}
- if want := uint64(500 * 1024); got != want {
+ if want := uint64(allocMemory); got != want {
t.Errorf("MemTotal got: %d, want: %d", got, want)
}
}
func TestNumCPU(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("cgroup-test")
- cmd := "cat /proc/cpuinfo | grep 'processor.*:' | wc -l"
- out, err := d.RunFg("--cpuset-cpus=0", "alpine", "sh", "-c", cmd)
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Read how many cores are in the container.
+ out, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Extra: []string{"--cpuset-cpus=0"},
+ }, "sh", "-c", "cat /proc/cpuinfo | grep 'processor.*:' | wc -l")
if err != nil {
- t.Fatal("docker run failed:", err)
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+ // Ensure it matches what we want.
got, err := strconv.Atoi(strings.TrimSpace(out))
if err != nil {
t.Fatalf("failed to parse %q: %v", out, err)
@@ -284,39 +292,39 @@ func TestNumCPU(t *testing.T) {
// TestJobControl tests that job control characters are handled properly.
func TestJobControl(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("job-control-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container with an attached PTY.
- _, ptmx, err := d.RunWithPty("alpine", "sh")
- if err != nil {
+ if _, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Pty: func(_ *exec.Cmd, ptmx *os.File) {
+ // Call "sleep 100" in the shell.
+ if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // Give shell a few seconds to start executing the sleep.
+ time.Sleep(2 * time.Second)
+
+ // Send a ^C to the pty, which should kill sleep, but
+ // not the shell. \x03 is ASCII "end of text", which
+ // is the same as ^C.
+ if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+
+ // The shell should still be alive at this point. Sleep
+ // should have exited with code 2+128=130. We'll exit
+ // with 10 plus that number, so that we can be sure
+ // that the shell did not get signalled.
+ if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
+ t.Fatalf("error writing to pty: %v", err)
+ }
+ },
+ }, "sh"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer ptmx.Close()
- defer d.CleanUp()
-
- // Call "sleep 100" in the shell.
- if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Give shell a few seconds to start executing the sleep.
- time.Sleep(2 * time.Second)
-
- // Send a ^C to the pty, which should kill sleep, but not the shell.
- // \x03 is ASCII "end of text", which is the same as ^C.
- if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // The shell should still be alive at this point. Sleep should have
- // exited with code 2+128=130. We'll exit with 10 plus that number, so
- // that we can be sure that the shell did not get signalled.
- if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
// Wait for the container to exit.
got, err := d.Wait(5 * time.Second)
@@ -332,14 +340,25 @@ func TestJobControl(t *testing.T) {
// TestTmpFile checks that files inside '/tmp' are not overridden. In addition,
// it checks that working dir is created if it doesn't exit.
func TestTmpFile(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Should work without ReadOnly
+ if _, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ WorkDir: "/tmp/foo/bar",
+ }, "touch", "/tmp/foo/bar/file"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
}
- d := dockerutil.MakeDocker("tmp-file-test")
- if err := d.Run("-w=/tmp/foo/bar", "--read-only", "alpine", "touch", "/tmp/foo/bar/file"); err != nil {
- t.Fatal("docker run failed:", err)
+
+ // Expect failure.
+ if _, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ WorkDir: "/tmp/foo/bar",
+ ReadOnly: true,
+ }, "touch", "/tmp/foo/bar/file"); err == nil {
+ t.Fatalf("docker run expected failure, but succeeded")
}
- defer d.CleanUp()
}
func TestMain(m *testing.M) {
diff --git a/test/e2e/regression_test.go b/test/e2e/regression_test.go
index 2488be383..327a2174c 100644
--- a/test/e2e/regression_test.go
+++ b/test/e2e/regression_test.go
@@ -18,7 +18,7 @@ import (
"strings"
"testing"
- "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
)
// Test that UDS can be created using overlay when parent directory is in lower
@@ -27,19 +27,19 @@ import (
// Prerequisite: the directory where the socket file is created must not have
// been open for write before bind(2) is called.
func TestBindOverlay(t *testing.T) {
- if err := dockerutil.Pull("ubuntu:trusty"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("bind-overlay-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
- cmd := "nc -l -U /var/run/sock & p=$! && sleep 1 && echo foobar-asdf | nc -U /var/run/sock && wait $p"
- got, err := d.RunFg("ubuntu:trusty", "bash", "-c", cmd)
+ // Run the container.
+ got, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/ubuntu",
+ }, "bash", "-c", "nc -l -U /var/run/sock & p=$! && sleep 1 && echo foobar-asdf | nc -U /var/run/sock && wait $p")
if err != nil {
- t.Fatal("docker run failed:", err)
+ t.Fatalf("docker run failed: %v", err)
}
+ // Check the output contains what we want.
if want := "foobar-asdf"; !strings.Contains(got, want) {
t.Fatalf("docker run output is missing %q: %s", want, got)
}
- defer d.CleanUp()
}
diff --git a/test/image/BUILD b/test/image/BUILD
index 7392ac54e..e749e47d4 100644
--- a/test/image/BUILD
+++ b/test/image/BUILD
@@ -22,8 +22,8 @@ go_test(
],
visibility = ["//:sandbox"],
deps = [
- "//runsc/dockerutil",
- "//runsc/testutil",
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
],
)
diff --git a/test/image/image_test.go b/test/image/image_test.go
index 0a1e19d6f..2e3543109 100644
--- a/test/image/image_test.go
+++ b/test/image/image_test.go
@@ -28,24 +28,29 @@ import (
"log"
"net/http"
"os"
- "path/filepath"
"strings"
"testing"
"time"
- "gvisor.dev/gvisor/runsc/dockerutil"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
func TestHelloWorld(t *testing.T) {
- d := dockerutil.MakeDocker("hello-test")
- if err := d.Run("hello-world"); err != nil {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ // Run the basic container.
+ out, err := d.Run(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "echo", "Hello world!")
+ if err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
- if _, err := d.WaitForOutput("Hello from Docker!", 5*time.Second); err != nil {
- t.Fatalf("docker didn't say hello: %v", err)
+ // Check the output.
+ if !strings.Contains(out, "Hello world!") {
+ t.Fatalf("docker didn't say hello: got %s", out)
}
}
@@ -102,27 +107,22 @@ func testHTTPServer(t *testing.T, port int) {
}
func TestHttpd(t *testing.T) {
- if err := dockerutil.Pull("httpd"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("http-test")
-
- dir, err := dockerutil.PrepareFiles("test/image/latin10k.txt")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container.
- mountArg := dockerutil.MountArg(dir, "/usr/local/apache2/htdocs", dockerutil.ReadOnly)
- if err := d.Run("-p", "80", mountArg, "httpd"); err != nil {
+ d.CopyFiles("/usr/local/apache2/htdocs", "test/image/latin10k.txt")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/httpd",
+ Ports: []int{80},
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Find where port 80 is mapped to.
port, err := d.FindPort(80)
if err != nil {
- t.Fatalf("docker.FindPort(80) failed: %v", err)
+ t.Fatalf("FindPort(80) failed: %v", err)
}
// Wait until it's up and running.
@@ -134,27 +134,22 @@ func TestHttpd(t *testing.T) {
}
func TestNginx(t *testing.T) {
- if err := dockerutil.Pull("nginx"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("net-test")
-
- dir, err := dockerutil.PrepareFiles("test/image/latin10k.txt")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start the container.
- mountArg := dockerutil.MountArg(dir, "/usr/share/nginx/html", dockerutil.ReadOnly)
- if err := d.Run("-p", "80", mountArg, "nginx"); err != nil {
+ d.CopyFiles("/usr/share/nginx/html", "test/image/latin10k.txt")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/nginx",
+ Ports: []int{80},
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Find where port 80 is mapped to.
port, err := d.FindPort(80)
if err != nil {
- t.Fatalf("docker.FindPort(80) failed: %v", err)
+ t.Fatalf("FindPort(80) failed: %v", err)
}
// Wait until it's up and running.
@@ -166,99 +161,58 @@ func TestNginx(t *testing.T) {
}
func TestMysql(t *testing.T) {
- if err := dockerutil.Pull("mysql"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("mysql-test")
+ server := dockerutil.MakeDocker(t)
+ defer server.CleanUp()
// Start the container.
- if err := d.Run("-e", "MYSQL_ROOT_PASSWORD=foobar123", "mysql"); err != nil {
+ if err := server.Spawn(dockerutil.RunOpts{
+ Image: "basic/mysql",
+ Env: []string{"MYSQL_ROOT_PASSWORD=foobar123"},
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Wait until it's up and running.
- if _, err := d.WaitForOutput("port: 3306 MySQL Community Server", 3*time.Minute); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
+ if _, err := server.WaitForOutput("port: 3306 MySQL Community Server", 3*time.Minute); err != nil {
+ t.Fatalf("WaitForOutput() timeout: %v", err)
}
- client := dockerutil.MakeDocker("mysql-client-test")
- dir, err := dockerutil.PrepareFiles("test/image/mysql.sql")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
+ // Generate the client and copy in the SQL payload.
+ client := dockerutil.MakeDocker(t)
+ defer client.CleanUp()
- // Tell mysql client to connect to the server and execute the file in verbose
- // mode to verify the output.
- args := []string{
- dockerutil.LinkArg(&d, "mysql"),
- dockerutil.MountArg(dir, "/sql", dockerutil.ReadWrite),
- "mysql",
- "mysql", "-hmysql", "-uroot", "-pfoobar123", "-v", "-e", "source /sql/mysql.sql",
- }
- if err := client.Run(args...); err != nil {
+ // Tell mysql client to connect to the server and execute the file in
+ // verbose mode to verify the output.
+ client.CopyFiles("/sql", "test/image/mysql.sql")
+ client.Link("mysql", server)
+ if _, err := client.Run(dockerutil.RunOpts{
+ Image: "basic/mysql",
+ }, "mysql", "-hmysql", "-uroot", "-pfoobar123", "-v", "-e", "source /sql/mysql.sql"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer client.CleanUp()
// Ensure file executed to the end and shutdown mysql.
- if _, err := client.WaitForOutput("--------------\nshutdown\n--------------", 15*time.Second); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
- }
- if _, err := d.WaitForOutput("mysqld: Shutdown complete", 30*time.Second); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
+ if _, err := server.WaitForOutput("mysqld: Shutdown complete", 30*time.Second); err != nil {
+ t.Fatalf("WaitForOutput() timeout: %v", err)
}
}
-func TestPythonHello(t *testing.T) {
- // TODO(b/136503277): Once we have more complete python runtime tests,
- // we can drop this one.
- const img = "gcr.io/gvisor-presubmit/python-hello"
- if err := dockerutil.Pull(img); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("python-hello-test")
- if err := d.Run("-p", "8080", img); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
+func TestTomcat(t *testing.T) {
+ d := dockerutil.MakeDocker(t)
defer d.CleanUp()
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatalf("WaitForHTTP() timeout: %v", err)
- }
-
- // Ensure that content is being served.
- url := fmt.Sprintf("http://localhost:%d", port)
- resp, err := http.Get(url)
- if err != nil {
- t.Errorf("Error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- t.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
-}
-
-func TestTomcat(t *testing.T) {
- if err := dockerutil.Pull("tomcat:8.0"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("tomcat-test")
- if err := d.Run("-p", "8080", "tomcat:8.0"); err != nil {
+ // Start the server.
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/tomcat",
+ Ports: []int{8080},
+ }); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Find where port 8080 is mapped to.
port, err := d.FindPort(8080)
if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
+ t.Fatalf("FindPort(8080) failed: %v", err)
}
// Wait until it's up and running.
@@ -278,28 +232,22 @@ func TestTomcat(t *testing.T) {
}
func TestRuby(t *testing.T) {
- if err := dockerutil.Pull("ruby"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("ruby-test")
-
- dir, err := dockerutil.PrepareFiles("test/image/ruby.rb", "test/image/ruby.sh")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
- if err := os.Chmod(filepath.Join(dir, "ruby.sh"), 0333); err != nil {
- t.Fatalf("os.Chmod(%q, 0333) failed: %v", dir, err)
- }
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
- if err := d.Run("-p", "8080", dockerutil.MountArg(dir, "/src", dockerutil.ReadOnly), "ruby", "/src/ruby.sh"); err != nil {
+ // Execute the ruby workload.
+ d.CopyFiles("/src", "test/image/ruby.rb", "test/image/ruby.sh")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/ruby",
+ Ports: []int{8080},
+ }, "/src/ruby.sh"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// Find where port 8080 is mapped to.
port, err := d.FindPort(8080)
if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
+ t.Fatalf("FindPort(8080) failed: %v", err)
}
// Wait until it's up and running, 'gem install' can take some time.
@@ -326,18 +274,17 @@ func TestRuby(t *testing.T) {
}
func TestStdio(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := dockerutil.MakeDocker("stdio-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
wantStdout := "hello stdout"
wantStderr := "bonjour stderr"
cmd := fmt.Sprintf("echo %q; echo %q 1>&2;", wantStdout, wantStderr)
- if err := d.Run("alpine", "/bin/sh", "-c", cmd); err != nil {
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "/bin/sh", "-c", cmd); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
for _, want := range []string{wantStdout, wantStderr} {
if _, err := d.WaitForOutput(want, 5*time.Second); err != nil {
diff --git a/test/image/ruby.sh b/test/image/ruby.sh
index ebe8d5b0e..ebe8d5b0e 100644..100755
--- a/test/image/ruby.sh
+++ b/test/image/ruby.sh
diff --git a/test/iptables/BUILD b/test/iptables/BUILD
index 6bb3b82b5..3e29ca90d 100644
--- a/test/iptables/BUILD
+++ b/test/iptables/BUILD
@@ -14,7 +14,7 @@ go_library(
],
visibility = ["//test/iptables:__subpackages__"],
deps = [
- "//runsc/testutil",
+ "//pkg/test/testutil",
],
)
@@ -23,14 +23,14 @@ go_test(
srcs = [
"iptables_test.go",
],
+ data = ["//test/iptables/runner"],
library = ":iptables",
tags = [
"local",
"manual",
],
deps = [
- "//pkg/log",
- "//runsc/dockerutil",
- "//runsc/testutil",
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
],
)
diff --git a/test/iptables/README.md b/test/iptables/README.md
index cc8a2fcac..b9f44bd40 100644
--- a/test/iptables/README.md
+++ b/test/iptables/README.md
@@ -38,7 +38,7 @@ Build the testing Docker container. Re-run this when you modify the test code in
this directory:
```bash
-$ bazel run //test/iptables/runner:runner-image -- --norun
+$ make load-iptables
```
Run an individual test via:
diff --git a/test/iptables/iptables.go b/test/iptables/iptables.go
index 2e565d988..16cb4f4da 100644
--- a/test/iptables/iptables.go
+++ b/test/iptables/iptables.go
@@ -18,12 +18,19 @@ package iptables
import (
"fmt"
"net"
+ "time"
)
// IPExchangePort is the port the container listens on to receive the IP
// address of the local process.
const IPExchangePort = 2349
+// TerminalStatement is the last statement in the test runner.
+const TerminalStatement = "Finished!"
+
+// TestTimeout is the timeout used for all tests.
+const TestTimeout = 10 * time.Minute
+
// A TestCase contains one action to run in the container and one to run
// locally. The actions run concurrently and each must succeed for the test
// pass.
diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go
index 493d69052..334d8e676 100644
--- a/test/iptables/iptables_test.go
+++ b/test/iptables/iptables_test.go
@@ -15,28 +15,14 @@
package iptables
import (
- "flag"
"fmt"
"net"
- "os"
- "path"
"testing"
- "time"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/runsc/dockerutil"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
-const timeout = 18 * time.Second
-
-var image = flag.String("image", "bazel/test/iptables/runner:runner-image", "image to run tests in")
-
-type result struct {
- output string
- err error
-}
-
// singleTest runs a TestCase. Each test follows a pattern:
// - Create a container.
// - Get the container's IP.
@@ -46,77 +32,45 @@ type result struct {
//
// Container output is logged to $TEST_UNDECLARED_OUTPUTS_DIR if it exists, or
// to stderr.
-func singleTest(test TestCase) error {
+func singleTest(t *testing.T, test TestCase) {
if _, ok := Tests[test.Name()]; !ok {
- return fmt.Errorf("no test found with name %q. Has it been registered?", test.Name())
+ t.Fatalf("no test found with name %q. Has it been registered?", test.Name())
}
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
// Create and start the container.
- cont := dockerutil.MakeDocker("gvisor-iptables")
- defer cont.CleanUp()
- resultChan := make(chan *result)
- go func() {
- output, err := cont.RunFg("--cap-add=NET_ADMIN", *image, "-name", test.Name())
- logContainer(output, err)
- resultChan <- &result{output, err}
- }()
+ d.CopyFiles("/runner", "test/iptables/runner/runner")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "iptables",
+ CapAdd: []string{"NET_ADMIN"},
+ }, "/runner/runner", "-name", test.Name()); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
// Get the container IP.
- ip, err := getIP(cont)
+ ip, err := d.FindIP()
if err != nil {
- return fmt.Errorf("failed to get container IP: %v", err)
+ t.Fatalf("failed to get container IP: %v", err)
}
// Give the container our IP.
if err := sendIP(ip); err != nil {
- return fmt.Errorf("failed to send IP to container: %v", err)
+ t.Fatalf("failed to send IP to container: %v", err)
}
// Run our side of the test.
- errChan := make(chan error)
- go func() {
- errChan <- test.LocalAction(ip)
- }()
-
- // Wait for both the container and local tests to finish.
- var res *result
- to := time.After(timeout)
- for localDone := false; res == nil || !localDone; {
- select {
- case res = <-resultChan:
- log.Infof("Container finished.")
- case err, localDone = <-errChan:
- log.Infof("Local finished.")
- if err != nil {
- return fmt.Errorf("local test failed: %v", err)
- }
- case <-to:
- return fmt.Errorf("timed out after %f seconds", timeout.Seconds())
- }
+ if err := test.LocalAction(ip); err != nil {
+ t.Fatalf("LocalAction failed: %v", err)
}
- return res.err
-}
-
-func getIP(cont dockerutil.Docker) (net.IP, error) {
- // The container might not have started yet, so retry a few times.
- var ipStr string
- to := time.After(timeout)
- for ipStr == "" {
- ipStr, _ = cont.FindIP()
- select {
- case <-to:
- return net.IP{}, fmt.Errorf("timed out getting IP after %f seconds", timeout.Seconds())
- default:
- time.Sleep(250 * time.Millisecond)
- }
- }
- ip := net.ParseIP(ipStr)
- if ip == nil {
- return net.IP{}, fmt.Errorf("invalid IP: %q", ipStr)
+ // Wait for the final statement. This structure has the side effect
+ // that all container logs will appear within the individual test
+ // context.
+ if _, err := d.WaitForOutput(TerminalStatement, TestTimeout); err != nil {
+ t.Fatalf("test failed: %v", err)
}
- log.Infof("Container has IP of %s", ipStr)
- return ip, nil
}
func sendIP(ip net.IP) error {
@@ -132,7 +86,7 @@ func sendIP(ip net.IP) error {
conn = c
return err
}
- if err := testutil.Poll(cb, timeout); err != nil {
+ if err := testutil.Poll(cb, TestTimeout); err != nil {
return fmt.Errorf("timed out waiting to send IP, most recent error: %v", err)
}
if _, err := conn.Write([]byte{0}); err != nil {
@@ -141,281 +95,184 @@ func sendIP(ip net.IP) error {
return nil
}
-func logContainer(output string, err error) {
- msg := fmt.Sprintf("Container error: %v\nContainer output:\n%v", err, output)
- if artifactsDir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); artifactsDir != "" {
- fpath := path.Join(artifactsDir, "container.log")
- if file, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
- log.Warningf("Failed to open log file %q: %v", fpath, err)
- } else {
- defer file.Close()
- if _, err := file.Write([]byte(msg)); err == nil {
- return
- }
- log.Warningf("Failed to write to log file %s: %v", fpath, err)
- }
- }
-
- // We couldn't write to the output directory -- just log to stderr.
- log.Infof(msg)
-}
-
func TestFilterInputDropUDP(t *testing.T) {
- if err := singleTest(FilterInputDropUDP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropUDP{})
}
func TestFilterInputDropUDPPort(t *testing.T) {
- if err := singleTest(FilterInputDropUDPPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropUDPPort{})
}
func TestFilterInputDropDifferentUDPPort(t *testing.T) {
- if err := singleTest(FilterInputDropDifferentUDPPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropDifferentUDPPort{})
}
func TestFilterInputDropAll(t *testing.T) {
- if err := singleTest(FilterInputDropAll{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropAll{})
}
func TestFilterInputDropOnlyUDP(t *testing.T) {
- if err := singleTest(FilterInputDropOnlyUDP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropOnlyUDP{})
}
func TestNATRedirectUDPPort(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATRedirectUDPPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATRedirectUDPPort{})
}
func TestNATRedirectTCPPort(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATRedirectTCPPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATRedirectTCPPort{})
}
func TestNATDropUDP(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATDropUDP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATDropUDP{})
}
func TestNATAcceptAll(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATAcceptAll{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATAcceptAll{})
}
func TestFilterInputDropTCPDestPort(t *testing.T) {
- if err := singleTest(FilterInputDropTCPDestPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropTCPDestPort{})
}
func TestFilterInputDropTCPSrcPort(t *testing.T) {
- if err := singleTest(FilterInputDropTCPSrcPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDropTCPSrcPort{})
}
func TestFilterInputCreateUserChain(t *testing.T) {
- if err := singleTest(FilterInputCreateUserChain{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputCreateUserChain{})
}
func TestFilterInputDefaultPolicyAccept(t *testing.T) {
- if err := singleTest(FilterInputDefaultPolicyAccept{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDefaultPolicyAccept{})
}
func TestFilterInputDefaultPolicyDrop(t *testing.T) {
- if err := singleTest(FilterInputDefaultPolicyDrop{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDefaultPolicyDrop{})
}
func TestFilterInputReturnUnderflow(t *testing.T) {
- if err := singleTest(FilterInputReturnUnderflow{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputReturnUnderflow{})
}
func TestFilterOutputDropTCPDestPort(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("filter OUTPUT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(FilterOutputDropTCPDestPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputDropTCPDestPort{})
}
func TestFilterOutputDropTCPSrcPort(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("filter OUTPUT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(FilterOutputDropTCPSrcPort{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputDropTCPSrcPort{})
}
func TestFilterOutputAcceptTCPOwner(t *testing.T) {
- if err := singleTest(FilterOutputAcceptTCPOwner{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputAcceptTCPOwner{})
}
func TestFilterOutputDropTCPOwner(t *testing.T) {
- if err := singleTest(FilterOutputDropTCPOwner{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputDropTCPOwner{})
}
func TestFilterOutputAcceptUDPOwner(t *testing.T) {
- if err := singleTest(FilterOutputAcceptUDPOwner{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputAcceptUDPOwner{})
}
func TestFilterOutputDropUDPOwner(t *testing.T) {
- if err := singleTest(FilterOutputDropUDPOwner{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputDropUDPOwner{})
}
func TestFilterOutputOwnerFail(t *testing.T) {
- if err := singleTest(FilterOutputOwnerFail{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputOwnerFail{})
}
func TestJumpSerialize(t *testing.T) {
- if err := singleTest(FilterInputSerializeJump{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputSerializeJump{})
}
func TestJumpBasic(t *testing.T) {
- if err := singleTest(FilterInputJumpBasic{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputJumpBasic{})
}
func TestJumpReturn(t *testing.T) {
- if err := singleTest(FilterInputJumpReturn{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputJumpReturn{})
}
func TestJumpReturnDrop(t *testing.T) {
- if err := singleTest(FilterInputJumpReturnDrop{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputJumpReturnDrop{})
}
func TestJumpBuiltin(t *testing.T) {
- if err := singleTest(FilterInputJumpBuiltin{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputJumpBuiltin{})
}
func TestJumpTwice(t *testing.T) {
- if err := singleTest(FilterInputJumpTwice{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputJumpTwice{})
}
func TestInputDestination(t *testing.T) {
- if err := singleTest(FilterInputDestination{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputDestination{})
}
func TestInputInvertDestination(t *testing.T) {
- if err := singleTest(FilterInputInvertDestination{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterInputInvertDestination{})
}
func TestOutputDestination(t *testing.T) {
- if err := singleTest(FilterOutputDestination{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputDestination{})
}
func TestOutputInvertDestination(t *testing.T) {
- if err := singleTest(FilterOutputInvertDestination{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, FilterOutputInvertDestination{})
}
func TestNATOutRedirectIP(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATOutRedirectIP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATOutRedirectIP{})
}
func TestNATOutDontRedirectIP(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATOutDontRedirectIP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATOutDontRedirectIP{})
}
func TestNATOutRedirectInvert(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATOutRedirectInvert{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATOutRedirectInvert{})
}
func TestNATPreRedirectIP(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATPreRedirectIP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATPreRedirectIP{})
}
func TestNATPreDontRedirectIP(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATPreDontRedirectIP{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATPreDontRedirectIP{})
}
func TestNATPreRedirectInvert(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATPreRedirectInvert{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATPreRedirectInvert{})
}
func TestNATRedirectRequiresProtocol(t *testing.T) {
// TODO(gvisor.dev/issue/170): Enable when supported.
t.Skip("NAT isn't supported yet (gvisor.dev/issue/170).")
- if err := singleTest(NATRedirectRequiresProtocol{}); err != nil {
- t.Fatal(err)
- }
+ singleTest(t, NATRedirectRequiresProtocol{})
}
diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go
index 134391e8d..2a00677be 100644
--- a/test/iptables/iptables_util.go
+++ b/test/iptables/iptables_util.go
@@ -20,7 +20,7 @@ import (
"os/exec"
"time"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
const iptablesBinary = "iptables"
diff --git a/test/iptables/runner/BUILD b/test/iptables/runner/BUILD
index b9199387a..24504a1b9 100644
--- a/test/iptables/runner/BUILD
+++ b/test/iptables/runner/BUILD
@@ -1,4 +1,4 @@
-load("//tools:defs.bzl", "container_image", "go_binary", "go_image")
+load("//tools:defs.bzl", "go_binary")
package(licenses = ["notice"])
@@ -6,18 +6,7 @@ go_binary(
name = "runner",
testonly = 1,
srcs = ["main.go"],
- deps = ["//test/iptables"],
-)
-
-container_image(
- name = "iptables-base",
- base = "@iptables-test//image",
-)
-
-go_image(
- name = "runner-image",
- testonly = 1,
- srcs = ["main.go"],
- base = ":iptables-base",
+ pure = True,
+ visibility = ["//test/iptables:__subpackages__"],
deps = ["//test/iptables"],
)
diff --git a/test/iptables/runner/main.go b/test/iptables/runner/main.go
index 3c794114e..6f77c0684 100644
--- a/test/iptables/runner/main.go
+++ b/test/iptables/runner/main.go
@@ -46,6 +46,9 @@ func main() {
if err := test.ContainerAction(ip); err != nil {
log.Fatalf("Failed running test %q: %v", *name, err)
}
+
+ // Emit the final line.
+ log.Printf("%s", iptables.TerminalStatement)
}
// getIP listens for a connection from the local process and returns the source
diff --git a/test/packetdrill/packetdrill_test.sh b/test/packetdrill/packetdrill_test.sh
index c8268170f..922547d65 100755
--- a/test/packetdrill/packetdrill_test.sh
+++ b/test/packetdrill/packetdrill_test.sh
@@ -85,23 +85,26 @@ if [[ ! -x "${INIT_SCRIPT-}" ]]; then
exit 2
fi
+function new_net_prefix() {
+ # Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
+ echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
+}
+
# Variables specific to the control network and interface start with CTRL_.
# Variables specific to the test network and interface start with TEST_.
# Variables specific to the DUT start with DUT_.
# Variables specific to the test runner start with TEST_RUNNER_.
declare -r PACKETDRILL="/packetdrill/gtests/net/packetdrill/packetdrill"
# Use random numbers so that test networks don't collide.
-declare -r CTRL_NET="ctrl_net-$(shuf -i 0-99999999 -n 1)"
-declare -r TEST_NET="test_net-$(shuf -i 0-99999999 -n 1)"
+declare CTRL_NET="ctrl_net-$(shuf -i 0-99999999 -n 1)"
+declare CTRL_NET_PREFIX=$(new_net_prefix)
+declare TEST_NET="test_net-$(shuf -i 0-99999999 -n 1)"
+declare TEST_NET_PREFIX=$(new_net_prefix)
declare -r tolerance_usecs=100000
# On both DUT and test runner, testing packets are on the eth2 interface.
declare -r TEST_DEVICE="eth2"
# Number of bits in the *_NET_PREFIX variables.
declare -r NET_MASK="24"
-function new_net_prefix() {
- # Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
- echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
-}
# Last bits of the DUT's IP address.
declare -r DUT_NET_SUFFIX=".10"
# Control port.
@@ -137,23 +140,21 @@ function finish {
trap finish EXIT
# Subnet for control packets between test runner and DUT.
-declare CTRL_NET_PREFIX=$(new_net_prefix)
while ! docker network create \
"--subnet=${CTRL_NET_PREFIX}.0/${NET_MASK}" "${CTRL_NET}"; do
sleep 0.1
- declare CTRL_NET_PREFIX=$(new_net_prefix)
+ CTRL_NET_PREFIX=$(new_net_prefix)
+ CTRL_NET="ctrl_net-$(shuf -i 0-99999999 -n 1)"
done
# Subnet for the packets that are part of the test.
-declare TEST_NET_PREFIX=$(new_net_prefix)
while ! docker network create \
"--subnet=${TEST_NET_PREFIX}.0/${NET_MASK}" "${TEST_NET}"; do
sleep 0.1
- declare TEST_NET_PREFIX=$(new_net_prefix)
+ TEST_NET_PREFIX=$(new_net_prefix)
+ TEST_NET="test_net-$(shuf -i 0-99999999 -n 1)"
done
-docker pull "${IMAGE_TAG}"
-
# Create the DUT container and connect to network.
DUT=$(docker create ${RUNTIME_ARG} --privileged --rm \
--stop-timeout ${TIMEOUT} -it ${IMAGE_TAG})
diff --git a/test/packetimpact/testbench/BUILD b/test/packetimpact/testbench/BUILD
index b6a254882..3ceceb9d7 100644
--- a/test/packetimpact/testbench/BUILD
+++ b/test/packetimpact/testbench/BUILD
@@ -23,7 +23,6 @@ go_library(
"//test/packetimpact/proto:posix_server_go_proto",
"@com_github_google_go-cmp//cmp:go_default_library",
"@com_github_google_go-cmp//cmp/cmpopts:go_default_library",
- "@com_github_imdario_mergo//:go_default_library",
"@com_github_mohae_deepcopy//:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//keepalive:go_default_library",
@@ -37,5 +36,8 @@ go_test(
size = "small",
srcs = ["layers_test.go"],
library = ":testbench",
- deps = ["//pkg/tcpip"],
+ deps = [
+ "//pkg/tcpip",
+ "@com_github_mohae_deepcopy//:go_default_library",
+ ],
)
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index f84fd8ba7..952a717e0 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -72,7 +72,8 @@ type layerState interface {
// incoming creates an expected Layer for comparing against a received Layer.
// Because the expectation can depend on values in the received Layer, it is
// an input to incoming. For example, the ACK number needs to be checked in a
- // TCP packet but only if the ACK flag is set in the received packet.
+ // TCP packet but only if the ACK flag is set in the received packet. The
+ // calles takes ownership of the returned Layer.
incoming(received Layer) Layer
// sent updates the layerState based on the Layer that was sent. The input is
@@ -124,6 +125,7 @@ func (s *etherState) outgoing() Layer {
return &s.out
}
+// incoming implements layerState.incoming.
func (s *etherState) incoming(Layer) Layer {
return deepcopy.Copy(&s.in).(Layer)
}
@@ -168,6 +170,7 @@ func (s *ipv4State) outgoing() Layer {
return &s.out
}
+// incoming implements layerState.incoming.
func (s *ipv4State) incoming(Layer) Layer {
return deepcopy.Copy(&s.in).(Layer)
}
@@ -234,6 +237,7 @@ func (s *tcpState) outgoing() Layer {
return &newOutgoing
}
+// incoming implements layerState.incoming.
func (s *tcpState) incoming(received Layer) Layer {
tcpReceived, ok := received.(*TCP)
if !ok {
@@ -328,6 +332,7 @@ func (s *udpState) outgoing() Layer {
return &s.out
}
+// incoming implements layerState.incoming.
func (s *udpState) incoming(Layer) Layer {
return deepcopy.Copy(&s.in).(Layer)
}
@@ -363,16 +368,33 @@ type Connection struct {
// reverse is never a match. override overrides the default matchers for each
// Layer.
func (conn *Connection) match(override, received Layers) bool {
- if len(received) < len(conn.layerStates) {
+ var layersToMatch int
+ if len(override) < len(conn.layerStates) {
+ layersToMatch = len(conn.layerStates)
+ } else {
+ layersToMatch = len(override)
+ }
+ if len(received) < layersToMatch {
return false
}
- for i, s := range conn.layerStates {
- toMatch := s.incoming(received[i])
- if toMatch == nil {
- return false
- }
- if i < len(override) {
- toMatch.merge(override[i])
+ for i := 0; i < layersToMatch; i++ {
+ var toMatch Layer
+ if i < len(conn.layerStates) {
+ s := conn.layerStates[i]
+ toMatch = s.incoming(received[i])
+ if toMatch == nil {
+ return false
+ }
+ if i < len(override) {
+ if err := toMatch.merge(override[i]); err != nil {
+ conn.t.Fatalf("failed to merge: %s", err)
+ }
+ }
+ } else {
+ toMatch = override[i]
+ if toMatch == nil {
+ conn.t.Fatalf("expect the overriding layers to be non-nil")
+ }
}
if !toMatch.match(received[i]) {
return false
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index 9335909c0..3f340c6bc 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -132,7 +132,7 @@ func (dut *DUT) CreateBoundSocket(typ, proto int32, addr net.IP) (int32, uint16)
copy(sa.Addr[:], addr.To16())
dut.Bind(fd, &sa)
} else {
- dut.t.Fatal("unknown ip addr type for remoteIP")
+ dut.t.Fatalf("unknown ip addr type for remoteIP")
}
sa := dut.GetSockName(fd)
var port int
diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go
index 5ce324f0d..01e99567d 100644
--- a/test/packetimpact/testbench/layers.go
+++ b/test/packetimpact/testbench/layers.go
@@ -22,7 +22,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
- "github.com/imdario/mergo"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -111,13 +110,31 @@ func equalLayer(x, y Layer) bool {
return cmp.Equal(x, y, opt, cmpopts.IgnoreTypes(LayerBase{}))
}
-// mergeLayer merges other in layer. Any non-nil value in other overrides the
-// corresponding value in layer. If other is nil, no action is performed.
-func mergeLayer(layer, other Layer) error {
- if other == nil {
+// mergeLayer merges y into x. Any fields for which y has a non-nil value, that
+// value overwrite the corresponding fields in x.
+func mergeLayer(x, y Layer) error {
+ if y == nil {
return nil
}
- return mergo.Merge(layer, other, mergo.WithOverride)
+ if reflect.TypeOf(x) != reflect.TypeOf(y) {
+ return fmt.Errorf("can't merge %T into %T", y, x)
+ }
+ vx := reflect.ValueOf(x).Elem()
+ vy := reflect.ValueOf(y).Elem()
+ t := vy.Type()
+ for i := 0; i < vy.NumField(); i++ {
+ t := t.Field(i)
+ if t.Anonymous {
+ // Ignore the LayerBase in the Layer struct.
+ continue
+ }
+ v := vy.Field(i)
+ if v.IsNil() {
+ continue
+ }
+ vx.Field(i).Set(v)
+ }
+ return nil
}
func stringLayer(l Layer) string {
@@ -243,8 +260,7 @@ func (l *Ether) length() int {
return header.EthernetMinimumSize
}
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
+// merge implements Layer.merge.
func (l *Ether) merge(other Layer) error {
return mergeLayer(l, other)
}
@@ -399,8 +415,7 @@ func (l *IPv4) length() int {
return int(*l.IHL)
}
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
+// merge implements Layer.merge.
func (l *IPv4) merge(other Layer) error {
return mergeLayer(l, other)
}
@@ -544,8 +559,7 @@ func (l *TCP) length() int {
return int(*l.DataOffset)
}
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
+// merge implements Layer.merge.
func (l *TCP) merge(other Layer) error {
return mergeLayer(l, other)
}
@@ -622,8 +636,7 @@ func (l *UDP) length() int {
return int(*l.Length)
}
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
+// merge implements Layer.merge.
func (l *UDP) merge(other Layer) error {
return mergeLayer(l, other)
}
@@ -659,8 +672,7 @@ func (l *Payload) length() int {
return len(l.Bytes)
}
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
+// merge implements Layer.merge.
func (l *Payload) merge(other Layer) error {
return mergeLayer(l, other)
}
diff --git a/test/packetimpact/testbench/layers_test.go b/test/packetimpact/testbench/layers_test.go
index b32efda93..f07ec5eb2 100644
--- a/test/packetimpact/testbench/layers_test.go
+++ b/test/packetimpact/testbench/layers_test.go
@@ -17,6 +17,7 @@ package testbench
import (
"testing"
+ "github.com/mohae/deepcopy"
"gvisor.dev/gvisor/pkg/tcpip"
)
@@ -52,6 +53,114 @@ func TestLayerMatch(t *testing.T) {
}
}
+func TestLayerMergeMismatch(t *testing.T) {
+ tcp := &TCP{}
+ otherTCP := &TCP{}
+ ipv4 := &IPv4{}
+ ether := &Ether{}
+ for _, tt := range []struct {
+ a, b Layer
+ success bool
+ }{
+ {tcp, tcp, true},
+ {tcp, otherTCP, true},
+ {tcp, ipv4, false},
+ {tcp, ether, false},
+ {tcp, nil, true},
+
+ {otherTCP, otherTCP, true},
+ {otherTCP, ipv4, false},
+ {otherTCP, ether, false},
+ {otherTCP, nil, true},
+
+ {ipv4, ipv4, true},
+ {ipv4, ether, false},
+ {ipv4, nil, true},
+
+ {ether, ether, true},
+ {ether, nil, true},
+ } {
+ if err := tt.a.merge(tt.b); (err == nil) != tt.success {
+ t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.a, tt.b, err)
+ }
+ if tt.b != nil {
+ if err := tt.b.merge(tt.a); (err == nil) != tt.success {
+ t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.b, tt.a, err)
+ }
+ }
+ }
+}
+
+func TestLayerMerge(t *testing.T) {
+ zero := Uint32(0)
+ one := Uint32(1)
+ two := Uint32(2)
+ empty := []byte{}
+ foo := []byte("foo")
+ bar := []byte("bar")
+ for _, tt := range []struct {
+ a, b Layer
+ want Layer
+ }{
+ {&TCP{AckNum: nil}, &TCP{AckNum: nil}, &TCP{AckNum: nil}},
+ {&TCP{AckNum: nil}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
+ {&TCP{AckNum: nil}, &TCP{AckNum: one}, &TCP{AckNum: one}},
+ {&TCP{AckNum: nil}, &TCP{AckNum: two}, &TCP{AckNum: two}},
+ {&TCP{AckNum: nil}, nil, &TCP{AckNum: nil}},
+
+ {&TCP{AckNum: zero}, &TCP{AckNum: nil}, &TCP{AckNum: zero}},
+ {&TCP{AckNum: zero}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
+ {&TCP{AckNum: zero}, &TCP{AckNum: one}, &TCP{AckNum: one}},
+ {&TCP{AckNum: zero}, &TCP{AckNum: two}, &TCP{AckNum: two}},
+ {&TCP{AckNum: zero}, nil, &TCP{AckNum: zero}},
+
+ {&TCP{AckNum: one}, &TCP{AckNum: nil}, &TCP{AckNum: one}},
+ {&TCP{AckNum: one}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
+ {&TCP{AckNum: one}, &TCP{AckNum: one}, &TCP{AckNum: one}},
+ {&TCP{AckNum: one}, &TCP{AckNum: two}, &TCP{AckNum: two}},
+ {&TCP{AckNum: one}, nil, &TCP{AckNum: one}},
+
+ {&TCP{AckNum: two}, &TCP{AckNum: nil}, &TCP{AckNum: two}},
+ {&TCP{AckNum: two}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
+ {&TCP{AckNum: two}, &TCP{AckNum: one}, &TCP{AckNum: one}},
+ {&TCP{AckNum: two}, &TCP{AckNum: two}, &TCP{AckNum: two}},
+ {&TCP{AckNum: two}, nil, &TCP{AckNum: two}},
+
+ {&Payload{Bytes: nil}, &Payload{Bytes: nil}, &Payload{Bytes: nil}},
+ {&Payload{Bytes: nil}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
+ {&Payload{Bytes: nil}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
+ {&Payload{Bytes: nil}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
+ {&Payload{Bytes: nil}, nil, &Payload{Bytes: nil}},
+
+ {&Payload{Bytes: empty}, &Payload{Bytes: nil}, &Payload{Bytes: empty}},
+ {&Payload{Bytes: empty}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
+ {&Payload{Bytes: empty}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
+ {&Payload{Bytes: empty}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
+ {&Payload{Bytes: empty}, nil, &Payload{Bytes: empty}},
+
+ {&Payload{Bytes: foo}, &Payload{Bytes: nil}, &Payload{Bytes: foo}},
+ {&Payload{Bytes: foo}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
+ {&Payload{Bytes: foo}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
+ {&Payload{Bytes: foo}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
+ {&Payload{Bytes: foo}, nil, &Payload{Bytes: foo}},
+
+ {&Payload{Bytes: bar}, &Payload{Bytes: nil}, &Payload{Bytes: bar}},
+ {&Payload{Bytes: bar}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
+ {&Payload{Bytes: bar}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
+ {&Payload{Bytes: bar}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
+ {&Payload{Bytes: bar}, nil, &Payload{Bytes: bar}},
+ } {
+ a := deepcopy.Copy(tt.a).(Layer)
+ if err := a.merge(tt.b); err != nil {
+ t.Errorf("%s.merge(%s) = %s, wanted nil", tt.a, tt.b, err)
+ continue
+ }
+ if a.String() != tt.want.String() {
+ t.Errorf("%s.merge(%s) merge result got %s, want %s", tt.a, tt.b, a, tt.want)
+ }
+ }
+}
+
func TestLayerStringFormat(t *testing.T) {
for _, tt := range []struct {
name string
@@ -154,3 +263,53 @@ func TestLayerStringFormat(t *testing.T) {
})
}
}
+
+func TestConnectionMatch(t *testing.T) {
+ conn := Connection{
+ layerStates: []layerState{&etherState{}},
+ }
+ protoNum0 := tcpip.NetworkProtocolNumber(0)
+ protoNum1 := tcpip.NetworkProtocolNumber(1)
+ for _, tt := range []struct {
+ description string
+ override, received Layers
+ wantMatch bool
+ }{
+ {
+ description: "shorter override",
+ override: []Layer{&Ether{}},
+ received: []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
+ wantMatch: true,
+ },
+ {
+ description: "longer override",
+ override: []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
+ received: []Layer{&Ether{}},
+ wantMatch: false,
+ },
+ {
+ description: "ether layer mismatch",
+ override: []Layer{&Ether{Type: &protoNum0}},
+ received: []Layer{&Ether{Type: &protoNum1}},
+ wantMatch: false,
+ },
+ {
+ description: "both nil",
+ override: nil,
+ received: nil,
+ wantMatch: false,
+ },
+ {
+ description: "nil override",
+ override: nil,
+ received: []Layer{&Ether{}},
+ wantMatch: true,
+ },
+ } {
+ t.Run(tt.description, func(t *testing.T) {
+ if gotMatch := conn.match(tt.override, tt.received); gotMatch != tt.wantMatch {
+ t.Fatalf("conn.match(%s, %s) = %t, want %t", tt.override, tt.received, gotMatch, tt.wantMatch)
+ }
+ })
+ }
+}
diff --git a/test/packetimpact/tests/defs.bzl b/test/packetimpact/tests/defs.bzl
index 8c0d058b2..27c5de375 100644
--- a/test/packetimpact/tests/defs.bzl
+++ b/test/packetimpact/tests/defs.bzl
@@ -59,7 +59,11 @@ _packetimpact_test = rule(
PACKETIMPACT_TAGS = ["local", "manual"]
-def packetimpact_linux_test(name, testbench_binary, **kwargs):
+def packetimpact_linux_test(
+ name,
+ testbench_binary,
+ expect_failure = False,
+ **kwargs):
"""Add a packetimpact test on linux.
Args:
@@ -67,28 +71,37 @@ def packetimpact_linux_test(name, testbench_binary, **kwargs):
testbench_binary: the testbench binary
**kwargs: all the other args, forwarded to _packetimpact_test
"""
+ expect_failure_flag = ["--expect_failure"] if expect_failure else []
_packetimpact_test(
name = name + "_linux_test",
testbench_binary = testbench_binary,
- flags = ["--dut_platform", "linux"],
+ flags = ["--dut_platform", "linux"] + expect_failure_flag,
tags = PACKETIMPACT_TAGS + ["packetimpact"],
**kwargs
)
-def packetimpact_netstack_test(name, testbench_binary, **kwargs):
+def packetimpact_netstack_test(
+ name,
+ testbench_binary,
+ expect_failure = False,
+ **kwargs):
"""Add a packetimpact test on netstack.
Args:
name: name of the test
testbench_binary: the testbench binary
+ expect_failure: the test must fail
**kwargs: all the other args, forwarded to _packetimpact_test
"""
+ expect_failure_flag = []
+ if expect_failure:
+ expect_failure_flag = ["--expect_failure"]
_packetimpact_test(
name = name + "_netstack_test",
testbench_binary = testbench_binary,
# This is the default runtime unless
# "--test_arg=--runtime=OTHER_RUNTIME" is used to override the value.
- flags = ["--dut_platform", "netstack", "--runtime=runsc-d"],
+ flags = ["--dut_platform", "netstack", "--runtime=runsc-d"] + expect_failure_flag,
tags = PACKETIMPACT_TAGS + ["packetimpact"],
**kwargs
)
@@ -112,7 +125,13 @@ def packetimpact_go_test(name, size = "small", pure = True, linux = True, netsta
tags = PACKETIMPACT_TAGS,
**kwargs
)
- if linux:
- packetimpact_linux_test(name = name, testbench_binary = testbench_binary)
- if netstack:
- packetimpact_netstack_test(name = name, testbench_binary = testbench_binary)
+ packetimpact_linux_test(
+ name = name,
+ expect_failure = not linux,
+ testbench_binary = testbench_binary,
+ )
+ packetimpact_netstack_test(
+ name = name,
+ expect_failure = not netstack,
+ testbench_binary = testbench_binary,
+ )
diff --git a/test/packetimpact/tests/test_runner.sh b/test/packetimpact/tests/test_runner.sh
index e99fc7d09..e938de782 100755
--- a/test/packetimpact/tests/test_runner.sh
+++ b/test/packetimpact/tests/test_runner.sh
@@ -29,7 +29,7 @@ function failure() {
}
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
-declare -r LONGOPTS="dut_platform:,posix_server_binary:,testbench_binary:,runtime:,tshark,extra_test_arg:"
+declare -r LONGOPTS="dut_platform:,posix_server_binary:,testbench_binary:,runtime:,tshark,extra_test_arg:,expect_failure"
# Don't use declare below so that the error from getopt will end the script.
PARSED=$(getopt --options "" --longoptions=$LONGOPTS --name "$0" -- "$@")
@@ -68,6 +68,10 @@ while true; do
EXTRA_TEST_ARGS+="$2"
shift 2
;;
+ --expect_failure)
+ declare -r EXPECT_FAILURE="1"
+ shift 1
+ ;;
--)
shift
break
@@ -103,21 +107,24 @@ if [[ ! -f "${TESTBENCH_BINARY-}" ]]; then
exit 2
fi
+function new_net_prefix() {
+ # Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
+ echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
+}
+
# Variables specific to the control network and interface start with CTRL_.
# Variables specific to the test network and interface start with TEST_.
# Variables specific to the DUT start with DUT_.
# Variables specific to the test bench start with TESTBENCH_.
# Use random numbers so that test networks don't collide.
-declare -r CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
-declare -r TEST_NET="test_net-${RANDOM}${RANDOM}"
+declare CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
+declare CTRL_NET_PREFIX=$(new_net_prefix)
+declare TEST_NET="test_net-${RANDOM}${RANDOM}"
+declare TEST_NET_PREFIX=$(new_net_prefix)
# On both DUT and test bench, testing packets are on the eth2 interface.
declare -r TEST_DEVICE="eth2"
# Number of bits in the *_NET_PREFIX variables.
declare -r NET_MASK="24"
-function new_net_prefix() {
- # Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
- echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
-}
# Last bits of the DUT's IP address.
declare -r DUT_NET_SUFFIX=".10"
# Control port.
@@ -126,6 +133,7 @@ declare -r CTRL_PORT="40000"
declare -r TESTBENCH_NET_SUFFIX=".20"
declare -r TIMEOUT="60"
declare -r IMAGE_TAG="gcr.io/gvisor-presubmit/packetimpact"
+
# Make sure that docker is installed.
docker --version
@@ -165,19 +173,19 @@ function finish {
trap finish EXIT
# Subnet for control packets between test bench and DUT.
-declare CTRL_NET_PREFIX=$(new_net_prefix)
while ! docker network create \
"--subnet=${CTRL_NET_PREFIX}.0/${NET_MASK}" "${CTRL_NET}"; do
sleep 0.1
- declare CTRL_NET_PREFIX=$(new_net_prefix)
+ CTRL_NET_PREFIX=$(new_net_prefix)
+ CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
done
# Subnet for the packets that are part of the test.
-declare TEST_NET_PREFIX=$(new_net_prefix)
while ! docker network create \
"--subnet=${TEST_NET_PREFIX}.0/${NET_MASK}" "${TEST_NET}"; do
sleep 0.1
- declare TEST_NET_PREFIX=$(new_net_prefix)
+ TEST_NET_PREFIX=$(new_net_prefix)
+ TEST_NET="test_net-${RANDOM}${RANDOM}"
done
docker pull "${IMAGE_TAG}"
@@ -254,7 +262,10 @@ sleep 3
# Start a packetimpact test on the test bench. The packetimpact test sends and
# receives packets and also sends POSIX socket commands to the posix_server to
# be executed on the DUT.
-docker exec -t "${TESTBENCH}" \
+docker exec \
+ -e XML_OUTPUT_FILE="/test.xml" \
+ -e TEST_TARGET \
+ -t "${TESTBENCH}" \
/bin/bash -c "${DOCKER_TESTBENCH_BINARY} \
${EXTRA_TEST_ARGS[@]-} \
--posix_server_ip=${CTRL_NET_PREFIX}${DUT_NET_SUFFIX} \
@@ -263,6 +274,15 @@ docker exec -t "${TESTBENCH}" \
--local_ipv4=${TEST_NET_PREFIX}${TESTBENCH_NET_SUFFIX} \
--remote_mac=${REMOTE_MAC} \
--local_mac=${LOCAL_MAC} \
- --device=${TEST_DEVICE}"
-
+ --device=${TEST_DEVICE}" && true
+declare -r TEST_RESULT="${?}"
+if [[ -z "${EXPECT_FAILURE-}" && "${TEST_RESULT}" != 0 ]]; then
+ echo 'FAIL: This test was expected to pass.'
+ exit ${TEST_RESULT}
+fi
+if [[ ! -z "${EXPECT_FAILURE-}" && "${TEST_RESULT}" == 0 ]]; then
+ echo 'FAIL: This test was expected to fail but passed. Enable the test and' \
+ 'mark the corresponding bug as fixed.'
+ exit 1
+fi
echo PASS: No errors.
diff --git a/test/root/BUILD b/test/root/BUILD
index 05166673a..17e51e66e 100644
--- a/test/root/BUILD
+++ b/test/root/BUILD
@@ -33,14 +33,12 @@ go_test(
],
visibility = ["//:sandbox"],
deps = [
- "//runsc/boot",
+ "//pkg/test/criutil",
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
"//runsc/cgroup",
"//runsc/container",
- "//runsc/criutil",
- "//runsc/dockerutil",
"//runsc/specutils",
- "//runsc/testutil",
- "//test/root/testdata",
"@com_github_cenkalti_backoff//:go_default_library",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
"@com_github_syndtr_gocapability//capability:go_default_library",
diff --git a/test/root/cgroup_test.go b/test/root/cgroup_test.go
index 679342def..8876d0d61 100644
--- a/test/root/cgroup_test.go
+++ b/test/root/cgroup_test.go
@@ -26,9 +26,9 @@ import (
"testing"
"time"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/runsc/cgroup"
- "gvisor.dev/gvisor/runsc/dockerutil"
- "gvisor.dev/gvisor/runsc/testutil"
)
func verifyPid(pid int, path string) error {
@@ -56,54 +56,70 @@ func verifyPid(pid int, path string) error {
return fmt.Errorf("got: %v, want: %d", gots, pid)
}
-// TestCgroup sets cgroup options and checks that cgroup was properly configured.
func TestMemCGroup(t *testing.T) {
- allocMemSize := 128 << 20
- if err := dockerutil.Pull("python"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("memusage-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// Start a new container and allocate the specified about of memory.
- args := []string{
- "--memory=256MB",
- "python",
- "python",
- "-c",
- fmt.Sprintf("import time; s = 'a' * %d; time.sleep(100)", allocMemSize),
- }
- if err := d.Run(args...); err != nil {
- t.Fatal("docker create failed:", err)
+ allocMemSize := 128 << 20
+ allocMemLimit := 2 * allocMemSize
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/python",
+ Memory: allocMemLimit / 1024, // Must be in Kb.
+ }, "python", "-c", fmt.Sprintf("import time; s = 'a' * %d; time.sleep(100)", allocMemSize)); err != nil {
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+ // Extract the ID to lookup the cgroup.
gid, err := d.ID()
if err != nil {
t.Fatalf("Docker.ID() failed: %v", err)
}
t.Logf("cgroup ID: %s", gid)
- path := filepath.Join("/sys/fs/cgroup/memory/docker", gid, "memory.usage_in_bytes")
- memUsage := 0
-
// Wait when the container will allocate memory.
+ memUsage := 0
start := time.Now()
- for time.Now().Sub(start) < 30*time.Second {
+ for time.Since(start) < 30*time.Second {
+ // Sleep for a brief period of time after spawning the
+ // container (so that Docker can create the cgroup etc.
+ // or after looping below (so the application can start).
+ time.Sleep(100 * time.Millisecond)
+
+ // Read the cgroup memory limit.
+ path := filepath.Join("/sys/fs/cgroup/memory/docker", gid, "memory.limit_in_bytes")
outRaw, err := ioutil.ReadFile(path)
if err != nil {
- t.Fatalf("failed to read %q: %v", path, err)
+ // It's possible that the container does not exist yet.
+ continue
}
out := strings.TrimSpace(string(outRaw))
+ memLimit, err := strconv.Atoi(out)
+ if err != nil {
+ t.Fatalf("Atoi(%v): %v", out, err)
+ }
+ if memLimit != allocMemLimit {
+ // The group may not have had the correct limit set yet.
+ continue
+ }
+
+ // Read the cgroup memory usage.
+ path = filepath.Join("/sys/fs/cgroup/memory/docker", gid, "memory.max_usage_in_bytes")
+ outRaw, err = ioutil.ReadFile(path)
+ if err != nil {
+ t.Fatalf("error reading usage: %v", err)
+ }
+ out = strings.TrimSpace(string(outRaw))
memUsage, err = strconv.Atoi(out)
if err != nil {
t.Fatalf("Atoi(%v): %v", out, err)
}
+ t.Logf("read usage: %v, wanted: %v", memUsage, allocMemSize)
- if memUsage > allocMemSize {
+ // Are we done?
+ if memUsage >= allocMemSize {
return
}
-
- time.Sleep(100 * time.Millisecond)
}
t.Fatalf("%vMB is less than %vMB", memUsage>>20, allocMemSize>>20)
@@ -111,10 +127,8 @@ func TestMemCGroup(t *testing.T) {
// TestCgroup sets cgroup options and checks that cgroup was properly configured.
func TestCgroup(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("cgroup-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
// This is not a comprehensive list of attributes.
//
@@ -179,10 +193,11 @@ func TestCgroup(t *testing.T) {
want: "5",
},
{
- arg: "--blkio-weight=750",
- ctrl: "blkio",
- file: "blkio.weight",
- want: "750",
+ arg: "--blkio-weight=750",
+ ctrl: "blkio",
+ file: "blkio.weight",
+ want: "750",
+ skipIfNotFound: true, // blkio groups may not be available.
},
}
@@ -191,12 +206,15 @@ func TestCgroup(t *testing.T) {
args = append(args, attr.arg)
}
- args = append(args, "alpine", "sleep", "10000")
- if err := d.Run(args...); err != nil {
- t.Fatal("docker create failed:", err)
+ // Start the container.
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Extra: args, // Cgroup arguments.
+ }, "sleep", "10000"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+ // Lookup the relevant cgroup ID.
gid, err := d.ID()
if err != nil {
t.Fatalf("Docker.ID() failed: %v", err)
@@ -245,17 +263,21 @@ func TestCgroup(t *testing.T) {
}
}
+// TestCgroup sets cgroup options and checks that cgroup was properly configured.
func TestCgroupParent(t *testing.T) {
- if err := dockerutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := dockerutil.MakeDocker("cgroup-test")
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
- parent := testutil.RandomName("runsc")
- if err := d.Run("--cgroup-parent", parent, "alpine", "sleep", "10000"); err != nil {
- t.Fatal("docker create failed:", err)
+ // Construct a known cgroup name.
+ parent := testutil.RandomID("runsc-")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Extra: []string{fmt.Sprintf("--cgroup-parent=%s", parent)},
+ }, "sleep", "10000"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
+
+ // Extract the ID to look up the cgroup.
gid, err := d.ID()
if err != nil {
t.Fatalf("Docker.ID() failed: %v", err)
diff --git a/test/root/chroot_test.go b/test/root/chroot_test.go
index be0f63d18..a306132a4 100644
--- a/test/root/chroot_test.go
+++ b/test/root/chroot_test.go
@@ -24,17 +24,20 @@ import (
"strings"
"testing"
- "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
)
// TestChroot verifies that the sandbox is chroot'd and that mounts are cleaned
// up after the sandbox is destroyed.
func TestChroot(t *testing.T) {
- d := dockerutil.MakeDocker("chroot-test")
- if err := d.Run("alpine", "sleep", "10000"); err != nil {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sleep", "10000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
pid, err := d.SandboxPid()
if err != nil {
@@ -76,11 +79,14 @@ func TestChroot(t *testing.T) {
}
func TestChrootGofer(t *testing.T) {
- d := dockerutil.MakeDocker("chroot-test")
- if err := d.Run("alpine", "sleep", "10000"); err != nil {
+ d := dockerutil.MakeDocker(t)
+ defer d.CleanUp()
+
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: "basic/alpine",
+ }, "sleep", "10000"); err != nil {
t.Fatalf("docker run failed: %v", err)
}
- defer d.CleanUp()
// It's tricky to find gofers. Get sandbox PID first, then find parent. From
// parent get all immediate children, remove the sandbox, and everything else
diff --git a/test/root/crictl_test.go b/test/root/crictl_test.go
index 3f90c4c6a..85007dcce 100644
--- a/test/root/crictl_test.go
+++ b/test/root/crictl_test.go
@@ -16,6 +16,7 @@ package root
import (
"bytes"
+ "encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -29,16 +30,58 @@ import (
"testing"
"time"
- "gvisor.dev/gvisor/runsc/criutil"
- "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/criutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/testutil"
- "gvisor.dev/gvisor/test/root/testdata"
)
// Tests for crictl have to be run as root (rather than in a user namespace)
// because crictl creates named network namespaces in /var/run/netns/.
+// SimpleSpec returns a JSON config for a simple container that runs the
+// specified command in the specified image.
+func SimpleSpec(name, image string, cmd []string, extra map[string]interface{}) string {
+ s := map[string]interface{}{
+ "metadata": map[string]string{
+ "name": name,
+ },
+ "image": map[string]string{
+ "image": testutil.ImageByName(image),
+ },
+ "log_path": fmt.Sprintf("%s.log", name),
+ }
+ if len(cmd) > 0 { // Omit if empty.
+ s["command"] = cmd
+ }
+ for k, v := range extra {
+ s[k] = v // Extra settings.
+ }
+ v, err := json.Marshal(s)
+ if err != nil {
+ // This shouldn't happen.
+ panic(err)
+ }
+ return string(v)
+}
+
+// Sandbox is a default JSON config for a sandbox.
+var Sandbox = `{
+ "metadata": {
+ "name": "default-sandbox",
+ "namespace": "default",
+ "attempt": 1,
+ "uid": "hdishd83djaidwnduwk28bcsb"
+ },
+ "linux": {
+ },
+ "log_directory": "/tmp"
+}
+`
+
+// Httpd is a JSON config for an httpd container.
+var Httpd = SimpleSpec("httpd", "basic/httpd", nil, nil)
+
// TestCrictlSanity refers to b/112433158.
func TestCrictlSanity(t *testing.T) {
// Setup containerd and crictl.
@@ -47,9 +90,9 @@ func TestCrictlSanity(t *testing.T) {
t.Fatalf("failed to setup crictl: %v", err)
}
defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("httpd", testdata.Sandbox, testdata.Httpd)
+ podID, contID, err := crictl.StartPodAndContainer("basic/httpd", Sandbox, Httpd)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("start failed: %v", err)
}
// Look for the httpd page.
@@ -59,10 +102,38 @@ func TestCrictlSanity(t *testing.T) {
// Stop everything.
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
+ t.Fatalf("stop failed: %v", err)
}
}
+// HttpdMountPaths is a JSON config for an httpd container with additional
+// mounts.
+var HttpdMountPaths = SimpleSpec("httpd", "basic/httpd", nil, map[string]interface{}{
+ "mounts": []map[string]interface{}{
+ map[string]interface{}{
+ "container_path": "/var/run/secrets/kubernetes.io/serviceaccount",
+ "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/volumes/kubernetes.io~secret/default-token-2rpfx",
+ "readonly": true,
+ },
+ map[string]interface{}{
+ "container_path": "/etc/hosts",
+ "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/etc-hosts",
+ "readonly": false,
+ },
+ map[string]interface{}{
+ "container_path": "/dev/termination-log",
+ "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/containers/httpd/d1709580",
+ "readonly": false,
+ },
+ map[string]interface{}{
+ "container_path": "/usr/local/apache2/htdocs/test",
+ "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064",
+ "readonly": true,
+ },
+ },
+ "linux": map[string]interface{}{},
+})
+
// TestMountPaths refers to b/117635704.
func TestMountPaths(t *testing.T) {
// Setup containerd and crictl.
@@ -71,9 +142,9 @@ func TestMountPaths(t *testing.T) {
t.Fatalf("failed to setup crictl: %v", err)
}
defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("httpd", testdata.Sandbox, testdata.HttpdMountPaths)
+ podID, contID, err := crictl.StartPodAndContainer("basic/httpd", Sandbox, HttpdMountPaths)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("start failed: %v", err)
}
// Look for the directory available at /test.
@@ -83,7 +154,7 @@ func TestMountPaths(t *testing.T) {
// Stop everything.
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
+ t.Fatalf("stop failed: %v", err)
}
}
@@ -95,14 +166,16 @@ func TestMountOverSymlinks(t *testing.T) {
t.Fatalf("failed to setup crictl: %v", err)
}
defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("k8s.gcr.io/busybox", testdata.Sandbox, testdata.MountOverSymlink)
+
+ spec := SimpleSpec("busybox", "basic/resolv", []string{"sleep", "1000"}, nil)
+ podID, contID, err := crictl.StartPodAndContainer("basic/resolv", Sandbox, spec)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("start failed: %v", err)
}
out, err := crictl.Exec(contID, "readlink", "/etc/resolv.conf")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("readlink failed: %v, out: %s", err, out)
}
if want := "/tmp/resolv.conf"; !strings.Contains(string(out), want) {
t.Fatalf("/etc/resolv.conf is not pointing to %q: %q", want, string(out))
@@ -110,11 +183,11 @@ func TestMountOverSymlinks(t *testing.T) {
etc, err := crictl.Exec(contID, "cat", "/etc/resolv.conf")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("cat failed: %v, out: %s", err, etc)
}
tmp, err := crictl.Exec(contID, "cat", "/tmp/resolv.conf")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("cat failed: %v, out: %s", err, out)
}
if tmp != etc {
t.Fatalf("file content doesn't match:\n\t/etc/resolv.conf: %s\n\t/tmp/resolv.conf: %s", string(etc), string(tmp))
@@ -122,7 +195,7 @@ func TestMountOverSymlinks(t *testing.T) {
// Stop everything.
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
+ t.Fatalf("stop failed: %v", err)
}
}
@@ -135,16 +208,16 @@ func TestHomeDir(t *testing.T) {
t.Fatalf("failed to setup crictl: %v", err)
}
defer cleanup()
- contSpec := testdata.SimpleSpec("root", "k8s.gcr.io/busybox", []string{"sleep", "1000"})
- podID, contID, err := crictl.StartPodAndContainer("k8s.gcr.io/busybox", testdata.Sandbox, contSpec)
+ contSpec := SimpleSpec("root", "basic/busybox", []string{"sleep", "1000"}, nil)
+ podID, contID, err := crictl.StartPodAndContainer("basic/busybox", Sandbox, contSpec)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("start failed: %v", err)
}
t.Run("root container", func(t *testing.T) {
out, err := crictl.Exec(contID, "sh", "-c", "echo $HOME")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("exec failed: %v, out: %s", err, out)
}
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
@@ -153,32 +226,47 @@ func TestHomeDir(t *testing.T) {
t.Run("sub-container", func(t *testing.T) {
// Create a sub container in the same pod.
- subContSpec := testdata.SimpleSpec("subcontainer", "k8s.gcr.io/busybox", []string{"sleep", "1000"})
- subContID, err := crictl.StartContainer(podID, "k8s.gcr.io/busybox", testdata.Sandbox, subContSpec)
+ subContSpec := SimpleSpec("subcontainer", "basic/busybox", []string{"sleep", "1000"}, nil)
+ subContID, err := crictl.StartContainer(podID, "basic/busybox", Sandbox, subContSpec)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("start failed: %v", err)
}
out, err := crictl.Exec(subContID, "sh", "-c", "echo $HOME")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("exec failed: %v, out: %s", err, out)
}
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
t.Fatalf("Home directory invalid. Got %q, Want: %q", got, want)
}
if err := crictl.StopContainer(subContID); err != nil {
- t.Fatal(err)
+ t.Fatalf("stop failed: %v", err)
}
})
// Stop everything.
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
+ t.Fatalf("stop failed: %v", err)
}
}
+// containerdConfigTemplate is a .toml config for containerd. It contains a
+// formatting verb so the runtime field can be set via fmt.Sprintf.
+const containerdConfigTemplate = `
+disabled_plugins = ["restart"]
+[plugins.linux]
+ runtime = "%s"
+ runtime_root = "/tmp/test-containerd/runsc"
+ shim = "/usr/local/bin/gvisor-containerd-shim"
+ shim_debug = true
+
+[plugins.cri.containerd.runtimes.runsc]
+ runtime_type = "io.containerd.runtime.v1.linux"
+ runtime_engine = "%s"
+`
+
// setup sets up before a test. Specifically it:
// * Creates directories and a socket for containerd to utilize.
// * Runs containerd and waits for it to reach a "ready" state for testing.
@@ -213,50 +301,52 @@ func setup(t *testing.T) (*criutil.Crictl, func(), error) {
if err != nil {
t.Fatalf("error discovering runtime path: %v", err)
}
- config, err := testutil.WriteTmpFile("containerd-config", testdata.ContainerdConfig(runtime))
+ config, configCleanup, err := testutil.WriteTmpFile("containerd-config", fmt.Sprintf(containerdConfigTemplate, runtime, runtime))
if err != nil {
t.Fatalf("failed to write containerd config")
}
- cleanups = append(cleanups, func() { os.RemoveAll(config) })
+ cleanups = append(cleanups, configCleanup)
// Start containerd.
- containerd := exec.Command(getContainerd(),
+ cmd := exec.Command(getContainerd(),
"--config", config,
"--log-level", "debug",
"--root", containerdRoot,
"--state", containerdState,
"--address", sockAddr)
+ startupR, startupW := io.Pipe()
+ defer startupR.Close()
+ defer startupW.Close()
+ stderr := &bytes.Buffer{}
+ stdout := &bytes.Buffer{}
+ cmd.Stderr = io.MultiWriter(startupW, stderr)
+ cmd.Stdout = io.MultiWriter(startupW, stdout)
cleanups = append(cleanups, func() {
- if err := testutil.KillCommand(containerd); err != nil {
- log.Printf("error killing containerd: %v", err)
- }
+ t.Logf("containerd stdout: %s", stdout.String())
+ t.Logf("containerd stderr: %s", stderr.String())
})
- containerdStderr, err := containerd.StderrPipe()
- if err != nil {
- t.Fatalf("failed to get containerd stderr: %v", err)
- }
- containerdStdout, err := containerd.StdoutPipe()
- if err != nil {
- t.Fatalf("failed to get containerd stdout: %v", err)
- }
- if err := containerd.Start(); err != nil {
+
+ // Start the process.
+ if err := cmd.Start(); err != nil {
t.Fatalf("failed running containerd: %v", err)
}
- // Wait for containerd to boot. Then put all containerd output into a
- // buffer to be logged at the end of the test.
- testutil.WaitUntilRead(containerdStderr, "Start streaming server", nil, 10*time.Second)
- stdoutBuf := &bytes.Buffer{}
- stderrBuf := &bytes.Buffer{}
- go func() { io.Copy(stdoutBuf, containerdStdout) }()
- go func() { io.Copy(stderrBuf, containerdStderr) }()
+ // Wait for containerd to boot.
+ if err := testutil.WaitUntilRead(startupR, "Start streaming server", nil, 10*time.Second); err != nil {
+ t.Fatalf("failed to start containerd: %v", err)
+ }
+
+ // Kill must be the last cleanup (as it will be executed first).
+ cc := criutil.NewCrictl(t, sockAddr)
cleanups = append(cleanups, func() {
- t.Logf("containerd stdout: %s", string(stdoutBuf.Bytes()))
- t.Logf("containerd stderr: %s", string(stderrBuf.Bytes()))
+ cc.CleanUp() // Remove tmp files, etc.
+ if err := testutil.KillCommand(cmd); err != nil {
+ log.Printf("error killing containerd: %v", err)
+ }
})
cleanup.Release()
- return criutil.NewCrictl(20*time.Second, sockAddr), cleanupFunc, nil
+ return cc, cleanupFunc, nil
}
// httpGet GETs the contents of a file served from a pod on port 80.
diff --git a/test/root/main_test.go b/test/root/main_test.go
index d74dec85f..9fb17e0dd 100644
--- a/test/root/main_test.go
+++ b/test/root/main_test.go
@@ -21,7 +21,7 @@ import (
"testing"
"github.com/syndtr/gocapability/capability"
- "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
"gvisor.dev/gvisor/runsc/specutils"
)
diff --git a/test/root/oom_score_adj_test.go b/test/root/oom_score_adj_test.go
index 22488b05d..9a3cecd97 100644
--- a/test/root/oom_score_adj_test.go
+++ b/test/root/oom_score_adj_test.go
@@ -20,10 +20,9 @@ import (
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/runsc/boot"
+ "gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/runsc/container"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/testutil"
)
var (
@@ -40,15 +39,6 @@ var (
// TestOOMScoreAdjSingle tests that oom_score_adj is set properly in a
// single container sandbox.
func TestOOMScoreAdjSingle(t *testing.T) {
- rootDir, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer os.RemoveAll(rootDir)
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
ppid, err := specutils.GetParentPid(os.Getpid())
if err != nil {
t.Fatalf("getting parent pid: %v", err)
@@ -89,11 +79,11 @@ func TestOOMScoreAdjSingle(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
- id := testutil.UniqueContainerID()
+ id := testutil.RandomContainerID()
s := testutil.NewSpecWithArgs("sleep", "1000")
s.Process.OOMScoreAdj = testCase.OOMScoreAdj
- containers, cleanup, err := startContainers(conf, []*specs.Spec{s}, []string{id})
+ containers, cleanup, err := startContainers(t, []*specs.Spec{s}, []string{id})
if err != nil {
t.Fatalf("error starting containers: %v", err)
}
@@ -131,15 +121,6 @@ func TestOOMScoreAdjSingle(t *testing.T) {
// TestOOMScoreAdjMulti tests that oom_score_adj is set properly in a
// multi-container sandbox.
func TestOOMScoreAdjMulti(t *testing.T) {
- rootDir, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer os.RemoveAll(rootDir)
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
ppid, err := specutils.GetParentPid(os.Getpid())
if err != nil {
t.Fatalf("getting parent pid: %v", err)
@@ -257,7 +238,7 @@ func TestOOMScoreAdjMulti(t *testing.T) {
}
}
- containers, cleanup, err := startContainers(conf, specs, ids)
+ containers, cleanup, err := startContainers(t, specs, ids)
if err != nil {
t.Fatalf("error starting containers: %v", err)
}
@@ -321,7 +302,7 @@ func TestOOMScoreAdjMulti(t *testing.T) {
func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) {
var specs []*specs.Spec
var ids []string
- rootID := testutil.UniqueContainerID()
+ rootID := testutil.RandomContainerID()
for i, cmd := range cmds {
spec := testutil.NewSpecWithArgs(cmd...)
@@ -335,35 +316,48 @@ func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) {
specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeContainer,
specutils.ContainerdSandboxIDAnnotation: rootID,
}
- ids = append(ids, testutil.UniqueContainerID())
+ ids = append(ids, testutil.RandomContainerID())
}
specs = append(specs, spec)
}
return specs, ids
}
-func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*container.Container, func(), error) {
- if len(conf.RootDir) == 0 {
- panic("conf.RootDir not set. Call testutil.SetupRootDir() to set.")
- }
-
- var containers []*container.Container
- var bundles []string
- cleanup := func() {
+func startContainers(t *testing.T, specs []*specs.Spec, ids []string) ([]*container.Container, func(), error) {
+ var (
+ containers []*container.Container
+ cleanups []func()
+ )
+ cleanups = append(cleanups, func() {
for _, c := range containers {
c.Destroy()
}
- for _, b := range bundles {
- os.RemoveAll(b)
+ })
+ cleanupAll := func() {
+ for _, c := range cleanups {
+ c()
}
}
+ localClean := specutils.MakeCleanup(cleanupAll)
+ defer localClean.Clean()
+
+ // All containers must share the same root.
+ rootDir, cleanup, err := testutil.SetupRootDir()
+ if err != nil {
+ t.Fatalf("error creating root dir: %v", err)
+ }
+ cleanups = append(cleanups, cleanup)
+
+ // Point this to from the configuration.
+ conf := testutil.TestConfig(t)
+ conf.RootDir = rootDir
+
for i, spec := range specs {
- bundleDir, err := testutil.SetupBundleDir(spec)
+ bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
if err != nil {
- cleanup()
- return nil, nil, fmt.Errorf("error setting up container: %v", err)
+ return nil, nil, fmt.Errorf("error setting up bundle: %v", err)
}
- bundles = append(bundles, bundleDir)
+ cleanups = append(cleanups, cleanup)
args := container.Args{
ID: ids[i],
@@ -372,15 +366,15 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*c
}
cont, err := container.New(conf, args)
if err != nil {
- cleanup()
return nil, nil, fmt.Errorf("error creating container: %v", err)
}
containers = append(containers, cont)
if err := cont.Start(conf); err != nil {
- cleanup()
return nil, nil, fmt.Errorf("error starting container: %v", err)
}
}
- return containers, cleanup, nil
+
+ localClean.Release()
+ return containers, cleanupAll, nil
}
diff --git a/test/root/runsc_test.go b/test/root/runsc_test.go
index 90373e2db..25204bebb 100644
--- a/test/root/runsc_test.go
+++ b/test/root/runsc_test.go
@@ -28,8 +28,8 @@ import (
"github.com/cenkalti/backoff"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/testutil"
)
// TestDoKill checks that when "runsc do..." is killed, the sandbox process is
diff --git a/test/root/testdata/BUILD b/test/root/testdata/BUILD
deleted file mode 100644
index 6859541ad..000000000
--- a/test/root/testdata/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "testdata",
- srcs = [
- "busybox.go",
- "containerd_config.go",
- "httpd.go",
- "httpd_mount_paths.go",
- "sandbox.go",
- "simple.go",
- ],
- visibility = [
- "//:sandbox",
- ],
-)
diff --git a/test/root/testdata/busybox.go b/test/root/testdata/busybox.go
deleted file mode 100644
index e4dbd2843..000000000
--- a/test/root/testdata/busybox.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testdata
-
-// MountOverSymlink is a JSON config for a container that /etc/resolv.conf is a
-// symlink to /tmp/resolv.conf.
-var MountOverSymlink = `
-{
- "metadata": {
- "name": "busybox"
- },
- "image": {
- "image": "k8s.gcr.io/busybox"
- },
- "command": [
- "sleep",
- "1000"
- ]
-}
-`
diff --git a/test/root/testdata/containerd_config.go b/test/root/testdata/containerd_config.go
deleted file mode 100644
index e12f1ec88..000000000
--- a/test/root/testdata/containerd_config.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package testdata contains data required for root tests.
-package testdata
-
-import "fmt"
-
-// containerdConfigTemplate is a .toml config for containerd. It contains a
-// formatting verb so the runtime field can be set via fmt.Sprintf.
-const containerdConfigTemplate = `
-disabled_plugins = ["restart"]
-[plugins.linux]
- runtime = "%s"
- runtime_root = "/tmp/test-containerd/runsc"
- shim = "/usr/local/bin/gvisor-containerd-shim"
- shim_debug = true
-
-[plugins.cri.containerd.runtimes.runsc]
- runtime_type = "io.containerd.runtime.v1.linux"
- runtime_engine = "%s"
-`
-
-// ContainerdConfig returns a containerd config file with the specified
-// runtime.
-func ContainerdConfig(runtime string) string {
- return fmt.Sprintf(containerdConfigTemplate, runtime, runtime)
-}
diff --git a/test/root/testdata/httpd.go b/test/root/testdata/httpd.go
deleted file mode 100644
index 45d5e33d4..000000000
--- a/test/root/testdata/httpd.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testdata
-
-// Httpd is a JSON config for an httpd container.
-const Httpd = `
-{
- "metadata": {
- "name": "httpd"
- },
- "image":{
- "image": "httpd"
- },
- "mounts": [
- ],
- "linux": {
- },
- "log_path": "httpd.log"
-}
-`
diff --git a/test/root/testdata/httpd_mount_paths.go b/test/root/testdata/httpd_mount_paths.go
deleted file mode 100644
index ac3f4446a..000000000
--- a/test/root/testdata/httpd_mount_paths.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testdata
-
-// HttpdMountPaths is a JSON config for an httpd container with additional
-// mounts.
-const HttpdMountPaths = `
-{
- "metadata": {
- "name": "httpd"
- },
- "image":{
- "image": "httpd"
- },
- "mounts": [
- {
- "container_path": "/var/run/secrets/kubernetes.io/serviceaccount",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/volumes/kubernetes.io~secret/default-token-2rpfx",
- "readonly": true
- },
- {
- "container_path": "/etc/hosts",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/etc-hosts",
- "readonly": false
- },
- {
- "container_path": "/dev/termination-log",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/containers/httpd/d1709580",
- "readonly": false
- },
- {
- "container_path": "/usr/local/apache2/htdocs/test",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064",
- "readonly": true
- }
- ],
- "linux": {
- },
- "log_path": "httpd.log"
-}
-`
diff --git a/test/root/testdata/sandbox.go b/test/root/testdata/sandbox.go
deleted file mode 100644
index 0db210370..000000000
--- a/test/root/testdata/sandbox.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testdata
-
-// Sandbox is a default JSON config for a sandbox.
-const Sandbox = `
-{
- "metadata": {
- "name": "default-sandbox",
- "namespace": "default",
- "attempt": 1,
- "uid": "hdishd83djaidwnduwk28bcsb"
- },
- "linux": {
- },
- "log_directory": "/tmp"
-}
-`
diff --git a/test/root/testdata/simple.go b/test/root/testdata/simple.go
deleted file mode 100644
index 1cca53f0c..000000000
--- a/test/root/testdata/simple.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package testdata
-
-import (
- "encoding/json"
- "fmt"
-)
-
-// SimpleSpec returns a JSON config for a simple container that runs the
-// specified command in the specified image.
-func SimpleSpec(name, image string, cmd []string) string {
- cmds, err := json.Marshal(cmd)
- if err != nil {
- // This shouldn't happen.
- panic(err)
- }
- return fmt.Sprintf(`
-{
- "metadata": {
- "name": %q
- },
- "image": {
- "image": %q
- },
- "command": %s
- }
-`, name, image, cmds)
-}
diff --git a/test/runner/BUILD b/test/runner/BUILD
index 9959ef9b0..6833c9986 100644
--- a/test/runner/BUILD
+++ b/test/runner/BUILD
@@ -12,8 +12,8 @@ go_binary(
visibility = ["//:sandbox"],
deps = [
"//pkg/log",
+ "//pkg/test/testutil",
"//runsc/specutils",
- "//runsc/testutil",
"//test/runner/gtest",
"//test/uds",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
diff --git a/test/runner/runner.go b/test/runner/runner.go
index 0d3742f71..14c9cbc47 100644
--- a/test/runner/runner.go
+++ b/test/runner/runner.go
@@ -32,8 +32,8 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/log"
+ "gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/testutil"
"gvisor.dev/gvisor/test/runner/gtest"
"gvisor.dev/gvisor/test/uds"
)
@@ -115,20 +115,20 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
//
// Returns an error if the sandboxed application exits non-zero.
func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
- bundleDir, err := testutil.SetupBundleDir(spec)
+ bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
if err != nil {
return fmt.Errorf("SetupBundleDir failed: %v", err)
}
- defer os.RemoveAll(bundleDir)
+ defer cleanup()
- rootDir, err := testutil.SetupRootDir()
+ rootDir, cleanup, err := testutil.SetupRootDir()
if err != nil {
return fmt.Errorf("SetupRootDir failed: %v", err)
}
- defer os.RemoveAll(rootDir)
+ defer cleanup()
name := tc.FullName()
- id := testutil.UniqueContainerID()
+ id := testutil.RandomContainerID()
log.Infof("Running test %q in container %q", name, id)
specutils.LogSpec(spec)
diff --git a/test/runtimes/BUILD b/test/runtimes/BUILD
index 2c472bf8d..4cd627222 100644
--- a/test/runtimes/BUILD
+++ b/test/runtimes/BUILD
@@ -1,20 +1,7 @@
-# These packages are used to run language runtime tests inside gVisor sandboxes.
-
-load("//tools:defs.bzl", "go_binary", "go_test")
-load("//test/runtimes:build_defs.bzl", "runtime_test")
+load("//test/runtimes:defs.bzl", "runtime_test")
package(licenses = ["notice"])
-go_binary(
- name = "runner",
- testonly = 1,
- srcs = ["runner.go"],
- deps = [
- "//runsc/dockerutil",
- "//runsc/testutil",
- ],
-)
-
runtime_test(
name = "go1.12",
blacklist_file = "blacklist_go1.12.csv",
@@ -44,10 +31,3 @@ runtime_test(
blacklist_file = "blacklist_python3.7.3.csv",
lang = "python",
)
-
-go_test(
- name = "blacklist_test",
- size = "small",
- srcs = ["blacklist_test.go"],
- library = ":runner",
-)
diff --git a/test/runtimes/README.md b/test/runtimes/README.md
deleted file mode 100644
index 42d722553..000000000
--- a/test/runtimes/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Runtimes Tests Dockerfiles
-
-The Dockerfiles defined under this path are configured to host the execution of
-the runtimes language tests. Each Dockerfile can support the language indicated
-by its directory.
-
-The following runtimes are currently supported:
-
-- Go 1.12
-- Java 11
-- Node.js 12
-- PHP 7.3
-- Python 3.7
-
-### Building and pushing the images:
-
-The canonical source of images is the
-[gvisor-presubmit container registry](https://gcr.io/gvisor-presubmit/). You can
-build new images with the following command:
-
-```bash
-$ cd images
-$ docker build -f Dockerfile_$LANG [-t $NAME] .
-```
-
-To push them to our container registry, set the tag in the command above to
-`gcr.io/gvisor-presubmit/$LANG`, then push them. (Note that you will need
-appropriate permissions to the `gvisor-presubmit` GCP project.)
-
-```bash
-gcloud docker -- push gcr.io/gvisor-presubmit/$LANG
-```
-
-#### Running in Docker locally:
-
-1) [Install and configure Docker](https://docs.docker.com/install/)
-
-2) Pull the image you want to run:
-
-```bash
-$ docker pull gcr.io/gvisor-presubmit/$LANG
-```
-
-3) Run docker with the image.
-
-```bash
-$ docker run [--runtime=runsc] --rm -it $NAME [FLAG]
-```
-
-Running the command with no flags will cause all the available tests to execute.
-
-Flags can be added for additional functionality:
-
-- --list: Print a list of all available tests
-- --test &lt;name&gt;: Run a single test from the list of available tests
-- --v: Print the language version
diff --git a/test/runtimes/build_defs.bzl b/test/runtimes/build_defs.bzl
deleted file mode 100644
index 92e275a76..000000000
--- a/test/runtimes/build_defs.bzl
+++ /dev/null
@@ -1,75 +0,0 @@
-"""Defines a rule for runtime test targets."""
-
-load("//tools:defs.bzl", "go_test", "loopback")
-
-def runtime_test(
- name,
- lang,
- image_repo = "gcr.io/gvisor-presubmit",
- image_name = None,
- blacklist_file = None,
- shard_count = 50,
- size = "enormous"):
- """Generates sh_test and blacklist test targets for a given runtime.
-
- Args:
- name: The name of the runtime being tested. Typically, the lang + version.
- This is used in the names of the generated test targets.
- lang: The language being tested.
- image_repo: The docker repository containing the proctor image to run.
- i.e., the prefix to the fully qualified docker image id.
- image_name: The name of the image in the image_repo.
- Defaults to the test name.
- blacklist_file: A test blacklist to pass to the runtime test's runner.
- shard_count: See Bazel common test attributes.
- size: See Bazel common test attributes.
- """
- if image_name == None:
- image_name = name
- args = [
- "--lang",
- lang,
- "--image",
- "/".join([image_repo, image_name]),
- ]
- data = [
- ":runner",
- loopback,
- ]
- if blacklist_file:
- args += ["--blacklist_file", "test/runtimes/" + blacklist_file]
- data += [blacklist_file]
-
- # Add a test that the blacklist parses correctly.
- blacklist_test(name, blacklist_file)
-
- sh_test(
- name = name + "_test",
- srcs = ["runner.sh"],
- args = args,
- data = data,
- size = size,
- shard_count = shard_count,
- tags = [
- # Requires docker and runsc to be configured before the test runs.
- "local",
- # Don't include test target in wildcard target patterns.
- "manual",
- ],
- )
-
-def blacklist_test(name, blacklist_file):
- """Test that a blacklist parses correctly."""
- go_test(
- name = name + "_blacklist_test",
- library = ":runner",
- srcs = ["blacklist_test.go"],
- args = ["--blacklist_file", "test/runtimes/" + blacklist_file],
- data = [blacklist_file],
- )
-
-def sh_test(**kwargs):
- """Wraps the standard sh_test."""
- native.sh_test(
- **kwargs
- )
diff --git a/test/runtimes/defs.bzl b/test/runtimes/defs.bzl
new file mode 100644
index 000000000..f836dd952
--- /dev/null
+++ b/test/runtimes/defs.bzl
@@ -0,0 +1,79 @@
+"""Defines a rule for runtime test targets."""
+
+load("//tools:defs.bzl", "go_test")
+
+def _runtime_test_impl(ctx):
+ # Construct arguments.
+ args = [
+ "--lang",
+ ctx.attr.lang,
+ "--image",
+ ctx.attr.image,
+ ]
+ if ctx.attr.blacklist_file:
+ args += [
+ "--blacklist_file",
+ ctx.files.blacklist_file[0].short_path,
+ ]
+
+ # Build a runner.
+ runner = ctx.actions.declare_file("%s-executer" % ctx.label.name)
+ runner_content = "\n".join([
+ "#!/bin/bash",
+ "%s %s\n" % (ctx.files._runner[0].short_path, " ".join(args)),
+ ])
+ ctx.actions.write(runner, runner_content, is_executable = True)
+
+ # Return the runner.
+ return [DefaultInfo(
+ executable = runner,
+ runfiles = ctx.runfiles(
+ files = ctx.files._runner + ctx.files.blacklist_file + ctx.files._proctor,
+ collect_default = True,
+ collect_data = True,
+ ),
+ )]
+
+_runtime_test = rule(
+ implementation = _runtime_test_impl,
+ attrs = {
+ "image": attr.string(
+ mandatory = False,
+ ),
+ "lang": attr.string(
+ mandatory = True,
+ ),
+ "blacklist_file": attr.label(
+ mandatory = False,
+ allow_single_file = True,
+ ),
+ "_runner": attr.label(
+ default = "//test/runtimes/runner:runner",
+ ),
+ "_proctor": attr.label(
+ default = "//test/runtimes/proctor:proctor",
+ ),
+ },
+ test = True,
+)
+
+def runtime_test(name, **kwargs):
+ _runtime_test(
+ name = name,
+ image = name, # Resolved as images/runtimes/%s.
+ tags = [
+ "local",
+ "manual",
+ ],
+ **kwargs
+ )
+
+def blacklist_test(name, blacklist_file):
+ """Test that a blacklist parses correctly."""
+ go_test(
+ name = name + "_blacklist_test",
+ library = ":runner",
+ srcs = ["blacklist_test.go"],
+ args = ["--blacklist_file", "test/runtimes/" + blacklist_file],
+ data = [blacklist_file],
+ )
diff --git a/test/runtimes/images/proctor/BUILD b/test/runtimes/proctor/BUILD
index 85e004c45..50a26d182 100644
--- a/test/runtimes/images/proctor/BUILD
+++ b/test/runtimes/proctor/BUILD
@@ -12,7 +12,8 @@ go_binary(
"proctor.go",
"python.go",
],
- visibility = ["//test/runtimes/images:__subpackages__"],
+ pure = True,
+ visibility = ["//test/runtimes:__pkg__"],
)
go_test(
@@ -21,6 +22,6 @@ go_test(
srcs = ["proctor_test.go"],
library = ":proctor",
deps = [
- "//runsc/testutil",
+ "//pkg/test/testutil",
],
)
diff --git a/test/runtimes/images/proctor/go.go b/test/runtimes/proctor/go.go
index 3e2d5d8db..3e2d5d8db 100644
--- a/test/runtimes/images/proctor/go.go
+++ b/test/runtimes/proctor/go.go
diff --git a/test/runtimes/images/proctor/java.go b/test/runtimes/proctor/java.go
index 8b362029d..8b362029d 100644
--- a/test/runtimes/images/proctor/java.go
+++ b/test/runtimes/proctor/java.go
diff --git a/test/runtimes/images/proctor/nodejs.go b/test/runtimes/proctor/nodejs.go
index bd57db444..bd57db444 100644
--- a/test/runtimes/images/proctor/nodejs.go
+++ b/test/runtimes/proctor/nodejs.go
diff --git a/test/runtimes/images/proctor/php.go b/test/runtimes/proctor/php.go
index 9115040e1..9115040e1 100644
--- a/test/runtimes/images/proctor/php.go
+++ b/test/runtimes/proctor/php.go
diff --git a/test/runtimes/images/proctor/proctor.go b/test/runtimes/proctor/proctor.go
index b54abe434..b54abe434 100644
--- a/test/runtimes/images/proctor/proctor.go
+++ b/test/runtimes/proctor/proctor.go
diff --git a/test/runtimes/images/proctor/proctor_test.go b/test/runtimes/proctor/proctor_test.go
index 6bb61d142..6ef2de085 100644
--- a/test/runtimes/images/proctor/proctor_test.go
+++ b/test/runtimes/proctor/proctor_test.go
@@ -23,24 +23,24 @@ import (
"strings"
"testing"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
func touch(t *testing.T, name string) {
t.Helper()
f, err := os.Create(name)
if err != nil {
- t.Fatal(err)
+ t.Fatalf("error creating file %q: %v", name, err)
}
if err := f.Close(); err != nil {
- t.Fatal(err)
+ t.Fatalf("error closing file %q: %v", name, err)
}
}
func TestSearchEmptyDir(t *testing.T) {
td, err := ioutil.TempDir(testutil.TmpDir(), "searchtest")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("error creating searchtest: %v", err)
}
defer os.RemoveAll(td)
@@ -60,7 +60,7 @@ func TestSearchEmptyDir(t *testing.T) {
func TestSearch(t *testing.T) {
td, err := ioutil.TempDir(testutil.TmpDir(), "searchtest")
if err != nil {
- t.Fatal(err)
+ t.Fatalf("error creating searchtest: %v", err)
}
defer os.RemoveAll(td)
@@ -101,14 +101,14 @@ func TestSearch(t *testing.T) {
if strings.HasSuffix(item, "/") {
// This item is a directory, create it.
if err := os.MkdirAll(filepath.Join(td, item), 0755); err != nil {
- t.Fatal(err)
+ t.Fatalf("error making directory: %v", err)
}
} else {
// This item is a file, create the directory and touch file.
// Create directory in which file should be created
fullDirPath := filepath.Join(td, filepath.Dir(item))
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
- t.Fatal(err)
+ t.Fatalf("error making directory: %v", err)
}
// Create file with full path to file.
touch(t, filepath.Join(td, item))
diff --git a/test/runtimes/images/proctor/python.go b/test/runtimes/proctor/python.go
index b9e0fbe6f..b9e0fbe6f 100644
--- a/test/runtimes/images/proctor/python.go
+++ b/test/runtimes/proctor/python.go
diff --git a/test/runtimes/runner.sh b/test/runtimes/runner.sh
deleted file mode 100755
index a8d9a3460..000000000
--- a/test/runtimes/runner.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-# Copyright 2018 The gVisor Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euf -x -o pipefail
-
-echo -- "$@"
-
-# Create outputs dir if it does not exist.
-if [[ -n "${TEST_UNDECLARED_OUTPUTS_DIR}" ]]; then
- mkdir -p "${TEST_UNDECLARED_OUTPUTS_DIR}"
- chmod a+rwx "${TEST_UNDECLARED_OUTPUTS_DIR}"
-fi
-
-# Update the timestamp on the shard status file. Bazel looks for this.
-touch "${TEST_SHARD_STATUS_FILE}"
-
-# Get location of runner binary.
-readonly runner=$(find "${TEST_SRCDIR}" -name runner)
-
-# Pass the arguments of this script directly to the runner.
-exec "${runner}" "$@"
-
diff --git a/test/runtimes/runner/BUILD b/test/runtimes/runner/BUILD
new file mode 100644
index 000000000..63924b9c5
--- /dev/null
+++ b/test/runtimes/runner/BUILD
@@ -0,0 +1,21 @@
+load("//tools:defs.bzl", "go_binary", "go_test")
+
+package(licenses = ["notice"])
+
+go_binary(
+ name = "runner",
+ testonly = 1,
+ srcs = ["main.go"],
+ visibility = ["//test/runtimes:__pkg__"],
+ deps = [
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
+ ],
+)
+
+go_test(
+ name = "blacklist_test",
+ size = "small",
+ srcs = ["blacklist_test.go"],
+ library = ":runner",
+)
diff --git a/test/runtimes/blacklist_test.go b/test/runtimes/runner/blacklist_test.go
index 0ff69ab18..0ff69ab18 100644
--- a/test/runtimes/blacklist_test.go
+++ b/test/runtimes/runner/blacklist_test.go
diff --git a/test/runtimes/runner.go b/test/runtimes/runner/main.go
index 3c98f4570..57540e00e 100644
--- a/test/runtimes/runner.go
+++ b/test/runtimes/runner/main.go
@@ -26,8 +26,8 @@ import (
"testing"
"time"
- "gvisor.dev/gvisor/runsc/dockerutil"
- "gvisor.dev/gvisor/runsc/testutil"
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
var (
@@ -45,7 +45,6 @@ func main() {
fmt.Fprintf(os.Stderr, "lang and image flags must not be empty\n")
os.Exit(1)
}
-
os.Exit(runTests())
}
@@ -60,8 +59,8 @@ func runTests() int {
return 1
}
- // Create a single docker container that will be used for all tests.
- d := dockerutil.MakeDocker("gvisor-" + *lang)
+ // Construct the shared docker instance.
+ d := dockerutil.MakeDocker(testutil.DefaultLogger(*lang))
defer d.CleanUp()
// Get a slice of tests to run. This will also start a single Docker
@@ -77,21 +76,18 @@ func runTests() int {
return m.Run()
}
-// getTests returns a slice of tests to run, subject to the shard size and
-// index.
-func getTests(d dockerutil.Docker, blacklist map[string]struct{}) ([]testing.InternalTest, error) {
- // Pull the image.
- if err := dockerutil.Pull(*image); err != nil {
- return nil, fmt.Errorf("docker pull %q failed: %v", *image, err)
- }
-
- // Run proctor with --pause flag to keep container alive forever.
- if err := d.Run(*image, "--pause"); err != nil {
+// getTests executes all tests as table tests.
+func getTests(d *dockerutil.Docker, blacklist map[string]struct{}) ([]testing.InternalTest, error) {
+ // Start the container.
+ d.CopyFiles("/proctor", "test/runtimes/proctor/proctor")
+ if err := d.Spawn(dockerutil.RunOpts{
+ Image: fmt.Sprintf("runtimes/%s", *image),
+ }, "/proctor/proctor", "--pause"); err != nil {
return nil, fmt.Errorf("docker run failed: %v", err)
}
// Get a list of all tests in the image.
- list, err := d.Exec("/proctor", "--runtime", *lang, "--list")
+ list, err := d.Exec(dockerutil.RunOpts{}, "/proctor/proctor", "--runtime", *lang, "--list")
if err != nil {
return nil, fmt.Errorf("docker exec failed: %v", err)
}
@@ -126,7 +122,7 @@ func getTests(d dockerutil.Docker, blacklist map[string]struct{}) ([]testing.Int
go func() {
fmt.Printf("RUNNING %s...\n", tc)
- output, err = d.Exec("/proctor", "--runtime", *lang, "--test", tc)
+ output, err = d.Exec(dockerutil.RunOpts{}, "/proctor/proctor", "--runtime", *lang, "--test", tc)
close(done)
}()
@@ -143,6 +139,7 @@ func getTests(d dockerutil.Docker, blacklist map[string]struct{}) ([]testing.Int
},
})
}
+
return itests, nil
}
@@ -153,11 +150,7 @@ func getBlacklist() (map[string]struct{}, error) {
if *blacklistFile == "" {
return blacklist, nil
}
- file, err := testutil.FindFile(*blacklistFile)
- if err != nil {
- return nil, err
- }
- f, err := os.Open(file)
+ f, err := os.Open(*blacklistFile)
if err != nil {
return nil, err
}
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index d3000dbc6..9400ffaeb 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -319,6 +319,49 @@ TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
tcpSimpleConnectTest(listener, connector, false);
}
+TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
+ const auto& param = GetParam();
+
+ const TestAddress& listener = param.listener;
+ const TestAddress& connector = param.connector;
+
+ constexpr int kBacklog = 5;
+
+ // Create the listening socket.
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
+ listener.addr_len),
+ SyscallSucceeds());
+
+ ASSERT_THAT(listen(listen_fd.get(), kBacklog), SyscallSucceeds());
+ ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RD), SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), kBacklog), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(),
+ reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
+ SyscallSucceeds());
+ const uint16_t port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+
+ for (int i = 0; i < kBacklog; i++) {
+ auto client = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+ }
+ for (int i = 0; i < kBacklog; i++) {
+ ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), SyscallSucceeds());
+ }
+}
+
TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
auto const& param = GetParam();