summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/BUILD53
-rw-r--r--runsc/boot/BUILD147
-rw-r--r--runsc/boot/boot_amd64_state_autogen.go3
-rw-r--r--runsc/boot/boot_arm64_state_autogen.go3
-rw-r--r--runsc/boot/boot_state_autogen.go37
-rw-r--r--runsc/boot/compat_test.go90
-rw-r--r--runsc/boot/filter/BUILD28
-rw-r--r--runsc/boot/filter/filter_amd64_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_arm64_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_race_state_autogen.go5
-rw-r--r--runsc/boot/filter/filter_state_autogen.go6
-rw-r--r--runsc/boot/fs_test.go251
-rw-r--r--runsc/boot/loader_test.go721
-rw-r--r--runsc/boot/platforms/BUILD15
-rw-r--r--runsc/boot/platforms/platforms_state_autogen.go3
-rw-r--r--runsc/boot/pprof/BUILD11
-rw-r--r--runsc/boot/pprof/pprof_state_autogen.go3
-rw-r--r--runsc/cgroup/BUILD27
-rw-r--r--runsc/cgroup/cgroup_state_autogen.go3
-rw-r--r--runsc/cgroup/cgroup_test.go815
-rw-r--r--runsc/cli/BUILD22
-rw-r--r--runsc/cli/cli_state_autogen.go3
-rw-r--r--runsc/cmd/BUILD98
-rw-r--r--runsc/cmd/capability_test.go127
-rw-r--r--runsc/cmd/cmd_state_autogen.go3
-rw-r--r--runsc/cmd/delete_test.go41
-rw-r--r--runsc/cmd/exec_test.go154
-rw-r--r--runsc/cmd/gofer_test.go163
-rw-r--r--runsc/config/BUILD28
-rw-r--r--runsc/config/config_state_autogen.go3
-rw-r--r--runsc/config/config_test.go272
-rw-r--r--runsc/console/BUILD17
-rw-r--r--runsc/console/console_state_autogen.go3
-rw-r--r--runsc/container/BUILD77
-rw-r--r--runsc/container/console_test.go658
-rw-r--r--runsc/container/container_norace_test.go20
-rw-r--r--runsc/container/container_race_test.go20
-rw-r--r--runsc/container/container_state_autogen.go3
-rw-r--r--runsc/container/container_test.go2447
-rw-r--r--runsc/container/multi_container_test.go1908
-rw-r--r--runsc/container/shared_volume_test.go265
-rw-r--r--runsc/flag/BUILD9
-rw-r--r--runsc/flag/flag_state_autogen.go3
-rw-r--r--runsc/fsgofer/BUILD39
-rw-r--r--runsc/fsgofer/filter/BUILD26
-rw-r--r--runsc/fsgofer/filter/filter_amd64_state_autogen.go5
-rw-r--r--runsc/fsgofer/filter/filter_arm64_state_autogen.go5
-rw-r--r--runsc/fsgofer/filter/filter_race_state_autogen.go5
-rw-r--r--runsc/fsgofer/filter/filter_state_autogen.go6
-rw-r--r--runsc/fsgofer/fsgofer_amd64_unsafe_state_autogen.go5
-rw-r--r--runsc/fsgofer/fsgofer_arm64_unsafe_state_autogen.go5
-rw-r--r--runsc/fsgofer/fsgofer_state_autogen.go3
-rw-r--r--runsc/fsgofer/fsgofer_test.go1122
-rw-r--r--runsc/fsgofer/fsgofer_unsafe_state_autogen.go3
-rw-r--r--runsc/mitigate/BUILD20
-rw-r--r--runsc/mitigate/cpu.go377
-rw-r--r--runsc/mitigate/cpu_test.go504
-rw-r--r--runsc/mitigate/mitigate.go20
-rw-r--r--runsc/sandbox/BUILD38
-rw-r--r--runsc/sandbox/sandbox_state_autogen.go3
-rw-r--r--runsc/sandbox/sandbox_unsafe_state_autogen.go3
-rw-r--r--runsc/specutils/BUILD34
-rw-r--r--runsc/specutils/seccomp/BUILD34
-rw-r--r--runsc/specutils/seccomp/seccomp_amd64_state_autogen.go5
-rw-r--r--runsc/specutils/seccomp/seccomp_arm64_state_autogen.go5
-rw-r--r--runsc/specutils/seccomp/seccomp_state_autogen.go3
-rw-r--r--runsc/specutils/seccomp/seccomp_test.go414
-rw-r--r--runsc/specutils/specutils_state_autogen.go3
-rw-r--r--runsc/specutils/specutils_test.go265
-rwxr-xr-xrunsc/version_test.sh36
70 files changed, 150 insertions, 11413 deletions
diff --git a/runsc/BUILD b/runsc/BUILD
deleted file mode 100644
index 3b91b984a..000000000
--- a/runsc/BUILD
+++ /dev/null
@@ -1,53 +0,0 @@
-load("//tools:defs.bzl", "go_binary")
-
-package(licenses = ["notice"])
-
-go_binary(
- name = "runsc",
- srcs = [
- "main.go",
- "version.go",
- ],
- pure = True,
- visibility = [
- "//visibility:public",
- ],
- x_defs = {"main.version": "{STABLE_VERSION}"},
- deps = ["//runsc/cli"],
-)
-
-# The runsc-race target is a race-compatible BUILD target. This must be built
-# via: bazel build --features=race :runsc-race
-#
-# This is neccessary because the race feature must apply to all dependencies
-# due a bug in gazelle file selection. The pure attribute must be off because
-# the race detector requires linking with non-Go components, although we still
-# require a static binary.
-#
-# Note that in the future this might be convertible to a compatible target by
-# using the pure and static attributes within a select function, but select is
-# not currently compatible with string attributes [1].
-#
-# [1] https://github.com/bazelbuild/bazel/issues/1698
-go_binary(
- name = "runsc-race",
- srcs = [
- "main.go",
- "version.go",
- ],
- static = True,
- visibility = [
- "//visibility:public",
- ],
- x_defs = {"main.version": "{STABLE_VERSION}"},
- deps = ["//runsc/cli"],
-)
-
-sh_test(
- name = "version_test",
- size = "small",
- srcs = ["version_test.sh"],
- args = ["$(location :runsc)"],
- data = [":runsc"],
- tags = ["noguitar"],
-)
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD
deleted file mode 100644
index 67307ab3c..000000000
--- a/runsc/boot/BUILD
+++ /dev/null
@@ -1,147 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "boot",
- srcs = [
- "compat.go",
- "compat_amd64.go",
- "compat_arm64.go",
- "controller.go",
- "debug.go",
- "events.go",
- "fs.go",
- "limits.go",
- "loader.go",
- "network.go",
- "strace.go",
- "vfs.go",
- ],
- visibility = [
- "//pkg/test:__subpackages__",
- "//runsc:__subpackages__",
- "//test:__subpackages__",
- ],
- deps = [
- "//pkg/abi",
- "//pkg/abi/linux",
- "//pkg/bpf",
- "//pkg/cleanup",
- "//pkg/context",
- "//pkg/control/server",
- "//pkg/cpuid",
- "//pkg/eventchannel",
- "//pkg/fd",
- "//pkg/flipcall",
- "//pkg/fspath",
- "//pkg/log",
- "//pkg/memutil",
- "//pkg/rand",
- "//pkg/refs",
- "//pkg/refsvfs2",
- "//pkg/sentry/arch",
- "//pkg/sentry/arch:registers_go_proto",
- "//pkg/sentry/control",
- "//pkg/sentry/devices/memdev",
- "//pkg/sentry/devices/ttydev",
- "//pkg/sentry/devices/tundev",
- "//pkg/sentry/fdimport",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/dev",
- "//pkg/sentry/fs/gofer",
- "//pkg/sentry/fs/host",
- "//pkg/sentry/fs/proc",
- "//pkg/sentry/fs/ramfs",
- "//pkg/sentry/fs/sys",
- "//pkg/sentry/fs/tmpfs",
- "//pkg/sentry/fs/tty",
- "//pkg/sentry/fs/user",
- "//pkg/sentry/fsimpl/devpts",
- "//pkg/sentry/fsimpl/devtmpfs",
- "//pkg/sentry/fsimpl/fuse",
- "//pkg/sentry/fsimpl/gofer",
- "//pkg/sentry/fsimpl/host",
- "//pkg/sentry/fsimpl/overlay",
- "//pkg/sentry/fsimpl/proc",
- "//pkg/sentry/fsimpl/sys",
- "//pkg/sentry/fsimpl/tmpfs",
- "//pkg/sentry/inet",
- "//pkg/sentry/kernel",
- "//pkg/sentry/kernel:uncaught_signal_go_proto",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/limits",
- "//pkg/sentry/loader",
- "//pkg/sentry/pgalloc",
- "//pkg/sentry/platform",
- "//pkg/sentry/sighandling",
- "//pkg/sentry/socket/hostinet",
- "//pkg/sentry/socket/netfilter",
- "//pkg/sentry/socket/netlink",
- "//pkg/sentry/socket/netlink/route",
- "//pkg/sentry/socket/netlink/uevent",
- "//pkg/sentry/socket/netstack",
- "//pkg/sentry/socket/unix",
- "//pkg/sentry/state",
- "//pkg/sentry/strace",
- "//pkg/sentry/syscalls/linux/vfs2",
- "//pkg/sentry/time",
- "//pkg/sentry/unimpl:unimplemented_syscall_go_proto",
- "//pkg/sentry/usage",
- "//pkg/sentry/vfs",
- "//pkg/sentry/watchdog",
- "//pkg/sync",
- "//pkg/syserror",
- "//pkg/tcpip",
- "//pkg/tcpip/link/fdbased",
- "//pkg/tcpip/link/loopback",
- "//pkg/tcpip/link/packetsocket",
- "//pkg/tcpip/link/qdisc/fifo",
- "//pkg/tcpip/link/sniffer",
- "//pkg/tcpip/network/arp",
- "//pkg/tcpip/network/ipv4",
- "//pkg/tcpip/network/ipv6",
- "//pkg/tcpip/stack",
- "//pkg/tcpip/transport/icmp",
- "//pkg/tcpip/transport/raw",
- "//pkg/tcpip/transport/tcp",
- "//pkg/tcpip/transport/udp",
- "//pkg/urpc",
- "//runsc/boot/filter",
- "//runsc/boot/platforms",
- "//runsc/boot/pprof",
- "//runsc/config",
- "//runsc/specutils",
- "//runsc/specutils/seccomp",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_google_protobuf//proto:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "boot_test",
- size = "small",
- srcs = [
- "compat_test.go",
- "fs_test.go",
- "loader_test.go",
- ],
- library = ":boot",
- deps = [
- "//pkg/control/server",
- "//pkg/fd",
- "//pkg/fspath",
- "//pkg/log",
- "//pkg/p9",
- "//pkg/sentry/contexttest",
- "//pkg/sentry/fs",
- "//pkg/sentry/vfs",
- "//pkg/sync",
- "//pkg/unet",
- "//runsc/config",
- "//runsc/fsgofer",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/boot/boot_amd64_state_autogen.go b/runsc/boot/boot_amd64_state_autogen.go
new file mode 100644
index 000000000..23dd4b7b3
--- /dev/null
+++ b/runsc/boot/boot_amd64_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package boot
diff --git a/runsc/boot/boot_arm64_state_autogen.go b/runsc/boot/boot_arm64_state_autogen.go
new file mode 100644
index 000000000..23dd4b7b3
--- /dev/null
+++ b/runsc/boot/boot_arm64_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package boot
diff --git a/runsc/boot/boot_state_autogen.go b/runsc/boot/boot_state_autogen.go
new file mode 100644
index 000000000..92736f360
--- /dev/null
+++ b/runsc/boot/boot_state_autogen.go
@@ -0,0 +1,37 @@
+// automatically generated by stateify.
+
+package boot
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (f *sandboxNetstackCreator) StateTypeName() string {
+ return "runsc/boot.sandboxNetstackCreator"
+}
+
+func (f *sandboxNetstackCreator) StateFields() []string {
+ return []string{
+ "clock",
+ "uniqueID",
+ }
+}
+
+func (f *sandboxNetstackCreator) beforeSave() {}
+
+func (f *sandboxNetstackCreator) StateSave(stateSinkObject state.Sink) {
+ f.beforeSave()
+ stateSinkObject.Save(0, &f.clock)
+ stateSinkObject.Save(1, &f.uniqueID)
+}
+
+func (f *sandboxNetstackCreator) afterLoad() {}
+
+func (f *sandboxNetstackCreator) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &f.clock)
+ stateSourceObject.Load(1, &f.uniqueID)
+}
+
+func init() {
+ state.Register((*sandboxNetstackCreator)(nil))
+}
diff --git a/runsc/boot/compat_test.go b/runsc/boot/compat_test.go
deleted file mode 100644
index 839c5303b..000000000
--- a/runsc/boot/compat_test.go
+++ /dev/null
@@ -1,90 +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 boot
-
-import (
- "testing"
-)
-
-func TestOnceTracker(t *testing.T) {
- o := onceTracker{}
- if !o.shouldReport(nil) {
- t.Error("first call to checkAndMark, got: false, want: true")
- }
- o.onReported(nil)
- for i := 0; i < 2; i++ {
- if o.shouldReport(nil) {
- t.Error("after first call to checkAndMark, got: true, want: false")
- }
- }
-}
-
-func TestArgsTracker(t *testing.T) {
- for _, tc := range []struct {
- name string
- idx []int
- arg1_1 uint64
- arg1_2 uint64
- arg2_1 uint64
- arg2_2 uint64
- want bool
- }{
- {name: "same arg1", idx: []int{0}, arg1_1: 123, arg1_2: 123, want: false},
- {name: "same arg2", idx: []int{1}, arg2_1: 123, arg2_2: 123, want: false},
- {name: "diff arg1", idx: []int{0}, arg1_1: 123, arg1_2: 321, want: true},
- {name: "diff arg2", idx: []int{1}, arg2_1: 123, arg2_2: 321, want: true},
- {name: "cmd is uint32", idx: []int{0}, arg2_1: 0xdead00000123, arg2_2: 0xbeef00000123, want: false},
- {name: "same 2 args", idx: []int{0, 1}, arg2_1: 123, arg1_1: 321, arg2_2: 123, arg1_2: 321, want: false},
- {name: "diff 2 args", idx: []int{0, 1}, arg2_1: 123, arg1_1: 321, arg2_2: 789, arg1_2: 987, want: true},
- } {
- t.Run(tc.name, func(t *testing.T) {
- c := newArgsTracker(tc.idx...)
- regs := newRegs()
- setArgVal(0, tc.arg1_1, regs)
- setArgVal(1, tc.arg2_1, regs)
- if !c.shouldReport(regs) {
- t.Error("first call to shouldReport, got: false, want: true")
- }
- c.onReported(regs)
-
- setArgVal(0, tc.arg1_2, regs)
- setArgVal(1, tc.arg2_2, regs)
- if got := c.shouldReport(regs); tc.want != got {
- t.Errorf("second call to shouldReport, got: %t, want: %t", got, tc.want)
- }
- })
- }
-}
-
-func TestArgsTrackerLimit(t *testing.T) {
- c := newArgsTracker(0, 1)
- for i := 0; i < reportLimit; i++ {
- regs := newRegs()
- setArgVal(0, 123, regs)
- setArgVal(1, uint64(i), regs)
- if !c.shouldReport(regs) {
- t.Error("shouldReport before limit was reached, got: false, want: true")
- }
- c.onReported(regs)
- }
-
- // Should hit the count limit now.
- regs := newRegs()
- setArgVal(0, 123, regs)
- setArgVal(1, 123456, regs)
- if c.shouldReport(regs) {
- t.Error("shouldReport after limit was reached, got: true, want: false")
- }
-}
diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD
deleted file mode 100644
index ed18f0047..000000000
--- a/runsc/boot/filter/BUILD
+++ /dev/null
@@ -1,28 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "filter",
- srcs = [
- "config.go",
- "config_amd64.go",
- "config_arm64.go",
- "config_profile.go",
- "extra_filters.go",
- "extra_filters_msan.go",
- "extra_filters_race.go",
- "filter.go",
- ],
- visibility = [
- "//runsc/boot:__subpackages__",
- ],
- deps = [
- "//pkg/abi/linux",
- "//pkg/log",
- "//pkg/seccomp",
- "//pkg/sentry/platform",
- "//pkg/tcpip/link/fdbased",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/boot/filter/filter_amd64_state_autogen.go b/runsc/boot/filter/filter_amd64_state_autogen.go
new file mode 100644
index 000000000..0f27e5568
--- /dev/null
+++ b/runsc/boot/filter/filter_amd64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package filter
diff --git a/runsc/boot/filter/filter_arm64_state_autogen.go b/runsc/boot/filter/filter_arm64_state_autogen.go
new file mode 100644
index 000000000..e87cf5af7
--- /dev/null
+++ b/runsc/boot/filter/filter_arm64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package filter
diff --git a/runsc/boot/filter/filter_race_state_autogen.go b/runsc/boot/filter/filter_race_state_autogen.go
new file mode 100644
index 000000000..c4a858e80
--- /dev/null
+++ b/runsc/boot/filter/filter_race_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build race
+
+package filter
diff --git a/runsc/boot/filter/filter_state_autogen.go b/runsc/boot/filter/filter_state_autogen.go
new file mode 100644
index 000000000..41ff99424
--- /dev/null
+++ b/runsc/boot/filter/filter_state_autogen.go
@@ -0,0 +1,6 @@
+// automatically generated by stateify.
+
+// +build !msan,!race
+// +build msan
+
+package filter
diff --git a/runsc/boot/fs_test.go b/runsc/boot/fs_test.go
deleted file mode 100644
index e986231e5..000000000
--- a/runsc/boot/fs_test.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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 boot
-
-import (
- "reflect"
- "strings"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/runsc/config"
-)
-
-func TestPodMountHintsHappy(t *testing.T) {
- spec := &specs.Spec{
- Annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
-
- MountPrefix + "mount2.source": "bar",
- MountPrefix + "mount2.type": "bind",
- MountPrefix + "mount2.share": "container",
- MountPrefix + "mount2.options": "rw,private",
- },
- }
- podHints, err := newPodMountHints(spec)
- if err != nil {
- t.Fatalf("newPodMountHints failed: %v", err)
- }
-
- // Check that fields were set correctly.
- mount1 := podHints.mounts["mount1"]
- if want := "mount1"; want != mount1.name {
- t.Errorf("mount1 name, want: %q, got: %q", want, mount1.name)
- }
- if want := "foo"; want != mount1.mount.Source {
- t.Errorf("mount1 source, want: %q, got: %q", want, mount1.mount.Source)
- }
- if want := "tmpfs"; want != mount1.mount.Type {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Type)
- }
- if want := pod; want != mount1.share {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.share)
- }
- if want := []string(nil); !reflect.DeepEqual(want, mount1.mount.Options) {
- t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Options)
- }
-
- mount2 := podHints.mounts["mount2"]
- if want := "mount2"; want != mount2.name {
- t.Errorf("mount2 name, want: %q, got: %q", want, mount2.name)
- }
- if want := "bar"; want != mount2.mount.Source {
- t.Errorf("mount2 source, want: %q, got: %q", want, mount2.mount.Source)
- }
- if want := "bind"; want != mount2.mount.Type {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Type)
- }
- if want := container; want != mount2.share {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.share)
- }
- if want := []string{"private", "rw"}; !reflect.DeepEqual(want, mount2.mount.Options) {
- t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Options)
- }
-}
-
-func TestPodMountHintsErrors(t *testing.T) {
- for _, tst := range []struct {
- name string
- annotations map[string]string
- error string
- }{
- {
- name: "too short",
- annotations: map[string]string{
- MountPrefix + "mount1": "foo",
- },
- error: "invalid mount annotation",
- },
- {
- name: "no name",
- annotations: map[string]string{
- MountPrefix + ".source": "foo",
- },
- error: "invalid mount name",
- },
- {
- name: "missing source",
- annotations: map[string]string{
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- },
- error: "source field",
- },
- {
- name: "missing type",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.share": "pod",
- },
- error: "type field",
- },
- {
- name: "missing share",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- },
- error: "share field",
- },
- {
- name: "invalid field name",
- annotations: map[string]string{
- MountPrefix + "mount1.invalid": "foo",
- },
- error: "invalid mount annotation",
- },
- {
- name: "invalid source",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- },
- error: "source cannot be empty",
- },
- {
- name: "invalid type",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "invalid-type",
- MountPrefix + "mount1.share": "pod",
- },
- error: "invalid type",
- },
- {
- name: "invalid share",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "invalid-share",
- },
- error: "invalid share",
- },
- {
- name: "invalid options",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
- MountPrefix + "mount1.options": "invalid-option",
- },
- error: "unknown mount option",
- },
- {
- name: "duplicate source",
- annotations: map[string]string{
- MountPrefix + "mount1.source": "foo",
- MountPrefix + "mount1.type": "tmpfs",
- MountPrefix + "mount1.share": "pod",
-
- MountPrefix + "mount2.source": "foo",
- MountPrefix + "mount2.type": "bind",
- MountPrefix + "mount2.share": "container",
- },
- error: "have the same mount source",
- },
- } {
- t.Run(tst.name, func(t *testing.T) {
- spec := &specs.Spec{Annotations: tst.annotations}
- podHints, err := newPodMountHints(spec)
- if err == nil || !strings.Contains(err.Error(), tst.error) {
- t.Errorf("newPodMountHints invalid error, want: .*%s.*, got: %v", tst.error, err)
- }
- if podHints != nil {
- t.Errorf("newPodMountHints must return nil on failure: %+v", podHints)
- }
- })
- }
-}
-
-func TestGetMountAccessType(t *testing.T) {
- const source = "foo"
- for _, tst := range []struct {
- name string
- annotations map[string]string
- want config.FileAccessType
- }{
- {
- name: "container=exclusive",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "container",
- },
- want: config.FileAccessExclusive,
- },
- {
- name: "pod=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "pod",
- },
- want: config.FileAccessShared,
- },
- {
- name: "shared=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source,
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "shared",
- },
- want: config.FileAccessShared,
- },
- {
- name: "default=shared",
- annotations: map[string]string{
- MountPrefix + "mount1.source": source + "mismatch",
- MountPrefix + "mount1.type": "bind",
- MountPrefix + "mount1.share": "container",
- },
- want: config.FileAccessShared,
- },
- } {
- t.Run(tst.name, func(t *testing.T) {
- spec := &specs.Spec{Annotations: tst.annotations}
- podHints, err := newPodMountHints(spec)
- if err != nil {
- t.Fatalf("newPodMountHints failed: %v", err)
- }
- mounter := containerMounter{hints: podHints}
- if got := mounter.getMountAccessType(specs.Mount{Source: source}); got != tst.want {
- t.Errorf("getMountAccessType(), want: %v, got: %v", tst.want, got)
- }
- })
- }
-}
diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go
deleted file mode 100644
index b77b4762e..000000000
--- a/runsc/boot/loader_test.go
+++ /dev/null
@@ -1,721 +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 boot
-
-import (
- "fmt"
- "math/rand"
- "os"
- "reflect"
- "syscall"
- "testing"
- "time"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/control/server"
- "gvisor.dev/gvisor/pkg/fd"
- "gvisor.dev/gvisor/pkg/fspath"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/p9"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
- "gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/unet"
- "gvisor.dev/gvisor/runsc/config"
- "gvisor.dev/gvisor/runsc/fsgofer"
-)
-
-func init() {
- log.SetLevel(log.Debug)
- rand.Seed(time.Now().UnixNano())
- if err := fsgofer.OpenProcSelfFD(); err != nil {
- panic(err)
- }
- config.RegisterFlags()
-}
-
-func testConfig() *config.Config {
- conf, err := config.NewFromFlags()
- if err != nil {
- panic(err)
- }
- // Change test defaults.
- conf.RootDir = "unused_root_dir"
- conf.Network = config.NetworkNone
- conf.DisableSeccomp = true
- return conf
-}
-
-// testSpec returns a simple spec that can be used in tests.
-func testSpec() *specs.Spec {
- return &specs.Spec{
- // The host filesystem root is the sandbox root.
- Root: &specs.Root{
- Path: "/",
- Readonly: true,
- },
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- }
-}
-
-// startGofer starts a new gofer routine serving 'root' path. It returns the
-// sandbox side of the connection, and a function that when called will stop the
-// gofer.
-func startGofer(root string) (int, func(), error) {
- fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
- if err != nil {
- return 0, nil, err
- }
- sandboxEnd, goferEnd := fds[0], fds[1]
-
- socket, err := unet.NewSocket(goferEnd)
- if err != nil {
- syscall.Close(sandboxEnd)
- syscall.Close(goferEnd)
- return 0, nil, fmt.Errorf("error creating server on FD %d: %v", goferEnd, err)
- }
- at, err := fsgofer.NewAttachPoint(root, fsgofer.Config{ROMount: true})
- if err != nil {
- return 0, nil, err
- }
- go func() {
- s := p9.NewServer(at)
- if err := s.Handle(socket); err != nil {
- log.Infof("Gofer is stopping. FD: %d, err: %v\n", goferEnd, err)
- }
- }()
- // Closing the gofer socket will stop the gofer and exit goroutine above.
- cleanup := func() {
- if err := socket.Close(); err != nil {
- log.Warningf("Error closing gofer socket: %v", err)
- }
- }
- return sandboxEnd, cleanup, nil
-}
-
-func createLoader(vfsEnabled bool, spec *specs.Spec) (*Loader, func(), error) {
- fd, err := server.CreateSocket(ControlSocketAddr(fmt.Sprintf("%010d", rand.Int())[:10]))
- if err != nil {
- return nil, nil, err
- }
- conf := testConfig()
- conf.VFS2 = vfsEnabled
-
- sandEnd, cleanup, err := startGofer(spec.Root.Path)
- if err != nil {
- return nil, nil, err
- }
-
- // Loader takes ownership of stdio.
- var stdio []int
- for _, f := range []*os.File{os.Stdin, os.Stdout, os.Stderr} {
- newFd, err := unix.Dup(int(f.Fd()))
- if err != nil {
- return nil, nil, err
- }
- stdio = append(stdio, newFd)
- }
-
- args := Args{
- ID: "foo",
- Spec: spec,
- Conf: conf,
- ControllerFD: fd,
- GoferFDs: []int{sandEnd},
- StdioFDs: stdio,
- }
- l, err := New(args)
- if err != nil {
- cleanup()
- return nil, nil, err
- }
- return l, cleanup, nil
-}
-
-// TestRun runs a simple application in a sandbox and checks that it succeeds.
-func TestRun(t *testing.T) {
- doRun(t, false)
-}
-
-// TestRunVFS2 runs TestRun in VFSv2.
-func TestRunVFS2(t *testing.T) {
- doRun(t, true)
-}
-
-func doRun(t *testing.T, vfsEnabled bool) {
- l, cleanup, err := createLoader(vfsEnabled, testSpec())
- if err != nil {
- t.Fatalf("error creating loader: %v", err)
- }
-
- defer l.Destroy()
- defer cleanup()
-
- // Start a goroutine to read the start chan result, otherwise Run will
- // block forever.
- var resultChanErr error
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- resultChanErr = <-l.ctrl.manager.startResultChan
- wg.Done()
- }()
-
- // Run the container.
- if err := l.Run(); err != nil {
- t.Errorf("error running container: %v", err)
- }
-
- // We should have not gotten an error on the startResultChan.
- wg.Wait()
- if resultChanErr != nil {
- t.Errorf("error on startResultChan: %v", resultChanErr)
- }
-
- // Wait for the application to exit. It should succeed.
- if status := l.WaitExit(); status.Code != 0 || status.Signo != 0 {
- t.Errorf("application exited with status %+v, want 0", status)
- }
-}
-
-// TestStartSignal tests that the controller Start message will cause
-// WaitForStartSignal to return.
-func TestStartSignal(t *testing.T) {
- doStartSignal(t, false)
-}
-
-// TestStartSignalVFS2 does TestStartSignal with VFS2.
-func TestStartSignalVFS2(t *testing.T) {
- doStartSignal(t, true)
-}
-
-func doStartSignal(t *testing.T, vfsEnabled bool) {
- l, cleanup, err := createLoader(vfsEnabled, testSpec())
- if err != nil {
- t.Fatalf("error creating loader: %v", err)
- }
- defer l.Destroy()
- defer cleanup()
-
- // We aren't going to wait on this application, so the control server
- // needs to be shut down manually.
- defer l.ctrl.srv.Stop()
-
- // Start a goroutine that calls WaitForStartSignal and writes to a
- // channel when it returns.
- waitFinished := make(chan struct{})
- go func() {
- l.WaitForStartSignal()
- // Pretend that Run() executed and returned no error.
- l.ctrl.manager.startResultChan <- nil
- waitFinished <- struct{}{}
- }()
-
- // Nothing has been written to the channel, so waitFinished should not
- // return. Give it a little bit of time to make sure the goroutine has
- // started.
- select {
- case <-waitFinished:
- t.Errorf("WaitForStartSignal completed but it should not have")
- case <-time.After(50 * time.Millisecond):
- // OK.
- }
-
- // Trigger the control server StartRoot method.
- cid := "foo"
- if err := l.ctrl.manager.StartRoot(&cid, nil); err != nil {
- t.Errorf("error calling StartRoot: %v", err)
- }
-
- // Now WaitForStartSignal should return (within a short amount of
- // time).
- select {
- case <-waitFinished:
- // OK.
- case <-time.After(50 * time.Millisecond):
- t.Errorf("WaitForStartSignal did not complete but it should have")
- }
-
-}
-
-type CreateMountTestcase struct {
- name string
- // Spec that will be used to create the mount manager. Note
- // that we can't mount procfs without a kernel, so each spec
- // MUST contain something other than procfs mounted at /proc.
- spec specs.Spec
- // Paths that are expected to exist in the resulting fs.
- expectedPaths []string
-}
-
-func createMountTestcases() []*CreateMountTestcase {
- testCases := []*CreateMountTestcase{
- {
- // Only proc.
- name: "only proc mount",
- spec: specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- },
- },
- // /proc, /dev, and /sys should always be mounted.
- expectedPaths: []string{"/proc", "/dev", "/sys"},
- },
- {
- // Mount at a deep path, with many components that do
- // not exist in the root.
- name: "deep mount path",
- spec: specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/some/very/very/deep/path",
- Type: "tmpfs",
- },
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- },
- },
- // /some/deep/path should be mounted, along with /proc, /dev, and /sys.
- expectedPaths: []string{"/some/very/very/deep/path", "/proc", "/dev", "/sys"},
- },
- {
- // Mounts are nested inside each other.
- name: "nested mounts",
- spec: specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- {
- Destination: "/foo",
- Type: "tmpfs",
- },
- {
- Destination: "/foo/qux",
- Type: "tmpfs",
- },
- {
- // File mounts with the same prefix.
- Destination: "/foo/qux-quz",
- Type: "tmpfs",
- },
- {
- Destination: "/foo/bar",
- Type: "tmpfs",
- },
- {
- Destination: "/foo/bar/baz",
- Type: "tmpfs",
- },
- {
- // A deep path that is in foo but not the other mounts.
- Destination: "/foo/some/very/very/deep/path",
- Type: "tmpfs",
- },
- },
- },
- expectedPaths: []string{"/foo", "/foo/bar", "/foo/bar/baz", "/foo/qux",
- "/foo/qux-quz", "/foo/some/very/very/deep/path", "/proc", "/dev", "/sys"},
- },
- {
- name: "mount inside /dev",
- spec: specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- {
- Destination: "/dev",
- Type: "tmpfs",
- },
- {
- // Mounted by runsc by default.
- Destination: "/dev/fd",
- Type: "tmpfs",
- },
- {
- // Mount with the same prefix.
- Destination: "/dev/fd-foo",
- Type: "tmpfs",
- },
- {
- // Unsupported fs type.
- Destination: "/dev/mqueue",
- Type: "mqueue",
- },
- {
- Destination: "/dev/foo",
- Type: "tmpfs",
- },
- {
- Destination: "/dev/bar",
- Type: "tmpfs",
- },
- },
- },
- expectedPaths: []string{"/proc", "/dev", "/dev/fd-foo", "/dev/foo", "/dev/bar", "/sys"},
- },
- {
- name: "mounts inside mandatory mounts",
- spec: specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- {
- Destination: "/sys/bar",
- Type: "tmpfs",
- },
- {
- Destination: "/tmp/baz",
- Type: "tmpfs",
- },
- {
- Destination: "/dev/goo",
- Type: "tmpfs",
- },
- },
- },
- expectedPaths: []string{"/proc", "/sys", "/sys/bar", "/tmp", "/tmp/baz", "/dev/goo"},
- },
- }
-
- return testCases
-}
-
-// Test that MountNamespace can be created with various specs.
-func TestCreateMountNamespace(t *testing.T) {
- for _, tc := range createMountTestcases() {
- t.Run(tc.name, func(t *testing.T) {
- conf := testConfig()
- ctx := contexttest.Context(t)
-
- sandEnd, cleanup, err := startGofer(tc.spec.Root.Path)
- if err != nil {
- t.Fatalf("failed to create gofer: %v", err)
- }
- defer cleanup()
-
- mntr := newContainerMounter(&tc.spec, []*fd.FD{fd.New(sandEnd)}, nil, &podMountHints{})
- mns, err := mntr.createMountNamespace(ctx, conf)
- if err != nil {
- t.Fatalf("failed to create mount namespace: %v", err)
- }
- ctx = fs.WithRoot(ctx, mns.Root())
- if err := mntr.mountSubmounts(ctx, conf, mns); err != nil {
- t.Fatalf("failed to create mount namespace: %v", err)
- }
-
- root := mns.Root()
- defer root.DecRef(ctx)
- for _, p := range tc.expectedPaths {
- maxTraversals := uint(0)
- if d, err := mns.FindInode(ctx, root, root, p, &maxTraversals); err != nil {
- t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
- } else {
- d.DecRef(ctx)
- }
- }
- })
- }
-}
-
-// Test that MountNamespace can be created with various specs.
-func TestCreateMountNamespaceVFS2(t *testing.T) {
- for _, tc := range createMountTestcases() {
- t.Run(tc.name, func(t *testing.T) {
- spec := testSpec()
- spec.Mounts = tc.spec.Mounts
- spec.Root = tc.spec.Root
-
- t.Logf("Using root: %q", spec.Root.Path)
- l, loaderCleanup, err := createLoader(true /* VFS2 Enabled */, spec)
- if err != nil {
- t.Fatalf("failed to create loader: %v", err)
- }
- defer l.Destroy()
- defer loaderCleanup()
-
- mntr := newContainerMounter(l.root.spec, l.root.goferFDs, l.k, l.mountHints)
- if err := mntr.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil {
- t.Fatalf("failed process hints: %v", err)
- }
-
- ctx := l.k.SupervisorContext()
- mns, err := mntr.mountAll(l.root.conf, &l.root.procArgs)
- if err != nil {
- t.Fatalf("mountAll: %v", err)
- }
-
- root := mns.Root()
- root.IncRef()
- defer root.DecRef(ctx)
- for _, p := range tc.expectedPaths {
- target := &vfs.PathOperation{
- Root: root,
- Start: root,
- Path: fspath.Parse(p),
- }
-
- if d, err := l.k.VFS().GetDentryAt(ctx, l.root.procArgs.Credentials, target, &vfs.GetDentryOptions{}); err != nil {
- t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
- } else {
- d.DecRef(ctx)
- }
- }
- })
- }
-}
-
-// TestRestoreEnvironment tests that the correct mounts are collected from the spec and config
-// in order to build the environment for restoring.
-func TestRestoreEnvironment(t *testing.T) {
- testCases := []struct {
- name string
- spec *specs.Spec
- ioFDs []int
- errorExpected bool
- expectedRenv fs.RestoreEnvironment
- }{
- {
- name: "basic spec test",
- spec: &specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/some/very/very/deep/path",
- Type: "tmpfs",
- },
- {
- Destination: "/proc",
- Type: "tmpfs",
- },
- },
- },
- ioFDs: []int{0},
- errorExpected: false,
- expectedRenv: fs.RestoreEnvironment{
- MountSources: map[string][]fs.MountArgs{
- "9p": {
- {
- Dev: "9pfs-/",
- Flags: fs.MountSourceFlags{ReadOnly: true},
- DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
- },
- },
- "tmpfs": {
- {
- Dev: "none",
- },
- {
- Dev: "none",
- },
- {
- Dev: "none",
- },
- },
- "devtmpfs": {
- {
- Dev: "none",
- },
- },
- "devpts": {
- {
- Dev: "none",
- },
- },
- "sysfs": {
- {
- Dev: "none",
- },
- },
- },
- },
- },
- {
- name: "bind type test",
- spec: &specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/dev/fd-foo",
- Type: "bind",
- },
- },
- },
- ioFDs: []int{0, 1},
- errorExpected: false,
- expectedRenv: fs.RestoreEnvironment{
- MountSources: map[string][]fs.MountArgs{
- "9p": {
- {
- Dev: "9pfs-/",
- Flags: fs.MountSourceFlags{ReadOnly: true},
- DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
- },
- {
- Dev: "9pfs-/dev/fd-foo",
- DataString: "trans=fd,rfdno=1,wfdno=1,privateunixsocket=true,cache=remote_revalidating",
- },
- },
- "tmpfs": {
- {
- Dev: "none",
- },
- },
- "devtmpfs": {
- {
- Dev: "none",
- },
- },
- "devpts": {
- {
- Dev: "none",
- },
- },
- "proc": {
- {
- Dev: "none",
- },
- },
- "sysfs": {
- {
- Dev: "none",
- },
- },
- },
- },
- },
- {
- name: "options test",
- spec: &specs.Spec{
- Root: &specs.Root{
- Path: os.TempDir(),
- Readonly: true,
- },
- Mounts: []specs.Mount{
- {
- Destination: "/dev/fd-foo",
- Type: "tmpfs",
- Options: []string{"uid=1022", "noatime"},
- },
- },
- },
- ioFDs: []int{0},
- errorExpected: false,
- expectedRenv: fs.RestoreEnvironment{
- MountSources: map[string][]fs.MountArgs{
- "9p": {
- {
- Dev: "9pfs-/",
- Flags: fs.MountSourceFlags{ReadOnly: true},
- DataString: "trans=fd,rfdno=0,wfdno=0,privateunixsocket=true",
- },
- },
- "tmpfs": {
- {
- Dev: "none",
- Flags: fs.MountSourceFlags{NoAtime: true},
- DataString: "uid=1022",
- },
- {
- Dev: "none",
- },
- },
- "devtmpfs": {
- {
- Dev: "none",
- },
- },
- "devpts": {
- {
- Dev: "none",
- },
- },
- "proc": {
- {
- Dev: "none",
- },
- },
- "sysfs": {
- {
- Dev: "none",
- },
- },
- },
- },
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- conf := testConfig()
- var ioFDs []*fd.FD
- for _, ioFD := range tc.ioFDs {
- ioFDs = append(ioFDs, fd.New(ioFD))
- }
- mntr := newContainerMounter(tc.spec, ioFDs, nil, &podMountHints{})
- actualRenv, err := mntr.createRestoreEnvironment(conf)
- if !tc.errorExpected && err != nil {
- t.Fatalf("could not create restore environment for test:%s", tc.name)
- } else if tc.errorExpected {
- if err == nil {
- t.Errorf("expected an error, but no error occurred.")
- }
- } else {
- if !reflect.DeepEqual(*actualRenv, tc.expectedRenv) {
- t.Errorf("restore environments did not match for test:%s\ngot:%+v\nwant:%+v\n", tc.name, *actualRenv, tc.expectedRenv)
- }
- }
- })
- }
-}
diff --git a/runsc/boot/platforms/BUILD b/runsc/boot/platforms/BUILD
deleted file mode 100644
index 77774f43c..000000000
--- a/runsc/boot/platforms/BUILD
+++ /dev/null
@@ -1,15 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "platforms",
- srcs = ["platforms.go"],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "//pkg/sentry/platform/kvm",
- "//pkg/sentry/platform/ptrace",
- ],
-)
diff --git a/runsc/boot/platforms/platforms_state_autogen.go b/runsc/boot/platforms/platforms_state_autogen.go
new file mode 100644
index 000000000..8676d25c1
--- /dev/null
+++ b/runsc/boot/platforms/platforms_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package platforms
diff --git a/runsc/boot/pprof/BUILD b/runsc/boot/pprof/BUILD
deleted file mode 100644
index 29cb42b2f..000000000
--- a/runsc/boot/pprof/BUILD
+++ /dev/null
@@ -1,11 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "pprof",
- srcs = ["pprof.go"],
- visibility = [
- "//runsc:__subpackages__",
- ],
-)
diff --git a/runsc/boot/pprof/pprof_state_autogen.go b/runsc/boot/pprof/pprof_state_autogen.go
new file mode 100644
index 000000000..cabd43173
--- /dev/null
+++ b/runsc/boot/pprof/pprof_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package pprof
diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD
deleted file mode 100644
index 37f4253ba..000000000
--- a/runsc/cgroup/BUILD
+++ /dev/null
@@ -1,27 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "cgroup",
- srcs = ["cgroup.go"],
- visibility = ["//:sandbox"],
- deps = [
- "//pkg/cleanup",
- "//pkg/log",
- "@com_github_cenkalti_backoff//:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- ],
-)
-
-go_test(
- name = "cgroup_test",
- size = "small",
- srcs = ["cgroup_test.go"],
- library = ":cgroup",
- tags = ["local"],
- deps = [
- "//pkg/test/testutil",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- ],
-)
diff --git a/runsc/cgroup/cgroup_state_autogen.go b/runsc/cgroup/cgroup_state_autogen.go
new file mode 100644
index 000000000..934ed169b
--- /dev/null
+++ b/runsc/cgroup/cgroup_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package cgroup
diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go
deleted file mode 100644
index 48d71cfa6..000000000
--- a/runsc/cgroup/cgroup_test.go
+++ /dev/null
@@ -1,815 +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 cgroup
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/pkg/test/testutil"
-)
-
-var debianMountinfo = `
-35 24 0:30 / /sys/fs/cgroup ro shared:9 - tmpfs tmpfs ro
-36 35 0:31 / /sys/fs/cgroup/unified rw shared:10 - cgroup2 cgroup2 rw
-37 35 0:32 / /sys/fs/cgroup/systemd rw - cgroup cgroup rw,name=systemd
-41 35 0:36 / /sys/fs/cgroup/cpu,cpuacct rw shared:16 - cgroup cgroup rw,cpu,cpuacct
-42 35 0:37 / /sys/fs/cgroup/freezer rw shared:17 - cgroup cgroup rw,freezer
-43 35 0:38 / /sys/fs/cgroup/hugetlb rw shared:18 - cgroup cgroup rw,hugetlb
-44 35 0:39 / /sys/fs/cgroup/cpuset rw shared:19 - cgroup cgroup rw,cpuset
-45 35 0:40 / /sys/fs/cgroup/net_cls,net_prio rw shared:20 - cgroup cgroup rw,net_cls,net_prio
-46 35 0:41 / /sys/fs/cgroup/pids rw shared:21 - cgroup cgroup rw,pids
-47 35 0:42 / /sys/fs/cgroup/perf_event rw shared:22 - cgroup cgroup rw,perf_event
-48 35 0:43 / /sys/fs/cgroup/memory rw shared:23 - cgroup cgroup rw,memory
-49 35 0:44 / /sys/fs/cgroup/blkio rw shared:24 - cgroup cgroup rw,blkio
-50 35 0:45 / /sys/fs/cgroup/devices rw shared:25 - cgroup cgroup rw,devices
-51 35 0:46 / /sys/fs/cgroup/rdma rw shared:26 - cgroup cgroup rw,rdma
-`
-
-var dindMountinfo = `
-1305 1304 0:64 / /sys/fs/cgroup rw - tmpfs tmpfs rw,mode=755
-1306 1305 0:32 /docker/136 /sys/fs/cgroup/systemd ro master:11 - cgroup cgroup rw,xattr,name=systemd
-1307 1305 0:36 /docker/136 /sys/fs/cgroup/cpu,cpuacct ro master:16 - cgroup cgroup rw,cpu,cpuacct
-1308 1305 0:37 /docker/136 /sys/fs/cgroup/freezer ro master:17 - cgroup cgroup rw,freezer
-1309 1305 0:38 /docker/136 /sys/fs/cgroup/hugetlb ro master:18 - cgroup cgroup rw,hugetlb
-1310 1305 0:39 /docker/136 /sys/fs/cgroup/cpuset ro master:19 - cgroup cgroup rw,cpuset
-1311 1305 0:40 /docker/136 /sys/fs/cgroup/net_cls,net_prio ro master:20 - cgroup cgroup rw,net_cls,net_prio
-1312 1305 0:41 /docker/136 /sys/fs/cgroup/pids ro master:21 - cgroup cgroup rw,pids
-1313 1305 0:42 /docker/136 /sys/fs/cgroup/perf_event ro master:22 - cgroup cgroup rw,perf_event
-1314 1305 0:43 /docker/136 /sys/fs/cgroup/memory ro master:23 - cgroup cgroup rw,memory
-1316 1305 0:44 /docker/136 /sys/fs/cgroup/blkio ro master:24 - cgroup cgroup rw,blkio
-1317 1305 0:45 /docker/136 /sys/fs/cgroup/devices ro master:25 - cgroup cgroup rw,devices
-1318 1305 0:46 / /sys/fs/cgroup/rdma ro master:26 - cgroup cgroup rw,rdma
-`
-
-func TestUninstallEnoent(t *testing.T) {
- c := Cgroup{
- // set a non-existent name
- Name: "runsc-test-uninstall-656e6f656e740a",
- }
- c.Own = make(map[string]bool)
- for key := range controllers {
- c.Own[key] = true
- }
- if err := c.Uninstall(); err != nil {
- t.Errorf("Uninstall() failed: %v", err)
- }
-}
-
-func TestCountCpuset(t *testing.T) {
- for _, tc := range []struct {
- str string
- want int
- error bool
- }{
- {str: "0", want: 1},
- {str: "0,1,2,8,9,10", want: 6},
- {str: "0-1", want: 2},
- {str: "0-7", want: 8},
- {str: "0-7,16,32-39,64,65", want: 19},
- {str: "a", error: true},
- {str: "5-a", error: true},
- {str: "a-5", error: true},
- {str: "-10", error: true},
- {str: "15-", error: true},
- {str: "-", error: true},
- {str: "--", error: true},
- } {
- t.Run(tc.str, func(t *testing.T) {
- got, err := countCpuset(tc.str)
- if tc.error {
- if err == nil {
- t.Errorf("countCpuset(%q) should have failed", tc.str)
- }
- } else {
- if err != nil {
- t.Errorf("countCpuset(%q) failed: %v", tc.str, err)
- }
- if tc.want != got {
- t.Errorf("countCpuset(%q) want: %d, got: %d", tc.str, tc.want, got)
- }
- }
- })
- }
-}
-
-func uint16Ptr(v uint16) *uint16 {
- return &v
-}
-
-func uint32Ptr(v uint32) *uint32 {
- return &v
-}
-
-func int64Ptr(v int64) *int64 {
- return &v
-}
-
-func uint64Ptr(v uint64) *uint64 {
- return &v
-}
-
-func boolPtr(v bool) *bool {
- return &v
-}
-
-func checkDir(t *testing.T, dir string, contents map[string]string) {
- all, err := ioutil.ReadDir(dir)
- if err != nil {
- t.Fatalf("ReadDir(%q): %v", dir, err)
- }
- fileCount := 0
- for _, file := range all {
- if file.IsDir() {
- // Only want to compare files.
- continue
- }
- fileCount++
-
- want, ok := contents[file.Name()]
- if !ok {
- t.Errorf("file not expected: %q", file.Name())
- continue
- }
- gotBytes, err := ioutil.ReadFile(filepath.Join(dir, file.Name()))
- if err != nil {
- t.Fatal(err.Error())
- }
- got := strings.TrimSuffix(string(gotBytes), "\n")
- if got != want {
- t.Errorf("wrong file content, file: %q, want: %q, got: %q", file.Name(), want, got)
- }
- }
- if fileCount != len(contents) {
- t.Errorf("file is missing, want: %v, got: %v", contents, all)
- }
-}
-
-func makeLinuxWeightDevice(major, minor int64, weight, leafWeight *uint16) specs.LinuxWeightDevice {
- rv := specs.LinuxWeightDevice{
- Weight: weight,
- LeafWeight: leafWeight,
- }
- rv.Major = major
- rv.Minor = minor
- return rv
-}
-
-func makeLinuxThrottleDevice(major, minor int64, rate uint64) specs.LinuxThrottleDevice {
- rv := specs.LinuxThrottleDevice{
- Rate: rate,
- }
- rv.Major = major
- rv.Minor = minor
- return rv
-}
-
-func TestBlockIO(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxBlockIO
- wants map[string]string
- }{
- {
- name: "simple",
- spec: &specs.LinuxBlockIO{
- Weight: uint16Ptr(1),
- LeafWeight: uint16Ptr(2),
- },
- wants: map[string]string{
- "blkio.weight": "1",
- "blkio.leaf_weight": "2",
- },
- },
- {
- name: "weight_device",
- spec: &specs.LinuxBlockIO{
- WeightDevice: []specs.LinuxWeightDevice{
- makeLinuxWeightDevice(1, 2, uint16Ptr(3), uint16Ptr(4)),
- },
- },
- wants: map[string]string{
- "blkio.weight_device": "1:2 3",
- "blkio.leaf_weight_device": "1:2 4",
- },
- },
- {
- name: "weight_device_nil_values",
- spec: &specs.LinuxBlockIO{
- WeightDevice: []specs.LinuxWeightDevice{
- makeLinuxWeightDevice(1, 2, nil, nil),
- },
- },
- },
- {
- name: "throttle",
- spec: &specs.LinuxBlockIO{
- ThrottleReadBpsDevice: []specs.LinuxThrottleDevice{
- makeLinuxThrottleDevice(1, 2, 3),
- },
- ThrottleReadIOPSDevice: []specs.LinuxThrottleDevice{
- makeLinuxThrottleDevice(4, 5, 6),
- },
- ThrottleWriteBpsDevice: []specs.LinuxThrottleDevice{
- makeLinuxThrottleDevice(7, 8, 9),
- },
- ThrottleWriteIOPSDevice: []specs.LinuxThrottleDevice{
- makeLinuxThrottleDevice(10, 11, 12),
- },
- },
- wants: map[string]string{
- "blkio.throttle.read_bps_device": "1:2 3",
- "blkio.throttle.read_iops_device": "4:5 6",
- "blkio.throttle.write_bps_device": "7:8 9",
- "blkio.throttle.write_iops_device": "10:11 12",
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxBlockIO{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- BlockIO: tc.spec,
- }
- ctrlr := blockIO{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestCPU(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxCPU
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxCPU{
- Shares: uint64Ptr(1),
- Quota: int64Ptr(2),
- Period: uint64Ptr(3),
- RealtimeRuntime: int64Ptr(4),
- RealtimePeriod: uint64Ptr(5),
- },
- wants: map[string]string{
- "cpu.shares": "1",
- "cpu.cfs_quota_us": "2",
- "cpu.cfs_period_us": "3",
- "cpu.rt_runtime_us": "4",
- "cpu.rt_period_us": "5",
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxCPU{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- CPU: tc.spec,
- }
- ctrlr := cpu{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestCPUSet(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxCPU
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxCPU{
- Cpus: "foo",
- Mems: "bar",
- },
- wants: map[string]string{
- "cpuset.cpus": "foo",
- "cpuset.mems": "bar",
- },
- },
- // Don't test nil values because they are copied from the parent.
- // See TestCPUSetAncestor().
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- CPU: tc.spec,
- }
- ctrlr := cpuSet{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-// TestCPUSetAncestor checks that, when not available, value is read from
-// parent directory.
-func TestCPUSetAncestor(t *testing.T) {
- // Prepare master directory with cgroup files that will be propagated to
- // children.
- grandpa, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(grandpa)
-
- if err := ioutil.WriteFile(filepath.Join(grandpa, "cpuset.cpus"), []byte("parent-cpus"), 0666); err != nil {
- t.Fatalf("ioutil.WriteFile(): %v", err)
- }
- if err := ioutil.WriteFile(filepath.Join(grandpa, "cpuset.mems"), []byte("parent-mems"), 0666); err != nil {
- t.Fatalf("ioutil.WriteFile(): %v", err)
- }
-
- for _, tc := range []struct {
- name string
- spec *specs.LinuxCPU
- }{
- {
- name: "nil_values",
- spec: &specs.LinuxCPU{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- // Create empty files in intermediate directory. They should be ignored
- // when reading, and then populated from parent.
- parent, err := ioutil.TempDir(grandpa, "parent")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(parent)
- if _, err := os.Create(filepath.Join(parent, "cpuset.cpus")); err != nil {
- t.Fatalf("os.Create(): %v", err)
- }
- if _, err := os.Create(filepath.Join(parent, "cpuset.mems")); err != nil {
- t.Fatalf("os.Create(): %v", err)
- }
-
- // cgroup files mmust exist.
- dir, err := ioutil.TempDir(parent, "child")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- if _, err := os.Create(filepath.Join(dir, "cpuset.cpus")); err != nil {
- t.Fatalf("os.Create(): %v", err)
- }
- if _, err := os.Create(filepath.Join(dir, "cpuset.mems")); err != nil {
- t.Fatalf("os.Create(): %v", err)
- }
-
- spec := &specs.LinuxResources{
- CPU: tc.spec,
- }
- ctrlr := cpuSet{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- want := map[string]string{
- "cpuset.cpus": "parent-cpus",
- "cpuset.mems": "parent-mems",
- }
- // Both path and dir must have been populated from grandpa.
- checkDir(t, parent, want)
- checkDir(t, dir, want)
- })
- }
-}
-
-func TestHugeTlb(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec []specs.LinuxHugepageLimit
- wants map[string]string
- }{
- {
- name: "single",
- spec: []specs.LinuxHugepageLimit{
- {
- Pagesize: "1G",
- Limit: 123,
- },
- },
- wants: map[string]string{
- "hugetlb.1G.limit_in_bytes": "123",
- },
- },
- {
- name: "multiple",
- spec: []specs.LinuxHugepageLimit{
- {
- Pagesize: "1G",
- Limit: 123,
- },
- {
- Pagesize: "2G",
- Limit: 456,
- },
- {
- Pagesize: "1P",
- Limit: 789,
- },
- },
- wants: map[string]string{
- "hugetlb.1G.limit_in_bytes": "123",
- "hugetlb.2G.limit_in_bytes": "456",
- "hugetlb.1P.limit_in_bytes": "789",
- },
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- HugepageLimits: tc.spec,
- }
- ctrlr := hugeTLB{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestMemory(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxMemory
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxMemory{
- Limit: int64Ptr(1),
- Reservation: int64Ptr(2),
- Swap: int64Ptr(3),
- Kernel: int64Ptr(4),
- KernelTCP: int64Ptr(5),
- Swappiness: uint64Ptr(6),
- DisableOOMKiller: boolPtr(true),
- },
- wants: map[string]string{
- "memory.limit_in_bytes": "1",
- "memory.soft_limit_in_bytes": "2",
- "memory.memsw.limit_in_bytes": "3",
- "memory.kmem.limit_in_bytes": "4",
- "memory.kmem.tcp.limit_in_bytes": "5",
- "memory.swappiness": "6",
- "memory.oom_control": "1",
- },
- },
- {
- // Disable OOM killer should only write when set to true.
- name: "oomkiller",
- spec: &specs.LinuxMemory{
- DisableOOMKiller: boolPtr(false),
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxMemory{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- Memory: tc.spec,
- }
- ctrlr := memory{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestNetworkClass(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxNetwork
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxNetwork{
- ClassID: uint32Ptr(1),
- },
- wants: map[string]string{
- "net_cls.classid": "1",
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxNetwork{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- Network: tc.spec,
- }
- ctrlr := networkClass{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestNetworkPriority(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxNetwork
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxNetwork{
- Priorities: []specs.LinuxInterfacePriority{
- {
- Name: "foo",
- Priority: 1,
- },
- },
- },
- wants: map[string]string{
- "net_prio.ifpriomap": "foo 1",
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxNetwork{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- Network: tc.spec,
- }
- ctrlr := networkPrio{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestPids(t *testing.T) {
- for _, tc := range []struct {
- name string
- spec *specs.LinuxPids
- wants map[string]string
- }{
- {
- name: "all",
- spec: &specs.LinuxPids{Limit: 1},
- wants: map[string]string{
- "pids.max": "1",
- },
- },
- {
- name: "nil_values",
- spec: &specs.LinuxPids{},
- },
- {
- name: "nil",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "cgroup")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- spec := &specs.LinuxResources{
- Pids: tc.spec,
- }
- ctrlr := pids{}
- if err := ctrlr.set(spec, dir); err != nil {
- t.Fatalf("ctrlr.set(): %v", err)
- }
- checkDir(t, dir, tc.wants)
- })
- }
-}
-
-func TestLoadPaths(t *testing.T) {
- for _, tc := range []struct {
- name string
- cgroups string
- mountinfo string
- want map[string]string
- err string
- }{
- {
- name: "abs-path-unknown-controller",
- cgroups: "0:ctr:/path",
- mountinfo: debianMountinfo,
- want: map[string]string{"ctr": "/path"},
- },
- {
- name: "rel-path",
- cgroups: "0:ctr:rel-path",
- mountinfo: debianMountinfo,
- want: map[string]string{"ctr": "rel-path"},
- },
- {
- name: "non-controller",
- cgroups: "0:name=systemd:/path",
- mountinfo: debianMountinfo,
- want: map[string]string{"systemd": "path"},
- },
- {
- name: "empty",
- mountinfo: debianMountinfo,
- },
- {
- name: "multiple",
- cgroups: "0:ctr0:/path0\n" +
- "1:ctr1:/path1\n" +
- "2::/empty\n",
- mountinfo: debianMountinfo,
- want: map[string]string{
- "ctr0": "/path0",
- "ctr1": "/path1",
- },
- },
- {
- name: "missing-field",
- cgroups: "0:nopath\n",
- mountinfo: debianMountinfo,
- err: "invalid cgroups file",
- },
- {
- name: "too-many-fields",
- cgroups: "0:ctr:/path:extra\n",
- mountinfo: debianMountinfo,
- err: "invalid cgroups file",
- },
- {
- name: "multiple-malformed",
- cgroups: "0:ctr0:/path0\n" +
- "1:ctr1:/path1\n" +
- "2:\n",
- mountinfo: debianMountinfo,
- err: "invalid cgroups file",
- },
- {
- name: "nested-cgroup",
- cgroups: `9:memory:/docker/136
-2:cpu,cpuacct:/docker/136
-1:name=systemd:/docker/136
-0::/system.slice/containerd.service`,
- mountinfo: dindMountinfo,
- // we want relative path to /sys/fs/cgroup inside the nested container.
- // Subcroup inside the container will be created at /sys/fs/cgroup/cpu
- // This will be /sys/fs/cgroup/cpu/docker/136/CGROUP_NAME
- // outside the container
- want: map[string]string{
- "memory": ".",
- "cpu": ".",
- "cpuacct": ".",
- "systemd": ".",
- },
- },
- {
- name: "nested-cgroup-submount",
- cgroups: "9:memory:/docker/136/test",
- mountinfo: dindMountinfo,
- want: map[string]string{
- "memory": "test",
- },
- },
- {
- name: "invalid-mount-info",
- cgroups: "0:memory:/path",
- mountinfo: "41 35 0:36 / /sys/fs/cgroup/memory rw shared:16 - invalid",
- want: map[string]string{
- "memory": "/path",
- },
- },
- {
- name: "invalid-rel-path-in-proc-cgroup",
- cgroups: "9:memory:./invalid",
- mountinfo: dindMountinfo,
- err: "can't make ./invalid relative to /docker/136",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- r := strings.NewReader(tc.cgroups)
- mountinfo := strings.NewReader(tc.mountinfo)
- got, err := loadPathsHelperWithMountinfo(r, mountinfo)
- if len(tc.err) == 0 {
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- } else if !strings.Contains(err.Error(), tc.err) {
- t.Fatalf("Wrong error message, want: *%s*, got: %v", tc.err, err)
- }
- for key, vWant := range tc.want {
- vGot, ok := got[key]
- if !ok {
- t.Errorf("Missing controller %q", key)
- }
- if vWant != vGot {
- t.Errorf("Wrong controller %q value, want: %q, got: %q", key, vWant, vGot)
- }
- delete(got, key)
- }
- for k, v := range got {
- t.Errorf("Unexpected controller %q: %q", k, v)
- }
- })
- }
-}
diff --git a/runsc/cli/BUILD b/runsc/cli/BUILD
deleted file mode 100644
index 32cce2a18..000000000
--- a/runsc/cli/BUILD
+++ /dev/null
@@ -1,22 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "cli",
- srcs = ["main.go"],
- visibility = [
- "//:__pkg__",
- "//runsc:__pkg__",
- ],
- deps = [
- "//pkg/log",
- "//pkg/refs",
- "//pkg/sentry/platform",
- "//runsc/cmd",
- "//runsc/config",
- "//runsc/flag",
- "//runsc/specutils",
- "@com_github_google_subcommands//:go_default_library",
- ],
-)
diff --git a/runsc/cli/cli_state_autogen.go b/runsc/cli/cli_state_autogen.go
new file mode 100644
index 000000000..e81991e0b
--- /dev/null
+++ b/runsc/cli/cli_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package cli
diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD
deleted file mode 100644
index 19520d7ab..000000000
--- a/runsc/cmd/BUILD
+++ /dev/null
@@ -1,98 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "cmd",
- srcs = [
- "boot.go",
- "capability.go",
- "checkpoint.go",
- "chroot.go",
- "cmd.go",
- "create.go",
- "debug.go",
- "delete.go",
- "do.go",
- "error.go",
- "events.go",
- "exec.go",
- "gofer.go",
- "help.go",
- "install.go",
- "kill.go",
- "list.go",
- "path.go",
- "pause.go",
- "ps.go",
- "restore.go",
- "resume.go",
- "run.go",
- "spec.go",
- "start.go",
- "state.go",
- "statefile.go",
- "symbolize.go",
- "syscalls.go",
- "wait.go",
- ],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "//pkg/coverage",
- "//pkg/log",
- "//pkg/p9",
- "//pkg/sentry/control",
- "//pkg/sentry/kernel",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/platform",
- "//pkg/state/pretty",
- "//pkg/state/statefile",
- "//pkg/sync",
- "//pkg/unet",
- "//pkg/urpc",
- "//runsc/boot",
- "//runsc/config",
- "//runsc/console",
- "//runsc/container",
- "//runsc/flag",
- "//runsc/fsgofer",
- "//runsc/fsgofer/filter",
- "//runsc/specutils",
- "@com_github_google_subcommands//:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@com_github_syndtr_gocapability//capability:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "cmd_test",
- size = "small",
- srcs = [
- "capability_test.go",
- "delete_test.go",
- "exec_test.go",
- "gofer_test.go",
- ],
- data = [
- "//runsc",
- ],
- library = ":cmd",
- deps = [
- "//pkg/abi/linux",
- "//pkg/log",
- "//pkg/sentry/control",
- "//pkg/sentry/kernel/auth",
- "//pkg/test/testutil",
- "//pkg/urpc",
- "//runsc/config",
- "//runsc/container",
- "//runsc/specutils",
- "@com_github_google_go_cmp//cmp:go_default_library",
- "@com_github_google_go_cmp//cmp/cmpopts:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@com_github_syndtr_gocapability//capability:go_default_library",
- ],
-)
diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go
deleted file mode 100644
index e13a94486..000000000
--- a/runsc/cmd/capability_test.go
+++ /dev/null
@@ -1,127 +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 cmd
-
-import (
- "flag"
- "fmt"
- "os"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/syndtr/gocapability/capability"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/runsc/config"
- "gvisor.dev/gvisor/runsc/container"
- "gvisor.dev/gvisor/runsc/specutils"
-)
-
-func init() {
- log.SetLevel(log.Debug)
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
- }
-}
-
-func checkProcessCaps(pid int, wantCaps *specs.LinuxCapabilities) error {
- curCaps, err := capability.NewPid2(pid)
- if err != nil {
- return fmt.Errorf("capability.NewPid2(%d) failed: %v", pid, err)
- }
- if err := curCaps.Load(); err != nil {
- return fmt.Errorf("unable to load capabilities: %v", err)
- }
- fmt.Printf("Capabilities (PID: %d): %v\n", pid, curCaps)
-
- for _, c := range allCapTypes {
- if err := checkCaps(c, curCaps, wantCaps); err != nil {
- return err
- }
- }
- return nil
-}
-
-func checkCaps(which capability.CapType, curCaps capability.Capabilities, wantCaps *specs.LinuxCapabilities) error {
- wantNames := getCaps(which, wantCaps)
- for name, c := range capFromName {
- want := specutils.ContainsStr(wantNames, name)
- got := curCaps.Get(which, c)
- if want != got {
- if want {
- return fmt.Errorf("capability %v:%s should be set", which, name)
- }
- return fmt.Errorf("capability %v:%s should NOT be set", which, name)
- }
- }
- return nil
-}
-
-func TestCapabilities(t *testing.T) {
- stop := testutil.StartReaper()
- defer stop()
-
- spec := testutil.NewSpecWithArgs("/bin/sleep", "10000")
- caps := []string{
- "CAP_CHOWN",
- "CAP_SYS_PTRACE", // ptrace is added due to the platform choice.
- }
- spec.Process.Capabilities = &specs.LinuxCapabilities{
- Permitted: caps,
- Bounding: caps,
- Effective: caps,
- Inheritable: caps,
- }
-
- conf := testutil.TestConfig(t)
-
- // Use --network=host to make sandbox use spec's capabilities.
- conf.Network = config.NetworkHost
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := container.Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := container.New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Check that sandbox and gofer have the proper capabilities.
- if err := checkProcessCaps(c.Sandbox.Pid, spec.Process.Capabilities); err != nil {
- t.Error(err)
- }
- if err := checkProcessCaps(c.GoferPid, goferCaps); err != nil {
- t.Error(err)
- }
-}
-
-func TestMain(m *testing.M) {
- flag.Parse()
- specutils.MaybeRunAsRoot()
- os.Exit(m.Run())
-}
diff --git a/runsc/cmd/cmd_state_autogen.go b/runsc/cmd/cmd_state_autogen.go
new file mode 100644
index 000000000..de8aa267b
--- /dev/null
+++ b/runsc/cmd/cmd_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package cmd
diff --git a/runsc/cmd/delete_test.go b/runsc/cmd/delete_test.go
deleted file mode 100644
index e2d994a05..000000000
--- a/runsc/cmd/delete_test.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 cmd
-
-import (
- "io/ioutil"
- "testing"
-
- "gvisor.dev/gvisor/runsc/config"
-)
-
-func TestNotFound(t *testing.T) {
- ids := []string{"123"}
- dir, err := ioutil.TempDir("", "metadata")
- if err != nil {
- t.Fatalf("error creating dir: %v", err)
- }
- conf := &config.Config{RootDir: dir}
-
- d := Delete{}
- if err := d.execute(ids, conf); err == nil {
- t.Error("Deleting non-existent container should have failed")
- }
-
- d = Delete{force: true}
- if err := d.execute(ids, conf); err != nil {
- t.Errorf("Deleting non-existent container with --force should NOT have failed: %v", err)
- }
-}
diff --git a/runsc/cmd/exec_test.go b/runsc/cmd/exec_test.go
deleted file mode 100644
index a1e980d08..000000000
--- a/runsc/cmd/exec_test.go
+++ /dev/null
@@ -1,154 +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 cmd
-
-import (
- "os"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/control"
- "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/urpc"
-)
-
-func TestUser(t *testing.T) {
- testCases := []struct {
- input string
- want user
- wantErr bool
- }{
- {input: "0", want: user{kuid: 0, kgid: 0}},
- {input: "7", want: user{kuid: 7, kgid: 0}},
- {input: "49:343", want: user{kuid: 49, kgid: 343}},
- {input: "0:2401", want: user{kuid: 0, kgid: 2401}},
- {input: "", wantErr: true},
- {input: "foo", wantErr: true},
- {input: ":123", wantErr: true},
- {input: "1:2:3", wantErr: true},
- }
-
- for _, tc := range testCases {
- var u user
- if err := u.Set(tc.input); err != nil && tc.wantErr {
- // We got an error and wanted one.
- continue
- } else if err == nil && tc.wantErr {
- t.Errorf("user.Set(%s): got no error, but wanted one", tc.input)
- } else if err != nil && !tc.wantErr {
- t.Errorf("user.Set(%s): got error %v, but wanted none", tc.input, err)
- } else if u != tc.want {
- t.Errorf("user.Set(%s): got %+v, but wanted %+v", tc.input, u, tc.want)
- }
- }
-}
-
-func TestCLIArgs(t *testing.T) {
- testCases := []struct {
- ex Exec
- argv []string
- expected control.ExecArgs
- }{
- {
- ex: Exec{
- cwd: "/foo/bar",
- user: user{kuid: 0, kgid: 0},
- extraKGIDs: []string{"1", "2", "3"},
- caps: []string{"CAP_DAC_OVERRIDE"},
- processPath: "",
- },
- argv: []string{"ls", "/"},
- expected: control.ExecArgs{
- Argv: []string{"ls", "/"},
- WorkingDirectory: "/foo/bar",
- FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}},
- KUID: 0,
- KGID: 0,
- ExtraKGIDs: []auth.KGID{1, 2, 3},
- Capabilities: &auth.TaskCapabilities{
- BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- InheritableCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- PermittedCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- },
- },
- },
- }
-
- for _, tc := range testCases {
- e, err := tc.ex.argsFromCLI(tc.argv, true)
- if err != nil {
- t.Errorf("argsFromCLI(%+v): got error: %+v", tc.ex, err)
- } else if !cmp.Equal(*e, tc.expected, cmpopts.IgnoreUnexported(os.File{})) {
- t.Errorf("argsFromCLI(%+v): got %+v, but expected %+v", tc.ex, *e, tc.expected)
- }
- }
-}
-
-func TestJSONArgs(t *testing.T) {
- testCases := []struct {
- // ex is provided to make sure it is overridden by p.
- ex Exec
- p specs.Process
- expected control.ExecArgs
- }{
- {
- ex: Exec{
- cwd: "/baz/quux",
- user: user{kuid: 1, kgid: 1},
- extraKGIDs: []string{"4", "5", "6"},
- caps: []string{"CAP_SETGID"},
- processPath: "/bin/foo",
- },
- p: specs.Process{
- User: specs.User{UID: 0, GID: 0, AdditionalGids: []uint32{1, 2, 3}},
- Args: []string{"ls", "/"},
- Cwd: "/foo/bar",
- Capabilities: &specs.LinuxCapabilities{
- Bounding: []string{"CAP_DAC_OVERRIDE"},
- Effective: []string{"CAP_DAC_OVERRIDE"},
- Inheritable: []string{"CAP_DAC_OVERRIDE"},
- Permitted: []string{"CAP_DAC_OVERRIDE"},
- },
- },
- expected: control.ExecArgs{
- Argv: []string{"ls", "/"},
- WorkingDirectory: "/foo/bar",
- FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}},
- KUID: 0,
- KGID: 0,
- ExtraKGIDs: []auth.KGID{1, 2, 3},
- Capabilities: &auth.TaskCapabilities{
- BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- InheritableCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- PermittedCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- },
- },
- },
- }
-
- for _, tc := range testCases {
- e, err := argsFromProcess(&tc.p, true)
- if err != nil {
- t.Errorf("argsFromProcess(%+v): got error: %+v", tc.p, err)
- } else if !cmp.Equal(*e, tc.expected, cmpopts.IgnoreUnexported(os.File{})) {
- t.Errorf("argsFromProcess(%+v): got %+v, but expected %+v", tc.p, *e, tc.expected)
- }
- }
-}
diff --git a/runsc/cmd/gofer_test.go b/runsc/cmd/gofer_test.go
deleted file mode 100644
index fea62a4f4..000000000
--- a/runsc/cmd/gofer_test.go
+++ /dev/null
@@ -1,163 +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 cmd
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "testing"
-)
-
-func tmpDir() string {
- if dir, ok := os.LookupEnv("TEST_TMPDIR"); ok {
- return dir
- }
- return "/tmp"
-}
-
-type dir struct {
- rel string
- link string
-}
-
-func construct(root string, dirs []dir) error {
- for _, d := range dirs {
- p := path.Join(root, d.rel)
- if d.link == "" {
- if err := os.MkdirAll(p, 0755); err != nil {
- return fmt.Errorf("error creating dir: %v", err)
- }
- } else {
- if err := os.MkdirAll(path.Dir(p), 0755); err != nil {
- return fmt.Errorf("error creating dir: %v", err)
- }
- if err := os.Symlink(d.link, p); err != nil {
- return fmt.Errorf("error creating symlink: %v", err)
- }
- }
- }
- return nil
-}
-
-func TestResolveSymlinks(t *testing.T) {
- root, err := ioutil.TempDir(tmpDir(), "root")
- if err != nil {
- t.Fatal("ioutil.TempDir() failed:", err)
- }
- dirs := []dir{
- {"dir1/dir11/dir111/dir1111", ""}, // Just a boring dir
- {"dir1/lnk12", "dir11"}, // Link to sibling
- {"dir1/lnk13", "./dir11"}, // Link to sibling through self
- {"dir1/lnk14", "../dir1/dir11"}, // Link to sibling through parent
- {"dir1/dir15/lnk151", ".."}, // Link to parent
- {"dir1/lnk16", "dir11/dir111"}, // Link to child
- {"dir1/lnk17", "."}, // Link to self
- {"dir1/lnk18", "lnk13"}, // Link to link
- {"lnk2", "dir1/lnk13"}, // Link to link to link
- {"dir3/dir21/lnk211", "../.."}, // Link to root relative
- {"dir3/lnk22", "/"}, // Link to root absolute
- {"dir3/lnk23", "/dir1"}, // Link to dir absolute
- {"dir3/lnk24", "/dir1/lnk12"}, // Link to link absolute
- {"lnk5", "../../.."}, // Link outside root
- }
- if err := construct(root, dirs); err != nil {
- t.Fatal("construct failed:", err)
- }
-
- tests := []struct {
- name string
- rel string
- want string
- compareHost bool
- }{
- {name: "root", rel: "/", want: "/", compareHost: true},
- {name: "basic dir", rel: "/dir1/dir11/dir111", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "dot 1", rel: "/dir1/dir11/./dir111", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "dot 2", rel: "/dir1/././dir11/./././././dir111/.", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "dotdot 1", rel: "/dir1/dir11/../dir15", want: "/dir1/dir15", compareHost: true},
- {name: "dotdot 2", rel: "/dir1/dir11/dir1111/../..", want: "/dir1", compareHost: true},
-
- {name: "link sibling", rel: "/dir1/lnk12", want: "/dir1/dir11", compareHost: true},
- {name: "link sibling + dir", rel: "/dir1/lnk12/dir111", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "link sibling through self", rel: "/dir1/lnk13", want: "/dir1/dir11", compareHost: true},
- {name: "link sibling through parent", rel: "/dir1/lnk14", want: "/dir1/dir11", compareHost: true},
-
- {name: "link parent", rel: "/dir1/dir15/lnk151", want: "/dir1", compareHost: true},
- {name: "link parent + dir", rel: "/dir1/dir15/lnk151/dir11", want: "/dir1/dir11", compareHost: true},
- {name: "link child", rel: "/dir1/lnk16", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "link child + dir", rel: "/dir1/lnk16/dir1111", want: "/dir1/dir11/dir111/dir1111", compareHost: true},
- {name: "link self", rel: "/dir1/lnk17", want: "/dir1", compareHost: true},
- {name: "link self + dir", rel: "/dir1/lnk17/dir11", want: "/dir1/dir11", compareHost: true},
-
- {name: "link^2", rel: "/dir1/lnk18", want: "/dir1/dir11", compareHost: true},
- {name: "link^2 + dir", rel: "/dir1/lnk18/dir111", want: "/dir1/dir11/dir111", compareHost: true},
- {name: "link^3", rel: "/lnk2", want: "/dir1/dir11", compareHost: true},
- {name: "link^3 + dir", rel: "/lnk2/dir111", want: "/dir1/dir11/dir111", compareHost: true},
-
- {name: "link abs", rel: "/dir3/lnk23", want: "/dir1"},
- {name: "link abs + dir", rel: "/dir3/lnk23/dir11", want: "/dir1/dir11"},
- {name: "link^2 abs", rel: "/dir3/lnk24", want: "/dir1/dir11"},
- {name: "link^2 abs + dir", rel: "/dir3/lnk24/dir111", want: "/dir1/dir11/dir111"},
-
- {name: "root link rel", rel: "/dir3/dir21/lnk211", want: "/", compareHost: true},
- {name: "root link abs", rel: "/dir3/lnk22", want: "/"},
- {name: "root contain link", rel: "/lnk5/dir1", want: "/dir1"},
- {name: "root contain dotdot", rel: "/dir1/dir11/../../../../../../../..", want: "/"},
-
- {name: "crazy", rel: "/dir3/dir21/lnk211/dir3/lnk22/dir1/dir11/../../lnk5/dir3/../dir3/lnk24/dir111/dir1111/..", want: "/dir1/dir11/dir111"},
- }
- for _, tst := range tests {
- t.Run(tst.name, func(t *testing.T) {
- got, err := resolveSymlinks(root, tst.rel)
- if err != nil {
- t.Errorf("resolveSymlinks(root, %q) failed: %v", tst.rel, err)
- }
- want := path.Join(root, tst.want)
- if got != want {
- t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, got, want)
- }
- if tst.compareHost {
- // Check that host got to the same end result.
- host, err := filepath.EvalSymlinks(path.Join(root, tst.rel))
- if err != nil {
- t.Errorf("path.EvalSymlinks(root, %q) failed: %v", tst.rel, err)
- }
- if host != got {
- t.Errorf("resolveSymlinks(root, %q) got: %q, want: %q", tst.rel, host, got)
- }
- }
- })
- }
-}
-
-func TestResolveSymlinksLoop(t *testing.T) {
- root, err := ioutil.TempDir(tmpDir(), "root")
- if err != nil {
- t.Fatal("ioutil.TempDir() failed:", err)
- }
- dirs := []dir{
- {"loop1", "loop2"},
- {"loop2", "loop1"},
- }
- if err := construct(root, dirs); err != nil {
- t.Fatal("construct failed:", err)
- }
- if _, err := resolveSymlinks(root, "loop1"); err == nil {
- t.Errorf("resolveSymlinks() should have failed")
- }
-}
diff --git a/runsc/config/BUILD b/runsc/config/BUILD
deleted file mode 100644
index b1672bb9d..000000000
--- a/runsc/config/BUILD
+++ /dev/null
@@ -1,28 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "config",
- srcs = [
- "config.go",
- "flags.go",
- ],
- visibility = ["//:sandbox"],
- deps = [
- "//pkg/refs",
- "//pkg/sentry/watchdog",
- "//pkg/sync",
- "//runsc/flag",
- ],
-)
-
-go_test(
- name = "config_test",
- size = "small",
- srcs = [
- "config_test.go",
- ],
- library = ":config",
- deps = ["//runsc/flag"],
-)
diff --git a/runsc/config/config_state_autogen.go b/runsc/config/config_state_autogen.go
new file mode 100644
index 000000000..92fa90265
--- /dev/null
+++ b/runsc/config/config_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package config
diff --git a/runsc/config/config_test.go b/runsc/config/config_test.go
deleted file mode 100644
index fb162b7eb..000000000
--- a/runsc/config/config_test.go
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright 2020 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 config
-
-import (
- "strings"
- "testing"
-
- "gvisor.dev/gvisor/runsc/flag"
-)
-
-func init() {
- RegisterFlags()
-}
-
-func TestDefault(t *testing.T) {
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- // "--root" is always set to something different than the default. Reset it
- // to make it easier to test that default values do not generate flags.
- c.RootDir = ""
-
- // All defaults doesn't require setting flags.
- flags := c.ToFlags()
- if len(flags) > 0 {
- t.Errorf("default flags not set correctly for: %s", flags)
- }
-}
-
-func setDefault(name string) {
- fl := flag.CommandLine.Lookup(name)
- fl.Value.Set(fl.DefValue)
-}
-
-func TestFromFlags(t *testing.T) {
- flag.CommandLine.Lookup("root").Value.Set("some-path")
- flag.CommandLine.Lookup("debug").Value.Set("true")
- flag.CommandLine.Lookup("num-network-channels").Value.Set("123")
- flag.CommandLine.Lookup("network").Value.Set("none")
- defer func() {
- setDefault("root")
- setDefault("debug")
- setDefault("num-network-channels")
- setDefault("network")
- }()
-
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- if want := "some-path"; c.RootDir != want {
- t.Errorf("RootDir=%v, want: %v", c.RootDir, want)
- }
- if want := true; c.Debug != want {
- t.Errorf("Debug=%v, want: %v", c.Debug, want)
- }
- if want := 123; c.NumNetworkChannels != want {
- t.Errorf("NumNetworkChannels=%v, want: %v", c.NumNetworkChannels, want)
- }
- if want := NetworkNone; c.Network != want {
- t.Errorf("Network=%v, want: %v", c.Network, want)
- }
-}
-
-func TestToFlags(t *testing.T) {
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- c.RootDir = "some-path"
- c.Debug = true
- c.NumNetworkChannels = 123
- c.Network = NetworkNone
-
- flags := c.ToFlags()
- if len(flags) != 4 {
- t.Errorf("wrong number of flags set, want: 4, got: %d: %s", len(flags), flags)
- }
- t.Logf("Flags: %s", flags)
- fm := map[string]string{}
- for _, f := range flags {
- kv := strings.Split(f, "=")
- fm[kv[0]] = kv[1]
- }
- for name, want := range map[string]string{
- "--root": "some-path",
- "--debug": "true",
- "--num-network-channels": "123",
- "--network": "none",
- } {
- if got, ok := fm[name]; ok {
- if got != want {
- t.Errorf("flag %q, want: %q, got: %q", name, want, got)
- }
- } else {
- t.Errorf("flag %q not set", name)
- }
- }
-}
-
-// TestInvalidFlags checks that enum flags fail when value is not in enum set.
-func TestInvalidFlags(t *testing.T) {
- for _, tc := range []struct {
- name string
- error string
- }{
- {
- name: "file-access",
- error: "invalid file access type",
- },
- {
- name: "network",
- error: "invalid network type",
- },
- {
- name: "qdisc",
- error: "invalid qdisc",
- },
- {
- name: "watchdog-action",
- error: "invalid watchdog action",
- },
- {
- name: "ref-leak-mode",
- error: "invalid ref leak mode",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- defer setDefault(tc.name)
- if err := flag.CommandLine.Lookup(tc.name).Value.Set("invalid"); err == nil || !strings.Contains(err.Error(), tc.error) {
- t.Errorf("flag.Value.Set(invalid) wrong error reported: %v", err)
- }
- })
- }
-}
-
-func TestValidationFail(t *testing.T) {
- for _, tc := range []struct {
- name string
- flags map[string]string
- error string
- }{
- {
- name: "shared+overlay",
- flags: map[string]string{
- "file-access": "shared",
- "overlay": "true",
- },
- error: "overlay flag is incompatible",
- },
- {
- name: "network-channels",
- flags: map[string]string{
- "num-network-channels": "-1",
- },
- error: "num_network_channels must be > 0",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- for name, val := range tc.flags {
- defer setDefault(name)
- if err := flag.CommandLine.Lookup(name).Value.Set(val); err != nil {
- t.Errorf("%s=%q: %v", name, val, err)
- }
- }
- if _, err := NewFromFlags(); err == nil || !strings.Contains(err.Error(), tc.error) {
- t.Errorf("NewFromFlags() wrong error reported: %v", err)
- }
- })
- }
-}
-
-func TestOverride(t *testing.T) {
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- c.AllowFlagOverride = true
-
- t.Run("string", func(t *testing.T) {
- c.RootDir = "foobar"
- if err := c.Override("root", "bar"); err != nil {
- t.Fatalf("Override(root, bar) failed: %v", err)
- }
- defer setDefault("root")
- if c.RootDir != "bar" {
- t.Errorf("Override(root, bar) didn't work: %+v", c)
- }
- })
-
- t.Run("bool", func(t *testing.T) {
- c.Debug = true
- if err := c.Override("debug", "false"); err != nil {
- t.Fatalf("Override(debug, false) failed: %v", err)
- }
- defer setDefault("debug")
- if c.Debug {
- t.Errorf("Override(debug, false) didn't work: %+v", c)
- }
- })
-
- t.Run("enum", func(t *testing.T) {
- c.FileAccess = FileAccessShared
- if err := c.Override("file-access", "exclusive"); err != nil {
- t.Fatalf("Override(file-access, exclusive) failed: %v", err)
- }
- defer setDefault("file-access")
- if c.FileAccess != FileAccessExclusive {
- t.Errorf("Override(file-access, exclusive) didn't work: %+v", c)
- }
- })
-}
-
-func TestOverrideDisabled(t *testing.T) {
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- const errMsg = "flag override disabled"
- if err := c.Override("root", "path"); err == nil || !strings.Contains(err.Error(), errMsg) {
- t.Errorf("Override() wrong error: %v", err)
- }
-}
-
-func TestOverrideError(t *testing.T) {
- c, err := NewFromFlags()
- if err != nil {
- t.Fatal(err)
- }
- c.AllowFlagOverride = true
- for _, tc := range []struct {
- name string
- value string
- error string
- }{
- {
- name: "invalid",
- value: "valid",
- error: `flag "invalid" not found`,
- },
- {
- name: "debug",
- value: "invalid",
- error: "error setting flag debug",
- },
- {
- name: "file-access",
- value: "invalid",
- error: "invalid file access type",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- if err := c.Override(tc.name, tc.value); err == nil || !strings.Contains(err.Error(), tc.error) {
- t.Errorf("Override(%q, %q) wrong error: %v", tc.name, tc.value, err)
- }
- })
- }
-}
diff --git a/runsc/console/BUILD b/runsc/console/BUILD
deleted file mode 100644
index 06924bccd..000000000
--- a/runsc/console/BUILD
+++ /dev/null
@@ -1,17 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "console",
- srcs = [
- "console.go",
- ],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "@com_github_kr_pty//:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/console/console_state_autogen.go b/runsc/console/console_state_autogen.go
new file mode 100644
index 000000000..80521cdb7
--- /dev/null
+++ b/runsc/console/console_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package console
diff --git a/runsc/container/BUILD b/runsc/container/BUILD
deleted file mode 100644
index 8793c8916..000000000
--- a/runsc/container/BUILD
+++ /dev/null
@@ -1,77 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test", "more_shards")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "container",
- srcs = [
- "container.go",
- "hook.go",
- "state_file.go",
- "status.go",
- ],
- visibility = [
- "//runsc:__subpackages__",
- "//test:__subpackages__",
- ],
- deps = [
- "//pkg/abi/linux",
- "//pkg/cleanup",
- "//pkg/log",
- "//pkg/sentry/control",
- "//pkg/sentry/sighandling",
- "//pkg/sync",
- "//runsc/boot",
- "//runsc/cgroup",
- "//runsc/config",
- "//runsc/console",
- "//runsc/sandbox",
- "//runsc/specutils",
- "@com_github_cenkalti_backoff//:go_default_library",
- "@com_github_gofrs_flock//:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- ],
-)
-
-go_test(
- name = "container_test",
- size = "large",
- srcs = [
- "console_test.go",
- "container_norace_test.go",
- "container_race_test.go",
- "container_test.go",
- "multi_container_test.go",
- "shared_volume_test.go",
- ],
- data = [
- "//runsc",
- "//test/cmd/test_app",
- ],
- library = ":container",
- shard_count = more_shards,
- tags = [
- "requires-kvm",
- ],
- deps = [
- "//pkg/abi/linux",
- "//pkg/bits",
- "//pkg/cleanup",
- "//pkg/log",
- "//pkg/sentry/control",
- "//pkg/sentry/kernel",
- "//pkg/sentry/kernel/auth",
- "//pkg/sync",
- "//pkg/test/testutil",
- "//pkg/unet",
- "//pkg/urpc",
- "//runsc/boot",
- "//runsc/boot/platforms",
- "//runsc/config",
- "//runsc/specutils",
- "@com_github_cenkalti_backoff//:go_default_library",
- "@com_github_kr_pty//:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go
deleted file mode 100644
index 7a3d5a523..000000000
--- a/runsc/container/console_test.go
+++ /dev/null
@@ -1,658 +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 container
-
-import (
- "bytes"
- "fmt"
- "io"
- "math/rand"
- "os"
- "path/filepath"
- "syscall"
- "testing"
- "time"
-
- "github.com/kr/pty"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/sentry/control"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/pkg/unet"
- "gvisor.dev/gvisor/pkg/urpc"
-)
-
-// socketPath creates a path inside bundleDir and ensures that the returned
-// path is under 108 charactors (the unix socket path length limit),
-// relativizing the path if necessary.
-func socketPath(bundleDir string) (string, error) {
- num := rand.Intn(10000)
- path := filepath.Join(bundleDir, fmt.Sprintf("socket-%4d", num))
- const maxPathLen = 108
- if len(path) <= maxPathLen {
- return path, nil
- }
-
- // Path is too large, try to make it smaller.
- cwd, err := os.Getwd()
- if err != nil {
- return "", fmt.Errorf("error getting cwd: %v", err)
- }
- path, err = filepath.Rel(cwd, path)
- if err != nil {
- return "", fmt.Errorf("error getting relative path for %q from cwd %q: %v", path, cwd, err)
- }
- if len(path) > maxPathLen {
- return "", fmt.Errorf("could not get socket path under length limit %d: %s", maxPathLen, path)
- }
- return path, nil
-}
-
-// createConsoleSocket creates a socket at the given path that will receive a
-// console fd from the sandbox. If an error occurs, t.Fatalf will be called.
-// The function returning should be deferred as cleanup.
-func createConsoleSocket(t *testing.T, path string) (*unet.ServerSocket, func()) {
- t.Helper()
- srv, err := unet.BindAndListen(path, false)
- if err != nil {
- t.Fatalf("error binding and listening to socket %q: %v", path, err)
- }
-
- cleanup := func() {
- // Log errors; nothing can be done.
- if err := srv.Close(); err != nil {
- t.Logf("error closing socket %q: %v", path, err)
- }
- if err := os.Remove(path); err != nil {
- t.Logf("error removing socket %q: %v", path, err)
- }
- }
-
- return srv, cleanup
-}
-
-// receiveConsolePTY accepts a connection on the server socket and reads fds.
-// It fails if more than one FD is received, or if the FD is not a PTY. It
-// returns the PTY master file.
-func receiveConsolePTY(srv *unet.ServerSocket) (*os.File, error) {
- sock, err := srv.Accept()
- if err != nil {
- return nil, fmt.Errorf("error accepting socket connection: %v", err)
- }
-
- // Allow 3 fds to be received. We only expect 1.
- r := sock.Reader(true /* blocking */)
- r.EnableFDs(1)
-
- // The socket is closed right after sending the FD, so EOF is
- // an allowed error.
- b := [][]byte{{}}
- if _, err := r.ReadVec(b); err != nil && err != io.EOF {
- return nil, fmt.Errorf("error reading from socket connection: %v", err)
- }
-
- // We should have gotten a control message.
- fds, err := r.ExtractFDs()
- if err != nil {
- return nil, fmt.Errorf("error extracting fds from socket connection: %v", err)
- }
- if len(fds) != 1 {
- return nil, fmt.Errorf("got %d fds from socket, wanted 1", len(fds))
- }
-
- // Verify that the fd is a terminal.
- if _, err := unix.IoctlGetTermios(fds[0], unix.TCGETS); err != nil {
- return nil, fmt.Errorf("fd is not a terminal (ioctl TGGETS got %v)", err)
- }
-
- return os.NewFile(uintptr(fds[0]), "pty_master"), nil
-}
-
-// Test that an pty FD is sent over the console socket if one is provided.
-func TestConsoleSocket(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- spec := testutil.NewSpecWithArgs("true")
- spec.Process.Terminal = true
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- sock, err := socketPath(bundleDir)
- if err != nil {
- t.Fatalf("error getting socket path: %v", err)
- }
- srv, cleanup := createConsoleSocket(t, sock)
- defer cleanup()
-
- // Create the container and pass the socket name.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- ConsoleSocket: sock,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
-
- // Make sure we get a console PTY.
- ptyMaster, err := receiveConsolePTY(srv)
- if err != nil {
- t.Fatalf("error receiving console FD: %v", err)
- }
- ptyMaster.Close()
- })
- }
-}
-
-// Test that an pty FD is sent over the console socket if one is provided.
-func TestMultiContainerConsoleSocket(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- tru := []string{"true"}
- testSpecs, ids := createSpecs(sleep, tru)
- testSpecs[1].Process.Terminal = true
-
- bundleDir, cleanup, err := testutil.SetupBundleDir(testSpecs[0])
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: ids[0],
- Spec: testSpecs[0],
- BundleDir: bundleDir,
- }
- rootCont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer rootCont.Destroy()
- if err := rootCont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- bundleDir, cleanup, err = testutil.SetupBundleDir(testSpecs[0])
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- sock, err := socketPath(bundleDir)
- if err != nil {
- t.Fatalf("error getting socket path: %v", err)
- }
- srv, cleanup := createConsoleSocket(t, sock)
- defer cleanup()
-
- // Create the container and pass the socket name.
- args = Args{
- ID: ids[1],
- Spec: testSpecs[1],
- BundleDir: bundleDir,
- ConsoleSocket: sock,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Make sure we get a console PTY.
- ptyMaster, err := receiveConsolePTY(srv)
- if err != nil {
- t.Fatalf("error receiving console FD: %v", err)
- }
- ptyMaster.Close()
- })
- }
-}
-
-// Test that job control signals work on a console created with "exec -ti".
-func TestJobControlSignalExec(t *testing.T) {
- spec := testutil.NewSpecWithArgs("/bin/sleep", "10000")
- conf := testutil.TestConfig(t)
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Create a pty master/replica. The replica will be passed to the exec
- // process.
- ptyMaster, ptyReplica, err := pty.Open()
- if err != nil {
- t.Fatalf("error opening pty: %v", err)
- }
- defer ptyMaster.Close()
- defer ptyReplica.Close()
-
- // Exec bash and attach a terminal. Note that occasionally /bin/sh
- // may be a different shell or have a different configuration (such
- // as disabling interactive mode and job control). Since we want to
- // explicitly test interactive mode, use /bin/bash. See b/116981926.
- execArgs := &control.ExecArgs{
- Filename: "/bin/bash",
- // Don't let bash execute from profile or rc files, otherwise
- // our PID counts get messed up.
- Argv: []string{"/bin/bash", "--noprofile", "--norc"},
- // Pass the pty replica as FD 0, 1, and 2.
- FilePayload: urpc.FilePayload{
- Files: []*os.File{ptyReplica, ptyReplica, ptyReplica},
- },
- StdioIsPty: true,
- }
-
- pid, err := c.Execute(execArgs)
- if err != nil {
- t.Fatalf("error executing: %v", err)
- }
- if pid != 2 {
- t.Fatalf("exec got pid %d, wanted %d", pid, 2)
- }
-
- // Make sure all the processes are running.
- expectedPL := []*control.Process{
- // Root container process.
- newProcessBuilder().Cmd("sleep").Process(),
- // Bash from exec process.
- newProcessBuilder().PID(2).Cmd("bash").Process(),
- }
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Execute sleep.
- ptyMaster.Write([]byte("sleep 100\n"))
-
- // Wait for it to start. Sleep's PPID is bash's PID.
- expectedPL = append(expectedPL, newProcessBuilder().PID(3).PPID(2).Cmd("sleep").Process())
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Send a SIGTERM to the foreground process for the exec PID. Note that
- // although we pass in the PID of "bash", it should actually terminate
- // "sleep", since that is the foreground process.
- if err := c.Sandbox.SignalProcess(c.ID, pid, syscall.SIGTERM, true /* fgProcess */); err != nil {
- t.Fatalf("error signaling container: %v", err)
- }
-
- // Sleep process should be gone.
- expectedPL = expectedPL[:len(expectedPL)-1]
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Sleep is dead, but it may take more time for bash to notice and
- // change the foreground process back to itself. We know it is done
- // when bash writes "Terminated" to the pty.
- if err := testutil.WaitUntilRead(ptyMaster, "Terminated", 5*time.Second); err != nil {
- t.Fatalf("bash did not take over pty: %v", err)
- }
-
- // Send a SIGKILL to the foreground process again. This time "bash"
- // should be killed. We use SIGKILL instead of SIGTERM or SIGINT
- // because bash ignores those.
- if err := c.Sandbox.SignalProcess(c.ID, pid, syscall.SIGKILL, true /* fgProcess */); err != nil {
- t.Fatalf("error signaling container: %v", err)
- }
- expectedPL = expectedPL[:1]
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Make sure the process indicates it was killed by a SIGKILL.
- ws, err := c.WaitPID(pid)
- if err != nil {
- t.Errorf("waiting on container failed: %v", err)
- }
- if !ws.Signaled() {
- t.Error("ws.Signaled() got false, want true")
- }
- if got, want := ws.Signal(), syscall.SIGKILL; got != want {
- t.Errorf("ws.Signal() got %v, want %v", got, want)
- }
-}
-
-// Test that job control signals work on a console created with "run -ti".
-func TestJobControlSignalRootContainer(t *testing.T) {
- conf := testutil.TestConfig(t)
- // Don't let bash execute from profile or rc files, otherwise our PID
- // counts get messed up.
- spec := testutil.NewSpecWithArgs("/bin/bash", "--noprofile", "--norc")
- spec.Process.Terminal = true
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- sock, err := socketPath(bundleDir)
- if err != nil {
- t.Fatalf("error getting socket path: %v", err)
- }
- srv, cleanup := createConsoleSocket(t, sock)
- defer cleanup()
-
- // Create the container and pass the socket name.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- ConsoleSocket: sock,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
-
- // Get the PTY master.
- ptyMaster, err := receiveConsolePTY(srv)
- if err != nil {
- t.Fatalf("error receiving console FD: %v", err)
- }
- defer ptyMaster.Close()
-
- // Bash output as well as sandbox output will be written to the PTY
- // file. Writes after a certain point will block unless we drain the
- // PTY, so we must continually copy from it.
- //
- // We log the output to stderr for debugabilitly, and also to a buffer,
- // since we wait on particular output from bash below. We use a custom
- // blockingBuffer which is thread-safe and also blocks on Read calls,
- // which makes this a suitable Reader for WaitUntilRead.
- ptyBuf := newBlockingBuffer()
- tee := io.TeeReader(ptyMaster, ptyBuf)
- go io.Copy(os.Stderr, tee)
-
- // Start the container.
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Start waiting for the container to exit in a goroutine. We do this
- // very early, otherwise it might exit before we have a chance to call
- // Wait.
- var (
- ws syscall.WaitStatus
- wg sync.WaitGroup
- )
- wg.Add(1)
- go func() {
- var err error
- ws, err = c.Wait()
- if err != nil {
- t.Errorf("error waiting on container: %v", err)
- }
- wg.Done()
- }()
-
- // Wait for bash to start.
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).Cmd("bash").Process(),
- }
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- // Execute sleep via the terminal.
- ptyMaster.Write([]byte("sleep 100\n"))
-
- // Wait for sleep to start.
- expectedPL = append(expectedPL, newProcessBuilder().PID(2).PPID(1).Cmd("sleep").Process())
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- // Reset the pty buffer, so there is less output for us to scan later.
- ptyBuf.Reset()
-
- // Send a SIGTERM to the foreground process. We pass PID=0, indicating
- // that the root process should be killed. However, by setting
- // fgProcess=true, the signal should actually be sent to sleep.
- if err := c.Sandbox.SignalProcess(c.ID, 0 /* PID */, syscall.SIGTERM, true /* fgProcess */); err != nil {
- t.Fatalf("error signaling container: %v", err)
- }
-
- // Sleep process should be gone.
- expectedPL = expectedPL[:len(expectedPL)-1]
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Sleep is dead, but it may take more time for bash to notice and
- // change the foreground process back to itself. We know it is done
- // when bash writes "Terminated" to the pty.
- if err := testutil.WaitUntilRead(ptyBuf, "Terminated", 5*time.Second); err != nil {
- t.Fatalf("bash did not take over pty: %v", err)
- }
-
- // Send a SIGKILL to the foreground process again. This time "bash"
- // should be killed. We use SIGKILL instead of SIGTERM or SIGINT
- // because bash ignores those.
- if err := c.Sandbox.SignalProcess(c.ID, 0 /* PID */, syscall.SIGKILL, true /* fgProcess */); err != nil {
- t.Fatalf("error signaling container: %v", err)
- }
-
- // Wait for the sandbox to exit. It should exit with a SIGKILL status.
- wg.Wait()
- if !ws.Signaled() {
- t.Error("ws.Signaled() got false, want true")
- }
- if got, want := ws.Signal(), syscall.SIGKILL; got != want {
- t.Errorf("ws.Signal() got %v, want %v", got, want)
- }
-}
-
-// Test that terminal works with root and sub-containers.
-func TestMultiContainerTerminal(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Don't let bash execute from profile or rc files, otherwise our PID
- // counts get messed up.
- bash := []string{"/bin/bash", "--noprofile", "--norc"}
- testSpecs, ids := createSpecs(bash, bash)
-
- type termContainer struct {
- container *Container
- master *os.File
- }
- var containers []termContainer
- for i, spec := range testSpecs {
- bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- spec.Process.Terminal = true
- sock, err := socketPath(bundleDir)
- if err != nil {
- t.Fatalf("error getting socket path: %v", err)
- }
- srv, cleanup := createConsoleSocket(t, sock)
- defer cleanup()
-
- // Create the container and pass the socket name.
- args := Args{
- ID: ids[i],
- Spec: spec,
- BundleDir: bundleDir,
- ConsoleSocket: sock,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
-
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Make sure we get a console PTY.
- ptyMaster, err := receiveConsolePTY(srv)
- if err != nil {
- t.Fatalf("error receiving console FD: %v", err)
- }
- defer ptyMaster.Close()
-
- containers = append(containers, termContainer{
- container: cont,
- master: ptyMaster,
- })
- }
-
- for _, tc := range containers {
- // Bash output as well as sandbox output will be written to the PTY
- // file. Writes after a certain point will block unless we drain the
- // PTY, so we must continually copy from it.
- //
- // We log the output to stderr for debugabilitly, and also to a buffer,
- // since we wait on particular output from bash below. We use a custom
- // blockingBuffer which is thread-safe and also blocks on Read calls,
- // which makes this a suitable Reader for WaitUntilRead.
- ptyBuf := newBlockingBuffer()
- tee := io.TeeReader(tc.master, ptyBuf)
- go io.Copy(os.Stderr, tee)
-
- // Wait for bash to start.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("bash").Process(),
- }
- if err := waitForProcessList(tc.container, expectedPL); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- // Execute echo command and check that it was executed correctly. Use
- // a variable to ensure it's not matching against command echo.
- tc.master.Write([]byte("echo foo-${PWD}-123\n"))
- if err := testutil.WaitUntilRead(ptyBuf, "foo-/-123", 5*time.Second); err != nil {
- t.Fatalf("echo didn't execute: %v", err)
- }
- }
- })
- }
-}
-
-// blockingBuffer is a thread-safe buffer that blocks when reading if the
-// buffer is empty. It implements io.ReadWriter.
-type blockingBuffer struct {
- // A send to readCh indicates that a previously empty buffer now has
- // data for reading.
- readCh chan struct{}
-
- // mu protects buf.
- mu sync.Mutex
- buf bytes.Buffer
-}
-
-func newBlockingBuffer() *blockingBuffer {
- return &blockingBuffer{
- readCh: make(chan struct{}, 1),
- }
-}
-
-// Write implements Writer.Write.
-func (bb *blockingBuffer) Write(p []byte) (int, error) {
- bb.mu.Lock()
- defer bb.mu.Unlock()
- l := bb.buf.Len()
- n, err := bb.buf.Write(p)
- if l == 0 && n > 0 {
- // New data!
- bb.readCh <- struct{}{}
- }
- return n, err
-}
-
-// Read implements Reader.Read. It will block until data is available.
-func (bb *blockingBuffer) Read(p []byte) (int, error) {
- for {
- bb.mu.Lock()
- n, err := bb.buf.Read(p)
- if n > 0 || err != io.EOF {
- if bb.buf.Len() == 0 {
- // Reset the readCh.
- select {
- case <-bb.readCh:
- default:
- }
- }
- bb.mu.Unlock()
- return n, err
- }
- bb.mu.Unlock()
-
- // Wait for new data.
- <-bb.readCh
- }
-}
-
-// Reset resets the buffer.
-func (bb *blockingBuffer) Reset() {
- bb.mu.Lock()
- defer bb.mu.Unlock()
- bb.buf.Reset()
- // Reset the readCh.
- select {
- case <-bb.readCh:
- default:
- }
-}
diff --git a/runsc/container/container_norace_test.go b/runsc/container/container_norace_test.go
deleted file mode 100644
index 838c1e20a..000000000
--- a/runsc/container/container_norace_test.go
+++ /dev/null
@@ -1,20 +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.
-
-// +build !race
-
-package container
-
-// Allow both kvm and ptrace for non-race builds.
-var platformOptions = []configOption{ptrace, kvm}
diff --git a/runsc/container/container_race_test.go b/runsc/container/container_race_test.go
deleted file mode 100644
index 9fb4c4fc0..000000000
--- a/runsc/container/container_race_test.go
+++ /dev/null
@@ -1,20 +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.
-
-// +build race
-
-package container
-
-// Only enabled ptrace with race builds.
-var platformOptions = []configOption{ptrace}
diff --git a/runsc/container/container_state_autogen.go b/runsc/container/container_state_autogen.go
new file mode 100644
index 000000000..5bc1c1aff
--- /dev/null
+++ b/runsc/container/container_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package container
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
deleted file mode 100644
index d50bbcd9f..000000000
--- a/runsc/container/container_test.go
+++ /dev/null
@@ -1,2447 +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 container
-
-import (
- "bytes"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "math"
- "os"
- "path"
- "path/filepath"
- "reflect"
- "strconv"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "github.com/cenkalti/backoff"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/bits"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/sentry/control"
- "gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/pkg/urpc"
- "gvisor.dev/gvisor/runsc/boot/platforms"
- "gvisor.dev/gvisor/runsc/config"
- "gvisor.dev/gvisor/runsc/specutils"
-)
-
-// waitForProcessList waits for the given process list to show up in the container.
-func waitForProcessList(cont *Container, want []*control.Process) error {
- cb := func() error {
- got, err := cont.Processes()
- if err != nil {
- err = fmt.Errorf("error getting process data from container: %w", err)
- return &backoff.PermanentError{Err: err}
- }
- if !procListsEqual(got, want) {
- return fmt.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(want))
- }
- return nil
- }
- // Gives plenty of time as tests can run slow under --race.
- return testutil.Poll(cb, 30*time.Second)
-}
-
-// waitForProcess waits for the given process to show up in the container.
-func waitForProcess(cont *Container, want *control.Process) error {
- cb := func() error {
- gots, err := cont.Processes()
- if err != nil {
- err = fmt.Errorf("error getting process data from container: %w", err)
- return &backoff.PermanentError{Err: err}
- }
- for _, got := range gots {
- if procEqual(got, want) {
- return nil
- }
- }
- return fmt.Errorf("container got process list: %s, want: %+v", procListToString(gots), want)
- }
- // Gives plenty of time as tests can run slow under --race.
- return testutil.Poll(cb, 30*time.Second)
-}
-
-func waitForProcessCount(cont *Container, want int) error {
- cb := func() error {
- pss, err := cont.Processes()
- if err != nil {
- err = fmt.Errorf("error getting process data from container: %w", err)
- return &backoff.PermanentError{Err: err}
- }
- if got := len(pss); got != want {
- log.Infof("Waiting for process count to reach %d. Current: %d", want, got)
- return fmt.Errorf("wrong process count, got: %d, want: %d", got, want)
- }
- return nil
- }
- // Gives plenty of time as tests can run slow under --race.
- return testutil.Poll(cb, 30*time.Second)
-}
-
-func blockUntilWaitable(pid int) error {
- _, _, err := specutils.RetryEintr(func() (uintptr, uintptr, error) {
- var err error
- _, _, err1 := syscall.Syscall6(syscall.SYS_WAITID, 1, uintptr(pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
- if err1 != 0 {
- err = err1
- }
- return 0, 0, err
- })
- return err
-}
-
-// procListsEqual is used to check whether 2 Process lists are equal. Fields
-// set to -1 in wants are ignored. Timestamp and threads fields are always
-// ignored.
-func procListsEqual(gots, wants []*control.Process) bool {
- if len(gots) != len(wants) {
- return false
- }
- for i := range gots {
- if !procEqual(gots[i], wants[i]) {
- return false
- }
- }
- return true
-}
-
-func procEqual(got, want *control.Process) bool {
- if want.UID != math.MaxUint32 && want.UID != got.UID {
- return false
- }
- if want.PID != -1 && want.PID != got.PID {
- return false
- }
- if want.PPID != -1 && want.PPID != got.PPID {
- return false
- }
- if len(want.TTY) != 0 && want.TTY != got.TTY {
- return false
- }
- if len(want.Cmd) != 0 && want.Cmd != got.Cmd {
- return false
- }
- return true
-}
-
-type processBuilder struct {
- process control.Process
-}
-
-func newProcessBuilder() *processBuilder {
- return &processBuilder{
- process: control.Process{
- UID: math.MaxUint32,
- PID: -1,
- PPID: -1,
- },
- }
-}
-
-func (p *processBuilder) Cmd(cmd string) *processBuilder {
- p.process.Cmd = cmd
- return p
-}
-
-func (p *processBuilder) PID(pid kernel.ThreadID) *processBuilder {
- p.process.PID = pid
- return p
-}
-
-func (p *processBuilder) PPID(ppid kernel.ThreadID) *processBuilder {
- p.process.PPID = ppid
- return p
-}
-
-func (p *processBuilder) UID(uid auth.KUID) *processBuilder {
- p.process.UID = uid
- return p
-}
-
-func (p *processBuilder) Process() *control.Process {
- return &p.process
-}
-
-func procListToString(pl []*control.Process) string {
- strs := make([]string, 0, len(pl))
- for _, p := range pl {
- strs = append(strs, fmt.Sprintf("%+v", p))
- }
- return fmt.Sprintf("[%s]", strings.Join(strs, ","))
-}
-
-// createWriteableOutputFile creates an output file that can be read and
-// written to in the sandbox.
-func createWriteableOutputFile(path string) (*os.File, error) {
- outputFile, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
- if err != nil {
- return nil, fmt.Errorf("error creating file: %q, %v", path, err)
- }
-
- // Chmod to allow writing after umask.
- if err := outputFile.Chmod(0666); err != nil {
- return nil, fmt.Errorf("error chmoding file: %q, %v", path, err)
- }
- return outputFile, nil
-}
-
-func waitForFileNotEmpty(f *os.File) error {
- op := func() error {
- fi, err := f.Stat()
- if err != nil {
- return err
- }
- if fi.Size() == 0 {
- return fmt.Errorf("file %q is empty", f.Name())
- }
- return nil
- }
-
- return testutil.Poll(op, 30*time.Second)
-}
-
-func waitForFileExist(path string) error {
- op := func() error {
- if _, err := os.Stat(path); os.IsNotExist(err) {
- return err
- }
- return nil
- }
-
- return testutil.Poll(op, 30*time.Second)
-}
-
-// readOutputNum reads a file at given filepath and returns the int at the
-// requested position.
-func readOutputNum(file string, position int) (int, error) {
- f, err := os.Open(file)
- if err != nil {
- return 0, fmt.Errorf("error opening file: %q, %v", file, err)
- }
-
- // Ensure that there is content in output file.
- if err := waitForFileNotEmpty(f); err != nil {
- return 0, fmt.Errorf("error waiting for output file: %v", err)
- }
-
- b, err := ioutil.ReadAll(f)
- if err != nil {
- return 0, fmt.Errorf("error reading file: %v", err)
- }
- if len(b) == 0 {
- return 0, fmt.Errorf("error no content was read")
- }
-
- // Strip leading null bytes caused by file offset not being 0 upon restore.
- b = bytes.Trim(b, "\x00")
- nums := strings.Split(string(b), "\n")
-
- if position >= len(nums) {
- return 0, fmt.Errorf("position %v is not within the length of content %v", position, nums)
- }
- if position == -1 {
- // Expectation of newline at the end of last position.
- position = len(nums) - 2
- }
- num, err := strconv.Atoi(nums[position])
- if err != nil {
- return 0, fmt.Errorf("error getting number from file: %v", err)
- }
- return num, nil
-}
-
-// run starts the sandbox and waits for it to exit, checking that the
-// application succeeded.
-func run(spec *specs.Spec, conf *config.Config) error {
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- return fmt.Errorf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create, start and wait for the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- Attached: true,
- }
- ws, err := Run(conf, args)
- if err != nil {
- return fmt.Errorf("running container: %v", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- return fmt.Errorf("container failed, waitStatus: %v", ws)
- }
- return nil
-}
-
-type configOption int
-
-const (
- overlay configOption = iota
- ptrace
- kvm
- nonExclusiveFS
-)
-
-var (
- noOverlay = append(platformOptions, nonExclusiveFS)
- all = append(noOverlay, overlay)
-)
-
-func configsHelper(t *testing.T, opts ...configOption) map[string]*config.Config {
- // Always load the default config.
- cs := make(map[string]*config.Config)
- testutil.TestConfig(t)
- for _, o := range opts {
- c := testutil.TestConfig(t)
- switch o {
- case overlay:
- c.Overlay = true
- cs["overlay"] = c
- case ptrace:
- c.Platform = platforms.Ptrace
- cs["ptrace"] = c
- case kvm:
- c.Platform = platforms.KVM
- cs["kvm"] = c
- case nonExclusiveFS:
- c.FileAccess = config.FileAccessShared
- cs["non-exclusive"] = c
- default:
- panic(fmt.Sprintf("unknown config option %v", o))
- }
- }
- return cs
-}
-
-// configs generates different configurations to run tests.
-//
-// TODO(gvisor.dev/issue/1624): Remove VFS1 dimension.
-func configs(t *testing.T, opts ...configOption) map[string]*config.Config {
- all := configsHelper(t, opts...)
- for key, value := range configsHelper(t, opts...) {
- value.VFS2 = true
- all[key+"VFS2"] = value
- }
- return all
-}
-
-// TestLifecycle tests the basic Create/Start/Signal/Destroy container lifecycle.
-// It verifies after each step that the container can be loaded from disk, and
-// has the correct status.
-func TestLifecycle(t *testing.T) {
- // Start the child reaper.
- childReaper := &testutil.Reaper{}
- childReaper.Start()
- defer childReaper.Stop()
-
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- // The container will just sleep for a long time. We will kill it before
- // it finishes sleeping.
- spec := testutil.NewSpecWithArgs("sleep", "100")
-
- rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // expectedPL lists the expected process state of the container.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").Process(),
- }
- // Create the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
-
- // Load the container from disk and check the status.
- c, err = Load(rootDir, FullID{ContainerID: args.ID}, LoadOpts{})
- if err != nil {
- t.Fatalf("error loading container: %v", err)
- }
- if got, want := c.Status, Created; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // List should return the container id.
- ids, err := List(rootDir)
- if err != nil {
- t.Fatalf("error listing containers: %v", err)
- }
- fullID := FullID{
- SandboxID: args.ID,
- ContainerID: args.ID,
- }
- if got, want := ids, []FullID{fullID}; !reflect.DeepEqual(got, want) {
- t.Errorf("container list got %v, want %v", got, want)
- }
-
- // Start the container.
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Load the container from disk and check the status.
- c, err = Load(rootDir, fullID, LoadOpts{Exact: true})
- if err != nil {
- t.Fatalf("error loading container: %v", err)
- }
- if got, want := c.Status, Running; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Verify that "sleep 100" is running.
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Wait on the container.
- ch := make(chan error)
- go func() {
- ws, err := c.Wait()
- if err != nil {
- ch <- err
- }
- if got, want := ws.Signal(), syscall.SIGTERM; got != want {
- ch <- fmt.Errorf("got signal %v, want %v", got, want)
- }
- ch <- nil
- }()
-
- // Wait a bit to ensure that we've started waiting on
- // the container before we signal.
- time.Sleep(time.Second)
-
- // Send the container a SIGTERM which will cause it to stop.
- if err := c.SignalContainer(syscall.SIGTERM, false); err != nil {
- t.Fatalf("error sending signal %v to container: %v", syscall.SIGTERM, err)
- }
-
- // Wait for it to die.
- if err := <-ch; err != nil {
- t.Fatalf("error waiting for container: %v", err)
- }
-
- // Load the container from disk and check the status.
- c, err = Load(rootDir, fullID, LoadOpts{Exact: true})
- if err != nil {
- t.Fatalf("error loading container: %v", err)
- }
- if got, want := c.Status, Stopped; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Destroy the container.
- if err := c.Destroy(); err != nil {
- t.Fatalf("error destroying container: %v", err)
- }
-
- // List should not return the container id.
- ids, err = List(rootDir)
- if err != nil {
- t.Fatalf("error listing containers: %v", err)
- }
- if len(ids) != 0 {
- t.Errorf("expected container list to be empty, but got %v", ids)
- }
-
- // Loading the container by id should fail.
- if _, err = Load(rootDir, fullID, LoadOpts{Exact: true}); err == nil {
- t.Errorf("expected loading destroyed container to fail, but it did not")
- }
- })
- }
-}
-
-// Test the we can execute the application with different path formats.
-func TestExePath(t *testing.T) {
- // Create two directories that will be prepended to PATH.
- firstPath, err := ioutil.TempDir(testutil.TmpDir(), "first")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(firstPath)
- secondPath, err := ioutil.TempDir(testutil.TmpDir(), "second")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- defer os.RemoveAll(secondPath)
-
- // Create two minimal executables in the second path, two of which
- // will be masked by files in first path.
- for _, p := range []string{"unmasked", "masked1", "masked2"} {
- path := filepath.Join(secondPath, p)
- f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0777)
- if err != nil {
- t.Fatalf("error opening path: %v", err)
- }
- defer f.Close()
- if _, err := io.WriteString(f, "#!/bin/true\n"); err != nil {
- t.Fatalf("error writing contents: %v", err)
- }
- }
-
- // Create a non-executable file in the first path which masks a healthy
- // executable in the second.
- nonExecutable := filepath.Join(firstPath, "masked1")
- f2, err := os.OpenFile(nonExecutable, os.O_CREATE|os.O_EXCL, 0666)
- if err != nil {
- t.Fatalf("error opening file: %v", err)
- }
- f2.Close()
-
- // Create a non-regular file in the first path which masks a healthy
- // executable in the second.
- nonRegular := filepath.Join(firstPath, "masked2")
- if err := os.Mkdir(nonRegular, 0777); err != nil {
- t.Fatalf("error making directory: %v", err)
- }
-
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- for _, test := range []struct {
- path string
- success bool
- }{
- {path: "true", success: true},
- {path: "bin/true", success: true},
- {path: "/bin/true", success: true},
- {path: "thisfiledoesntexit", success: false},
- {path: "bin/thisfiledoesntexit", success: false},
- {path: "/bin/thisfiledoesntexit", success: false},
-
- {path: "unmasked", success: true},
- {path: filepath.Join(firstPath, "unmasked"), success: false},
- {path: filepath.Join(secondPath, "unmasked"), success: true},
-
- {path: "masked1", success: true},
- {path: filepath.Join(firstPath, "masked1"), success: false},
- {path: filepath.Join(secondPath, "masked1"), success: true},
-
- {path: "masked2", success: true},
- {path: filepath.Join(firstPath, "masked2"), success: false},
- {path: filepath.Join(secondPath, "masked2"), success: true},
- } {
- t.Run(fmt.Sprintf("path=%s,success=%t", test.path, test.success), func(t *testing.T) {
- spec := testutil.NewSpecWithArgs(test.path)
- spec.Process.Env = []string{
- fmt.Sprintf("PATH=%s:%s:%s", firstPath, secondPath, os.Getenv("PATH")),
- }
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("exec: error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- Attached: true,
- }
- ws, err := Run(conf, args)
-
- if test.success {
- if err != nil {
- t.Errorf("exec: error running container: %v", err)
- }
- if ws.ExitStatus() != 0 {
- t.Errorf("exec: got exit status %v want %v", ws.ExitStatus(), 0)
- }
- } else {
- if err == nil {
- t.Errorf("exec: got: no error, want: error")
- }
- }
- })
- }
- })
- }
-}
-
-// Test the we can retrieve the application exit status from the container.
-func TestAppExitStatus(t *testing.T) {
- doAppExitStatus(t, false)
-}
-
-// This is TestAppExitStatus for VFSv2.
-func TestAppExitStatusVFS2(t *testing.T) {
- doAppExitStatus(t, true)
-}
-
-func doAppExitStatus(t *testing.T, vfs2 bool) {
- // First container will succeed.
- succSpec := testutil.NewSpecWithArgs("true")
- conf := testutil.TestConfig(t)
- conf.VFS2 = vfs2
- _, bundleDir, cleanup, err := testutil.SetupContainer(succSpec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: succSpec,
- BundleDir: bundleDir,
- Attached: true,
- }
- ws, err := Run(conf, args)
- if err != nil {
- t.Fatalf("error running container: %v", err)
- }
- if ws.ExitStatus() != 0 {
- t.Errorf("got exit status %v want %v", ws.ExitStatus(), 0)
- }
-
- // Second container exits with non-zero status.
- wantStatus := 123
- errSpec := testutil.NewSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus))
-
- _, bundleDir2, cleanup2, err := testutil.SetupContainer(errSpec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup2()
-
- args2 := Args{
- ID: testutil.RandomContainerID(),
- Spec: errSpec,
- BundleDir: bundleDir2,
- Attached: true,
- }
- ws, err = Run(conf, args2)
- if err != nil {
- t.Fatalf("error running container: %v", err)
- }
- if ws.ExitStatus() != wantStatus {
- t.Errorf("got exit status %v want %v", ws.ExitStatus(), wantStatus)
- }
-}
-
-// TestExec verifies that a container can exec a new program.
-func TestExec(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "exec-test")
- if err != nil {
- t.Fatalf("error creating temporary directory: %v", err)
- }
- // Note that some shells may exec the final command in a sequence as
- // an optimization. We avoid this here by adding the exit 0.
- cmd := fmt.Sprintf("ln -s /bin/true %q/symlink && sleep 100 && exit 0", dir)
- spec := testutil.NewSpecWithArgs("sh", "-c", cmd)
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Wait until sleep is running to ensure the symlink was created.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sh").Process(),
- newProcessBuilder().Cmd("sleep").Process(),
- }
- if err := waitForProcessList(cont, expectedPL); err != nil {
- t.Fatalf("waitForProcessList: %v", err)
- }
-
- for _, tc := range []struct {
- name string
- args control.ExecArgs
- }{
- {
- name: "complete",
- args: control.ExecArgs{
- Filename: "/bin/true",
- Argv: []string{"/bin/true"},
- },
- },
- {
- name: "filename",
- args: control.ExecArgs{
- Filename: "/bin/true",
- },
- },
- {
- name: "argv",
- args: control.ExecArgs{
- Argv: []string{"/bin/true"},
- },
- },
- {
- name: "filename resolution",
- args: control.ExecArgs{
- Filename: "true",
- Envv: []string{"PATH=/bin"},
- },
- },
- {
- name: "argv resolution",
- args: control.ExecArgs{
- Argv: []string{"true"},
- Envv: []string{"PATH=/bin"},
- },
- },
- {
- name: "argv symlink",
- args: control.ExecArgs{
- Argv: []string{filepath.Join(dir, "symlink")},
- },
- },
- {
- name: "working dir",
- args: control.ExecArgs{
- Argv: []string{"/bin/sh", "-c", `if [[ "${PWD}" != "/tmp" ]]; then exit 1; fi`},
- WorkingDirectory: "/tmp",
- },
- },
- {
- name: "user",
- args: control.ExecArgs{
- Argv: []string{"/bin/sh", "-c", `if [[ "$(id -u)" != "343" ]]; then exit 1; fi`},
- KUID: 343,
- },
- },
- {
- name: "group",
- args: control.ExecArgs{
- Argv: []string{"/bin/sh", "-c", `if [[ "$(id -g)" != "343" ]]; then exit 1; fi`},
- KGID: 343,
- },
- },
- {
- name: "env",
- args: control.ExecArgs{
- Argv: []string{"/bin/sh", "-c", `if [[ "${FOO}" != "123" ]]; then exit 1; fi`},
- Envv: []string{"FOO=123"},
- },
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- // t.Parallel()
- if ws, err := cont.executeSync(&tc.args); err != nil {
- t.Fatalf("executeAsync(%+v): %v", tc.args, err)
- } else if ws != 0 {
- t.Fatalf("executeAsync(%+v) failed with exit: %v", tc.args, ws)
- }
- })
- }
- })
- }
-}
-
-// TestExecProcList verifies that a container can exec a new program and it
-// shows correcly in the process list.
-func TestExecProcList(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- const uid = 343
- spec := testutil.NewSpecWithArgs("sleep", "100")
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- execArgs := &control.ExecArgs{
- Filename: "/bin/sleep",
- Argv: []string{"/bin/sleep", "5"},
- WorkingDirectory: "/",
- KUID: uid,
- }
-
- // Verify that "sleep 100" and "sleep 5" are running after exec. First,
- // start running exec (which blocks).
- ch := make(chan error)
- go func() {
- exitStatus, err := cont.executeSync(execArgs)
- if err != nil {
- ch <- err
- } else if exitStatus != 0 {
- ch <- fmt.Errorf("failed with exit status: %v", exitStatus)
- } else {
- ch <- nil
- }
- }()
-
- // expectedPL lists the expected process state of the container.
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).PPID(0).Cmd("sleep").UID(0).Process(),
- newProcessBuilder().PID(2).PPID(0).Cmd("sleep").UID(uid).Process(),
- }
- if err := waitForProcessList(cont, expectedPL); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- // Ensure that exec finished without error.
- select {
- case <-time.After(10 * time.Second):
- t.Fatalf("container timed out waiting for exec to finish.")
- case err := <-ch:
- if err != nil {
- t.Errorf("container failed to exec %v: %v", args, err)
- }
- }
- })
- }
-}
-
-// TestKillPid verifies that we can signal individual exec'd processes.
-func TestKillPid(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- const nProcs = 4
- spec := testutil.NewSpecWithArgs(app, "task-tree", "--depth", strconv.Itoa(nProcs-1), "--width=1", "--pause=true")
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Verify that all processes are running.
- if err := waitForProcessCount(cont, nProcs); err != nil {
- t.Fatalf("timed out waiting for processes to start: %v", err)
- }
-
- // Kill the child process with the largest PID.
- procs, err := cont.Processes()
- if err != nil {
- t.Fatalf("failed to get process list: %v", err)
- }
- var pid int32
- for _, p := range procs {
- if pid < int32(p.PID) {
- pid = int32(p.PID)
- }
- }
- if err := cont.SignalProcess(syscall.SIGKILL, pid); err != nil {
- t.Fatalf("failed to signal process %d: %v", pid, err)
- }
-
- // Verify that one process is gone.
- if err := waitForProcessCount(cont, nProcs-1); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- procs, err = cont.Processes()
- if err != nil {
- t.Fatalf("failed to get process list: %v", err)
- }
- for _, p := range procs {
- if pid == int32(p.PID) {
- t.Fatalf("pid %d is still alive, which should be killed", pid)
- }
- }
- })
- }
-}
-
-// TestCheckpointRestore creates a container that continuously writes successive
-// integers to a file. To test checkpoint and restore functionality, the
-// container is checkpointed and the last number printed to the file is
-// recorded. Then, it is restored in two new containers and the first number
-// printed from these containers is checked. Both should be the next consecutive
-// number after the last number from the checkpointed container.
-func TestCheckpointRestore(t *testing.T) {
- // Skip overlay because test requires writing to host file.
- for name, conf := range configs(t, noOverlay...) {
- t.Run(name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "checkpoint-test")
- if err != nil {
- t.Fatalf("ioutil.TempDir failed: %v", err)
- }
- defer os.RemoveAll(dir)
- if err := os.Chmod(dir, 0777); err != nil {
- t.Fatalf("error chmoding file: %q, %v", dir, err)
- }
-
- outputPath := filepath.Join(dir, "output")
- outputFile, err := createWriteableOutputFile(outputPath)
- if err != nil {
- t.Fatalf("error creating output file: %v", err)
- }
- defer outputFile.Close()
-
- script := fmt.Sprintf("for ((i=0; ;i++)); do echo $i >> %q; sleep 1; done", outputPath)
- spec := testutil.NewSpecWithArgs("bash", "-c", script)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Set the image path, which is where the checkpoint image will be saved.
- imagePath := filepath.Join(dir, "test-image-file")
-
- // Create the image file and open for writing.
- file, err := os.OpenFile(imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644)
- if err != nil {
- t.Fatalf("error opening new file at imagePath: %v", err)
- }
- defer file.Close()
-
- // Wait until application has ran.
- if err := waitForFileNotEmpty(outputFile); err != nil {
- t.Fatalf("Failed to wait for output file: %v", err)
- }
-
- // Checkpoint running container; save state into new file.
- if err := cont.Checkpoint(file); err != nil {
- t.Fatalf("error checkpointing container to empty file: %v", err)
- }
- defer os.RemoveAll(imagePath)
-
- lastNum, err := readOutputNum(outputPath, -1)
- if err != nil {
- t.Fatalf("error with outputFile: %v", err)
- }
-
- // Delete and recreate file before restoring.
- if err := os.Remove(outputPath); err != nil {
- t.Fatalf("error removing file")
- }
- outputFile2, err := createWriteableOutputFile(outputPath)
- if err != nil {
- t.Fatalf("error creating output file: %v", err)
- }
- defer outputFile2.Close()
-
- // Restore into a new container.
- args2 := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont2, err := New(conf, args2)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont2.Destroy()
-
- if err := cont2.Restore(spec, conf, imagePath); err != nil {
- t.Fatalf("error restoring container: %v", err)
- }
-
- // Wait until application has ran.
- if err := waitForFileNotEmpty(outputFile2); err != nil {
- t.Fatalf("Failed to wait for output file: %v", err)
- }
-
- firstNum, err := readOutputNum(outputPath, 0)
- if err != nil {
- t.Fatalf("error with outputFile: %v", err)
- }
-
- // Check that lastNum is one less than firstNum and that the container picks
- // up from where it left off.
- if lastNum+1 != firstNum {
- t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum)
- }
- cont2.Destroy()
-
- // Restore into another container!
- // Delete and recreate file before restoring.
- if err := os.Remove(outputPath); err != nil {
- t.Fatalf("error removing file")
- }
- outputFile3, err := createWriteableOutputFile(outputPath)
- if err != nil {
- t.Fatalf("error creating output file: %v", err)
- }
- defer outputFile3.Close()
-
- // Restore into a new container.
- args3 := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont3, err := New(conf, args3)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont3.Destroy()
-
- if err := cont3.Restore(spec, conf, imagePath); err != nil {
- t.Fatalf("error restoring container: %v", err)
- }
-
- // Wait until application has ran.
- if err := waitForFileNotEmpty(outputFile3); err != nil {
- t.Fatalf("Failed to wait for output file: %v", err)
- }
-
- firstNum2, err := readOutputNum(outputPath, 0)
- if err != nil {
- t.Fatalf("error with outputFile: %v", err)
- }
-
- // Check that lastNum is one less than firstNum and that the container picks
- // up from where it left off.
- if lastNum+1 != firstNum2 {
- t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum2)
- }
- cont3.Destroy()
- })
- }
-}
-
-// TestUnixDomainSockets checks that Checkpoint/Restore works in cases
-// with filesystem Unix Domain Socket use.
-func TestUnixDomainSockets(t *testing.T) {
- // Skip overlay because test requires writing to host file.
- for name, conf := range configs(t, noOverlay...) {
- t.Run(name, func(t *testing.T) {
- // UDS path is limited to 108 chars for compatibility with older systems.
- // Use '/tmp' (instead of testutil.TmpDir) to ensure the size limit is
- // not exceeded. Assumes '/tmp' exists in the system.
- dir, err := ioutil.TempDir("/tmp", "uds-test")
- if err != nil {
- t.Fatalf("ioutil.TempDir failed: %v", err)
- }
- defer os.RemoveAll(dir)
-
- outputPath := filepath.Join(dir, "uds_output")
- outputFile, err := os.OpenFile(outputPath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
- if err != nil {
- t.Fatalf("error creating output file: %v", err)
- }
- defer outputFile.Close()
-
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- socketPath := filepath.Join(dir, "uds_socket")
- defer os.Remove(socketPath)
-
- spec := testutil.NewSpecWithArgs(app, "uds", "--file", outputPath, "--socket", socketPath)
- spec.Process.User = specs.User{
- UID: uint32(os.Getuid()),
- GID: uint32(os.Getgid()),
- }
- spec.Mounts = []specs.Mount{{
- Type: "bind",
- Destination: dir,
- Source: dir,
- }}
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Set the image path, the location where the checkpoint image will be saved.
- imagePath := filepath.Join(dir, "test-image-file")
-
- // Create the image file and open for writing.
- file, err := os.OpenFile(imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644)
- if err != nil {
- t.Fatalf("error opening new file at imagePath: %v", err)
- }
- defer file.Close()
- defer os.RemoveAll(imagePath)
-
- // Wait until application has ran.
- if err := waitForFileNotEmpty(outputFile); err != nil {
- t.Fatalf("Failed to wait for output file: %v", err)
- }
-
- // Checkpoint running container; save state into new file.
- if err := cont.Checkpoint(file); err != nil {
- t.Fatalf("error checkpointing container to empty file: %v", err)
- }
-
- // Read last number outputted before checkpoint.
- lastNum, err := readOutputNum(outputPath, -1)
- if err != nil {
- t.Fatalf("error with outputFile: %v", err)
- }
-
- // Delete and recreate file before restoring.
- if err := os.Remove(outputPath); err != nil {
- t.Fatalf("error removing file")
- }
- outputFile2, err := os.OpenFile(outputPath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
- if err != nil {
- t.Fatalf("error creating output file: %v", err)
- }
- defer outputFile2.Close()
-
- // Restore into a new container.
- argsRestore := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- contRestore, err := New(conf, argsRestore)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer contRestore.Destroy()
-
- if err := contRestore.Restore(spec, conf, imagePath); err != nil {
- t.Fatalf("error restoring container: %v", err)
- }
-
- // Wait until application has ran.
- if err := waitForFileNotEmpty(outputFile2); err != nil {
- t.Fatalf("Failed to wait for output file: %v", err)
- }
-
- // Read first number outputted after restore.
- firstNum, err := readOutputNum(outputPath, 0)
- if err != nil {
- t.Fatalf("error with outputFile: %v", err)
- }
-
- // Check that lastNum is one less than firstNum.
- if lastNum+1 != firstNum {
- t.Errorf("error numbers not consecutive, previous: %d, next: %d", lastNum, firstNum)
- }
- contRestore.Destroy()
- })
- }
-}
-
-// TestPauseResume tests that we can successfully pause and resume a container.
-// The container will keep touching a file to indicate it's running. The test
-// pauses the container, removes the file, and checks that it doesn't get
-// recreated. Then it resumes the container, verify that the file gets created
-// again.
-func TestPauseResume(t *testing.T) {
- for name, conf := range configs(t, noOverlay...) {
- t.Run(name, func(t *testing.T) {
- tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "lock")
- if err != nil {
- t.Fatalf("error creating temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
-
- running := path.Join(tmpDir, "running")
- script := fmt.Sprintf("while [[ true ]]; do touch %q; sleep 0.1; done", running)
- spec := testutil.NewSpecWithArgs("/bin/bash", "-c", script)
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Wait until container starts running, observed by the existence of running
- // file.
- if err := waitForFileExist(running); err != nil {
- t.Errorf("error waiting for container to start: %v", err)
- }
-
- // Pause the running container.
- if err := cont.Pause(); err != nil {
- t.Errorf("error pausing container: %v", err)
- }
- if got, want := cont.Status, Paused; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- if err := os.Remove(running); err != nil {
- t.Fatalf("os.Remove(%q) failed: %v", running, err)
- }
- // Script touches the file every 100ms. Give a bit a time for it to run to
- // catch the case that pause didn't work.
- time.Sleep(200 * time.Millisecond)
- if _, err := os.Stat(running); !os.IsNotExist(err) {
- t.Fatalf("container did not pause: file exist check: %v", err)
- }
-
- // Resume the running container.
- if err := cont.Resume(); err != nil {
- t.Errorf("error pausing container: %v", err)
- }
- if got, want := cont.Status, Running; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Verify that the file is once again created by container.
- if err := waitForFileExist(running); err != nil {
- t.Fatalf("error resuming container: file exist check: %v", err)
- }
- })
- }
-}
-
-// TestPauseResumeStatus makes sure that the statuses are set correctly
-// with calls to pause and resume and that pausing and resuming only
-// occurs given the correct state.
-func TestPauseResumeStatus(t *testing.T) {
- spec := testutil.NewSpecWithArgs("sleep", "20")
- conf := testutil.TestConfig(t)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Pause the running container.
- if err := cont.Pause(); err != nil {
- t.Errorf("error pausing container: %v", err)
- }
- if got, want := cont.Status, Paused; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Try to Pause again. Should cause error.
- if err := cont.Pause(); err == nil {
- t.Errorf("error pausing container that was already paused: %v", err)
- }
- if got, want := cont.Status, Paused; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Resume the running container.
- if err := cont.Resume(); err != nil {
- t.Errorf("error resuming container: %v", err)
- }
- if got, want := cont.Status, Running; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-
- // Try to resume again. Should cause error.
- if err := cont.Resume(); err == nil {
- t.Errorf("error resuming container already running: %v", err)
- }
- if got, want := cont.Status, Running; got != want {
- t.Errorf("container status got %v, want %v", got, want)
- }
-}
-
-// TestCapabilities verifies that:
-// - Running exec as non-root UID and GID will result in an error (because the
-// executable file can't be read).
-// - Running exec as non-root with CAP_DAC_OVERRIDE succeeds because it skips
-// this check.
-func TestCapabilities(t *testing.T) {
- // Pick uid/gid different than ours.
- uid := auth.KUID(os.Getuid() + 1)
- gid := auth.KGID(os.Getgid() + 1)
-
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- spec := testutil.NewSpecWithArgs("sleep", "100")
- rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- if err := cont.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // expectedPL lists the expected process state of the container.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").Process(),
- }
- if err := waitForProcessList(cont, expectedPL); err != nil {
- t.Fatalf("Failed to wait for sleep to start, err: %v", err)
- }
-
- // Create an executable that can't be run with the specified UID:GID.
- // This shouldn't be callable within the container until we add the
- // CAP_DAC_OVERRIDE capability to skip the access check.
- exePath := filepath.Join(rootDir, "exe")
- if err := ioutil.WriteFile(exePath, []byte("#!/bin/sh\necho hello"), 0770); err != nil {
- t.Fatalf("couldn't create executable: %v", err)
- }
- defer os.Remove(exePath)
-
- // Need to traverse the intermediate directory.
- os.Chmod(rootDir, 0755)
-
- execArgs := &control.ExecArgs{
- Filename: exePath,
- Argv: []string{exePath},
- WorkingDirectory: "/",
- KUID: uid,
- KGID: gid,
- Capabilities: &auth.TaskCapabilities{},
- }
-
- // "exe" should fail because we don't have the necessary permissions.
- if _, err := cont.executeSync(execArgs); err == nil {
- t.Fatalf("container executed without error, but an error was expected")
- }
-
- // Now we run with the capability enabled and should succeed.
- execArgs.Capabilities = &auth.TaskCapabilities{
- EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- }
- // "exe" should not fail this time.
- if _, err := cont.executeSync(execArgs); err != nil {
- t.Fatalf("container failed to exec %v: %v", args, err)
- }
- })
- }
-}
-
-// TestRunNonRoot checks that sandbox can be configured when running as
-// non-privileged user.
-func TestRunNonRoot(t *testing.T) {
- for name, conf := range configs(t, noOverlay...) {
- t.Run(name, func(t *testing.T) {
- spec := testutil.NewSpecWithArgs("/bin/true")
-
- // Set a random user/group with no access to "blocked" dir.
- spec.Process.User.UID = 343
- spec.Process.User.GID = 2401
- spec.Process.Capabilities = nil
-
- // User running inside container can't list '$TMP/blocked' and would fail to
- // mount it.
- dir, err := ioutil.TempDir(testutil.TmpDir(), "blocked")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- if err := os.Chmod(dir, 0700); err != nil {
- t.Fatalf("os.MkDir(%q) failed: %v", dir, err)
- }
- dir = path.Join(dir, "test")
- if err := os.Mkdir(dir, 0755); err != nil {
- t.Fatalf("os.MkDir(%q) failed: %v", dir, err)
- }
-
- src, err := ioutil.TempDir(testutil.TmpDir(), "src")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
-
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Destination: dir,
- Source: src,
- Type: "bind",
- })
-
- if err := run(spec, conf); err != nil {
- t.Fatalf("error running sandbox: %v", err)
- }
- })
- }
-}
-
-// TestMountNewDir checks that runsc will create destination directory if it
-// doesn't exit.
-func TestMountNewDir(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- root, err := ioutil.TempDir(testutil.TmpDir(), "root")
- if err != nil {
- t.Fatal("ioutil.TempDir() failed:", err)
- }
-
- srcDir := path.Join(root, "src", "dir", "anotherdir")
- if err := os.MkdirAll(srcDir, 0755); err != nil {
- t.Fatalf("os.MkDir(%q) failed: %v", srcDir, err)
- }
-
- mountDir := path.Join(root, "dir", "anotherdir")
-
- spec := testutil.NewSpecWithArgs("/bin/ls", mountDir)
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Destination: mountDir,
- Source: srcDir,
- Type: "bind",
- })
- // Extra points for creating the mount with a readonly root.
- spec.Root.Readonly = true
-
- if err := run(spec, conf); err != nil {
- t.Fatalf("error running sandbox: %v", err)
- }
- })
- }
-}
-
-func TestReadonlyRoot(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- spec := testutil.NewSpecWithArgs("sleep", "100")
- spec.Root.Readonly = true
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Read mounts to check that root is readonly.
- out, ws, err := executeCombinedOutput(c, "/bin/sh", "-c", "mount | grep ' / '")
- if err != nil || ws != 0 {
- t.Fatalf("exec failed, ws: %v, err: %v", ws, err)
- }
- t.Logf("root mount: %q", out)
- if !strings.Contains(string(out), "(ro)") {
- t.Errorf("root not mounted readonly: %q", out)
- }
-
- // Check that file cannot be created.
- ws, err = execute(c, "/bin/touch", "/foo")
- if err != nil {
- t.Fatalf("touch file in ro mount: %v", err)
- }
- if !ws.Exited() || syscall.Errno(ws.ExitStatus()) != syscall.EPERM {
- t.Fatalf("wrong waitStatus: %v", ws)
- }
- })
- }
-}
-
-func TestReadonlyMount(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "ro-mount")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- spec := testutil.NewSpecWithArgs("sleep", "100")
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Destination: dir,
- Source: dir,
- Type: "bind",
- Options: []string{"ro"},
- })
- spec.Root.Readonly = false
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Read mounts to check that volume is readonly.
- cmd := fmt.Sprintf("mount | grep ' %s '", dir)
- out, ws, err := executeCombinedOutput(c, "/bin/sh", "-c", cmd)
- if err != nil || ws != 0 {
- t.Fatalf("exec failed, ws: %v, err: %v", ws, err)
- }
- t.Logf("mount: %q", out)
- if !strings.Contains(string(out), "(ro)") {
- t.Errorf("volume not mounted readonly: %q", out)
- }
-
- // Check that file cannot be created.
- ws, err = execute(c, "/bin/touch", path.Join(dir, "file"))
- if err != nil {
- t.Fatalf("touch file in ro mount: %v", err)
- }
- if !ws.Exited() || syscall.Errno(ws.ExitStatus()) != syscall.EPERM {
- t.Fatalf("wrong WaitStatus: %v", ws)
- }
- })
- }
-}
-
-func TestUIDMap(t *testing.T) {
- for name, conf := range configs(t, noOverlay...) {
- t.Run(name, func(t *testing.T) {
- testDir, err := ioutil.TempDir(testutil.TmpDir(), "test-mount")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- defer os.RemoveAll(testDir)
- testFile := path.Join(testDir, "testfile")
-
- spec := testutil.NewSpecWithArgs("touch", "/tmp/testfile")
- uid := os.Getuid()
- gid := os.Getgid()
- spec.Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{
- {Type: specs.UserNamespace},
- {Type: specs.PIDNamespace},
- {Type: specs.MountNamespace},
- },
- UIDMappings: []specs.LinuxIDMapping{
- {
- ContainerID: 0,
- HostID: uint32(uid),
- Size: 1,
- },
- },
- GIDMappings: []specs.LinuxIDMapping{
- {
- ContainerID: 0,
- HostID: uint32(gid),
- Size: 1,
- },
- },
- }
-
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Destination: "/tmp",
- Source: testDir,
- Type: "bind",
- })
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create, start and wait for the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- ws, err := c.Wait()
- if err != nil {
- t.Fatalf("error waiting on container: %v", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Fatalf("container failed, waitStatus: %v", ws)
- }
- st := syscall.Stat_t{}
- if err := syscall.Stat(testFile, &st); err != nil {
- t.Fatalf("error stat /testfile: %v", err)
- }
-
- if st.Uid != uint32(uid) || st.Gid != uint32(gid) {
- t.Fatalf("UID: %d (%d) GID: %d (%d)", st.Uid, uid, st.Gid, gid)
- }
- })
- }
-}
-
-// TestAbbreviatedIDs checks that runsc supports using abbreviated container
-// IDs in place of full IDs.
-func TestAbbreviatedIDs(t *testing.T) {
- doAbbreviatedIDsTest(t, false)
-}
-
-func TestAbbreviatedIDsVFS2(t *testing.T) {
- doAbbreviatedIDsTest(t, true)
-}
-
-func doAbbreviatedIDsTest(t *testing.T, vfs2 bool) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
- conf.VFS2 = vfs2
-
- cids := []string{
- "foo-" + testutil.RandomContainerID(),
- "bar-" + testutil.RandomContainerID(),
- "baz-" + testutil.RandomContainerID(),
- }
- for _, cid := range cids {
- spec := testutil.NewSpecWithArgs("sleep", "100")
- bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: cid,
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer cont.Destroy()
- }
-
- // These should all be unambigious.
- unambiguous := map[string]string{
- "f": cids[0],
- cids[0]: cids[0],
- "bar": cids[1],
- cids[1]: cids[1],
- "baz": cids[2],
- cids[2]: cids[2],
- }
- for shortid, longid := range unambiguous {
- if _, err := Load(rootDir, FullID{ContainerID: shortid}, LoadOpts{}); err != nil {
- t.Errorf("%q should resolve to %q: %v", shortid, longid, err)
- }
- }
-
- // These should be ambiguous.
- ambiguous := []string{
- "b",
- "ba",
- }
- for _, shortid := range ambiguous {
- if s, err := Load(rootDir, FullID{ContainerID: shortid}, LoadOpts{}); err == nil {
- t.Errorf("%q should be ambiguous, but resolved to %q", shortid, s.ID)
- }
- }
-}
-
-func TestGoferExits(t *testing.T) {
- doGoferExitTest(t, false)
-}
-
-func TestGoferExitsVFS2(t *testing.T) {
- doGoferExitTest(t, true)
-}
-
-func doGoferExitTest(t *testing.T, vfs2 bool) {
- spec := testutil.NewSpecWithArgs("/bin/sleep", "10000")
- conf := testutil.TestConfig(t)
- conf.VFS2 = vfs2
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
-
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Kill sandbox and expect gofer to exit on its own.
- sandboxProc, err := os.FindProcess(c.Sandbox.Pid)
- if err != nil {
- t.Fatalf("error finding sandbox process: %v", err)
- }
- if err := sandboxProc.Kill(); err != nil {
- t.Fatalf("error killing sandbox process: %v", err)
- }
-
- err = blockUntilWaitable(c.GoferPid)
- if err != nil && err != syscall.ECHILD {
- t.Errorf("error waiting for gofer to exit: %v", err)
- }
-}
-
-func TestRootNotMount(t *testing.T) {
- appSym, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- app, err := filepath.EvalSymlinks(appSym)
- if err != nil {
- t.Fatalf("error resolving %q symlink: %v", appSym, err)
- }
- log.Infof("App path %q is a symlink to %q", appSym, app)
-
- static, err := testutil.IsStatic(app)
- if err != nil {
- t.Fatalf("error reading application binary: %v", err)
- }
- if !static {
- // This happens during race builds; we cannot map in shared
- // libraries also, so we need to skip the test.
- t.Skip()
- }
-
- root := filepath.Dir(app)
- exe := "/" + filepath.Base(app)
- log.Infof("Executing %q in %q", exe, root)
-
- spec := testutil.NewSpecWithArgs(exe, "help")
- spec.Root.Path = root
- spec.Root.Readonly = true
- spec.Mounts = nil
-
- conf := testutil.TestConfig(t)
- if err := run(spec, conf); err != nil {
- t.Fatalf("error running sandbox: %v", err)
- }
-}
-
-func TestUserLog(t *testing.T) {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- // sched_rr_get_interval - not implemented in gvisor.
- num := strconv.Itoa(syscall.SYS_SCHED_RR_GET_INTERVAL)
- spec := testutil.NewSpecWithArgs(app, "syscall", "--syscall="+num)
- conf := testutil.TestConfig(t)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- dir, err := ioutil.TempDir(testutil.TmpDir(), "user_log_test")
- if err != nil {
- t.Fatalf("error creating tmp dir: %v", err)
- }
- userLog := filepath.Join(dir, "user.log")
-
- // Create, start and wait for the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- UserLog: userLog,
- Attached: true,
- }
- ws, err := Run(conf, args)
- if err != nil {
- t.Fatalf("error running container: %v", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Fatalf("container failed, waitStatus: %v", ws)
- }
-
- out, err := ioutil.ReadFile(userLog)
- if err != nil {
- t.Fatalf("error opening user log file %q: %v", userLog, err)
- }
- if want := "Unsupported syscall sched_rr_get_interval("; !strings.Contains(string(out), want) {
- t.Errorf("user log file doesn't contain %q, out: %s", want, string(out))
- }
-}
-
-func TestWaitOnExitedSandbox(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- // Run a shell that sleeps for 1 second and then exits with a
- // non-zero code.
- const wantExit = 17
- cmd := fmt.Sprintf("sleep 1; exit %d", wantExit)
- spec := testutil.NewSpecWithArgs("/bin/sh", "-c", cmd)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and Start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Wait on the sandbox. This will make an RPC to the sandbox
- // and get the actual exit status of the application.
- ws, err := c.Wait()
- if err != nil {
- t.Fatalf("error waiting on container: %v", err)
- }
- if got := ws.ExitStatus(); got != wantExit {
- t.Errorf("got exit status %d, want %d", got, wantExit)
- }
-
- // Now the sandbox has exited, but the zombie sandbox process
- // still exists. Calling Wait() now will return the sandbox
- // exit status.
- ws, err = c.Wait()
- if err != nil {
- t.Fatalf("error waiting on container: %v", err)
- }
- if got := ws.ExitStatus(); got != wantExit {
- t.Errorf("got exit status %d, want %d", got, wantExit)
- }
- })
- }
-}
-
-func TestDestroyNotStarted(t *testing.T) {
- doDestroyNotStartedTest(t, false)
-}
-
-func TestDestroyNotStartedVFS2(t *testing.T) {
- doDestroyNotStartedTest(t, true)
-}
-
-func doDestroyNotStartedTest(t *testing.T, vfs2 bool) {
- spec := testutil.NewSpecWithArgs("/bin/sleep", "100")
- conf := testutil.TestConfig(t)
- conf.VFS2 = vfs2
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create the container and check that it can be destroyed.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- if err := c.Destroy(); err != nil {
- t.Fatalf("deleting non-started container failed: %v", err)
- }
-}
-
-// TestDestroyStarting attempts to force a race between start and destroy.
-func TestDestroyStarting(t *testing.T) {
- doDestroyStartingTest(t, false)
-}
-
-func TestDestroyStartedVFS2(t *testing.T) {
- doDestroyStartingTest(t, true)
-}
-
-func doDestroyStartingTest(t *testing.T, vfs2 bool) {
- for i := 0; i < 10; i++ {
- spec := testutil.NewSpecWithArgs("/bin/sleep", "100")
- conf := testutil.TestConfig(t)
- conf.VFS2 = vfs2
- rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create the container and check that it can be destroyed.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
-
- // Container is not thread safe, so load another instance to run in
- // concurrently.
- startCont, err := Load(rootDir, FullID{ContainerID: args.ID}, LoadOpts{})
- if err != nil {
- t.Fatalf("error loading container: %v", err)
- }
- wg := sync.WaitGroup{}
- wg.Add(1)
- go func() {
- defer wg.Done()
- // Ignore failures, start can fail if destroy runs first.
- startCont.Start(conf)
- }()
-
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := c.Destroy(); err != nil {
- t.Errorf("deleting non-started container failed: %v", err)
- }
- }()
- wg.Wait()
- }
-}
-
-func TestCreateWorkingDir(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "cwd-create")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- dir := path.Join(tmpDir, "new/working/dir")
-
- // touch will fail if the directory doesn't exist.
- spec := testutil.NewSpecWithArgs("/bin/touch", path.Join(dir, "file"))
- spec.Process.Cwd = dir
- spec.Root.Readonly = true
-
- if err := run(spec, conf); err != nil {
- t.Fatalf("Error running container: %v", err)
- }
- })
- }
-}
-
-// TestMountPropagation verifies that mount propagates to slave but not to
-// private mounts.
-func TestMountPropagation(t *testing.T) {
- // Setup dir structure:
- // - src: is mounted as shared and is used as source for both private and
- // slave mounts
- // - dir: will be bind mounted inside src and should propagate to slave
- tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "mount")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- src := filepath.Join(tmpDir, "src")
- srcMnt := filepath.Join(src, "mnt")
- dir := filepath.Join(tmpDir, "dir")
- for _, path := range []string{src, srcMnt, dir} {
- if err := os.MkdirAll(path, 0777); err != nil {
- t.Fatalf("MkdirAll(%q): %v", path, err)
- }
- }
- dirFile := filepath.Join(dir, "file")
- f, err := os.Create(dirFile)
- if err != nil {
- t.Fatalf("os.Create(%q): %v", dirFile, err)
- }
- f.Close()
-
- // Setup src as a shared mount.
- if err := syscall.Mount(src, src, "bind", syscall.MS_BIND, ""); err != nil {
- t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err)
- }
- if err := syscall.Mount("", src, "", syscall.MS_SHARED, ""); err != nil {
- t.Fatalf("mount(%q, MS_SHARED): %v", srcMnt, err)
- }
-
- spec := testutil.NewSpecWithArgs("sleep", "1000")
-
- priv := filepath.Join(tmpDir, "priv")
- slave := filepath.Join(tmpDir, "slave")
- spec.Mounts = []specs.Mount{
- {
- Source: src,
- Destination: priv,
- Type: "bind",
- Options: []string{"private"},
- },
- {
- Source: src,
- Destination: slave,
- Type: "bind",
- Options: []string{"slave"},
- },
- }
-
- conf := testutil.TestConfig(t)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("creating container: %v", err)
- }
- defer cont.Destroy()
-
- if err := cont.Start(conf); err != nil {
- t.Fatalf("starting container: %v", err)
- }
-
- // After the container is started, mount dir inside source and check what
- // happens to both destinations.
- if err := syscall.Mount(dir, srcMnt, "bind", syscall.MS_BIND, ""); err != nil {
- t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err)
- }
-
- // Check that mount didn't propagate to private mount.
- privFile := filepath.Join(priv, "mnt", "file")
- if ws, err := execute(cont, "/usr/bin/test", "!", "-f", privFile); err != nil || ws != 0 {
- t.Fatalf("exec: test ! -f %q, ws: %v, err: %v", privFile, ws, err)
- }
-
- // Check that mount propagated to slave mount.
- slaveFile := filepath.Join(slave, "mnt", "file")
- if ws, err := execute(cont, "/usr/bin/test", "-f", slaveFile); err != nil || ws != 0 {
- t.Fatalf("exec: test -f %q, ws: %v, err: %v", privFile, ws, err)
- }
-}
-
-func TestMountSymlink(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "mount-symlink")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- defer os.RemoveAll(dir)
-
- source := path.Join(dir, "source")
- target := path.Join(dir, "target")
- for _, path := range []string{source, target} {
- if err := os.MkdirAll(path, 0777); err != nil {
- t.Fatalf("os.MkdirAll(): %v", err)
- }
- }
- f, err := os.Create(path.Join(source, "file"))
- if err != nil {
- t.Fatalf("os.Create(): %v", err)
- }
- f.Close()
-
- link := path.Join(dir, "link")
- if err := os.Symlink(target, link); err != nil {
- t.Fatalf("os.Symlink(%q, %q): %v", target, link, err)
- }
-
- spec := testutil.NewSpecWithArgs("/bin/sleep", "1000")
-
- // Mount to a symlink to ensure the mount code will follow it and mount
- // at the symlink target.
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Type: "bind",
- Destination: link,
- Source: source,
- })
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("creating container: %v", err)
- }
- defer cont.Destroy()
-
- if err := cont.Start(conf); err != nil {
- t.Fatalf("starting container: %v", err)
- }
-
- // Check that symlink was resolved and mount was created where the symlink
- // is pointing to.
- file := path.Join(target, "file")
- if ws, err := execute(cont, "/usr/bin/test", "-f", file); err != nil || ws != 0 {
- t.Fatalf("exec: test -f %q, ws: %v, err: %v", file, ws, err)
- }
- })
- }
-}
-
-// Check that --net-raw disables the CAP_NET_RAW capability.
-func TestNetRaw(t *testing.T) {
- capNetRaw := strconv.FormatUint(bits.MaskOf64(int(linux.CAP_NET_RAW)), 10)
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- for _, enableRaw := range []bool{true, false} {
- conf := testutil.TestConfig(t)
- conf.EnableRaw = enableRaw
-
- test := "--enabled"
- if !enableRaw {
- test = "--disabled"
- }
-
- spec := testutil.NewSpecWithArgs(app, "capability", test, capNetRaw)
- if err := run(spec, conf); err != nil {
- t.Fatalf("Error running container: %v", err)
- }
- }
-}
-
-// TestTTYField checks TTY field returned by container.Processes().
-func TestTTYField(t *testing.T) {
- stop := testutil.StartReaper()
- defer stop()
-
- testApp, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- testCases := []struct {
- name string
- useTTY bool
- wantTTYField string
- }{
- {
- name: "no tty",
- useTTY: false,
- wantTTYField: "?",
- },
- {
- name: "tty used",
- useTTY: true,
- wantTTYField: "pts/0",
- },
- }
-
- for _, test := range testCases {
- for _, vfs2 := range []bool{false, true} {
- name := test.name
- if vfs2 {
- name += "-vfs2"
- }
- t.Run(name, func(t *testing.T) {
- conf := testutil.TestConfig(t)
- conf.VFS2 = vfs2
-
- // We will run /bin/sleep, possibly with an open TTY.
- cmd := []string{"/bin/sleep", "10000"}
- if test.useTTY {
- // Run inside the "pty-runner".
- cmd = append([]string{testApp, "pty-runner"}, cmd...)
- }
-
- spec := testutil.NewSpecWithArgs(cmd...)
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Wait for sleep to be running, and check the TTY
- // field.
- var gotTTYField string
- cb := func() error {
- ps, err := c.Processes()
- if err != nil {
- err = fmt.Errorf("error getting process data from container: %v", err)
- return &backoff.PermanentError{Err: err}
- }
- for _, p := range ps {
- if strings.Contains(p.Cmd, "sleep") {
- gotTTYField = p.TTY
- return nil
- }
- }
- return fmt.Errorf("sleep not running")
- }
- if err := testutil.Poll(cb, 30*time.Second); err != nil {
- t.Fatalf("error waiting for sleep process: %v", err)
- }
-
- if gotTTYField != test.wantTTYField {
- t.Errorf("tty field got %q, want %q", gotTTYField, test.wantTTYField)
- }
- })
- }
- }
-}
-
-// Test that container can run even when there are corrupt state files in the
-// root directiry.
-func TestCreateWithCorruptedStateFile(t *testing.T) {
- conf := testutil.TestConfig(t)
- spec := testutil.NewSpecWithArgs("/bin/true")
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create corrupted state file.
- corruptID := testutil.RandomContainerID()
- corruptState := buildPath(conf.RootDir, FullID{SandboxID: corruptID, ContainerID: corruptID}, stateFileExtension)
- if err := ioutil.WriteFile(corruptState, []byte("this{file(is;not[valid.json"), 0777); err != nil {
- t.Fatalf("createCorruptStateFile(): %v", err)
- }
- defer os.Remove(corruptState)
-
- if _, err := Load(conf.RootDir, FullID{ContainerID: corruptID}, LoadOpts{SkipCheck: true}); err == nil {
- t.Fatalf("loading corrupted state file should have failed")
- }
-
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- Attached: true,
- }
- if ws, err := Run(conf, args); err != nil {
- t.Errorf("running container: %v", err)
- } else if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Errorf("container failed, waitStatus: %v", ws)
- }
-}
-
-func execute(cont *Container, name string, arg ...string) (syscall.WaitStatus, error) {
- args := &control.ExecArgs{
- Filename: name,
- Argv: append([]string{name}, arg...),
- }
- return cont.executeSync(args)
-}
-
-func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, syscall.WaitStatus, error) {
- r, w, err := os.Pipe()
- if err != nil {
- return nil, 0, err
- }
- defer r.Close()
-
- args := &control.ExecArgs{
- Filename: name,
- Argv: append([]string{name}, arg...),
- FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}},
- }
- ws, err := cont.executeSync(args)
- w.Close()
- if err != nil {
- return nil, 0, err
- }
- out, err := ioutil.ReadAll(r)
- return out, ws, err
-}
-
-// executeSync synchronously executes a new process.
-func (c *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus, error) {
- pid, err := c.Execute(args)
- if err != nil {
- return 0, fmt.Errorf("error executing: %v", err)
- }
- ws, err := c.WaitPID(pid)
- if err != nil {
- return 0, fmt.Errorf("error waiting: %v", err)
- }
- return ws, nil
-}
-
-func TestMain(m *testing.M) {
- log.SetLevel(log.Debug)
- flag.Parse()
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
- }
- specutils.MaybeRunAsRoot()
- os.Exit(m.Run())
-}
diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go
deleted file mode 100644
index 173332cc2..000000000
--- a/runsc/container/multi_container_test.go
+++ /dev/null
@@ -1,1908 +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 container
-
-import (
- "fmt"
- "io/ioutil"
- "math"
- "os"
- "path"
- "path/filepath"
- "strings"
- "syscall"
- "testing"
- "time"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/pkg/cleanup"
- "gvisor.dev/gvisor/pkg/sentry/control"
- "gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/runsc/boot"
- "gvisor.dev/gvisor/runsc/config"
- "gvisor.dev/gvisor/runsc/specutils"
-)
-
-func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) {
- var specs []*specs.Spec
- var ids []string
- rootID := testutil.RandomContainerID()
-
- for i, cmd := range cmds {
- spec := testutil.NewSpecWithArgs(cmd...)
- if i == 0 {
- spec.Annotations = map[string]string{
- specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeSandbox,
- }
- ids = append(ids, rootID)
- } else {
- spec.Annotations = map[string]string{
- specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeContainer,
- specutils.ContainerdSandboxIDAnnotation: rootID,
- }
- ids = append(ids, testutil.RandomContainerID())
- }
- specs = append(specs, spec)
- }
- return specs, ids
-}
-
-func startContainers(conf *config.Config, specs []*specs.Spec, ids []string) ([]*Container, func(), error) {
- if len(conf.RootDir) == 0 {
- panic("conf.RootDir not set. Call testutil.SetupRootDir() to set.")
- }
-
- cu := cleanup.Cleanup{}
- defer cu.Clean()
-
- var containers []*Container
- for i, spec := range specs {
- bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
- if err != nil {
- return nil, nil, fmt.Errorf("error setting up container: %v", err)
- }
- cu.Add(cleanup)
-
- args := Args{
- ID: ids[i],
- Spec: spec,
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- return nil, nil, fmt.Errorf("error creating container: %v", err)
- }
- cu.Add(func() { cont.Destroy() })
- containers = append(containers, cont)
-
- if err := cont.Start(conf); err != nil {
- return nil, nil, fmt.Errorf("error starting container: %v", err)
- }
- }
-
- return containers, cu.Release(), nil
-}
-
-type execDesc struct {
- c *Container
- cmd []string
- want int
- name string
-}
-
-func execMany(t *testing.T, execs []execDesc) {
- for _, exec := range execs {
- t.Run(exec.name, func(t *testing.T) {
- args := &control.ExecArgs{Argv: exec.cmd}
- if ws, err := exec.c.executeSync(args); err != nil {
- t.Errorf("error executing %+v: %v", args, err)
- } else if ws.ExitStatus() != exec.want {
- t.Errorf("%q: exec %q got exit status: %d, want: %d", exec.name, exec.cmd, ws.ExitStatus(), exec.want)
- }
- })
- }
-}
-
-func createSharedMount(mount specs.Mount, name string, pod ...*specs.Spec) {
- for _, spec := range pod {
- spec.Annotations[boot.MountPrefix+name+".source"] = mount.Source
- spec.Annotations[boot.MountPrefix+name+".type"] = mount.Type
- spec.Annotations[boot.MountPrefix+name+".share"] = "pod"
- if len(mount.Options) > 0 {
- spec.Annotations[boot.MountPrefix+name+".options"] = strings.Join(mount.Options, ",")
- }
- }
-}
-
-// TestMultiContainerSanity checks that it is possible to run 2 dead-simple
-// containers in the same sandbox.
-func TestMultiContainerSanity(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- specs, ids := createSpecs(sleep, sleep)
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check via ps that multiple processes are running.
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- expectedPL = []*control.Process{
- newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- })
- }
-}
-
-// TestMultiPIDNS checks that it is possible to run 2 dead-simple
-// containers in the same sandbox with different pidns.
-func TestMultiPIDNS(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- testSpecs, ids := createSpecs(sleep, sleep)
- testSpecs[1].Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{
- {
- Type: "pid",
- },
- },
- }
-
- containers, cleanup, err := startContainers(conf, testSpecs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check via ps that multiple processes are running.
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- expectedPL = []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- })
- }
-}
-
-// TestMultiPIDNSPath checks the pidns path.
-func TestMultiPIDNSPath(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- testSpecs, ids := createSpecs(sleep, sleep, sleep)
- testSpecs[0].Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{
- {
- Type: "pid",
- Path: "/proc/1/ns/pid",
- },
- },
- }
- testSpecs[1].Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{
- {
- Type: "pid",
- Path: "/proc/1/ns/pid",
- },
- },
- }
- testSpecs[2].Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{
- {
- Type: "pid",
- Path: "/proc/2/ns/pid",
- },
- },
- }
-
- containers, cleanup, err := startContainers(conf, testSpecs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check via ps that multiple processes are running.
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- if err := waitForProcessList(containers[2], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- expectedPL = []*control.Process{
- newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- })
- }
-}
-
-func TestMultiContainerWait(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // The first container should run the entire duration of the test.
- cmd1 := []string{"sleep", "100"}
- // We'll wait on the second container, which is much shorter lived.
- cmd2 := []string{"sleep", "1"}
- specs, ids := createSpecs(cmd1, cmd2)
-
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check that we can wait for the sub-container.
- c := containers[1]
- if ws, err := c.Wait(); err != nil {
- t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es)
- }
- if _, err := c.Wait(); err != nil {
- t.Errorf("wait for stopped container %s shouldn't fail: %v", c.Spec.Process.Args, err)
- }
-
- // After Wait returns, ensure that the root container is running and
- // the child has finished.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").PID(1).Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for %q to start: %v", strings.Join(containers[0].Spec.Process.Args, " "), err)
- }
-}
-
-// TestExecWait ensures what we can wait on containers and individual processes
-// in the sandbox that have already exited.
-func TestExecWait(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // The first container should run the entire duration of the test.
- cmd1 := []string{"sleep", "100"}
- // We'll wait on the second container, which is much shorter lived.
- cmd2 := []string{"sleep", "1"}
- specs, ids := createSpecs(cmd1, cmd2)
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check via ps that process is running.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Fatalf("failed to wait for sleep to start: %v", err)
- }
-
- // Wait for the second container to finish.
- if err := waitForProcessCount(containers[1], 0); err != nil {
- t.Fatalf("failed to wait for second container to stop: %v", err)
- }
-
- // Get the second container exit status.
- if ws, err := containers[1].Wait(); err != nil {
- t.Fatalf("failed to wait for process %s: %v", containers[1].Spec.Process.Args, err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Fatalf("process %s exited with non-zero status %d", containers[1].Spec.Process.Args, es)
- }
- if _, err := containers[1].Wait(); err != nil {
- t.Fatalf("wait for stopped container %s shouldn't fail: %v", containers[1].Spec.Process.Args, err)
- }
-
- // Execute another process in the first container.
- args := &control.ExecArgs{
- Filename: "/bin/sleep",
- Argv: []string{"/bin/sleep", "1"},
- WorkingDirectory: "/",
- KUID: 0,
- }
- pid, err := containers[0].Execute(args)
- if err != nil {
- t.Fatalf("error executing: %v", err)
- }
-
- // Wait for the exec'd process to exit.
- expectedPL = []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Fatalf("failed to wait for second container to stop: %v", err)
- }
-
- // Get the exit status from the exec'd process.
- if ws, err := containers[0].WaitPID(pid); err != nil {
- t.Fatalf("failed to wait for process %+v with pid %d: %v", args, pid, err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Fatalf("process %+v exited with non-zero status %d", args, es)
- }
- if _, err := containers[0].WaitPID(pid); err == nil {
- t.Fatalf("wait for stopped process %+v should fail", args)
- }
-}
-
-// TestMultiContainerMount tests that bind mounts can be used with multiple
-// containers.
-func TestMultiContainerMount(t *testing.T) {
- cmd1 := []string{"sleep", "100"}
-
- // 'src != dst' ensures that 'dst' doesn't exist in the host and must be
- // properly mapped inside the container to work.
- src, err := ioutil.TempDir(testutil.TmpDir(), "container")
- if err != nil {
- t.Fatal("ioutil.TempDir failed:", err)
- }
- dst := src + ".dst"
- cmd2 := []string{"touch", filepath.Join(dst, "file")}
-
- sps, ids := createSpecs(cmd1, cmd2)
- sps[1].Mounts = append(sps[1].Mounts, specs.Mount{
- Source: src,
- Destination: dst,
- Type: "bind",
- })
-
- // Setup the containers.
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- containers, cleanup, err := startContainers(conf, sps, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- ws, err := containers[1].Wait()
- if err != nil {
- t.Error("error waiting on container:", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Error("container failed, waitStatus:", ws)
- }
-}
-
-// TestMultiContainerSignal checks that it is possible to signal individual
-// containers without killing the entire sandbox.
-func TestMultiContainerSignal(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- specs, ids := createSpecs(sleep, sleep)
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check via ps that container 1 process is running.
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- // Kill process 2.
- if err := containers[1].SignalContainer(syscall.SIGKILL, false); err != nil {
- t.Errorf("failed to kill process 2: %v", err)
- }
-
- // Make sure process 1 is still running.
- expectedPL = []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- // goferPid is reset when container is destroyed.
- goferPid := containers[1].GoferPid
-
- // Destroy container and ensure container's gofer process has exited.
- if err := containers[1].Destroy(); err != nil {
- t.Errorf("failed to destroy container: %v", err)
- }
- _, _, err = specutils.RetryEintr(func() (uintptr, uintptr, error) {
- cpid, err := syscall.Wait4(goferPid, nil, 0, nil)
- return uintptr(cpid), 0, err
- })
- if err != syscall.ECHILD {
- t.Errorf("error waiting for gofer to exit: %v", err)
- }
- // Make sure process 1 is still running.
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- // Now that process 2 is gone, ensure we get an error trying to
- // signal it again.
- if err := containers[1].SignalContainer(syscall.SIGKILL, false); err == nil {
- t.Errorf("container %q shouldn't exist, but we were able to signal it", containers[1].ID)
- }
-
- // Kill process 1.
- if err := containers[0].SignalContainer(syscall.SIGKILL, false); err != nil {
- t.Errorf("failed to kill process 1: %v", err)
- }
-
- // Ensure that container's gofer and sandbox process are no more.
- err = blockUntilWaitable(containers[0].GoferPid)
- if err != nil && err != syscall.ECHILD {
- t.Errorf("error waiting for gofer to exit: %v", err)
- }
-
- err = blockUntilWaitable(containers[0].Sandbox.Pid)
- if err != nil && err != syscall.ECHILD {
- t.Errorf("error waiting for sandbox to exit: %v", err)
- }
-
- // The sentry should be gone, so signaling should yield an error.
- if err := containers[0].SignalContainer(syscall.SIGKILL, false); err == nil {
- t.Errorf("sandbox %q shouldn't exist, but we were able to signal it", containers[0].Sandbox.ID)
- }
-
- if err := containers[0].Destroy(); err != nil {
- t.Errorf("failed to destroy container: %v", err)
- }
- })
- }
-}
-
-// TestMultiContainerDestroy checks that container are properly cleaned-up when
-// they are destroyed.
-func TestMultiContainerDestroy(t *testing.T) {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // First container will remain intact while the second container is killed.
- podSpecs, ids := createSpecs(
- []string{"sleep", "100"},
- []string{app, "fork-bomb"})
-
- // Run the fork bomb in a PID namespace to prevent processes to be
- // re-parented to PID=1 in the root container.
- podSpecs[1].Linux = &specs.Linux{
- Namespaces: []specs.LinuxNamespace{{Type: "pid"}},
- }
- containers, cleanup, err := startContainers(conf, podSpecs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Exec more processes to ensure signal all works for exec'd processes too.
- args := &control.ExecArgs{
- Filename: app,
- Argv: []string{app, "fork-bomb"},
- }
- if _, err := containers[1].Execute(args); err != nil {
- t.Fatalf("error exec'ing: %v", err)
- }
-
- // Let it brew...
- time.Sleep(500 * time.Millisecond)
-
- if err := containers[1].Destroy(); err != nil {
- t.Fatalf("error destroying container: %v", err)
- }
-
- // Check that destroy killed all processes belonging to the container and
- // waited for them to exit before returning.
- pss, err := containers[0].Sandbox.Processes("")
- if err != nil {
- t.Fatalf("error getting process data from sandbox: %v", err)
- }
- expectedPL := []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if !procListsEqual(pss, expectedPL) {
- t.Errorf("container got process list: %s, want: %s: error: %v",
- procListToString(pss), procListToString(expectedPL), err)
- }
-
- // Check that cont.Destroy is safe to call multiple times.
- if err := containers[1].Destroy(); err != nil {
- t.Errorf("error destroying container: %v", err)
- }
- })
- }
-}
-
-func TestMultiContainerProcesses(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // Note: use curly braces to keep 'sh' process around. Otherwise, shell
- // will just execve into 'sleep' and both containers will look the
- // same.
- specs, ids := createSpecs(
- []string{"sleep", "100"},
- []string{"sh", "-c", "{ sleep 100; }"})
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Check root's container process list doesn't include other containers.
- expectedPL0 := []*control.Process{
- newProcessBuilder().PID(1).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL0); err != nil {
- t.Errorf("failed to wait for process to start: %v", err)
- }
-
- // Same for the other container.
- expectedPL1 := []*control.Process{
- newProcessBuilder().PID(2).Cmd("sh").Process(),
- newProcessBuilder().PID(3).PPID(2).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL1); err != nil {
- t.Errorf("failed to wait for process to start: %v", err)
- }
-
- // Now exec into the second container and verify it shows up in the container.
- args := &control.ExecArgs{
- Filename: "/bin/sleep",
- Argv: []string{"/bin/sleep", "100"},
- }
- if _, err := containers[1].Execute(args); err != nil {
- t.Fatalf("error exec'ing: %v", err)
- }
- expectedPL1 = append(expectedPL1, newProcessBuilder().PID(4).Cmd("sleep").Process())
- if err := waitForProcessList(containers[1], expectedPL1); err != nil {
- t.Errorf("failed to wait for process to start: %v", err)
- }
- // Root container should remain unchanged.
- if err := waitForProcessList(containers[0], expectedPL0); err != nil {
- t.Errorf("failed to wait for process to start: %v", err)
- }
-}
-
-// TestMultiContainerKillAll checks that all process that belong to a container
-// are killed when SIGKILL is sent to *all* processes in that container.
-func TestMultiContainerKillAll(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- for _, tc := range []struct {
- killContainer bool
- }{
- {killContainer: true},
- {killContainer: false},
- } {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- // First container will remain intact while the second container is killed.
- specs, ids := createSpecs(
- []string{app, "task-tree", "--depth=2", "--width=2"},
- []string{app, "task-tree", "--depth=4", "--width=2"})
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Wait until all processes are created.
- rootProcCount := int(math.Pow(2, 3) - 1)
- if err := waitForProcessCount(containers[0], rootProcCount); err != nil {
- t.Fatalf("error waitting for processes: %v", err)
- }
- procCount := int(math.Pow(2, 5) - 1)
- if err := waitForProcessCount(containers[1], procCount); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- // Exec more processes to ensure signal works for exec'd processes too.
- args := &control.ExecArgs{
- Filename: app,
- Argv: []string{app, "task-tree", "--depth=2", "--width=2"},
- }
- if _, err := containers[1].Execute(args); err != nil {
- t.Fatalf("error exec'ing: %v", err)
- }
- // Wait for these new processes to start.
- procCount += int(math.Pow(2, 3) - 1)
- if err := waitForProcessCount(containers[1], procCount); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
-
- if tc.killContainer {
- // First kill the init process to make the container be stopped with
- // processes still running inside.
- containers[1].SignalContainer(syscall.SIGKILL, false)
- op := func() error {
- c, err := Load(conf.RootDir, FullID{ContainerID: ids[1]}, LoadOpts{})
- if err != nil {
- return err
- }
- if c.Status != Stopped {
- return fmt.Errorf("container is not stopped")
- }
- return nil
- }
- if err := testutil.Poll(op, 5*time.Second); err != nil {
- t.Fatalf("container did not stop %q: %v", containers[1].ID, err)
- }
- }
-
- c, err := Load(conf.RootDir, FullID{ContainerID: ids[1]}, LoadOpts{})
- if err != nil {
- t.Fatalf("failed to load child container %q: %v", c.ID, err)
- }
- // Kill'Em All
- if err := c.SignalContainer(syscall.SIGKILL, true); err != nil {
- t.Fatalf("failed to send SIGKILL to container %q: %v", c.ID, err)
- }
-
- // Check that all processes are gone.
- if err := waitForProcessCount(containers[1], 0); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
- // Check that root container was not affected.
- if err := waitForProcessCount(containers[0], rootProcCount); err != nil {
- t.Fatalf("error waiting for processes: %v", err)
- }
- }
-}
-
-func TestMultiContainerDestroyNotStarted(t *testing.T) {
- specs, ids := createSpecs(
- []string{"/bin/sleep", "100"},
- []string{"/bin/sleep", "100"})
-
- conf := testutil.TestConfig(t)
- _, bundleDir, cleanup, err := testutil.SetupContainer(specs[0], conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- rootArgs := Args{
- ID: ids[0],
- Spec: specs[0],
- BundleDir: bundleDir,
- }
- root, err := New(conf, rootArgs)
- if err != nil {
- t.Fatalf("error creating root container: %v", err)
- }
- defer root.Destroy()
- if err := root.Start(conf); err != nil {
- t.Fatalf("error starting root container: %v", err)
- }
-
- // Create and destroy sub-container.
- bundleDir, cleanupSub, err := testutil.SetupBundleDir(specs[1])
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanupSub()
-
- args := Args{
- ID: ids[1],
- Spec: specs[1],
- BundleDir: bundleDir,
- }
- cont, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
-
- // Check that container can be destroyed.
- if err := cont.Destroy(); err != nil {
- t.Fatalf("deleting non-started container failed: %v", err)
- }
-}
-
-// TestMultiContainerDestroyStarting attempts to force a race between start
-// and destroy.
-func TestMultiContainerDestroyStarting(t *testing.T) {
- cmds := make([][]string, 10)
- for i := range cmds {
- cmds[i] = []string{"/bin/sleep", "100"}
- }
- specs, ids := createSpecs(cmds...)
-
- conf := testutil.TestConfig(t)
- rootDir, bundleDir, cleanup, err := testutil.SetupContainer(specs[0], conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- rootArgs := Args{
- ID: ids[0],
- Spec: specs[0],
- BundleDir: bundleDir,
- }
- root, err := New(conf, rootArgs)
- if err != nil {
- t.Fatalf("error creating root container: %v", err)
- }
- defer root.Destroy()
- if err := root.Start(conf); err != nil {
- t.Fatalf("error starting root container: %v", err)
- }
-
- wg := sync.WaitGroup{}
- for i := range cmds {
- if i == 0 {
- continue // skip root container
- }
-
- bundleDir, cleanup, err := testutil.SetupBundleDir(specs[i])
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- rootArgs := Args{
- ID: ids[i],
- Spec: specs[i],
- BundleDir: bundleDir,
- }
- cont, err := New(conf, rootArgs)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
-
- // Container is not thread safe, so load another instance to run in
- // concurrently.
- startCont, err := Load(rootDir, FullID{ContainerID: ids[i]}, LoadOpts{})
- if err != nil {
- t.Fatalf("error loading container: %v", err)
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
- startCont.Start(conf) // ignore failures, start can fail if destroy runs first.
- }()
-
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := cont.Destroy(); err != nil {
- t.Errorf("deleting non-started container failed: %v", err)
- }
- }()
- }
- wg.Wait()
-}
-
-// TestMultiContainerDifferentFilesystems tests that different containers have
-// different root filesystems.
-func TestMultiContainerDifferentFilesystems(t *testing.T) {
- filename := "/foo"
- // Root container will create file and then sleep.
- cmdRoot := []string{"sh", "-c", fmt.Sprintf("touch %q && sleep 100", filename)}
-
- // Child containers will assert that the file does not exist, and will
- // then create it.
- script := fmt.Sprintf("if [ -f %q ]; then exit 1; else touch %q; fi", filename, filename)
- cmd := []string{"sh", "-c", script}
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // Make sure overlay is enabled, and none of the root filesystems are
- // read-only, otherwise we won't be able to create the file.
- conf.Overlay = true
- specs, ids := createSpecs(cmdRoot, cmd, cmd)
- for _, s := range specs {
- s.Root.Readonly = false
- }
-
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Both child containers should exit successfully.
- for i, c := range containers {
- if i == 0 {
- // Don't wait on the root.
- continue
- }
- if ws, err := c.Wait(); err != nil {
- t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es)
- }
- }
-}
-
-// TestMultiContainerContainerDestroyStress tests that IO operations continue
-// to work after containers have been stopped and gofers killed.
-func TestMultiContainerContainerDestroyStress(t *testing.T) {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- // Setup containers. Root container just reaps children, while the others
- // perform some IOs. Children are executed in 3 batches of 10. Within the
- // batch there is overlap between containers starting and being destroyed. In
- // between batches all containers stop before starting another batch.
- cmds := [][]string{{app, "reaper"}}
- const batchSize = 10
- for i := 0; i < 3*batchSize; i++ {
- dir, err := ioutil.TempDir(testutil.TmpDir(), "gofer-stop-test")
- if err != nil {
- t.Fatal("ioutil.TempDir failed:", err)
- }
- defer os.RemoveAll(dir)
-
- cmd := "find /bin -type f | head | xargs -I SRC cp SRC " + dir
- cmds = append(cmds, []string{"sh", "-c", cmd})
- }
- allSpecs, allIDs := createSpecs(cmds...)
-
- // Split up the specs and IDs.
- rootSpec := allSpecs[0]
- rootID := allIDs[0]
- childrenSpecs := allSpecs[1:]
- childrenIDs := allIDs[1:]
-
- conf := testutil.TestConfig(t)
- _, bundleDir, cleanup, err := testutil.SetupContainer(rootSpec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Start root container.
- rootArgs := Args{
- ID: rootID,
- Spec: rootSpec,
- BundleDir: bundleDir,
- }
- root, err := New(conf, rootArgs)
- if err != nil {
- t.Fatalf("error creating root container: %v", err)
- }
- if err := root.Start(conf); err != nil {
- t.Fatalf("error starting root container: %v", err)
- }
- defer root.Destroy()
-
- // Run batches. Each batch starts containers in parallel, then wait and
- // destroy them before starting another batch.
- for i := 0; i < len(childrenSpecs); i += batchSize {
- t.Logf("Starting batch from %d to %d", i, i+batchSize)
- specs := childrenSpecs[i : i+batchSize]
- ids := childrenIDs[i : i+batchSize]
-
- var children []*Container
- for j, spec := range specs {
- bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: ids[j],
- Spec: spec,
- BundleDir: bundleDir,
- }
- child, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- children = append(children, child)
-
- if err := child.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // Give a small gap between containers.
- time.Sleep(50 * time.Millisecond)
- }
- for _, child := range children {
- ws, err := child.Wait()
- if err != nil {
- t.Fatalf("waiting for container: %v", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Fatalf("container failed, waitStatus: %x (%d)", ws, ws.ExitStatus())
- }
- if err := child.Destroy(); err != nil {
- t.Fatalf("error destroying container: %v", err)
- }
- }
- }
-}
-
-// Test that pod shared mounts are properly mounted in 2 containers and that
-// changes from one container is reflected in the other.
-func TestMultiContainerSharedMount(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- podSpec, ids := createSpecs(sleep, sleep)
- mnt0 := specs.Mount{
- Destination: "/mydir/test",
- Source: "/some/dir",
- Type: "tmpfs",
- Options: nil,
- }
- podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0)
-
- mnt1 := mnt0
- mnt1.Destination = "/mydir2/test2"
- podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1)
-
- createSharedMount(mnt0, "test-mount", podSpec...)
-
- containers, cleanup, err := startContainers(conf, podSpec, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- file0 := path.Join(mnt0.Destination, "abc")
- file1 := path.Join(mnt1.Destination, "abc")
- execs := []execDesc{
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-d", mnt0.Destination},
- name: "directory is mounted in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-d", mnt1.Destination},
- name: "directory is mounted in container1",
- },
- {
- c: containers[0],
- cmd: []string{"/bin/touch", file0},
- name: "create file in container0",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-f", file0},
- name: "file appears in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-f", file1},
- name: "file appears in container1",
- },
- {
- c: containers[1],
- cmd: []string{"/bin/rm", file1},
- name: "remove file from container1",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "!", "-f", file0},
- name: "file removed from container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "!", "-f", file1},
- name: "file removed from container1",
- },
- {
- c: containers[1],
- cmd: []string{"/bin/mkdir", file1},
- name: "create directory in container1",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-d", file0},
- name: "dir appears in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-d", file1},
- name: "dir appears in container1",
- },
- {
- c: containers[0],
- cmd: []string{"/bin/rmdir", file0},
- name: "remove directory from container0",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "!", "-d", file0},
- name: "dir removed from container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "!", "-d", file1},
- name: "dir removed from container1",
- },
- }
- execMany(t, execs)
- })
- }
-}
-
-// Test that pod mounts are mounted as readonly when requested.
-func TestMultiContainerSharedMountReadonly(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- podSpec, ids := createSpecs(sleep, sleep)
- mnt0 := specs.Mount{
- Destination: "/mydir/test",
- Source: "/some/dir",
- Type: "tmpfs",
- Options: []string{"ro"},
- }
- podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0)
-
- mnt1 := mnt0
- mnt1.Destination = "/mydir2/test2"
- podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1)
-
- createSharedMount(mnt0, "test-mount", podSpec...)
-
- containers, cleanup, err := startContainers(conf, podSpec, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- file0 := path.Join(mnt0.Destination, "abc")
- file1 := path.Join(mnt1.Destination, "abc")
- execs := []execDesc{
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-d", mnt0.Destination},
- name: "directory is mounted in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-d", mnt1.Destination},
- name: "directory is mounted in container1",
- },
- {
- c: containers[0],
- cmd: []string{"/bin/touch", file0},
- want: 1,
- name: "fails to write to container0",
- },
- {
- c: containers[1],
- cmd: []string{"/bin/touch", file1},
- want: 1,
- name: "fails to write to container1",
- },
- }
- execMany(t, execs)
- })
- }
-}
-
-// Test that shared pod mounts continue to work after container is restarted.
-func TestMultiContainerSharedMountRestart(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"sleep", "100"}
- podSpec, ids := createSpecs(sleep, sleep)
- mnt0 := specs.Mount{
- Destination: "/mydir/test",
- Source: "/some/dir",
- Type: "tmpfs",
- Options: nil,
- }
- podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0)
-
- mnt1 := mnt0
- mnt1.Destination = "/mydir2/test2"
- podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1)
-
- createSharedMount(mnt0, "test-mount", podSpec...)
-
- containers, cleanup, err := startContainers(conf, podSpec, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- file0 := path.Join(mnt0.Destination, "abc")
- file1 := path.Join(mnt1.Destination, "abc")
- execs := []execDesc{
- {
- c: containers[0],
- cmd: []string{"/bin/touch", file0},
- name: "create file in container0",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-f", file0},
- name: "file appears in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-f", file1},
- name: "file appears in container1",
- },
- }
- execMany(t, execs)
-
- containers[1].Destroy()
-
- bundleDir, cleanup, err := testutil.SetupBundleDir(podSpec[1])
- if err != nil {
- t.Fatalf("error restarting container: %v", err)
- }
- defer cleanup()
-
- args := Args{
- ID: ids[1],
- Spec: podSpec[1],
- BundleDir: bundleDir,
- }
- containers[1], err = New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- if err := containers[1].Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- execs = []execDesc{
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-f", file0},
- name: "file is still in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-f", file1},
- name: "file is still in container1",
- },
- {
- c: containers[1],
- cmd: []string{"/bin/rm", file1},
- name: "file removed from container1",
- },
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "!", "-f", file0},
- name: "file removed from container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "!", "-f", file1},
- name: "file removed from container1",
- },
- }
- execMany(t, execs)
- })
- }
-}
-
-// Test that unsupported pod mounts options are ignored when matching master and
-// replica mounts.
-func TestMultiContainerSharedMountUnsupportedOptions(t *testing.T) {
- for name, conf := range configs(t, all...) {
- t.Run(name, func(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"/bin/sleep", "100"}
- podSpec, ids := createSpecs(sleep, sleep)
- mnt0 := specs.Mount{
- Destination: "/mydir/test",
- Source: "/some/dir",
- Type: "tmpfs",
- Options: []string{"rw", "rbind", "relatime"},
- }
- podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0)
-
- mnt1 := mnt0
- mnt1.Destination = "/mydir2/test2"
- mnt1.Options = []string{"rw", "nosuid"}
- podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1)
-
- createSharedMount(mnt0, "test-mount", podSpec...)
-
- containers, cleanup, err := startContainers(conf, podSpec, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- execs := []execDesc{
- {
- c: containers[0],
- cmd: []string{"/usr/bin/test", "-d", mnt0.Destination},
- name: "directory is mounted in container0",
- },
- {
- c: containers[1],
- cmd: []string{"/usr/bin/test", "-d", mnt1.Destination},
- name: "directory is mounted in container1",
- },
- }
- execMany(t, execs)
- })
- }
-}
-
-// Test that one container can send an FD to another container, even though
-// they have distinct MountNamespaces.
-func TestMultiContainerMultiRootCanHandleFDs(t *testing.T) {
- app, err := testutil.FindFile("test/cmd/test_app/test_app")
- if err != nil {
- t.Fatal("error finding test_app:", err)
- }
-
- // We set up two containers with one shared mount that is used for a
- // shared socket. The first container will send an FD over the socket
- // to the second container. The FD corresponds to a file in the first
- // container's mount namespace that is not part of the second
- // container's mount namespace. However, the second container still
- // should be able to read the FD.
-
- // Create a shared mount where we will put the socket.
- sharedMnt := specs.Mount{
- Destination: "/mydir/test",
- Type: "tmpfs",
- // Shared mounts need a Source, even for tmpfs. It is only used
- // to match up different shared mounts inside the pod.
- Source: "/some/dir",
- }
- socketPath := filepath.Join(sharedMnt.Destination, "socket")
-
- // Create a writeable tmpfs mount where the FD sender app will create
- // files to send. This will only be mounted in the FD sender.
- writeableMnt := specs.Mount{
- Destination: "/tmp",
- Type: "tmpfs",
- }
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // Create the specs.
- specs, ids := createSpecs(
- []string{"sleep", "1000"},
- []string{app, "fd_sender", "--socket", socketPath},
- []string{app, "fd_receiver", "--socket", socketPath},
- )
- createSharedMount(sharedMnt, "shared-mount", specs...)
- specs[1].Mounts = append(specs[2].Mounts, sharedMnt, writeableMnt)
- specs[2].Mounts = append(specs[1].Mounts, sharedMnt)
-
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Both containers should exit successfully.
- for _, c := range containers[1:] {
- if ws, err := c.Wait(); err != nil {
- t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es)
- }
- }
-}
-
-// Test that container is destroyed when Gofer is killed.
-func TestMultiContainerGoferKilled(t *testing.T) {
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- sleep := []string{"sleep", "100"}
- specs, ids := createSpecs(sleep, sleep, sleep)
- containers, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Ensure container is running
- c := containers[2]
- expectedPL := []*control.Process{
- newProcessBuilder().PID(3).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(c, expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- // Kill container's gofer.
- if err := syscall.Kill(c.GoferPid, syscall.SIGKILL); err != nil {
- t.Fatalf("syscall.Kill(%d, SIGKILL)=%v", c.GoferPid, err)
- }
-
- // Wait until container stops.
- if err := waitForProcessList(c, nil); err != nil {
- t.Errorf("Container %q was not stopped after gofer death: %v", c.ID, err)
- }
-
- // Check that container isn't running anymore.
- if _, err := execute(c, "/bin/true"); err == nil {
- t.Fatalf("Container %q was not stopped after gofer death", c.ID)
- }
-
- // Check that other containers are unaffected.
- for i, c := range containers {
- if i == 2 {
- continue // container[2] has been killed.
- }
- pl := []*control.Process{
- newProcessBuilder().PID(kernel.ThreadID(i + 1)).Cmd("sleep").Process(),
- }
- if err := waitForProcessList(c, pl); err != nil {
- t.Errorf("Container %q was affected by another container: %v", c.ID, err)
- }
- if _, err := execute(c, "/bin/true"); err != nil {
- t.Fatalf("Container %q was affected by another container: %v", c.ID, err)
- }
- }
-
- // Kill root container's gofer to bring entire sandbox down.
- c = containers[0]
- if err := syscall.Kill(c.GoferPid, syscall.SIGKILL); err != nil {
- t.Fatalf("syscall.Kill(%d, SIGKILL)=%v", c.GoferPid, err)
- }
-
- // Wait until sandbox stops. waitForProcessList will loop until sandbox exits
- // and RPC errors out.
- impossiblePL := []*control.Process{
- newProcessBuilder().Cmd("non-existent-process").Process(),
- }
- if err := waitForProcessList(c, impossiblePL); err == nil {
- t.Fatalf("Sandbox was not killed after gofer death")
- }
-
- // Check that entire sandbox isn't running anymore.
- for _, c := range containers {
- if _, err := execute(c, "/bin/true"); err == nil {
- t.Fatalf("Container %q was not stopped after gofer death", c.ID)
- }
- }
-}
-
-func TestMultiContainerLoadSandbox(t *testing.T) {
- sleep := []string{"sleep", "100"}
- specs, ids := createSpecs(sleep, sleep, sleep)
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- // Create containers for the sandbox.
- wants, cleanup, err := startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Then create unrelated containers.
- for i := 0; i < 3; i++ {
- specs, ids = createSpecs(sleep, sleep, sleep)
- _, cleanup, err = startContainers(conf, specs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
- }
-
- // Create an unrelated directory under root.
- dir := filepath.Join(conf.RootDir, "not-a-container")
- if err := os.MkdirAll(dir, 0755); err != nil {
- t.Fatalf("os.MkdirAll(%q)=%v", dir, err)
- }
-
- // Create a valid but empty container directory.
- randomCID := testutil.RandomContainerID()
- dir = filepath.Join(conf.RootDir, randomCID)
- if err := os.MkdirAll(dir, 0755); err != nil {
- t.Fatalf("os.MkdirAll(%q)=%v", dir, err)
- }
-
- // Load the sandbox and check that the correct containers were returned.
- id := wants[0].Sandbox.ID
- gots, err := loadSandbox(conf.RootDir, id)
- if err != nil {
- t.Fatalf("loadSandbox()=%v", err)
- }
- wantIDs := make(map[string]struct{})
- for _, want := range wants {
- wantIDs[want.ID] = struct{}{}
- }
- for _, got := range gots {
- if got.Sandbox.ID != id {
- t.Errorf("wrong sandbox ID, got: %v, want: %v", got.Sandbox.ID, id)
- }
- if _, ok := wantIDs[got.ID]; !ok {
- t.Errorf("wrong container ID, got: %v, wants: %v", got.ID, wantIDs)
- }
- delete(wantIDs, got.ID)
- }
- if len(wantIDs) != 0 {
- t.Errorf("containers not found: %v", wantIDs)
- }
-}
-
-// TestMultiContainerRunNonRoot checks that child container can be configured
-// when running as non-privileged user.
-func TestMultiContainerRunNonRoot(t *testing.T) {
- cmdRoot := []string{"/bin/sleep", "100"}
- cmdSub := []string{"/bin/true"}
- podSpecs, ids := createSpecs(cmdRoot, cmdSub)
-
- // User running inside container can't list '$TMP/blocked' and would fail to
- // mount it.
- blocked, err := ioutil.TempDir(testutil.TmpDir(), "blocked")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
- if err := os.Chmod(blocked, 0700); err != nil {
- t.Fatalf("os.MkDir(%q) failed: %v", blocked, err)
- }
- dir := path.Join(blocked, "test")
- if err := os.Mkdir(dir, 0755); err != nil {
- t.Fatalf("os.MkDir(%q) failed: %v", dir, err)
- }
-
- src, err := ioutil.TempDir(testutil.TmpDir(), "src")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed: %v", err)
- }
-
- // Set a random user/group with no access to "blocked" dir.
- podSpecs[1].Process.User.UID = 343
- podSpecs[1].Process.User.GID = 2401
- podSpecs[1].Process.Capabilities = nil
-
- podSpecs[1].Mounts = append(podSpecs[1].Mounts, specs.Mount{
- Destination: dir,
- Source: src,
- Type: "bind",
- })
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
-
- conf := testutil.TestConfig(t)
- conf.RootDir = rootDir
-
- pod, cleanup, err := startContainers(conf, podSpecs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Once all containers are started, wait for the child container to exit.
- // This means that the volume was mounted properly.
- ws, err := pod[1].Wait()
- if err != nil {
- t.Fatalf("running child container: %v", err)
- }
- if !ws.Exited() || ws.ExitStatus() != 0 {
- t.Fatalf("child container failed, waitStatus: %v", ws)
- }
-}
-
-// TestMultiContainerHomeEnvDir tests that the HOME environment variable is set
-// for root containers, sub-containers, and exec'ed processes.
-func TestMultiContainerHomeEnvDir(t *testing.T) {
- // NOTE: Don't use overlay since we need changes to persist to the temp dir
- // outside the sandbox.
- for testName, conf := range configs(t, noOverlay...) {
- t.Run(testName, func(t *testing.T) {
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Create temp files we can write the value of $HOME to.
- homeDirs := map[string]*os.File{}
- for _, name := range []string{"root", "sub", "exec"} {
- homeFile, err := ioutil.TempFile(testutil.TmpDir(), name)
- if err != nil {
- t.Fatalf("creating temp file: %v", err)
- }
- homeDirs[name] = homeFile
- }
-
- // We will sleep in the root container in order to ensure that the root
- //container doesn't terminate before sub containers can be created.
- rootCmd := []string{"/bin/sh", "-c", fmt.Sprintf(`printf "$HOME" > %s; sleep 1000`, homeDirs["root"].Name())}
- subCmd := []string{"/bin/sh", "-c", fmt.Sprintf(`printf "$HOME" > %s`, homeDirs["sub"].Name())}
- execCmd := fmt.Sprintf(`printf "$HOME" > %s`, homeDirs["exec"].Name())
-
- // Setup the containers, a root container and sub container.
- specConfig, ids := createSpecs(rootCmd, subCmd)
- containers, cleanup, err := startContainers(conf, specConfig, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Exec into the root container synchronously.
- if _, err := execute(containers[0], "/bin/sh", "-c", execCmd); err != nil {
- t.Errorf("error executing %+v: %v", execCmd, err)
- }
-
- // Wait for the subcontainer to finish.
- _, err = containers[1].Wait()
- if err != nil {
- t.Errorf("wait on child container: %v", err)
- }
-
- // Wait until after `env` has executed.
- expectedProc := newProcessBuilder().Cmd("sleep").Process()
- if err := waitForProcess(containers[0], expectedProc); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
-
- // Check the written files.
- for name, tmpFile := range homeDirs {
- dirBytes, err := ioutil.ReadAll(tmpFile)
- if err != nil {
- t.Fatalf("reading %s temp file: %v", name, err)
- }
- got := string(dirBytes)
-
- want := "/"
- if got != want {
- t.Errorf("%s $HOME incorrect: got: %q, want: %q", name, got, want)
- }
- }
-
- })
- }
-}
-
-func TestMultiContainerEvent(t *testing.T) {
- conf := testutil.TestConfig(t)
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Setup the containers.
- sleep := []string{"/bin/sleep", "100"}
- busy := []string{"/bin/bash", "-c", "i=0 ; while true ; do (( i += 1 )) ; done"}
- quick := []string{"/bin/true"}
- podSpec, ids := createSpecs(sleep, busy, quick)
- containers, cleanup, err := startContainers(conf, podSpec, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- for _, cont := range containers {
- t.Logf("Running containerd %s", cont.ID)
- }
-
- // Wait for last container to stabilize the process count that is
- // checked further below.
- if ws, err := containers[2].Wait(); err != nil || ws != 0 {
- t.Fatalf("Container.Wait, status: %v, err: %v", ws, err)
- }
- expectedPL := []*control.Process{
- newProcessBuilder().Cmd("sleep").Process(),
- }
- if err := waitForProcessList(containers[0], expectedPL); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- expectedPL = []*control.Process{
- newProcessBuilder().Cmd("bash").Process(),
- }
- if err := waitForProcessList(containers[1], expectedPL); err != nil {
- t.Errorf("failed to wait for bash to start: %v", err)
- }
-
- // Check events for running containers.
- var prevUsage uint64
- for _, cont := range containers[:2] {
- ret, err := cont.Event()
- if err != nil {
- t.Errorf("Container.Events(): %v", err)
- }
- evt := ret.Event
- if want := "stats"; evt.Type != want {
- t.Errorf("Wrong event type, want: %s, got: %s", want, evt.Type)
- }
- if cont.ID != evt.ID {
- t.Errorf("Wrong container ID, want: %s, got: %s", cont.ID, evt.ID)
- }
- // One process per remaining container.
- if got, want := evt.Data.Pids.Current, uint64(2); got != want {
- t.Errorf("Wrong number of PIDs, want: %d, got: %d", want, got)
- }
-
- // Both remaining containers should have nonzero usage, and
- // 'busy' should have higher usage than 'sleep'.
- usage := evt.Data.CPU.Usage.Total
- if usage == 0 {
- t.Errorf("Running container should report nonzero CPU usage, but got %d", usage)
- }
- if usage <= prevUsage {
- t.Errorf("Expected container %s to use more than %d ns of CPU, but used %d", cont.ID, prevUsage, usage)
- }
- t.Logf("Container %s usage: %d", cont.ID, usage)
- prevUsage = usage
-
- // The exited container should have a usage of zero.
- if exited := ret.ContainerUsage[containers[2].ID]; exited != 0 {
- t.Errorf("Exited container should report 0 CPU usage, but got %d", exited)
- }
- }
-
- // Check that stop and destroyed containers return error.
- if err := containers[1].Destroy(); err != nil {
- t.Fatalf("container.Destroy: %v", err)
- }
- for _, cont := range containers[1:] {
- _, err := cont.Event()
- if err == nil {
- t.Errorf("Container.Events() should have failed, cid:%s, state: %v", cont.ID, cont.Status)
- }
- }
-}
-
-// Tests that duplicate variables in the spec are merged into a single one.
-func TestDuplicateEnvVariable(t *testing.T) {
- conf := testutil.TestConfig(t)
-
- rootDir, cleanup, err := testutil.SetupRootDir()
- if err != nil {
- t.Fatalf("error creating root dir: %v", err)
- }
- defer cleanup()
- conf.RootDir = rootDir
-
- // Create files to dump `env` output.
- files := [3]*os.File{}
- for i := 0; i < len(files); i++ {
- var err error
- files[i], err = ioutil.TempFile(testutil.TmpDir(), "env-var-test")
- if err != nil {
- t.Fatalf("creating temp file: %v", err)
- }
- defer files[i].Close()
- defer os.Remove(files[i].Name())
- }
-
- // Setup the containers. Use root container to test exec too.
- cmd1 := fmt.Sprintf("env > %q; sleep 1000", files[0].Name())
- cmd2 := fmt.Sprintf("env > %q", files[1].Name())
- cmdExec := fmt.Sprintf("env > %q", files[2].Name())
- testSpecs, ids := createSpecs([]string{"/bin/sh", "-c", cmd1}, []string{"/bin/sh", "-c", cmd2})
- testSpecs[0].Process.Env = append(testSpecs[0].Process.Env, "VAR=foo", "VAR=bar")
- testSpecs[1].Process.Env = append(testSpecs[1].Process.Env, "VAR=foo", "VAR=bar")
-
- containers, cleanup, err := startContainers(conf, testSpecs, ids)
- if err != nil {
- t.Fatalf("error starting containers: %v", err)
- }
- defer cleanup()
-
- // Wait until after `env` has executed.
- expectedProc := newProcessBuilder().Cmd("sleep").Process()
- if err := waitForProcess(containers[0], expectedProc); err != nil {
- t.Errorf("failed to wait for sleep to start: %v", err)
- }
- if ws, err := containers[1].Wait(); err != nil {
- t.Errorf("failed to wait container 1: %v", err)
- } else if es := ws.ExitStatus(); es != 0 {
- t.Errorf("container %s exited with non-zero status: %v", containers[1].ID, es)
- }
-
- execArgs := &control.ExecArgs{
- Filename: "/bin/sh",
- Argv: []string{"/bin/sh", "-c", cmdExec},
- Envv: []string{"VAR=foo", "VAR=bar"},
- }
- if ws, err := containers[0].executeSync(execArgs); err != nil || ws.ExitStatus() != 0 {
- t.Fatalf("exec failed, ws: %v, err: %v", ws, err)
- }
-
- // Now read and check that none of the env has repeated values.
- for _, file := range files {
- out, err := ioutil.ReadAll(file)
- if err != nil {
- t.Fatal(err)
- }
- t.Logf("Checking env %q:\n%s", file.Name(), out)
- envs := make(map[string]string)
- for _, line := range strings.Split(string(out), "\n") {
- if len(line) == 0 {
- continue
- }
- envVar := strings.SplitN(line, "=", 2)
- if len(envVar) != 2 {
- t.Fatalf("invalid env variable: %s", line)
- }
- key := envVar[0]
- if val, ok := envs[key]; ok {
- t.Errorf("env variable %q is duplicated: %q and %q", key, val, envVar[1])
- }
- envs[key] = envVar[1]
- }
- if _, ok := envs["VAR"]; !ok {
- t.Errorf("variable VAR missing: %v", envs)
- }
- }
-}
diff --git a/runsc/container/shared_volume_test.go b/runsc/container/shared_volume_test.go
deleted file mode 100644
index cb5bffb89..000000000
--- a/runsc/container/shared_volume_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-// 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 container
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "gvisor.dev/gvisor/pkg/sentry/control"
- "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/runsc/config"
-)
-
-// TestSharedVolume checks that modifications to a volume mount are propagated
-// into and out of the sandbox.
-func TestSharedVolume(t *testing.T) {
- conf := testutil.TestConfig(t)
- conf.FileAccess = config.FileAccessShared
-
- // Main process just sleeps. We will use "exec" to probe the state of
- // the filesystem.
- spec := testutil.NewSpecWithArgs("sleep", "1000")
-
- dir, err := ioutil.TempDir(testutil.TmpDir(), "shared-volume-test")
- if err != nil {
- t.Fatalf("TempDir failed: %v", err)
- }
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // File that will be used to check consistency inside/outside sandbox.
- filename := filepath.Join(dir, "file")
-
- // File does not exist yet. Reading from the sandbox should fail.
- argsTestFile := &control.ExecArgs{
- Filename: "/usr/bin/test",
- Argv: []string{"test", "-f", filename},
- }
- if ws, err := c.executeSync(argsTestFile); err != nil {
- t.Fatalf("unexpected error testing file %q: %v", filename, err)
- } else if ws.ExitStatus() == 0 {
- t.Errorf("test %q exited with code %v, wanted not zero", ws.ExitStatus(), err)
- }
-
- // Create the file from outside of the sandbox.
- if err := ioutil.WriteFile(filename, []byte("foobar"), 0777); err != nil {
- t.Fatalf("error writing to file %q: %v", filename, err)
- }
-
- // Now we should be able to test the file from within the sandbox.
- if ws, err := c.executeSync(argsTestFile); err != nil {
- t.Fatalf("unexpected error testing file %q: %v", filename, err)
- } else if ws.ExitStatus() != 0 {
- t.Errorf("test %q exited with code %v, wanted zero", filename, ws.ExitStatus())
- }
-
- // Rename the file from outside of the sandbox.
- newFilename := filepath.Join(dir, "newfile")
- if err := os.Rename(filename, newFilename); err != nil {
- t.Fatalf("os.Rename(%q, %q) failed: %v", filename, newFilename, err)
- }
-
- // File should no longer exist at the old path within the sandbox.
- if ws, err := c.executeSync(argsTestFile); err != nil {
- t.Fatalf("unexpected error testing file %q: %v", filename, err)
- } else if ws.ExitStatus() == 0 {
- t.Errorf("test %q exited with code %v, wanted not zero", filename, ws.ExitStatus())
- }
-
- // We should be able to test the new filename from within the sandbox.
- argsTestNewFile := &control.ExecArgs{
- Filename: "/usr/bin/test",
- Argv: []string{"test", "-f", newFilename},
- }
- if ws, err := c.executeSync(argsTestNewFile); err != nil {
- t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
- } else if ws.ExitStatus() != 0 {
- t.Errorf("test %q exited with code %v, wanted zero", newFilename, ws.ExitStatus())
- }
-
- // Delete the renamed file from outside of the sandbox.
- if err := os.Remove(newFilename); err != nil {
- t.Fatalf("error removing file %q: %v", filename, err)
- }
-
- // Renamed file should no longer exist at the old path within the sandbox.
- if ws, err := c.executeSync(argsTestNewFile); err != nil {
- t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
- } else if ws.ExitStatus() == 0 {
- t.Errorf("test %q exited with code %v, wanted not zero", newFilename, ws.ExitStatus())
- }
-
- // Now create the file from WITHIN the sandbox.
- argsTouch := &control.ExecArgs{
- Filename: "/usr/bin/touch",
- Argv: []string{"touch", filename},
- KUID: auth.KUID(os.Getuid()),
- KGID: auth.KGID(os.Getgid()),
- }
- if ws, err := c.executeSync(argsTouch); err != nil {
- t.Fatalf("unexpected error touching file %q: %v", filename, err)
- } else if ws.ExitStatus() != 0 {
- t.Errorf("touch %q exited with code %v, wanted zero", filename, ws.ExitStatus())
- }
-
- // File should exist outside the sandbox.
- if _, err := os.Stat(filename); err != nil {
- t.Errorf("stat %q got error %v, wanted nil", filename, err)
- }
-
- // File should exist outside the sandbox.
- if _, err := os.Stat(filename); err != nil {
- t.Errorf("stat %q got error %v, wanted nil", filename, err)
- }
-
- // Delete the file from within the sandbox.
- argsRemove := &control.ExecArgs{
- Filename: "/bin/rm",
- Argv: []string{"rm", filename},
- }
- if ws, err := c.executeSync(argsRemove); err != nil {
- t.Fatalf("unexpected error removing file %q: %v", filename, err)
- } else if ws.ExitStatus() != 0 {
- t.Errorf("remove %q exited with code %v, wanted zero", filename, ws.ExitStatus())
- }
-
- // File should not exist outside the sandbox.
- if _, err := os.Stat(filename); !os.IsNotExist(err) {
- t.Errorf("stat %q got error %v, wanted ErrNotExist", filename, err)
- }
-}
-
-func checkFile(c *Container, filename string, want []byte) error {
- cpy := filename + ".copy"
- if _, err := execute(c, "/bin/cp", "-f", filename, cpy); err != nil {
- return fmt.Errorf("unexpected error copying file %q to %q: %v", filename, cpy, err)
- }
- got, err := ioutil.ReadFile(cpy)
- if err != nil {
- return fmt.Errorf("Error reading file %q: %v", filename, err)
- }
- if !bytes.Equal(got, want) {
- return fmt.Errorf("file content inside the sandbox is wrong, got: %q, want: %q", got, want)
- }
- return nil
-}
-
-// TestSharedVolumeFile tests that changes to file content outside the sandbox
-// is reflected inside.
-func TestSharedVolumeFile(t *testing.T) {
- conf := testutil.TestConfig(t)
- conf.FileAccess = config.FileAccessShared
-
- // Main process just sleeps. We will use "exec" to probe the state of
- // the filesystem.
- spec := testutil.NewSpecWithArgs("sleep", "1000")
-
- dir, err := ioutil.TempDir(testutil.TmpDir(), "shared-volume-test")
- if err != nil {
- t.Fatalf("TempDir failed: %v", err)
- }
-
- _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
- if err != nil {
- t.Fatalf("error setting up container: %v", err)
- }
- defer cleanup()
-
- // Create and start the container.
- args := Args{
- ID: testutil.RandomContainerID(),
- Spec: spec,
- BundleDir: bundleDir,
- }
- c, err := New(conf, args)
- if err != nil {
- t.Fatalf("error creating container: %v", err)
- }
- defer c.Destroy()
- if err := c.Start(conf); err != nil {
- t.Fatalf("error starting container: %v", err)
- }
-
- // File that will be used to check consistency inside/outside sandbox.
- filename := filepath.Join(dir, "file")
-
- // Write file from outside the container and check that the same content is
- // read inside.
- want := []byte("host-")
- if err := ioutil.WriteFile(filename, []byte(want), 0666); err != nil {
- t.Fatalf("Error writing to %q: %v", filename, err)
- }
- if err := checkFile(c, filename, want); err != nil {
- t.Fatal(err.Error())
- }
-
- // Append to file inside the container and check that content is not lost.
- if _, err := execute(c, "/bin/bash", "-c", "echo -n sandbox- >> "+filename); err != nil {
- t.Fatalf("unexpected error appending file %q: %v", filename, err)
- }
- want = []byte("host-sandbox-")
- if err := checkFile(c, filename, want); err != nil {
- t.Fatal(err.Error())
- }
-
- // Write again from outside the container and check that the same content is
- // read inside.
- f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0)
- if err != nil {
- t.Fatalf("Error openning file %q: %v", filename, err)
- }
- defer f.Close()
- if _, err := f.Write([]byte("host")); err != nil {
- t.Fatalf("Error writing to file %q: %v", filename, err)
- }
- want = []byte("host-sandbox-host")
- if err := checkFile(c, filename, want); err != nil {
- t.Fatal(err.Error())
- }
-
- // Shrink file outside and check that the same content is read inside.
- if err := f.Truncate(5); err != nil {
- t.Fatalf("Error truncating file %q: %v", filename, err)
- }
- want = want[:5]
- if err := checkFile(c, filename, want); err != nil {
- t.Fatal(err.Error())
- }
-}
diff --git a/runsc/flag/BUILD b/runsc/flag/BUILD
deleted file mode 100644
index 5cb7604a8..000000000
--- a/runsc/flag/BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "flag",
- srcs = ["flag.go"],
- visibility = ["//:sandbox"],
-)
diff --git a/runsc/flag/flag_state_autogen.go b/runsc/flag/flag_state_autogen.go
new file mode 100644
index 000000000..933063e6c
--- /dev/null
+++ b/runsc/flag/flag_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package flag
diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD
deleted file mode 100644
index 3280b74fe..000000000
--- a/runsc/fsgofer/BUILD
+++ /dev/null
@@ -1,39 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "fsgofer",
- srcs = [
- "fsgofer.go",
- "fsgofer_amd64_unsafe.go",
- "fsgofer_arm64_unsafe.go",
- "fsgofer_unsafe.go",
- ],
- visibility = ["//runsc:__subpackages__"],
- deps = [
- "//pkg/cleanup",
- "//pkg/fd",
- "//pkg/log",
- "//pkg/p9",
- "//pkg/sync",
- "//pkg/syserr",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "fsgofer_test",
- size = "small",
- srcs = ["fsgofer_test.go"],
- library = ":fsgofer",
- deps = [
- "//pkg/fd",
- "//pkg/log",
- "//pkg/p9",
- "//pkg/test/testutil",
- "//runsc/specutils",
- "@com_github_syndtr_gocapability//capability:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/fsgofer/filter/BUILD b/runsc/fsgofer/filter/BUILD
deleted file mode 100644
index 82b48ef32..000000000
--- a/runsc/fsgofer/filter/BUILD
+++ /dev/null
@@ -1,26 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "filter",
- srcs = [
- "config.go",
- "config_amd64.go",
- "config_arm64.go",
- "extra_filters.go",
- "extra_filters_msan.go",
- "extra_filters_race.go",
- "filter.go",
- ],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "//pkg/abi/linux",
- "//pkg/flipcall",
- "//pkg/log",
- "//pkg/seccomp",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/fsgofer/filter/filter_amd64_state_autogen.go b/runsc/fsgofer/filter/filter_amd64_state_autogen.go
new file mode 100644
index 000000000..0f27e5568
--- /dev/null
+++ b/runsc/fsgofer/filter/filter_amd64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package filter
diff --git a/runsc/fsgofer/filter/filter_arm64_state_autogen.go b/runsc/fsgofer/filter/filter_arm64_state_autogen.go
new file mode 100644
index 000000000..e87cf5af7
--- /dev/null
+++ b/runsc/fsgofer/filter/filter_arm64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package filter
diff --git a/runsc/fsgofer/filter/filter_race_state_autogen.go b/runsc/fsgofer/filter/filter_race_state_autogen.go
new file mode 100644
index 000000000..c4a858e80
--- /dev/null
+++ b/runsc/fsgofer/filter/filter_race_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build race
+
+package filter
diff --git a/runsc/fsgofer/filter/filter_state_autogen.go b/runsc/fsgofer/filter/filter_state_autogen.go
new file mode 100644
index 000000000..41ff99424
--- /dev/null
+++ b/runsc/fsgofer/filter/filter_state_autogen.go
@@ -0,0 +1,6 @@
+// automatically generated by stateify.
+
+// +build !msan,!race
+// +build msan
+
+package filter
diff --git a/runsc/fsgofer/fsgofer_amd64_unsafe_state_autogen.go b/runsc/fsgofer/fsgofer_amd64_unsafe_state_autogen.go
new file mode 100644
index 000000000..df6721aaa
--- /dev/null
+++ b/runsc/fsgofer/fsgofer_amd64_unsafe_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package fsgofer
diff --git a/runsc/fsgofer/fsgofer_arm64_unsafe_state_autogen.go b/runsc/fsgofer/fsgofer_arm64_unsafe_state_autogen.go
new file mode 100644
index 000000000..d2a18c61c
--- /dev/null
+++ b/runsc/fsgofer/fsgofer_arm64_unsafe_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package fsgofer
diff --git a/runsc/fsgofer/fsgofer_state_autogen.go b/runsc/fsgofer/fsgofer_state_autogen.go
new file mode 100644
index 000000000..d2f978fb9
--- /dev/null
+++ b/runsc/fsgofer/fsgofer_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package fsgofer
diff --git a/runsc/fsgofer/fsgofer_test.go b/runsc/fsgofer/fsgofer_test.go
deleted file mode 100644
index 99ea9bd32..000000000
--- a/runsc/fsgofer/fsgofer_test.go
+++ /dev/null
@@ -1,1122 +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 fsgofer
-
-import (
- "fmt"
- "io/ioutil"
- "net"
- "os"
- "path"
- "path/filepath"
- "testing"
-
- "github.com/syndtr/gocapability/capability"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/fd"
- "gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/p9"
- "gvisor.dev/gvisor/pkg/test/testutil"
- "gvisor.dev/gvisor/runsc/specutils"
-)
-
-// Nodoby is the standard UID/GID for the nobody user/group.
-const nobody = 65534
-
-var allOpenFlags = []p9.OpenFlags{p9.ReadOnly, p9.WriteOnly, p9.ReadWrite}
-
-var (
- allTypes = []uint32{unix.S_IFREG, unix.S_IFDIR, unix.S_IFLNK}
-
- // allConfs is set in init().
- allConfs []Config
-
- rwConfs = []Config{{ROMount: false}}
- roConfs = []Config{{ROMount: true}}
-)
-
-func init() {
- log.SetLevel(log.Debug)
-
- allConfs = append(allConfs, rwConfs...)
- allConfs = append(allConfs, roConfs...)
-
- if err := OpenProcSelfFD(); err != nil {
- panic(err)
- }
-}
-
-func configTestName(conf *Config) string {
- if conf.ROMount {
- return "ROMount"
- }
- return "RWMount"
-}
-
-func assertPanic(t *testing.T, f func()) {
- defer func() {
- if r := recover(); r == nil {
- t.Errorf("function did not panic")
- }
- }()
- f()
-}
-
-func testReadWrite(f p9.File, flags p9.OpenFlags, content []byte) error {
- want := make([]byte, len(content))
- copy(want, content)
-
- b := []byte("test-1-2-3")
- w, err := f.WriteAt(b, uint64(len(content)))
- if flags == p9.WriteOnly || flags == p9.ReadWrite {
- if err != nil {
- return fmt.Errorf("WriteAt(): %v", err)
- }
- if w != len(b) {
- return fmt.Errorf("WriteAt() was partial, got: %d, want: %d", w, len(b))
- }
- want = append(want, b...)
- } else {
- if e, ok := err.(unix.Errno); !ok || e != unix.EBADF {
- return fmt.Errorf("WriteAt() should have failed, got: %d, want: EBADFD", err)
- }
- }
-
- rBuf := make([]byte, len(want))
- r, err := f.ReadAt(rBuf, 0)
- if flags == p9.ReadOnly || flags == p9.ReadWrite {
- if err != nil {
- return fmt.Errorf("ReadAt(): %v", err)
- }
- if r != len(rBuf) {
- return fmt.Errorf("ReadAt() was partial, got: %d, want: %d", r, len(rBuf))
- }
- if string(rBuf) != string(want) {
- return fmt.Errorf("ReadAt() wrong data, got: %s, want: %s", string(rBuf), want)
- }
- } else {
- if e, ok := err.(unix.Errno); !ok || e != unix.EBADF {
- return fmt.Errorf("ReadAt() should have failed, got: %d, want: EBADFD", err)
- }
- }
- return nil
-}
-
-type state struct {
- root *localFile
- file *localFile
- conf Config
- fileType uint32
-}
-
-func (s state) String() string {
- return fmt.Sprintf("type(%v)", s.fileType)
-}
-
-func typeName(fileType uint32) string {
- switch fileType {
- case unix.S_IFREG:
- return "file"
- case unix.S_IFDIR:
- return "directory"
- case unix.S_IFLNK:
- return "symlink"
- default:
- panic(fmt.Sprintf("invalid file type for test: %d", fileType))
- }
-}
-
-func runAll(t *testing.T, test func(*testing.T, state)) {
- runCustom(t, allTypes, allConfs, test)
-}
-
-func runCustom(t *testing.T, types []uint32, confs []Config, test func(*testing.T, state)) {
- for _, c := range confs {
- for _, ft := range types {
- name := fmt.Sprintf("%s/%s", configTestName(&c), typeName(ft))
- t.Run(name, func(t *testing.T) {
- path, name, err := setup(ft)
- if err != nil {
- t.Fatalf("%v", err)
- }
- defer os.RemoveAll(path)
-
- a, err := NewAttachPoint(path, c)
- if err != nil {
- t.Fatalf("NewAttachPoint failed: %v", err)
- }
- root, err := a.Attach()
- if err != nil {
- t.Fatalf("Attach failed, err: %v", err)
- }
-
- _, file, err := root.Walk([]string{name})
- if err != nil {
- root.Close()
- t.Fatalf("root.Walk({%q}) failed, err: %v", "symlink", err)
- }
-
- st := state{
- root: root.(*localFile),
- file: file.(*localFile),
- conf: c,
- fileType: ft,
- }
- test(t, st)
- file.Close()
- root.Close()
- })
- }
- }
-}
-
-func setup(fileType uint32) (string, string, error) {
- path, err := ioutil.TempDir(testutil.TmpDir(), "root-")
- if err != nil {
- return "", "", fmt.Errorf("ioutil.TempDir() failed, err: %v", err)
- }
-
- // First attach with writable configuration to setup tree.
- a, err := NewAttachPoint(path, Config{})
- if err != nil {
- return "", "", err
- }
- root, err := a.Attach()
- if err != nil {
- return "", "", fmt.Errorf("Attach failed, err: %v", err)
- }
- defer root.Close()
-
- var name string
- switch fileType {
- case unix.S_IFREG:
- name = "file"
- fd, f, _, _, err := root.Create(name, p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- return "", "", fmt.Errorf("createFile(root, %q) failed, err: %v", "test", err)
- }
- if fd != nil {
- fd.Close()
- }
- defer f.Close()
- case unix.S_IFDIR:
- name = "dir"
- if _, err := root.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- return "", "", fmt.Errorf("root.MkDir(%q) failed, err: %v", name, err)
- }
- case unix.S_IFLNK:
- name = "symlink"
- if _, err := root.Symlink("/some/target", name, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- return "", "", fmt.Errorf("root.Symlink(%q) failed, err: %v", name, err)
- }
- default:
- panic(fmt.Sprintf("unknown file type %v", fileType))
- }
- return path, name, nil
-}
-
-func createFile(dir *localFile, name string) (*localFile, error) {
- _, f, _, _, err := dir.Create(name, p9.ReadWrite, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- return nil, err
- }
- return f.(*localFile), nil
-}
-
-func TestReadWrite(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- child, err := createFile(s.file, "test")
- if err != nil {
- t.Fatalf("%v: createFile() failed, err: %v", s, err)
- }
- defer child.Close()
- want := []byte("foobar")
- w, err := child.WriteAt(want, 0)
- if err != nil {
- t.Fatalf("%v: Write() failed, err: %v", s, err)
- }
- if w != len(want) {
- t.Fatalf("%v: Write() was partial, got: %d, expected: %d", s, w, len(want))
- }
- for _, flags := range allOpenFlags {
- _, l, err := s.file.Walk([]string{"test"})
- if err != nil {
- t.Fatalf("%v: Walk(%s) failed, err: %v", s, "test", err)
- }
- fd, _, _, err := l.Open(flags)
- if err != nil {
- t.Fatalf("%v: Open(%v) failed, err: %v", s, flags, err)
- }
- if fd != nil {
- defer fd.Close()
- }
- if err := testReadWrite(l, flags, want); err != nil {
- t.Fatalf("%v: testReadWrite(%v) failed: %v", s, flags, err)
- }
- }
- })
-}
-
-func TestCreate(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- for i, flags := range allOpenFlags {
- _, l, _, _, err := s.file.Create(fmt.Sprintf("test-%d", i), flags, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- t.Fatalf("%v, %v: WriteAt() failed, err: %v", s, flags, err)
- }
-
- if err := testReadWrite(l, flags, nil); err != nil {
- t.Fatalf("%v: testReadWrite(%v) failed: %v", s, flags, err)
- }
- }
- })
-}
-
-func checkIDs(f p9.File, uid, gid int) error {
- _, _, stat, err := f.GetAttr(p9.AttrMask{UID: true, GID: true})
- if err != nil {
- return fmt.Errorf("GetAttr() failed, err: %v", err)
- }
- if want := p9.UID(uid); stat.UID != want {
- return fmt.Errorf("Wrong UID, want: %v, got: %v", want, stat.UID)
- }
- if want := p9.GID(gid); stat.GID != want {
- return fmt.Errorf("Wrong GID, want: %v, got: %v", want, stat.GID)
- }
- return nil
-}
-
-// TestCreateSetGID checks files/dirs/symlinks are created with the proper
-// owner when the parent directory has setgid set,
-func TestCreateSetGID(t *testing.T) {
- if !specutils.HasCapabilities(capability.CAP_CHOWN) {
- t.Skipf("Test requires CAP_CHOWN")
- }
-
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- // Change group and set setgid to the parent dir.
- if err := unix.Chown(s.file.hostPath, os.Getuid(), nobody); err != nil {
- t.Fatalf("Chown() failed: %v", err)
- }
- if err := unix.Chmod(s.file.hostPath, 02777); err != nil {
- t.Fatalf("Chmod() failed: %v", err)
- }
-
- t.Run("create", func(t *testing.T) {
- _, l, _, _, err := s.file.Create("test", p9.ReadOnly, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- t.Fatalf("WriteAt() failed: %v", err)
- }
- defer l.Close()
- if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
- t.Error(err)
- }
- })
-
- t.Run("mkdir", func(t *testing.T) {
- _, err := s.file.Mkdir("test-dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- t.Fatalf("WriteAt() failed: %v", err)
- }
- _, l, err := s.file.Walk([]string{"test-dir"})
- if err != nil {
- t.Fatalf("Walk() failed: %v", err)
- }
- defer l.Close()
- if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
- t.Error(err)
- }
- })
-
- t.Run("symlink", func(t *testing.T) {
- if _, err := s.file.Symlink("/some/target", "symlink", p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("Symlink() failed: %v", err)
- }
- _, l, err := s.file.Walk([]string{"symlink"})
- if err != nil {
- t.Fatalf("Walk() failed, err: %v", err)
- }
- defer l.Close()
- if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
- t.Error(err)
- }
- })
-
- t.Run("mknod", func(t *testing.T) {
- if _, err := s.file.Mknod("nod", p9.ModeRegular|0777, 0, 0, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("Mknod() failed: %v", err)
- }
- _, l, err := s.file.Walk([]string{"nod"})
- if err != nil {
- t.Fatalf("Walk() failed, err: %v", err)
- }
- defer l.Close()
- if err := checkIDs(l, os.Getuid(), os.Getgid()); err != nil {
- t.Error(err)
- }
- })
- })
-}
-
-// TestReadWriteDup tests that a file opened in any mode can be dup'ed and
-// reopened in any other mode.
-func TestReadWriteDup(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- child, err := createFile(s.file, "test")
- if err != nil {
- t.Fatalf("%v: createFile() failed, err: %v", s, err)
- }
- defer child.Close()
- want := []byte("foobar")
- w, err := child.WriteAt(want, 0)
- if err != nil {
- t.Fatalf("%v: Write() failed, err: %v", s, err)
- }
- if w != len(want) {
- t.Fatalf("%v: Write() was partial, got: %d, expected: %d", s, w, len(want))
- }
- for _, flags := range allOpenFlags {
- _, l, err := s.file.Walk([]string{"test"})
- if err != nil {
- t.Fatalf("%v: Walk(%s) failed, err: %v", s, "test", err)
- }
- defer l.Close()
- if _, _, _, err := l.Open(flags); err != nil {
- t.Fatalf("%v: Open(%v) failed, err: %v", s, flags, err)
- }
- for _, dupFlags := range allOpenFlags {
- t.Logf("Original flags: %v, dup flags: %v", flags, dupFlags)
- _, dup, err := l.Walk([]string{})
- if err != nil {
- t.Fatalf("%v: Walk(<empty>) failed: %v", s, err)
- }
- defer dup.Close()
- fd, _, _, err := dup.Open(dupFlags)
- if err != nil {
- t.Fatalf("%v: Open(%v) failed: %v", s, flags, err)
- }
- if fd != nil {
- defer fd.Close()
- }
- if err := testReadWrite(dup, dupFlags, want); err != nil {
- t.Fatalf("%v: testReadWrite(%v) failed: %v", s, dupFlags, err)
- }
- }
- }
- })
-}
-
-func TestUnopened(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFREG}, allConfs, func(t *testing.T, s state) {
- b := []byte("foobar")
- if _, err := s.file.WriteAt(b, 0); err != unix.EBADF {
- t.Errorf("%v: WriteAt() should have failed, got: %v, expected: unix.EBADF", s, err)
- }
- if _, err := s.file.ReadAt(b, 0); err != unix.EBADF {
- t.Errorf("%v: ReadAt() should have failed, got: %v, expected: unix.EBADF", s, err)
- }
- if _, err := s.file.Readdir(0, 100); err != unix.EBADF {
- t.Errorf("%v: Readdir() should have failed, got: %v, expected: unix.EBADF", s, err)
- }
- if err := s.file.FSync(); err != unix.EBADF {
- t.Errorf("%v: FSync() should have failed, got: %v, expected: unix.EBADF", s, err)
- }
- })
-}
-
-// TestOpenOPath is a regression test to ensure that a file that cannot be open
-// for read is allowed to be open. This was happening because the control file
-// was open with O_PATH, but Open() was not checking for it and allowing the
-// control file to be reused.
-func TestOpenOPath(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFREG}, rwConfs, func(t *testing.T, s state) {
- // Fist remove all permissions on the file.
- if err := s.file.SetAttr(p9.SetAttrMask{Permissions: true}, p9.SetAttr{Permissions: p9.FileMode(0)}); err != nil {
- t.Fatalf("SetAttr(): %v", err)
- }
- // Then walk to the file again to open a new control file.
- filename := filepath.Base(s.file.hostPath)
- _, newFile, err := s.root.Walk([]string{filename})
- if err != nil {
- t.Fatalf("root.Walk(%q): %v", filename, err)
- }
-
- if newFile.(*localFile).controlReadable {
- t.Fatalf("control file didn't open with O_PATH: %+v", newFile)
- }
- if _, _, _, err := newFile.Open(p9.ReadOnly); err != unix.EACCES {
- t.Fatalf("Open() should have failed, got: %v, wanted: EACCES", err)
- }
- })
-}
-
-func SetGetAttr(l *localFile, valid p9.SetAttrMask, attr p9.SetAttr) (p9.Attr, error) {
- if err := l.SetAttr(valid, attr); err != nil {
- return p9.Attr{}, err
- }
- _, _, a, err := l.GetAttr(p9.AttrMask{})
- if err != nil {
- return p9.Attr{}, err
- }
- return a, nil
-}
-
-func TestSetAttrPerm(t *testing.T) {
- runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
- valid := p9.SetAttrMask{Permissions: true}
- attr := p9.SetAttr{Permissions: 0777}
- got, err := SetGetAttr(s.file, valid, attr)
- if s.fileType == unix.S_IFLNK {
- if err == nil {
- t.Fatalf("%v: SetGetAttr(valid, %v) should have failed", s, attr.Permissions)
- }
- } else {
- if err != nil {
- t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.Permissions, err)
- }
- if got.Mode.Permissions() != attr.Permissions {
- t.Errorf("%v: wrong permission, got: %v, expected: %v", s, got.Mode.Permissions(), attr.Permissions)
- }
- }
- })
-}
-
-func TestSetAttrSize(t *testing.T) {
- runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
- for _, size := range []uint64{1024, 0, 1024 * 1024} {
- valid := p9.SetAttrMask{Size: true}
- attr := p9.SetAttr{Size: size}
- got, err := SetGetAttr(s.file, valid, attr)
- if s.fileType == unix.S_IFLNK || s.fileType == unix.S_IFDIR {
- if err == nil {
- t.Fatalf("%v: SetGetAttr(valid, %v) should have failed", s, attr.Permissions)
- }
- // Run for one size only, they will all fail the same way.
- return
- }
- if err != nil {
- t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.Size, err)
- }
- if got.Size != size {
- t.Errorf("%v: wrong size, got: %v, expected: %v", s, got.Size, size)
- }
- }
- })
-}
-
-func TestSetAttrTime(t *testing.T) {
- runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
- valid := p9.SetAttrMask{ATime: true, ATimeNotSystemTime: true}
- attr := p9.SetAttr{ATimeSeconds: 123, ATimeNanoSeconds: 456}
- got, err := SetGetAttr(s.file, valid, attr)
- if err != nil {
- t.Fatalf("%v: SetGetAttr(valid, %v:%v) failed, err: %v", s, attr.ATimeSeconds, attr.ATimeNanoSeconds, err)
- }
- if got.ATimeSeconds != 123 {
- t.Errorf("%v: wrong ATimeSeconds, got: %v, expected: %v", s, got.ATimeSeconds, 123)
- }
- if got.ATimeNanoSeconds != 456 {
- t.Errorf("%v: wrong ATimeNanoSeconds, got: %v, expected: %v", s, got.ATimeNanoSeconds, 456)
- }
-
- valid = p9.SetAttrMask{MTime: true, MTimeNotSystemTime: true}
- attr = p9.SetAttr{MTimeSeconds: 789, MTimeNanoSeconds: 012}
- got, err = SetGetAttr(s.file, valid, attr)
- if err != nil {
- t.Fatalf("%v: SetGetAttr(valid, %v:%v) failed, err: %v", s, attr.MTimeSeconds, attr.MTimeNanoSeconds, err)
- }
- if got.MTimeSeconds != 789 {
- t.Errorf("%v: wrong MTimeSeconds, got: %v, expected: %v", s, got.MTimeSeconds, 789)
- }
- if got.MTimeNanoSeconds != 012 {
- t.Errorf("%v: wrong MTimeNanoSeconds, got: %v, expected: %v", s, got.MTimeNanoSeconds, 012)
- }
- })
-}
-
-func TestSetAttrOwner(t *testing.T) {
- if !specutils.HasCapabilities(capability.CAP_CHOWN) {
- t.Skipf("SetAttr(owner) test requires CAP_CHOWN, running as %d", os.Getuid())
- }
-
- runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
- newUID := os.Getuid() + 1
- valid := p9.SetAttrMask{UID: true}
- attr := p9.SetAttr{UID: p9.UID(newUID)}
- got, err := SetGetAttr(s.file, valid, attr)
- if err != nil {
- t.Fatalf("%v: SetGetAttr(valid, %v) failed, err: %v", s, attr.UID, err)
- }
- if got.UID != p9.UID(newUID) {
- t.Errorf("%v: wrong uid, got: %v, expected: %v", s, got.UID, newUID)
- }
- })
-}
-
-func TestLink(t *testing.T) {
- if !specutils.HasCapabilities(capability.CAP_DAC_READ_SEARCH) {
- t.Skipf("Link test requires CAP_DAC_READ_SEARCH, running as %d", os.Getuid())
- }
- runCustom(t, allTypes, rwConfs, func(t *testing.T, s state) {
- const dirName = "linkdir"
- const linkFile = "link"
- if _, err := s.root.Mkdir(dirName, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("%v: MkDir(%s) failed, err: %v", s, dirName, err)
- }
- _, dir, err := s.root.Walk([]string{dirName})
- if err != nil {
- t.Fatalf("%v: Walk({%s}) failed, err: %v", s, dirName, err)
- }
-
- err = dir.Link(s.file, linkFile)
- if s.fileType == unix.S_IFDIR {
- if err != unix.EPERM {
- t.Errorf("%v: Link(target, %s) should have failed, got: %v, expected: unix.EPERM", s, linkFile, err)
- }
- return
- }
- if err != nil {
- t.Errorf("%v: Link(target, %s) failed, err: %v", s, linkFile, err)
- }
- })
-}
-
-func TestROMountChecks(t *testing.T) {
- const want = unix.EROFS
- uid := p9.UID(os.Getuid())
- gid := p9.GID(os.Getgid())
-
- runCustom(t, allTypes, roConfs, func(t *testing.T, s state) {
- if s.fileType != unix.S_IFLNK {
- if _, _, _, err := s.file.Open(p9.WriteOnly); err != want {
- t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
- }
- if _, _, _, err := s.file.Open(p9.ReadWrite); err != want {
- t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
- }
- if _, _, _, err := s.file.Open(p9.ReadOnly | p9.OpenTruncate); err != want {
- t.Errorf("Open() should have failed, got: %v, expected: %v", err, want)
- }
- f, _, _, err := s.file.Open(p9.ReadOnly)
- if err != nil {
- t.Errorf("Open() failed: %v", err)
- }
- if f != nil {
- _ = f.Close()
- }
- }
-
- if _, _, _, _, err := s.file.Create("some_file", p9.ReadWrite, 0777, uid, gid); err != want {
- t.Errorf("Create() should have failed, got: %v, expected: %v", err, want)
- }
- if _, err := s.file.Mkdir("some_dir", 0777, uid, gid); err != want {
- t.Errorf("MkDir() should have failed, got: %v, expected: %v", err, want)
- }
- if err := s.file.RenameAt("some_file", s.file, "other_file"); err != want {
- t.Errorf("Rename() should have failed, got: %v, expected: %v", err, want)
- }
- if _, err := s.file.Symlink("some_place", "some_symlink", uid, gid); err != want {
- t.Errorf("Symlink() should have failed, got: %v, expected: %v", err, want)
- }
- if err := s.file.UnlinkAt("some_file", 0); err != want {
- t.Errorf("UnlinkAt() should have failed, got: %v, expected: %v", err, want)
- }
- if err := s.file.Link(s.file, "some_link"); err != want {
- t.Errorf("Link() should have failed, got: %v, expected: %v", err, want)
- }
- if _, err := s.file.Mknod("some-nod", 0777, 1, 2, uid, gid); err != want {
- t.Errorf("Mknod() should have failed, got: %v, expected: %v", err, want)
- }
-
- valid := p9.SetAttrMask{Size: true}
- attr := p9.SetAttr{Size: 0}
- if err := s.file.SetAttr(valid, attr); err != want {
- t.Errorf("SetAttr() should have failed, got: %v, expected: %v", err, want)
- }
- })
-}
-
-func TestWalkNotFound(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, allConfs, func(t *testing.T, s state) {
- if _, _, err := s.file.Walk([]string{"nobody-here"}); err != unix.ENOENT {
- t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "nobody-here", err)
- }
- if _, _, err := s.file.Walk([]string{"nobody", "here"}); err != unix.ENOENT {
- t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "nobody/here", err)
- }
- if !s.conf.ROMount {
- if _, err := s.file.Mkdir("dir", 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("MkDir(dir) failed, err: %v", err)
- }
- if _, _, err := s.file.Walk([]string{"dir", "nobody-here"}); err != unix.ENOENT {
- t.Errorf("Walk(%q) should have failed, got: %v, expected: unix.ENOENT", "dir/nobody-here", err)
- }
- }
- })
-}
-
-func TestWalkPanic(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, allConfs, func(t *testing.T, s state) {
- for _, name := range []string{".", ".."} {
- assertPanic(t, func() {
- s.file.Walk([]string{name})
- })
- }
- })
-}
-
-func TestWalkDup(t *testing.T) {
- runAll(t, func(t *testing.T, s state) {
- _, dup, err := s.file.Walk([]string{})
- if err != nil {
- t.Fatalf("%v: Walk(nil) failed, err: %v", s, err)
- }
- // Check that 'dup' is usable.
- if _, _, _, err := dup.GetAttr(p9.AttrMask{}); err != nil {
- t.Errorf("%v: GetAttr() failed, err: %v", s, err)
- }
- })
-}
-
-func TestWalkMultiple(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- var names []string
- var parent p9.File = s.file
- for i := 0; i < 5; i++ {
- name := fmt.Sprintf("dir%d", i)
- names = append(names, name)
-
- if _, err := parent.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("MkDir(%q) failed, err: %v", name, err)
- }
-
- var err error
- _, parent, err = s.file.Walk(names)
- if err != nil {
- t.Errorf("Walk(%q): %v", name, err)
- }
- }
- })
-}
-
-func TestReaddir(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- name := "dir"
- if _, err := s.file.Mkdir(name, 0777, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("%v: MkDir(%s) failed, err: %v", s, name, err)
- }
- name = "symlink"
- if _, err := s.file.Symlink("/some/target", name, p9.UID(os.Getuid()), p9.GID(os.Getgid())); err != nil {
- t.Fatalf("%v: Symlink(%q) failed, err: %v", s, name, err)
- }
- name = "file"
- _, f, _, _, err := s.file.Create(name, p9.ReadWrite, 0555, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- t.Fatalf("%v: createFile(root, %q) failed, err: %v", s, name, err)
- }
- f.Close()
-
- if _, _, _, err := s.file.Open(p9.ReadOnly); err != nil {
- t.Fatalf("%v: Open(ReadOnly) failed, err: %v", s, err)
- }
-
- dirents, err := s.file.Readdir(0, 10)
- if err != nil {
- t.Fatalf("%v: Readdir(0, 10) failed, err: %v", s, err)
- }
- if len(dirents) != 3 {
- t.Fatalf("%v: Readdir(0, 10) wrong number of items, got: %v, expected: 3", s, len(dirents))
- }
- var dir, symlink, file bool
- for _, d := range dirents {
- switch d.Name {
- case "dir":
- if d.Type != p9.TypeDir {
- t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeDir)
- }
- dir = true
- case "symlink":
- if d.Type != p9.TypeSymlink {
- t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeSymlink)
- }
- symlink = true
- case "file":
- if d.Type != p9.TypeRegular {
- t.Errorf("%v: dirent.Type got: %v, expected: %v", s, d.Type, p9.TypeRegular)
- }
- file = true
- default:
- t.Errorf("%v: dirent.Name got: %v", s, d.Name)
- }
-
- _, f, err := s.file.Walk([]string{d.Name})
- if err != nil {
- t.Fatalf("%v: Walk({%s}) failed, err: %v", s, d.Name, err)
- }
- _, _, a, err := f.GetAttr(p9.AttrMask{})
- if err != nil {
- t.Fatalf("%v: GetAttr() failed, err: %v", s, err)
- }
- if d.Type != a.Mode.QIDType() {
- t.Errorf("%v: dirent.Type different than GetAttr().Mode.QIDType(), got: %v, expected: %v", s, d.Type, a.Mode.QIDType())
- }
- }
- if !dir || !symlink || !file {
- t.Errorf("%v: Readdir(0, 10) wrong files returned, dir: %v, symlink: %v, file: %v", s, dir, symlink, file)
- }
- })
-}
-
-// Test that attach point can be written to when it points to a file, e.g.
-// /etc/hosts.
-func TestAttachFile(t *testing.T) {
- conf := Config{ROMount: false}
- dir, err := ioutil.TempDir("", "root-")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed, err: %v", err)
- }
- defer os.RemoveAll(dir)
-
- path := path.Join(dir, "test")
- if _, err := os.Create(path); err != nil {
- t.Fatalf("os.Create(%q) failed, err: %v", path, err)
- }
-
- a, err := NewAttachPoint(path, conf)
- if err != nil {
- t.Fatalf("NewAttachPoint failed: %v", err)
- }
- root, err := a.Attach()
- if err != nil {
- t.Fatalf("Attach failed, err: %v", err)
- }
-
- if _, _, _, err := root.Open(p9.ReadWrite); err != nil {
- t.Fatalf("Open(ReadWrite) failed, err: %v", err)
- }
- defer root.Close()
-
- b := []byte("foobar")
- w, err := root.WriteAt(b, 0)
- if err != nil {
- t.Fatalf("Write() failed, err: %v", err)
- }
- if w != len(b) {
- t.Fatalf("Write() was partial, got: %d, expected: %d", w, len(b))
- }
- rBuf := make([]byte, len(b))
- r, err := root.ReadAt(rBuf, 0)
- if err != nil {
- t.Fatalf("ReadAt() failed, err: %v", err)
- }
- if r != len(rBuf) {
- t.Fatalf("ReadAt() was partial, got: %d, expected: %d", r, len(rBuf))
- }
- if string(rBuf) != "foobar" {
- t.Fatalf("ReadAt() wrong data, got: %s, expected: %s", string(rBuf), "foobar")
- }
-}
-
-func TestAttachInvalidType(t *testing.T) {
- dir, err := ioutil.TempDir("", "attach-")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed, err: %v", err)
- }
- defer os.RemoveAll(dir)
-
- fifo := filepath.Join(dir, "fifo")
- if err := unix.Mkfifo(fifo, 0755); err != nil {
- t.Fatalf("Mkfifo(%q): %v", fifo, err)
- }
-
- dirFile, err := os.Open(dir)
- if err != nil {
- t.Fatalf("Open(%s): %v", dir, err)
- }
- defer dirFile.Close()
-
- // Bind a socket via /proc to be sure that a length of a socket path
- // is less than UNIX_PATH_MAX.
- socket := filepath.Join(fmt.Sprintf("/proc/self/fd/%d", dirFile.Fd()), "socket")
- l, err := net.Listen("unix", socket)
- if err != nil {
- t.Fatalf("net.Listen(unix, %q): %v", socket, err)
- }
- defer l.Close()
-
- for _, tc := range []struct {
- name string
- path string
- }{
- {name: "fifo", path: fifo},
- {name: "socket", path: socket},
- } {
- t.Run(tc.name, func(t *testing.T) {
- conf := Config{ROMount: false}
- a, err := NewAttachPoint(tc.path, conf)
- if err != nil {
- t.Fatalf("NewAttachPoint failed: %v", err)
- }
- f, err := a.Attach()
- if f != nil || err == nil {
- t.Fatalf("Attach should have failed, got (%v, %v)", f, err)
- }
- })
- }
-}
-
-func TestDoubleAttachError(t *testing.T) {
- conf := Config{ROMount: false}
- root, err := ioutil.TempDir("", "root-")
- if err != nil {
- t.Fatalf("ioutil.TempDir() failed, err: %v", err)
- }
- defer os.RemoveAll(root)
- a, err := NewAttachPoint(root, conf)
- if err != nil {
- t.Fatalf("NewAttachPoint failed: %v", err)
- }
-
- if _, err := a.Attach(); err != nil {
- t.Fatalf("Attach failed: %v", err)
- }
- if _, err := a.Attach(); err == nil {
- t.Fatalf("Attach should have failed, got %v want non-nil", err)
- }
-}
-
-func TestTruncate(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- child, err := createFile(s.file, "test")
- if err != nil {
- t.Fatalf("createFile() failed: %v", err)
- }
- defer child.Close()
- want := []byte("foobar")
- w, err := child.WriteAt(want, 0)
- if err != nil {
- t.Fatalf("Write() failed: %v", err)
- }
- if w != len(want) {
- t.Fatalf("Write() was partial, got: %d, expected: %d", w, len(want))
- }
-
- _, l, err := s.file.Walk([]string{"test"})
- if err != nil {
- t.Fatalf("Walk(%s) failed: %v", "test", err)
- }
- if _, _, _, err := l.Open(p9.ReadOnly | p9.OpenTruncate); err != nil {
- t.Fatalf("Open() failed: %v", err)
- }
- _, mask, attr, err := l.GetAttr(p9.AttrMask{Size: true})
- if err != nil {
- t.Fatalf("GetAttr() failed: %v", err)
- }
- if !mask.Size {
- t.Fatalf("GetAttr() didn't return size: %+v", mask)
- }
- if attr.Size != 0 {
- t.Fatalf("truncate didn't work, want: 0, got: %d", attr.Size)
- }
- })
-}
-
-func TestMknod(t *testing.T) {
- runCustom(t, []uint32{unix.S_IFDIR}, rwConfs, func(t *testing.T, s state) {
- _, err := s.file.Mknod("test", p9.ModeRegular|0777, 1, 2, p9.UID(os.Getuid()), p9.GID(os.Getgid()))
- if err != nil {
- t.Fatalf("Mknod() failed: %v", err)
- }
-
- _, f, err := s.file.Walk([]string{"test"})
- if err != nil {
- t.Fatalf("Walk() failed: %v", err)
- }
- fd, _, _, err := f.Open(p9.ReadWrite)
- if err != nil {
- t.Fatalf("Open() failed: %v", err)
- }
- if fd != nil {
- defer fd.Close()
- }
- if err := testReadWrite(f, p9.ReadWrite, nil); err != nil {
- t.Fatalf("testReadWrite() failed: %v", err)
- }
- })
-}
-
-func BenchmarkWalkOne(b *testing.B) {
- path, name, err := setup(unix.S_IFDIR)
- if err != nil {
- b.Fatalf("%v", err)
- }
- defer os.RemoveAll(path)
-
- a, err := NewAttachPoint(path, Config{})
- if err != nil {
- b.Fatalf("NewAttachPoint failed: %v", err)
- }
- root, err := a.Attach()
- if err != nil {
- b.Fatalf("Attach failed, err: %v", err)
- }
- defer root.Close()
-
- names := []string{name}
- files := make([]p9.File, 0, 1000)
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _, file, err := root.Walk(names)
- if err != nil {
- b.Fatalf("Walk(%q): %v", name, err)
- }
- files = append(files, file)
-
- // Avoid running out of FDs.
- if len(files) == cap(files) {
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
- files = files[:0]
- b.StartTimer()
- }
- }
-
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
-}
-
-func BenchmarkCreate(b *testing.B) {
- path, _, err := setup(unix.S_IFDIR)
- if err != nil {
- b.Fatalf("%v", err)
- }
- defer os.RemoveAll(path)
-
- a, err := NewAttachPoint(path, Config{})
- if err != nil {
- b.Fatalf("NewAttachPoint failed: %v", err)
- }
- root, err := a.Attach()
- if err != nil {
- b.Fatalf("Attach failed, err: %v", err)
- }
- defer root.Close()
-
- files := make([]p9.File, 0, 500)
- fds := make([]*fd.FD, 0, 500)
- uid := p9.UID(os.Getuid())
- gid := p9.GID(os.Getgid())
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- name := fmt.Sprintf("same-%d", i)
- fd, file, _, _, err := root.Create(name, p9.ReadOnly, 0777, uid, gid)
- if err != nil {
- b.Fatalf("Create(%q): %v", name, err)
- }
- files = append(files, file)
- if fd != nil {
- fds = append(fds, fd)
- }
-
- // Avoid running out of FDs.
- if len(files) == cap(files) {
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
- files = files[:0]
- for _, fd := range fds {
- fd.Close()
- }
- fds = fds[:0]
- b.StartTimer()
- }
- }
-
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
- for _, fd := range fds {
- fd.Close()
- }
-}
-
-func BenchmarkCreateDiffOwner(b *testing.B) {
- if !specutils.HasCapabilities(capability.CAP_CHOWN) {
- b.Skipf("Test requires CAP_CHOWN")
- }
-
- path, _, err := setup(unix.S_IFDIR)
- if err != nil {
- b.Fatalf("%v", err)
- }
- defer os.RemoveAll(path)
-
- a, err := NewAttachPoint(path, Config{})
- if err != nil {
- b.Fatalf("NewAttachPoint failed: %v", err)
- }
- root, err := a.Attach()
- if err != nil {
- b.Fatalf("Attach failed, err: %v", err)
- }
- defer root.Close()
-
- files := make([]p9.File, 0, 500)
- fds := make([]*fd.FD, 0, 500)
- gid := p9.GID(os.Getgid())
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- name := fmt.Sprintf("diff-%d", i)
- fd, file, _, _, err := root.Create(name, p9.ReadOnly, 0777, nobody, gid)
- if err != nil {
- b.Fatalf("Create(%q): %v", name, err)
- }
- files = append(files, file)
- if fd != nil {
- fds = append(fds, fd)
- }
-
- // Avoid running out of FDs.
- if len(files) == cap(files) {
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
- files = files[:0]
- for _, fd := range fds {
- fd.Close()
- }
- fds = fds[:0]
- b.StartTimer()
- }
- }
-
- b.StopTimer()
- for _, file := range files {
- file.Close()
- }
- for _, fd := range fds {
- fd.Close()
- }
-}
diff --git a/runsc/fsgofer/fsgofer_unsafe_state_autogen.go b/runsc/fsgofer/fsgofer_unsafe_state_autogen.go
new file mode 100644
index 000000000..d2f978fb9
--- /dev/null
+++ b/runsc/fsgofer/fsgofer_unsafe_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package fsgofer
diff --git a/runsc/mitigate/BUILD b/runsc/mitigate/BUILD
deleted file mode 100644
index 3b0342d18..000000000
--- a/runsc/mitigate/BUILD
+++ /dev/null
@@ -1,20 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "mitigate",
- srcs = [
- "cpu.go",
- "mitigate.go",
- ],
- deps = ["@in_gopkg_yaml_v2//:go_default_library"],
-)
-
-go_test(
- name = "mitigate_test",
- size = "small",
- srcs = ["cpu_test.go"],
- library = ":mitigate",
- deps = ["@com_github_google_go_cmp//cmp:go_default_library"],
-)
diff --git a/runsc/mitigate/cpu.go b/runsc/mitigate/cpu.go
deleted file mode 100644
index ae4ce9579..000000000
--- a/runsc/mitigate/cpu.go
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2021 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 mitigate
-
-import (
- "fmt"
- "io/ioutil"
- "regexp"
- "strconv"
- "strings"
-)
-
-const (
- // constants of coomm
- meltdown = "cpu_meltdown"
- l1tf = "l1tf"
- mds = "mds"
- swapgs = "swapgs"
- taa = "taa"
-)
-
-const (
- processorKey = "processor"
- vendorIDKey = "vendor_id"
- cpuFamilyKey = "cpu family"
- modelKey = "model"
- physicalIDKey = "physical id"
- coreIDKey = "core id"
- bugsKey = "bugs"
-)
-
-const (
- cpuOnlineTemplate = "/sys/devices/system/cpu/cpu%d/online"
-)
-
-// cpuSet contains a map of all CPUs on the system, mapped
-// by Physical ID and CoreIDs. threads with the same
-// Core and Physical ID are Hyperthread pairs.
-type cpuSet map[cpuID]*threadGroup
-
-// newCPUSet creates a CPUSet from data read from /proc/cpuinfo.
-func newCPUSet(data []byte, vulnerable func(*thread) bool) (cpuSet, error) {
- processors, err := getThreads(string(data))
- if err != nil {
- return nil, err
- }
-
- set := make(cpuSet)
- for _, p := range processors {
- // Each ID is of the form physicalID:coreID. Hyperthread pairs
- // have identical physical and core IDs. We need to match
- // Hyperthread pairs so that we can shutdown all but one per
- // pair.
- core, ok := set[p.id]
- if !ok {
- core = &threadGroup{}
- set[p.id] = core
- }
- core.isVulnerable = core.isVulnerable || vulnerable(p)
- core.threads = append(core.threads, p)
- }
- return set, nil
-}
-
-// String implements the String method for CPUSet.
-func (c cpuSet) String() string {
- ret := ""
- for _, tg := range c {
- ret += fmt.Sprintf("%s\n", tg)
- }
- return ret
-}
-
-// getRemainingList returns the list of threads that will remain active
-// after mitigation.
-func (c cpuSet) getRemainingList() []*thread {
- threads := make([]*thread, 0, len(c))
- for _, core := range c {
- // If we're vulnerable, take only one thread from the pair.
- if core.isVulnerable {
- threads = append(threads, core.threads[0])
- continue
- }
- // Otherwise don't shutdown anything.
- threads = append(threads, core.threads...)
- }
- return threads
-}
-
-// getShutdownList returns the list of threads that will be shutdown on
-// mitigation.
-func (c cpuSet) getShutdownList() []*thread {
- threads := make([]*thread, 0)
- for _, core := range c {
- // Only if we're vulnerable do shutdown anything. In this case,
- // shutdown all but the first entry.
- if core.isVulnerable && len(core.threads) > 1 {
- threads = append(threads, core.threads[1:]...)
- }
- }
- return threads
-}
-
-// threadGroup represents Hyperthread pairs on the same physical/core ID.
-type threadGroup struct {
- threads []*thread
- isVulnerable bool
-}
-
-// String implements the String method for threadGroup.
-func (c *threadGroup) String() string {
- ret := fmt.Sprintf("ThreadGroup:\nIsVulnerable: %t\n", c.isVulnerable)
- for _, processor := range c.threads {
- ret += fmt.Sprintf("%s\n", processor)
- }
- return ret
-}
-
-// getThreads returns threads structs from reading /proc/cpuinfo.
-func getThreads(data string) ([]*thread, error) {
- // Each processor entry should start with the
- // processor key. Find the beginings of each.
- r := buildRegex(processorKey, `\d+`)
- indices := r.FindAllStringIndex(data, -1)
- if len(indices) < 1 {
- return nil, fmt.Errorf("no cpus found for: %s", data)
- }
-
- // Add the ending index for last entry.
- indices = append(indices, []int{len(data), -1})
-
- // Valid cpus are now defined by strings in between
- // indexes (e.g. data[index[i], index[i+1]]).
- // There should be len(indicies) - 1 CPUs
- // since the last index is the end of the string.
- var cpus = make([]*thread, 0, len(indices)-1)
- // Find each string that represents a CPU. These begin "processor".
- for i := 1; i < len(indices); i++ {
- start := indices[i-1][0]
- end := indices[i][0]
- // Parse the CPU entry, which should be between start/end.
- c, err := newThread(data[start:end])
- if err != nil {
- return nil, err
- }
- cpus = append(cpus, c)
- }
- return cpus, nil
-}
-
-// cpuID for each thread is defined by the physical and
-// core IDs. If equal, two threads are Hyperthread pairs.
-type cpuID struct {
- physicalID int64
- coreID int64
-}
-
-// type cpu represents pertinent info about a cpu.
-type thread struct {
- processorNumber int64 // the processor number of this CPU.
- vendorID string // the vendorID of CPU (e.g. AuthenticAMD).
- cpuFamily int64 // CPU family number (e.g. 6 for CascadeLake/Skylake).
- model int64 // CPU model number (e.g. 85 for CascadeLake/Skylake).
- id cpuID // id for this thread
- bugs map[string]struct{} // map of vulnerabilities parsed from the 'bugs' field.
-}
-
-// newThread parses a CPU from a single cpu entry from /proc/cpuinfo.
-func newThread(data string) (*thread, error) {
- processor, err := parseProcessor(data)
- if err != nil {
- return nil, err
- }
-
- vendorID, err := parseVendorID(data)
- if err != nil {
- return nil, err
- }
-
- cpuFamily, err := parseCPUFamily(data)
- if err != nil {
- return nil, err
- }
-
- model, err := parseModel(data)
- if err != nil {
- return nil, err
- }
-
- physicalID, err := parsePhysicalID(data)
- if err != nil {
- return nil, err
- }
-
- coreID, err := parseCoreID(data)
- if err != nil {
- return nil, err
- }
-
- bugs, err := parseBugs(data)
- if err != nil {
- return nil, err
- }
-
- return &thread{
- processorNumber: processor,
- vendorID: vendorID,
- cpuFamily: cpuFamily,
- model: model,
- id: cpuID{
- physicalID: physicalID,
- coreID: coreID,
- },
- bugs: bugs,
- }, nil
-}
-
-// String implements the String method for thread.
-func (t *thread) String() string {
- template := `CPU: %d
-CPU ID: %+v
-Vendor: %s
-Family/Model: %d/%d
-Bugs: %s
-`
- bugs := make([]string, 0)
- for bug := range t.bugs {
- bugs = append(bugs, bug)
- }
-
- return fmt.Sprintf(template, t.processorNumber, t.id, t.vendorID, t.cpuFamily, t.model, strings.Join(bugs, ","))
-}
-
-// shutdown turns off the CPU by writing 0 to /sys/devices/cpu/cpu{N}/online.
-func (t *thread) shutdown() error {
- cpuPath := fmt.Sprintf(cpuOnlineTemplate, t.processorNumber)
- return ioutil.WriteFile(cpuPath, []byte{'0'}, 0644)
-}
-
-// List of pertinent side channel vulnerablilites.
-// For mds, see: https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html.
-var vulnerabilities = []string{
- meltdown,
- l1tf,
- mds,
- swapgs,
- taa,
-}
-
-// isVulnerable checks if a CPU is vulnerable to pertinent bugs.
-func (t *thread) isVulnerable() bool {
- for _, bug := range vulnerabilities {
- if _, ok := t.bugs[bug]; ok {
- return true
- }
- }
- return false
-}
-
-// isActive checks if a CPU is active from /sys/devices/system/cpu/cpu{N}/online
-// If the file does not exist (ioutil returns in error), we assume the CPU is on.
-func (t *thread) isActive() bool {
- cpuPath := fmt.Sprintf(cpuOnlineTemplate, t.processorNumber)
- data, err := ioutil.ReadFile(cpuPath)
- if err != nil {
- return true
- }
- return len(data) > 0 && data[0] != '0'
-}
-
-// similarTo checks family/model/bugs fields for equality of two
-// processors.
-func (t *thread) similarTo(other *thread) bool {
- if t.vendorID != other.vendorID {
- return false
- }
-
- if other.cpuFamily != t.cpuFamily {
- return false
- }
-
- if other.model != t.model {
- return false
- }
-
- if len(other.bugs) != len(t.bugs) {
- return false
- }
-
- for bug := range t.bugs {
- if _, ok := other.bugs[bug]; !ok {
- return false
- }
- }
- return true
-}
-
-// parseProcessor grabs the processor field from /proc/cpuinfo output.
-func parseProcessor(data string) (int64, error) {
- return parseIntegerResult(data, processorKey)
-}
-
-// parseVendorID grabs the vendor_id field from /proc/cpuinfo output.
-func parseVendorID(data string) (string, error) {
- return parseRegex(data, vendorIDKey, `[\w\d]+`)
-}
-
-// parseCPUFamily grabs the cpu family field from /proc/cpuinfo output.
-func parseCPUFamily(data string) (int64, error) {
- return parseIntegerResult(data, cpuFamilyKey)
-}
-
-// parseModel grabs the model field from /proc/cpuinfo output.
-func parseModel(data string) (int64, error) {
- return parseIntegerResult(data, modelKey)
-}
-
-// parsePhysicalID parses the physical id field.
-func parsePhysicalID(data string) (int64, error) {
- return parseIntegerResult(data, physicalIDKey)
-}
-
-// parseCoreID parses the core id field.
-func parseCoreID(data string) (int64, error) {
- return parseIntegerResult(data, coreIDKey)
-}
-
-// parseBugs grabs the bugs field from /proc/cpuinfo output.
-func parseBugs(data string) (map[string]struct{}, error) {
- result, err := parseRegex(data, bugsKey, `[\d\w\s]*`)
- if err != nil {
- return nil, err
- }
- bugs := strings.Split(result, " ")
- ret := make(map[string]struct{}, len(bugs))
- for _, bug := range bugs {
- ret[bug] = struct{}{}
- }
- return ret, nil
-}
-
-// parseIntegerResult parses fields expecting an integer.
-func parseIntegerResult(data, key string) (int64, error) {
- result, err := parseRegex(data, key, `\d+`)
- if err != nil {
- return 0, err
- }
- return strconv.ParseInt(result, 0, 64)
-}
-
-// buildRegex builds a regex for parsing each CPU field.
-func buildRegex(key, match string) *regexp.Regexp {
- reg := fmt.Sprintf(`(?m)^%s\s*:\s*(.*)$`, key)
- return regexp.MustCompile(reg)
-}
-
-// parseRegex parses data with key inserted into a standard regex template.
-func parseRegex(data, key, match string) (string, error) {
- r := buildRegex(key, match)
- matches := r.FindStringSubmatch(data)
- if len(matches) < 2 {
- return "", fmt.Errorf("failed to match key %s: %s", key, data)
- }
- return matches[1], nil
-}
diff --git a/runsc/mitigate/cpu_test.go b/runsc/mitigate/cpu_test.go
deleted file mode 100644
index 21c12f586..000000000
--- a/runsc/mitigate/cpu_test.go
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2021 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 mitigate
-
-import (
- "fmt"
- "io/ioutil"
- "strings"
- "testing"
-)
-
-// cpuTestCase represents data from CPUs that will be mitigated.
-type cpuTestCase struct {
- name string
- vendorID string
- family int
- model int
- modelName string
- bugs string
- physicalCores int
- cores int
- threadsPerCore int
-}
-
-var cascadeLake4 = cpuTestCase{
- name: "CascadeLake",
- vendorID: "GenuineIntel",
- family: 6,
- model: 85,
- modelName: "Intel(R) Xeon(R) CPU",
- bugs: "spectre_v1 spectre_v2 spec_store_bypass mds swapgs taa",
- physicalCores: 1,
- cores: 2,
- threadsPerCore: 2,
-}
-
-var haswell2 = cpuTestCase{
- name: "Haswell",
- vendorID: "GenuineIntel",
- family: 6,
- model: 63,
- modelName: "Intel(R) Xeon(R) CPU",
- bugs: "cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs",
- physicalCores: 1,
- cores: 1,
- threadsPerCore: 2,
-}
-
-var haswell2core = cpuTestCase{
- name: "Haswell2Physical",
- vendorID: "GenuineIntel",
- family: 6,
- model: 63,
- modelName: "Intel(R) Xeon(R) CPU",
- bugs: "cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs",
- physicalCores: 2,
- cores: 1,
- threadsPerCore: 1,
-}
-
-var amd8 = cpuTestCase{
- name: "AMD",
- vendorID: "AuthenticAMD",
- family: 23,
- model: 49,
- modelName: "AMD EPYC 7B12",
- bugs: "sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass",
- physicalCores: 4,
- cores: 1,
- threadsPerCore: 2,
-}
-
-// makeCPUString makes a string formated like /proc/cpuinfo for each cpuTestCase
-func (tc cpuTestCase) makeCPUString() string {
- template := `processor : %d
-vendor_id : %s
-cpu family : %d
-model : %d
-model name : %s
-physical id : %d
-core id : %d
-cpu cores : %d
-bugs : %s
-`
- ret := ``
- for i := 0; i < tc.physicalCores; i++ {
- for j := 0; j < tc.cores; j++ {
- for k := 0; k < tc.threadsPerCore; k++ {
- processorNum := (i*tc.cores+j)*tc.threadsPerCore + k
- ret += fmt.Sprintf(template,
- processorNum, /*processor*/
- tc.vendorID, /*vendor_id*/
- tc.family, /*cpu family*/
- tc.model, /*model*/
- tc.modelName, /*model name*/
- i, /*physical id*/
- j, /*core id*/
- tc.cores*tc.physicalCores, /*cpu cores*/
- tc.bugs /*bugs*/)
- }
- }
- }
- return ret
-}
-
-// TestMockCPUSet tests mock cpu test cases against the cpuSet functions.
-func TestMockCPUSet(t *testing.T) {
- for _, tc := range []struct {
- testCase cpuTestCase
- isVulnerable bool
- }{
- {
- testCase: amd8,
- isVulnerable: false,
- },
- {
- testCase: haswell2,
- isVulnerable: true,
- },
- {
- testCase: haswell2core,
- isVulnerable: true,
- },
-
- {
- testCase: cascadeLake4,
- isVulnerable: true,
- },
- } {
- t.Run(tc.testCase.name, func(t *testing.T) {
- data := tc.testCase.makeCPUString()
- vulnerable := func(t *thread) bool {
- return t.isVulnerable()
- }
- set, err := newCPUSet([]byte(data), vulnerable)
- if err != nil {
- t.Fatalf("Failed to ")
- }
- remaining := set.getRemainingList()
- // In the non-vulnerable case, no cores should be shutdown so all should remain.
- want := tc.testCase.physicalCores * tc.testCase.cores * tc.testCase.threadsPerCore
- if tc.isVulnerable {
- want = tc.testCase.physicalCores * tc.testCase.cores
- }
-
- if want != len(remaining) {
- t.Fatalf("Failed to shutdown the correct number of cores: want: %d got: %d", want, len(remaining))
- }
-
- if !tc.isVulnerable {
- return
- }
-
- // If the set is vulnerable, we expect only 1 thread per hyperthread pair.
- for _, r := range remaining {
- if _, ok := set[r.id]; !ok {
- t.Fatalf("Entry %+v not in map, there must be two entries in the same thread group.", r)
- }
- delete(set, r.id)
- }
- })
- }
-}
-
-// TestGetCPU tests basic parsing of single CPU strings from reading
-// /proc/cpuinfo.
-func TestGetCPU(t *testing.T) {
- data := `processor : 0
-vendor_id : GenuineIntel
-cpu family : 6
-model : 85
-physical id: 0
-core id : 0
-bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
-`
- want := thread{
- processorNumber: 0,
- vendorID: "GenuineIntel",
- cpuFamily: 6,
- model: 85,
- id: cpuID{
- physicalID: 0,
- coreID: 0,
- },
- bugs: map[string]struct{}{
- "cpu_meltdown": struct{}{},
- "spectre_v1": struct{}{},
- "spectre_v2": struct{}{},
- "spec_store_bypass": struct{}{},
- "l1tf": struct{}{},
- "mds": struct{}{},
- "swapgs": struct{}{},
- "taa": struct{}{},
- "itlb_multihit": struct{}{},
- },
- }
-
- got, err := newThread(data)
- if err != nil {
- t.Fatalf("getCpu failed with error: %v", err)
- }
-
- if !want.similarTo(got) {
- t.Fatalf("Failed cpus not similar: got: %+v, want: %+v", got, want)
- }
-
- if !got.isVulnerable() {
- t.Fatalf("Failed: cpu should be vulnerable.")
- }
-}
-
-func TestInvalid(t *testing.T) {
- result, err := getThreads(`something not a processor`)
- if err == nil {
- t.Fatalf("getCPU set didn't return an error: %+v", result)
- }
-
- if !strings.Contains(err.Error(), "no cpus") {
- t.Fatalf("Incorrect error returned: %v", err)
- }
-}
-
-// TestCPUSet tests getting the right number of CPUs from
-// parsing full output of /proc/cpuinfo.
-func TestCPUSet(t *testing.T) {
- data := `processor : 0
-vendor_id : GenuineIntel
-cpu family : 6
-model : 63
-model name : Intel(R) Xeon(R) CPU @ 2.30GHz
-stepping : 0
-microcode : 0x1
-cpu MHz : 2299.998
-cache size : 46080 KB
-physical id : 0
-siblings : 2
-core id : 0
-cpu cores : 1
-apicid : 0
-initial apicid : 0
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
-bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
-bogomips : 4599.99
-clflush size : 64
-cache_alignment : 64
-address sizes : 46 bits physical, 48 bits virtual
-power management:
-
-processor : 1
-vendor_id : GenuineIntel
-cpu family : 6
-model : 63
-model name : Intel(R) Xeon(R) CPU @ 2.30GHz
-stepping : 0
-microcode : 0x1
-cpu MHz : 2299.998
-cache size : 46080 KB
-physical id : 0
-siblings : 2
-core id : 0
-cpu cores : 1
-apicid : 1
-initial apicid : 1
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
-bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
-bogomips : 4599.99
-clflush size : 64
-cache_alignment : 64
-address sizes : 46 bits physical, 48 bits virtual
-power management:
-`
- cpuSet, err := getThreads(data)
- if err != nil {
- t.Fatalf("getCPUSet failed: %v", err)
- }
-
- wantCPULen := 2
- if len(cpuSet) != wantCPULen {
- t.Fatalf("Num CPU mismatch: want: %d, got: %d", wantCPULen, len(cpuSet))
- }
-
- wantCPU := thread{
- vendorID: "GenuineIntel",
- cpuFamily: 6,
- model: 63,
- bugs: map[string]struct{}{
- "cpu_meltdown": struct{}{},
- "spectre_v1": struct{}{},
- "spectre_v2": struct{}{},
- "spec_store_bypass": struct{}{},
- "l1tf": struct{}{},
- "mds": struct{}{},
- "swapgs": struct{}{},
- },
- }
-
- for _, c := range cpuSet {
- if !wantCPU.similarTo(c) {
- t.Fatalf("Failed cpus not equal: got: %+v, want: %+v", c, wantCPU)
- }
- }
-}
-
-// TestReadFile is a smoke test for parsing methods.
-func TestReadFile(t *testing.T) {
- data, err := ioutil.ReadFile("/proc/cpuinfo")
- if err != nil {
- t.Fatalf("Failed to read cpuinfo: %v", err)
- }
-
- vulnerable := func(t *thread) bool {
- return t.isVulnerable()
- }
-
- set, err := newCPUSet(data, vulnerable)
- if err != nil {
- t.Fatalf("Failed to parse CPU data %v\n%s", err, data)
- }
-
- if len(set) < 1 {
- t.Fatalf("Failed to parse any CPUs: %d", len(set))
- }
-
- t.Log(set)
-}
-
-// TestVulnerable tests if the isVulnerable method is correct
-// among known CPUs in GCP.
-func TestVulnerable(t *testing.T) {
- const haswell = `processor : 0
-vendor_id : GenuineIntel
-cpu family : 6
-model : 63
-model name : Intel(R) Xeon(R) CPU @ 2.30GHz
-stepping : 0
-microcode : 0x1
-cpu MHz : 2299.998
-cache size : 46080 KB
-physical id : 0
-siblings : 4
-core id : 0
-cpu cores : 2
-apicid : 0
-initial apicid : 0
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
-bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
-bogomips : 4599.99
-clflush size : 64
-cache_alignment : 64
-address sizes : 46 bits physical, 48 bits virtual
-power management:`
-
- const skylake = `processor : 0
-vendor_id : GenuineIntel
-cpu family : 6
-model : 85
-model name : Intel(R) Xeon(R) CPU @ 2.00GHz
-stepping : 3
-microcode : 0x1
-cpu MHz : 2000.180
-cache size : 39424 KB
-physical id : 0
-siblings : 2
-core id : 0
-cpu cores : 1
-apicid : 0
-initial apicid : 0
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat md_clear arch_capabilities
-bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
-bogomips : 4000.36
-clflush size : 64
-cache_alignment : 64
-address sizes : 46 bits physical, 48 bits virtual
-power management:`
-
- const cascade = `processor : 0
-vendor_id : GenuineIntel
-cpu family : 6
-model : 85
-model name : Intel(R) Xeon(R) CPU
-stepping : 7
-microcode : 0x1
-cpu MHz : 2800.198
-cache size : 33792 KB
-physical id : 0
-siblings : 2
-core id : 0
-cpu cores : 1
-apicid : 0
-initial apicid : 0
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2
- ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmu
-lqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowpr
-efetch invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid r
-tm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves a
-rat avx512_vnni md_clear arch_capabilities
-bugs : spectre_v1 spectre_v2 spec_store_bypass mds swapgs taa
-bogomips : 5600.39
-clflush size : 64
-cache_alignment : 64
-address sizes : 46 bits physical, 48 bits virtual
-power management:`
-
- const amd = `processor : 0
-vendor_id : AuthenticAMD
-cpu family : 23
-model : 49
-model name : AMD EPYC 7B12
-stepping : 0
-microcode : 0x1000065
-cpu MHz : 2250.000
-cache size : 512 KB
-physical id : 0
-siblings : 2
-core id : 0
-cpu cores : 1
-apicid : 0
-initial apicid : 0
-fpu : yes
-fpu_exception : yes
-cpuid level : 13
-wp : yes
-flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb stibp vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr arat npt nrip_save umip rdpid
-bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
-bogomips : 4500.00
-TLB size : 3072 4K pages
-clflush size : 64
-cache_alignment : 64
-address sizes : 48 bits physical, 48 bits virtual
-power management:`
-
- for _, tc := range []struct {
- name string
- cpuString string
- vulnerable bool
- }{
- {
- name: "haswell",
- cpuString: haswell,
- vulnerable: true,
- }, {
- name: "skylake",
- cpuString: skylake,
- vulnerable: true,
- }, {
- name: "amd",
- cpuString: amd,
- vulnerable: false,
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- set, err := getThreads(tc.cpuString)
- if err != nil {
- t.Fatalf("Failed to getCPUSet:%v\n %s", err, tc.cpuString)
- }
-
- if len(set) < 1 {
- t.Fatalf("Returned empty cpu set: %v", set)
- }
-
- for _, c := range set {
- got := func() bool {
- return c.isVulnerable()
- }()
-
- if got != tc.vulnerable {
- t.Fatalf("Mismatch vulnerable for cpu %+s: got %t want: %t", tc.name, tc.vulnerable, got)
- }
- }
- })
- }
-}
diff --git a/runsc/mitigate/mitigate.go b/runsc/mitigate/mitigate.go
deleted file mode 100644
index 51d5449b6..000000000
--- a/runsc/mitigate/mitigate.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 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 mitigate provides libraries for the mitigate command. The
-// mitigate command mitigates side channel attacks such as MDS. Mitigate
-// shuts down CPUs via /sys/devices/system/cpu/cpu{N}/online. In addition,
-// the mitigate also handles computing available CPU in kubernetes kube_config
-// files.
-package mitigate
diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD
deleted file mode 100644
index f0a551a1e..000000000
--- a/runsc/sandbox/BUILD
+++ /dev/null
@@ -1,38 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "sandbox",
- srcs = [
- "network.go",
- "network_unsafe.go",
- "sandbox.go",
- ],
- visibility = [
- "//runsc:__subpackages__",
- ],
- deps = [
- "//pkg/cleanup",
- "//pkg/control/client",
- "//pkg/control/server",
- "//pkg/log",
- "//pkg/sentry/control",
- "//pkg/sentry/platform",
- "//pkg/sync",
- "//pkg/tcpip/header",
- "//pkg/tcpip/stack",
- "//pkg/urpc",
- "//runsc/boot",
- "//runsc/boot/platforms",
- "//runsc/cgroup",
- "//runsc/config",
- "//runsc/console",
- "//runsc/specutils",
- "@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",
- "@com_github_vishvananda_netlink//:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/sandbox/sandbox_state_autogen.go b/runsc/sandbox/sandbox_state_autogen.go
new file mode 100644
index 000000000..79ebc2220
--- /dev/null
+++ b/runsc/sandbox/sandbox_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package sandbox
diff --git a/runsc/sandbox/sandbox_unsafe_state_autogen.go b/runsc/sandbox/sandbox_unsafe_state_autogen.go
new file mode 100644
index 000000000..79ebc2220
--- /dev/null
+++ b/runsc/sandbox/sandbox_unsafe_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package sandbox
diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD
deleted file mode 100644
index 679d8bc8e..000000000
--- a/runsc/specutils/BUILD
+++ /dev/null
@@ -1,34 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "specutils",
- srcs = [
- "cri.go",
- "fs.go",
- "namespace.go",
- "specutils.go",
- ],
- visibility = ["//:sandbox"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/bits",
- "//pkg/log",
- "//pkg/sentry/kernel/auth",
- "//runsc/config",
- "@com_github_cenkalti_backoff//:go_default_library",
- "@com_github_mohae_deepcopy//:go_default_library",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@com_github_syndtr_gocapability//capability:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "specutils_test",
- size = "small",
- srcs = ["specutils_test.go"],
- library = ":specutils",
- deps = ["@com_github_opencontainers_runtime_spec//specs-go:go_default_library"],
-)
diff --git a/runsc/specutils/seccomp/BUILD b/runsc/specutils/seccomp/BUILD
deleted file mode 100644
index 3520f2d6d..000000000
--- a/runsc/specutils/seccomp/BUILD
+++ /dev/null
@@ -1,34 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "seccomp",
- srcs = [
- "audit_amd64.go",
- "audit_arm64.go",
- "seccomp.go",
- ],
- visibility = ["//:sandbox"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/bpf",
- "//pkg/log",
- "//pkg/seccomp",
- "//pkg/sentry/kernel",
- "//pkg/sentry/syscalls/linux",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- ],
-)
-
-go_test(
- name = "seccomp_test",
- size = "small",
- srcs = ["seccomp_test.go"],
- library = ":seccomp",
- deps = [
- "//pkg/binary",
- "//pkg/bpf",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- ],
-)
diff --git a/runsc/specutils/seccomp/seccomp_amd64_state_autogen.go b/runsc/specutils/seccomp/seccomp_amd64_state_autogen.go
new file mode 100644
index 000000000..27a96018b
--- /dev/null
+++ b/runsc/specutils/seccomp/seccomp_amd64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build amd64
+
+package seccomp
diff --git a/runsc/specutils/seccomp/seccomp_arm64_state_autogen.go b/runsc/specutils/seccomp/seccomp_arm64_state_autogen.go
new file mode 100644
index 000000000..96c64c23d
--- /dev/null
+++ b/runsc/specutils/seccomp/seccomp_arm64_state_autogen.go
@@ -0,0 +1,5 @@
+// automatically generated by stateify.
+
+// +build arm64
+
+package seccomp
diff --git a/runsc/specutils/seccomp/seccomp_state_autogen.go b/runsc/specutils/seccomp/seccomp_state_autogen.go
new file mode 100644
index 000000000..e16b5d7c2
--- /dev/null
+++ b/runsc/specutils/seccomp/seccomp_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package seccomp
diff --git a/runsc/specutils/seccomp/seccomp_test.go b/runsc/specutils/seccomp/seccomp_test.go
deleted file mode 100644
index 850c237ba..000000000
--- a/runsc/specutils/seccomp/seccomp_test.go
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright 2020 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 seccomp
-
-import (
- "fmt"
- "syscall"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.dev/gvisor/pkg/binary"
- "gvisor.dev/gvisor/pkg/bpf"
-)
-
-type seccompData struct {
- nr uint32
- arch uint32
- instructionPointer uint64
- args [6]uint64
-}
-
-// asInput converts a seccompData to a bpf.Input.
-func asInput(d seccompData) bpf.Input {
- return bpf.InputBytes{binary.Marshal(nil, binary.LittleEndian, d), binary.LittleEndian}
-}
-
-// testInput creates an Input struct with given seccomp input values.
-func testInput(arch uint32, syscallName string, args *[6]uint64) bpf.Input {
- syscallNo, err := lookupSyscallNo(arch, syscallName)
- if err != nil {
- // Assume tests set valid syscall names.
- panic(err)
- }
-
- if args == nil {
- argArray := [6]uint64{0, 0, 0, 0, 0, 0}
- args = &argArray
- }
-
- data := seccompData{
- nr: syscallNo,
- arch: arch,
- args: *args,
- }
-
- return asInput(data)
-}
-
-// testCase holds a seccomp test case.
-type testCase struct {
- name string
- config specs.LinuxSeccomp
- input bpf.Input
- expected uint32
-}
-
-var (
- // seccompTests is a list of speccomp test cases.
- seccompTests = []testCase{
- {
- name: "default_allow",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- },
- input: testInput(nativeArchAuditNo, "read", nil),
- expected: uint32(allowAction),
- },
- {
- name: "default_deny",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActErrno,
- },
- input: testInput(nativeArchAuditNo, "read", nil),
- expected: uint32(errnoAction),
- },
- {
- name: "deny_arch",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getcwd",
- },
- Action: specs.ActErrno,
- },
- },
- },
- // Syscall matches but the arch is AUDIT_ARCH_X86 so the return
- // value is the bad arch action.
- input: asInput(seccompData{nr: 183, arch: 0x40000003}), //
- expected: uint32(killThreadAction),
- },
- {
- name: "match_name_errno",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getcwd",
- "chmod",
- },
- Action: specs.ActErrno,
- },
- {
- Names: []string{
- "write",
- },
- Action: specs.ActTrace,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "getcwd", nil),
- expected: uint32(errnoAction),
- },
- {
- name: "match_name_trace",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getcwd",
- "chmod",
- },
- Action: specs.ActErrno,
- },
- {
- Names: []string{
- "write",
- },
- Action: specs.ActTrace,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "write", nil),
- expected: uint32(traceAction),
- },
- {
- name: "no_match_name_allow",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getcwd",
- "chmod",
- },
- Action: specs.ActErrno,
- },
- {
- Names: []string{
- "write",
- },
- Action: specs.ActTrace,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "openat", nil),
- expected: uint32(allowAction),
- },
- {
- name: "simple_match_args",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: syscall.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
- expected: uint32(errnoAction),
- },
- {
- name: "match_args_or",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: syscall.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- {
- Index: 0,
- Value: syscall.CLONE_VM,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
- expected: uint32(errnoAction),
- },
- {
- name: "match_args_and",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getsockopt",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 1,
- Value: syscall.SOL_SOCKET,
- Op: specs.OpEqualTo,
- },
- {
- Index: 2,
- Value: syscall.SO_PEERCRED,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET, syscall.SO_PEERCRED}),
- expected: uint32(errnoAction),
- },
- {
- name: "no_match_args_and",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "getsockopt",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 1,
- Value: syscall.SOL_SOCKET,
- Op: specs.OpEqualTo,
- },
- {
- Index: 2,
- Value: syscall.SO_PEERCRED,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET}),
- expected: uint32(allowAction),
- },
- {
- name: "Simple args (no match)",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: syscall.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_VM}),
- expected: uint32(allowAction),
- },
- {
- name: "OpMaskedEqual (match)",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: syscall.CLONE_FS,
- ValueTwo: syscall.CLONE_FS,
- Op: specs.OpMaskedEqual,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS | syscall.CLONE_VM}),
- expected: uint32(errnoAction),
- },
- {
- name: "OpMaskedEqual (no match)",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActAllow,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: syscall.CLONE_FS | syscall.CLONE_VM,
- ValueTwo: syscall.CLONE_FS | syscall.CLONE_VM,
- Op: specs.OpMaskedEqual,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
- expected: uint32(allowAction),
- },
- {
- name: "OpMaskedEqual (clone)",
- config: specs.LinuxSeccomp{
- DefaultAction: specs.ActErrno,
- Syscalls: []specs.LinuxSyscall{
- {
- Names: []string{
- "clone",
- },
- // This comes from the Docker default seccomp
- // profile for clone.
- Args: []specs.LinuxSeccompArg{
- {
- Index: 0,
- Value: 0x7e020000,
- ValueTwo: 0x0,
- Op: specs.OpMaskedEqual,
- },
- },
- Action: specs.ActAllow,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{0x50f00}),
- expected: uint32(allowAction),
- },
- }
-)
-
-// TestRunscSeccomp generates seccomp programs from OCI config and executes
-// them using runsc's library, comparing against expected results.
-func TestRunscSeccomp(t *testing.T) {
- for _, tc := range seccompTests {
- t.Run(tc.name, func(t *testing.T) {
- runscProgram, err := BuildProgram(&tc.config)
- if err != nil {
- t.Fatalf("generating runsc BPF: %v", err)
- }
-
- if err := checkProgram(runscProgram, tc.input, tc.expected); err != nil {
- t.Fatalf("running runsc BPF: %v", err)
- }
- })
- }
-}
-
-// checkProgram runs the given program over the given input and checks the
-// result against the expected output.
-func checkProgram(p bpf.Program, in bpf.Input, expected uint32) error {
- result, err := bpf.Exec(p, in)
- if err != nil {
- return err
- }
-
- if result != expected {
- // Include a decoded version of the program in output for debugging purposes.
- decoded, _ := bpf.DecodeProgram(p)
- return fmt.Errorf("Unexpected result: got: %d, expected: %d\nBPF Program\n%s", result, expected, decoded)
- }
-
- return nil
-}
diff --git a/runsc/specutils/specutils_state_autogen.go b/runsc/specutils/specutils_state_autogen.go
new file mode 100644
index 000000000..11eefbaa2
--- /dev/null
+++ b/runsc/specutils/specutils_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package specutils
diff --git a/runsc/specutils/specutils_test.go b/runsc/specutils/specutils_test.go
deleted file mode 100644
index 2c86fffe8..000000000
--- a/runsc/specutils/specutils_test.go
+++ /dev/null
@@ -1,265 +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 specutils
-
-import (
- "fmt"
- "os/exec"
- "strings"
- "testing"
- "time"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
-)
-
-func TestWaitForReadyHappy(t *testing.T) {
- cmd := exec.Command("/bin/sleep", "1000")
- if err := cmd.Start(); err != nil {
- t.Fatalf("cmd.Start() failed, err: %v", err)
- }
- defer cmd.Wait()
-
- var count int
- err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) {
- if count < 3 {
- count++
- return false, nil
- }
- return true, nil
- })
- if err != nil {
- t.Errorf("ProcessWaitReady got: %v, expected: nil", err)
- }
- cmd.Process.Kill()
-}
-
-func TestWaitForReadyFail(t *testing.T) {
- cmd := exec.Command("/bin/sleep", "1000")
- if err := cmd.Start(); err != nil {
- t.Fatalf("cmd.Start() failed, err: %v", err)
- }
- defer cmd.Wait()
-
- var count int
- err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) {
- if count < 3 {
- count++
- return false, nil
- }
- return false, fmt.Errorf("Fake error")
- })
- if err == nil {
- t.Errorf("ProcessWaitReady got: nil, expected: error")
- }
- cmd.Process.Kill()
-}
-
-func TestWaitForReadyNotRunning(t *testing.T) {
- cmd := exec.Command("/bin/true")
- if err := cmd.Start(); err != nil {
- t.Fatalf("cmd.Start() failed, err: %v", err)
- }
- defer cmd.Wait()
-
- err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) {
- return false, nil
- })
- if err != nil && !strings.Contains(err.Error(), "terminated") {
- t.Errorf("ProcessWaitReady got: %v, expected: process terminated", err)
- }
- if err == nil {
- t.Errorf("ProcessWaitReady incorrectly succeeded")
- }
-}
-
-func TestWaitForReadyTimeout(t *testing.T) {
- cmd := exec.Command("/bin/sleep", "1000")
- if err := cmd.Start(); err != nil {
- t.Fatalf("cmd.Start() failed, err: %v", err)
- }
- defer cmd.Wait()
-
- err := WaitForReady(cmd.Process.Pid, 50*time.Millisecond, func() (bool, error) {
- return false, nil
- })
- if !strings.Contains(err.Error(), "not running yet") {
- t.Errorf("ProcessWaitReady got: %v, expected: not running yet", err)
- }
- cmd.Process.Kill()
-}
-
-func TestSpecInvalid(t *testing.T) {
- for _, test := range []struct {
- name string
- spec specs.Spec
- error string
- }{
- {
- name: "valid",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Mounts: []specs.Mount{
- {
- Source: "src",
- Destination: "/dst",
- },
- },
- },
- error: "",
- },
- {
- name: "valid+warning",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- // This is normally set by docker and will just cause warnings to be logged.
- ApparmorProfile: "someprofile",
- },
- // This is normally set by docker and will just cause warnings to be logged.
- Linux: &specs.Linux{Seccomp: &specs.LinuxSeccomp{}},
- },
- error: "",
- },
- {
- name: "no root",
- spec: specs.Spec{
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- },
- error: "must be defined",
- },
- {
- name: "empty root",
- spec: specs.Spec{
- Root: &specs.Root{},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- },
- error: "must be defined",
- },
- {
- name: "no process",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- },
- error: "must be defined",
- },
- {
- name: "empty args",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{},
- },
- error: "must be defined",
- },
- {
- name: "selinux",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- SelinuxLabel: "somelabel",
- },
- },
- error: "is not supported",
- },
- {
- name: "solaris",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Solaris: &specs.Solaris{},
- },
- error: "is not supported",
- },
- {
- name: "windows",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Windows: &specs.Windows{},
- },
- error: "is not supported",
- },
- {
- name: "relative mount destination",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Mounts: []specs.Mount{
- {
- Source: "src",
- Destination: "dst",
- },
- },
- },
- error: "must be an absolute path",
- },
- {
- name: "invalid mount option",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Mounts: []specs.Mount{
- {
- Source: "/src",
- Destination: "/dst",
- Type: "bind",
- Options: []string{"shared"},
- },
- },
- },
- error: "is not supported",
- },
- {
- name: "invalid rootfs propagation",
- spec: specs.Spec{
- Root: &specs.Root{Path: "/"},
- Process: &specs.Process{
- Args: []string{"/bin/true"},
- },
- Linux: &specs.Linux{
- RootfsPropagation: "foo",
- },
- },
- error: "root mount propagation option must specify private or slave",
- },
- } {
- err := ValidateSpec(&test.spec)
- if len(test.error) == 0 {
- if err != nil {
- t.Errorf("ValidateSpec(%q) failed, err: %v", test.name, err)
- }
- } else {
- if err == nil || !strings.Contains(err.Error(), test.error) {
- t.Errorf("ValidateSpec(%q) wrong error, got: %v, want: .*%s.*", test.name, err, test.error)
- }
- }
- }
-}
diff --git a/runsc/version_test.sh b/runsc/version_test.sh
deleted file mode 100755
index 747350654..000000000
--- a/runsc/version_test.sh
+++ /dev/null
@@ -1,36 +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
-
-readonly runsc="$1"
-readonly version=$($runsc --version)
-
-# Version should should not match VERSION, which is the default and which will
-# also appear if something is wrong with workspace_status.sh script.
-if [[ $version =~ "VERSION" ]]; then
- echo "FAIL: Got bad version $version"
- exit 1
-fi
-
-# Version should contain at least one number.
-if [[ ! $version =~ [0-9] ]]; then
- echo "FAIL: Got bad version $version"
- exit 1
-fi
-
-echo "PASS: Got OK version $version"
-exit 0