summaryrefslogtreecommitdiffhomepage
path: root/runsc/specutils
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/specutils')
-rw-r--r--runsc/specutils/BUILD34
-rw-r--r--runsc/specutils/safemount_test/BUILD23
-rw-r--r--runsc/specutils/safemount_test/safemount_runner.go117
-rw-r--r--runsc/specutils/safemount_test/safemount_test.go53
-rw-r--r--runsc/specutils/seccomp/BUILD38
-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.go409
-rw-r--r--runsc/specutils/specutils_state_autogen.go3
-rw-r--r--runsc/specutils/specutils_test.go271
11 files changed, 16 insertions, 945 deletions
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/safemount_test/BUILD b/runsc/specutils/safemount_test/BUILD
deleted file mode 100644
index c39c40492..000000000
--- a/runsc/specutils/safemount_test/BUILD
+++ /dev/null
@@ -1,23 +0,0 @@
-load("//tools:defs.bzl", "go_binary", "go_test")
-
-package(licenses = ["notice"])
-
-go_test(
- name = "safemount_test",
- size = "small",
- srcs = ["safemount_test.go"],
- data = [":safemount_runner"],
- deps = [
- "//pkg/test/testutil",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_binary(
- name = "safemount_runner",
- srcs = ["safemount_runner.go"],
- deps = [
- "//runsc/specutils",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/specutils/safemount_test/safemount_runner.go b/runsc/specutils/safemount_test/safemount_runner.go
deleted file mode 100644
index b23193033..000000000
--- a/runsc/specutils/safemount_test/safemount_runner.go
+++ /dev/null
@@ -1,117 +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.
-
-// safemount_runner is used to test the SafeMount function. Because use of
-// unix.Mount requires privilege, tests must launch this process with
-// CLONE_NEWNS and CLONE_NEWUSER.
-package main
-
-import (
- "errors"
- "fmt"
- "log"
- "os"
- "path/filepath"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/runsc/specutils"
-)
-
-func main() {
- // The test temporary directory is the first argument.
- tempdir := os.Args[1]
-
- tcs := []struct {
- name string
- testfunc func() error
- }{{
- name: "unix.Mount to folder succeeds",
- testfunc: func() error {
- dir2Path := filepath.Join(tempdir, "subdir2")
- if err := unix.Mount(filepath.Join(tempdir, "subdir"), dir2Path, "bind", unix.MS_BIND, ""); err != nil {
- return fmt.Errorf("mount: %v", err)
- }
- return unix.Unmount(dir2Path, unix.MNT_DETACH)
- },
- }, {
- // unix.Mount doesn't care whether the target is a symlink.
- name: "unix.Mount to symlink succeeds",
- testfunc: func() error {
- symlinkPath := filepath.Join(tempdir, "symlink")
- if err := unix.Mount(filepath.Join(tempdir, "subdir"), symlinkPath, "bind", unix.MS_BIND, ""); err != nil {
- return fmt.Errorf("mount: %v", err)
- }
- return unix.Unmount(symlinkPath, unix.MNT_DETACH)
- },
- }, {
- name: "SafeMount to folder succeeds",
- testfunc: func() error {
- dir2Path := filepath.Join(tempdir, "subdir2")
- if err := specutils.SafeMount(filepath.Join(tempdir, "subdir"), dir2Path, "bind", unix.MS_BIND, "", "/proc"); err != nil {
- return fmt.Errorf("SafeMount: %v", err)
- }
- return unix.Unmount(dir2Path, unix.MNT_DETACH)
- },
- }, {
- name: "SafeMount to symlink fails",
- testfunc: func() error {
- err := specutils.SafeMount(filepath.Join(tempdir, "subdir"), filepath.Join(tempdir, "symlink"), "bind", unix.MS_BIND, "", "/proc")
- if err == nil {
- return fmt.Errorf("SafeMount didn't fail, but should have")
- }
- var symErr *specutils.ErrSymlinkMount
- if !errors.As(err, &symErr) {
- return fmt.Errorf("expected SafeMount to fail with ErrSymlinkMount, but got: %v", err)
- }
- return nil
- },
- }}
-
- for _, tc := range tcs {
- if err := runTest(tempdir, tc.testfunc); err != nil {
- log.Fatalf("failed test %q: %v", tc.name, err)
- }
- }
-}
-
-// runTest runs testfunc with the following directory structure:
-// tempdir/
-// subdir/
-// subdir2/
-// symlink --> ./subdir2
-func runTest(tempdir string, testfunc func() error) error {
- // Create tempdir/subdir/.
- dirPath := filepath.Join(tempdir, "subdir")
- if err := os.Mkdir(dirPath, 0777); err != nil {
- return fmt.Errorf("os.Mkdir(%s, 0777)", dirPath)
- }
- defer os.Remove(dirPath)
-
- // Create tempdir/subdir2/.
- dir2Path := filepath.Join(tempdir, "subdir2")
- if err := os.Mkdir(dir2Path, 0777); err != nil {
- return fmt.Errorf("os.Mkdir(%s, 0777)", dir2Path)
- }
- defer os.Remove(dir2Path)
-
- // Create tempdir/symlink, which points to ./subdir2.
- symlinkPath := filepath.Join(tempdir, "symlink")
- if err := os.Symlink("./subdir2", symlinkPath); err != nil {
- return fmt.Errorf("failed to create symlink %s: %v", symlinkPath, err)
- }
- defer os.Remove(symlinkPath)
-
- // Run the actual test.
- return testfunc()
-}
diff --git a/runsc/specutils/safemount_test/safemount_test.go b/runsc/specutils/safemount_test/safemount_test.go
deleted file mode 100644
index 8820978c4..000000000
--- a/runsc/specutils/safemount_test/safemount_test.go
+++ /dev/null
@@ -1,53 +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 safemount_test
-
-import (
- "os"
- "os/exec"
- "syscall"
- "testing"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/test/testutil"
-)
-
-func TestSafeMount(t *testing.T) {
- // We run the actual tests in another process, as we need CAP_SYS_ADMIN to
- // call mount(2). The new process runs in its own user and mount namespaces.
- runner, err := testutil.FindFile("runsc/specutils/safemount_test/safemount_runner")
- if err != nil {
- t.Fatalf("failed to find test runner binary: %v", err)
- }
- cmd := exec.Command(runner, t.TempDir())
- cmd.SysProcAttr = &unix.SysProcAttr{
- Cloneflags: unix.CLONE_NEWNS | unix.CLONE_NEWUSER,
- UidMappings: []syscall.SysProcIDMap{
- {ContainerID: 0, HostID: os.Getuid(), Size: 1},
- },
- GidMappings: []syscall.SysProcIDMap{
- {ContainerID: 0, HostID: os.Getgid(), Size: 1},
- },
- GidMappingsEnableSetgroups: false,
- Credential: &syscall.Credential{
- Uid: 0,
- Gid: 0,
- },
- }
- output, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("failed running %s with error: %v\ntest output:\n%s", cmd, err, output)
- }
-}
diff --git a/runsc/specutils/seccomp/BUILD b/runsc/specutils/seccomp/BUILD
deleted file mode 100644
index c5f5b863e..000000000
--- a/runsc/specutils/seccomp/BUILD
+++ /dev/null
@@ -1,38 +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",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "seccomp_test",
- size = "small",
- srcs = ["seccomp_test.go"],
- library = ":seccomp",
- deps = [
- "//pkg/abi/linux",
- "//pkg/bpf",
- "//pkg/hostarch",
- "//pkg/marshal",
- "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
- "@org_golang_x_sys//unix: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 20796bf14..000000000
--- a/runsc/specutils/seccomp/seccomp_test.go
+++ /dev/null
@@ -1,409 +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"
- "testing"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/bpf"
- "gvisor.dev/gvisor/pkg/hostarch"
- "gvisor.dev/gvisor/pkg/marshal"
-)
-
-// asInput converts a linux.SeccompData to a bpf.Input.
-func asInput(d *linux.SeccompData) bpf.Input {
- return bpf.InputBytes{marshal.Marshal(d), hostarch.ByteOrder}
-}
-
-// 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 := linux.SeccompData{
- Nr: int32(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(&linux.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: unix.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{unix.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: unix.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- {
- Index: 0,
- Value: unix.CLONE_VM,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{unix.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: unix.SOL_SOCKET,
- Op: specs.OpEqualTo,
- },
- {
- Index: 2,
- Value: unix.SO_PEERCRED,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, unix.SOL_SOCKET, unix.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: unix.SOL_SOCKET,
- Op: specs.OpEqualTo,
- },
- {
- Index: 2,
- Value: unix.SO_PEERCRED,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, unix.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: unix.CLONE_FS,
- Op: specs.OpEqualTo,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{unix.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: unix.CLONE_FS,
- ValueTwo: unix.CLONE_FS,
- Op: specs.OpMaskedEqual,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{unix.CLONE_FS | unix.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: unix.CLONE_FS | unix.CLONE_VM,
- ValueTwo: unix.CLONE_FS | unix.CLONE_VM,
- Op: specs.OpMaskedEqual,
- },
- },
- Action: specs.ActErrno,
- },
- },
- },
- input: testInput(nativeArchAuditNo, "clone", &[6]uint64{unix.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 e2d3a75dc..000000000
--- a/runsc/specutils/specutils_test.go
+++ /dev/null
@@ -1,271 +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 func() { _ = 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)
- }
- if err := cmd.Process.Kill(); err != nil {
- t.Errorf("cmd.ProcessKill(): %v", err)
- }
-}
-
-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 func() { _ = 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")
- }
- if err := cmd.Process.Kill(); err != nil {
- t.Errorf("cmd.ProcessKill(): %v", err)
- }
-}
-
-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 func() { _ = 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 func() { _ = cmd.Wait() }()
-
- err := WaitForReady(cmd.Process.Pid, 50*time.Millisecond, func() (bool, error) {
- return false, nil
- })
- if err == nil || !strings.Contains(err.Error(), "not running yet") {
- t.Errorf("ProcessWaitReady got: %v, expected: not running yet", err)
- }
- if err := cmd.Process.Kill(); err != nil {
- t.Errorf("cmd.ProcessKill(): %v", err)
- }
-}
-
-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)
- }
- }
- }
-}