From 28cb3692d9401606d24fb7d8db59ad1dee9f5f05 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 25 Nov 2017 23:51:14 -0800 Subject: [PATCH] validation: Use prove(1) as a TAP harness Capture stdout and stderr from create invocations, because we don't want to pollute the TAP output with things like runc's: Incorrect Usage. ... (which is for some reason printed to stdout) or: runc: "create" requires exactly 1 argument(s) which is printed to stderr. Instead, show the captured stderr as a diagnostic, and hide the stdout completely. Unless stderr is empty, in which case show stdout in case it contains something useful. Most of these tests are broken because we aren't collecting the container exit code or post-start stdout. But the tests haven't been doing that since the create/start split in 15577bd1 (add runtime struct; add create test, 2017-08-24, #447) anyway [1]. This commit just makes that more obvious. The patsubst and wildcard Makefile syntax is documented in [2]. The $(VALIDATION_TESTS) rule uses the static pattern rule syntax [3]. [1]: https://github.com/opencontainers/runtime-tools/pull/447#discussion_r136206073 [2]: https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html [3]: https://www.gnu.org/software/make/manual/html_node/Static-Usage.html#Static-Usage Signed-off-by: W. Trevor King --- Makefile | 11 +- validation/.gitignore | 1 + validation/create.go | 66 ++++ validation/default.go | 13 + validation/hostname.go | 14 + validation/linux_devices.go | 55 +++ validation/linux_gid_mappings.go | 14 + validation/linux_masked_paths.go | 20 ++ validation/linux_readonly_paths.go | 20 ++ validation/linux_rootfs_propagation_shared.go | 15 + .../linux_rootfs_propagation_unbindable.go | 15 + validation/linux_sysctl.go | 14 + validation/linux_uid_mappings.go | 14 + validation/mounts.go | 9 + validation/process.go | 23 ++ validation/process_capabilities.go | 22 ++ validation/process_oom_score_adj.go | 14 + validation/process_rlimits.go | 14 + validation/root_readonly_false.go | 14 + validation/root_readonly_true.go | 22 ++ validation/{ => util}/container.go | 50 ++- validation/util/test.go | 121 +++++++ validation/validation_test.go | 320 ------------------ 23 files changed, 551 insertions(+), 330 deletions(-) create mode 100644 validation/.gitignore create mode 100644 validation/create.go create mode 100644 validation/default.go create mode 100644 validation/hostname.go create mode 100644 validation/linux_devices.go create mode 100644 validation/linux_gid_mappings.go create mode 100644 validation/linux_masked_paths.go create mode 100644 validation/linux_readonly_paths.go create mode 100644 validation/linux_rootfs_propagation_shared.go create mode 100644 validation/linux_rootfs_propagation_unbindable.go create mode 100644 validation/linux_sysctl.go create mode 100644 validation/linux_uid_mappings.go create mode 100644 validation/mounts.go create mode 100644 validation/process.go create mode 100644 validation/process_capabilities.go create mode 100644 validation/process_oom_score_adj.go create mode 100644 validation/process_rlimits.go create mode 100644 validation/root_readonly_false.go create mode 100644 validation/root_readonly_true.go rename validation/{ => util}/container.go (74%) create mode 100644 validation/util/test.go delete mode 100644 validation/validation_test.go diff --git a/Makefile b/Makefile index 7ac3f751c..4052275f2 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ BUILDTAGS= RUNTIME ?= runc COMMIT=$(shell git rev-parse HEAD 2> /dev/null || true) VERSION := ${shell cat ./VERSION} +VALIDATION_TESTS ?= $(patsubst %.go,%.t,$(wildcard validation/*.go)) all: tool runtimetest @@ -35,10 +36,14 @@ uninstall: rm -f $(PREFIX)/share/bash-completion/completions/oci-runtime-tool clean: - rm -f oci-runtime-tool runtimetest *.1 + rm -f oci-runtime-tool runtimetest *.1 $(VALIDATION_TESTS) -localvalidation: runtimetest - RUNTIME=$(RUNTIME) go test -tags "$(BUILDTAGS)" ${TESTFLAGS} -v github.com/opencontainers/runtime-tools/validation +localvalidation: runtimetest $(VALIDATION_TESTS) + RUNTIME=$(RUNTIME) prove $(VALIDATION_TESTS) + +.PHONY: $(VALIDATION_TESTS) +$(VALIDATION_TESTS): %.t: %.go + go build -tags "$(BUILDTAGS)" ${TESTFLAGS} -o $@ $< .PHONY: test .gofmt .govet .golint diff --git a/validation/.gitignore b/validation/.gitignore new file mode 100644 index 000000000..141f81298 --- /dev/null +++ b/validation/.gitignore @@ -0,0 +1 @@ +/*.t diff --git a/validation/create.go b/validation/create.go new file mode 100644 index 000000000..0096f889e --- /dev/null +++ b/validation/create.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + "github.com/mndrix/tap-go" + rspecs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "github.com/opencontainers/runtime-tools/specerror" + "github.com/opencontainers/runtime-tools/validation/util" + "github.com/satori/go.uuid" +) + +func main() { + t := tap.New() + + g := generate.New() + g.SetRootPath(".") + g.SetProcessArgs([]string{"ls"}) + + bundleDir, err := util.PrepareBundle() + if err != nil { + util.Fatal(err) + } + + r, err := util.NewRuntime(util.RuntimeCommand, bundleDir) + if err != nil { + util.Fatal(err) + } + defer r.Clean(true) + + err = r.SetConfig(&g) + if err != nil { + util.Fatal(err) + } + + containerID := uuid.NewV4().String() + cases := []struct { + id string + errExpected bool + err error + }{ + {"", false, specerror.NewError(specerror.CreateWithBundlePathAndID, fmt.Errorf("create MUST generate an error if the ID is not provided"), rspecs.Version)}, + {containerID, true, specerror.NewError(specerror.CreateNewContainer, fmt.Errorf("create MUST create a new container"), rspecs.Version)}, + {containerID, false, specerror.NewError(specerror.CreateWithUniqueID, fmt.Errorf("create MUST generate an error if the ID provided is not unique"), rspecs.Version)}, + } + + for _, c := range cases { + r.SetID(c.id) + stderr, err := r.Create() + t.Ok((err == nil) == c.errExpected, c.err.(*specerror.Error).Err.Err.Error()) + t.Diagnostic(c.err.(*specerror.Error).Err.Reference) + t.Diagnostic(err.Error()) + if len(stderr) > 0 { + t.Diagnostic(string(stderr)) + } + + if err == nil { + state, _ := r.State() + t.Ok(state.ID == c.id, "") + t.Diagnosticf("container PID: %d, state ID: %d", c.id, state.ID) + } + } + + t.AutoPlan() +} diff --git a/validation/default.go b/validation/default.go new file mode 100644 index 000000000..07054f7bb --- /dev/null +++ b/validation/default.go @@ -0,0 +1,13 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/hostname.go b/validation/hostname.go new file mode 100644 index 000000000..139de84cd --- /dev/null +++ b/validation/hostname.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetHostname("hostname-specific") + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_devices.go b/validation/linux_devices.go new file mode 100644 index 000000000..a5768adcd --- /dev/null +++ b/validation/linux_devices.go @@ -0,0 +1,55 @@ +package main + +import ( + "os" + + rspecs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + + // add char device + cdev := rspecs.LinuxDevice{} + cdev.Path = "/dev/test1" + cdev.Type = "c" + cdev.Major = 10 + cdev.Minor = 666 + cmode := os.FileMode(int32(432)) + cdev.FileMode = &cmode + cuid := uint32(0) + cdev.UID = &cuid + cgid := uint32(0) + cdev.GID = &cgid + g.AddDevice(cdev) + + // add block device + bdev := rspecs.LinuxDevice{} + bdev.Path = "/dev/test2" + bdev.Type = "b" + bdev.Major = 8 + bdev.Minor = 666 + bmode := os.FileMode(int32(432)) + bdev.FileMode = &bmode + uid := uint32(0) + bdev.UID = &uid + gid := uint32(0) + bdev.GID = &gid + g.AddDevice(bdev) + + // add fifo device + pdev := rspecs.LinuxDevice{} + pdev.Path = "/dev/test3" + pdev.Type = "p" + pdev.Major = 8 + pdev.Minor = 666 + pmode := os.FileMode(int32(432)) + pdev.FileMode = &pmode + g.AddDevice(pdev) + + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_gid_mappings.go b/validation/linux_gid_mappings.go new file mode 100644 index 000000000..383d5ce65 --- /dev/null +++ b/validation/linux_gid_mappings.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(3200)) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_masked_paths.go b/validation/linux_masked_paths.go new file mode 100644 index 000000000..5d97f41ba --- /dev/null +++ b/validation/linux_masked_paths.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddLinuxMaskedPaths("/masktest") + err := util.RuntimeInsideValidate(g, func(path string) error { + pathName := filepath.Join(path, "masktest") + return os.MkdirAll(pathName, 0700) + }) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_readonly_paths.go b/validation/linux_readonly_paths.go new file mode 100644 index 000000000..6c1e37fa1 --- /dev/null +++ b/validation/linux_readonly_paths.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddLinuxReadonlyPaths("readonlytest") + err := util.RuntimeInsideValidate(g, func(path string) error { + pathName := filepath.Join(path, "readonlytest") + return os.MkdirAll(pathName, 0700) + }) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_rootfs_propagation_shared.go b/validation/linux_rootfs_propagation_shared.go new file mode 100644 index 000000000..c7699277b --- /dev/null +++ b/validation/linux_rootfs_propagation_shared.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetupPrivileged(true) + g.SetLinuxRootPropagation("shared") + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_rootfs_propagation_unbindable.go b/validation/linux_rootfs_propagation_unbindable.go new file mode 100644 index 000000000..c6ea24c88 --- /dev/null +++ b/validation/linux_rootfs_propagation_unbindable.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetupPrivileged(true) + g.SetLinuxRootPropagation("unbindable") + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_sysctl.go b/validation/linux_sysctl.go new file mode 100644 index 000000000..1daded44e --- /dev/null +++ b/validation/linux_sysctl.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddLinuxSysctl("net.ipv4.ip_forward", "1") + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/linux_uid_mappings.go b/validation/linux_uid_mappings.go new file mode 100644 index 000000000..856863736 --- /dev/null +++ b/validation/linux_uid_mappings.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddLinuxUIDMapping(uint32(1000), uint32(0), uint32(3200)) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/mounts.go b/validation/mounts.go new file mode 100644 index 000000000..e3df60b21 --- /dev/null +++ b/validation/mounts.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + util.Skip("TODO: mounts generation options have not been implemented", "") +} diff --git a/validation/process.go b/validation/process.go new file mode 100644 index 000000000..c132c19e9 --- /dev/null +++ b/validation/process.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetProcessCwd("/test") + g.AddProcessEnv("testa", "valuea") + g.AddProcessEnv("testb", "123") + + err := util.RuntimeInsideValidate(g, func(path string) error { + pathName := filepath.Join(path, "test") + return os.MkdirAll(pathName, 0700) + }) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/process_capabilities.go b/validation/process_capabilities.go new file mode 100644 index 000000000..3c676c554 --- /dev/null +++ b/validation/process_capabilities.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + "runtime" + + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + if "linux" != runtime.GOOS { + util.Skip("linux-specific process.capabilities test", runtime.GOOS) + os.Exit(0) + } + + g := util.GetDefaultGenerator() + g.SetupPrivileged(true) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/process_oom_score_adj.go b/validation/process_oom_score_adj.go new file mode 100644 index 000000000..ee8448c1f --- /dev/null +++ b/validation/process_oom_score_adj.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetProcessOOMScoreAdj(500) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/process_rlimits.go b/validation/process_rlimits.go new file mode 100644 index 000000000..9f146552e --- /dev/null +++ b/validation/process_rlimits.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.AddProcessRlimits("RLIMIT_NOFILE", 1024, 1024) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/root_readonly_false.go b/validation/root_readonly_false.go new file mode 100644 index 000000000..6349ccd15 --- /dev/null +++ b/validation/root_readonly_false.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + g := util.GetDefaultGenerator() + g.SetRootReadonly(false) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/root_readonly_true.go b/validation/root_readonly_true.go new file mode 100644 index 000000000..05907a02c --- /dev/null +++ b/validation/root_readonly_true.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + "runtime" + + "github.com/opencontainers/runtime-tools/validation/util" +) + +func main() { + if "windows" == runtime.GOOS { + util.Skip("non-Windows root.readonly test", runtime.GOOS) + os.Exit(0) + } + + g := util.GetDefaultGenerator() + g.SetRootReadonly(true) + err := util.RuntimeInsideValidate(g, nil) + if err != nil { + util.Fatal(err) + } +} diff --git a/validation/container.go b/validation/util/container.go similarity index 74% rename from validation/container.go rename to validation/util/container.go index 295b81e7d..f7f492a21 100644 --- a/validation/container.go +++ b/validation/util/container.go @@ -1,8 +1,10 @@ -package validation +package util import ( + "bytes" "encoding/json" "errors" + "io/ioutil" "os" "os/exec" "path/filepath" @@ -45,7 +47,7 @@ func (r *Runtime) SetID(id string) { } // Create a container -func (r *Runtime) Create() error { +func (r *Runtime) Create() (stderr []byte, err error) { var args []string args = append(args, "create") if r.ID != "" { @@ -59,13 +61,31 @@ func (r *Runtime) Create() error { cmd := exec.Command(r.RuntimeCommand, args...) cmd.Dir = r.BundleDir cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() + stdoutBuffer := new(bytes.Buffer) + cmd.Stdout = stdoutBuffer + stderrBuffer := new(bytes.Buffer) + cmd.Stderr = stderrBuffer + + err = cmd.Run() + + stdout, err2 := ioutil.ReadAll(stdoutBuffer) + if err == nil && err2 != nil { + err = err2 + } + + stderr, err2 = ioutil.ReadAll(stderrBuffer) + if err == nil && err2 != nil { + err = err2 + } + if len(stderr) == 0 { + stderr = stdout + } + + return stderr, err } // Start a container -func (r *Runtime) Start() error { +func (r *Runtime) Start() (stderr []byte, err error) { var args []string args = append(args, "start") if r.ID != "" { @@ -74,9 +94,25 @@ func (r *Runtime) Start() error { cmd := exec.Command(r.RuntimeCommand, args...) cmd.Stdin = os.Stdin + stdoutBuffer := new(bytes.Buffer) cmd.Stdout = os.Stdout + stderrBuffer := new(bytes.Buffer) cmd.Stderr = os.Stderr - return cmd.Run() + + stdout, err2 := ioutil.ReadAll(stdoutBuffer) + if err == nil && err2 != nil { + err = err2 + } + + stderr, err2 = ioutil.ReadAll(stderrBuffer) + if err == nil && err2 != nil { + err = err2 + } + if len(stderr) == 0 { + stderr = stdout + } + + return stderr, err } // State a container information diff --git a/validation/util/test.go b/validation/util/test.go new file mode 100644 index 000000000..614f936c3 --- /dev/null +++ b/validation/util/test.go @@ -0,0 +1,121 @@ +package util + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/mndrix/tap-go" + "github.com/mrunalp/fileutils" + "github.com/opencontainers/runtime-tools/generate" + "github.com/satori/go.uuid" +) + +var ( + RuntimeCommand = "runc" +) + +// PreFunc initializes the test environment after preparing the bundle +// but before creating the container. +type PreFunc func(string) error + +func init() { + runtimeInEnv := os.Getenv("RUNTIME") + if runtimeInEnv != "" { + RuntimeCommand = runtimeInEnv + } +} + +// Fatal prints a warning to stderr and exits. +func Fatal(err error) { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) +} + +// Skip skips a full TAP suite. +func Skip(message string, diagnostic string) { + t := tap.New() + t.Header(1) + t.Skip(1, message) + if diagnostic != "" { + t.Diagnostic(diagnostic) + } +} + +// PrepareBundle creates a test bundle in a temporary directory. +func PrepareBundle() (string, error) { + bundleDir, err := ioutil.TempDir("", "ocitest") + if err != nil { + return "", err + } + + // Untar the root fs + untarCmd := exec.Command("tar", "-xf", fmt.Sprintf("rootfs-%s.tar.gz", runtime.GOARCH), "-C", bundleDir) + output, err := untarCmd.CombinedOutput() + if err != nil { + os.Stderr.Write(output) + os.RemoveAll(bundleDir) + return "", err + } + + return bundleDir, nil +} + +// GetDefaultGenerator creates a default configuration generator. +func GetDefaultGenerator() *generate.Generator { + g := generate.New() + g.SetRootPath(".") + g.SetProcessArgs([]string{"/runtimetest", "--path=/"}) + return &g +} + +// RuntimeInsideValidate runs runtimetest inside a container. +func RuntimeInsideValidate(g *generate.Generator, f PreFunc) (err error) { + bundleDir, err := PrepareBundle() + if err != nil { + return err + } + + if f != nil { + if err := f(bundleDir); err != nil { + return err + } + } + + r, err := NewRuntime(RuntimeCommand, bundleDir) + if err != nil { + os.RemoveAll(bundleDir) + return err + } + defer r.Clean(true) + err = r.SetConfig(g) + if err != nil { + return err + } + err = fileutils.CopyFile("runtimetest", filepath.Join(r.BundleDir, "runtimetest")) + if err != nil { + return err + } + + r.SetID(uuid.NewV4().String()) + stderr, err := r.Create() + if err != nil { + os.Stderr.WriteString("failed to create the container") + os.Stderr.Write(stderr) + return err + } + + stderr, err = r.Start() + if err != nil { + return err + } + + t := tap.New() + t.Header(1) + t.Fail("FIXME: we don't wait for the container to exit or collect its stdout") + + return nil +} diff --git a/validation/validation_test.go b/validation/validation_test.go deleted file mode 100644 index edc041b87..000000000 --- a/validation/validation_test.go +++ /dev/null @@ -1,320 +0,0 @@ -package validation - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - - "github.com/mrunalp/fileutils" - rspecs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/satori/go.uuid" - "github.com/stretchr/testify/assert" - - "github.com/opencontainers/runtime-tools/generate" - "github.com/opencontainers/runtime-tools/specerror" -) - -var ( - runtimeCommand = "runc" -) - -// build test environment before running container -type preFunc func(string) error - -func init() { - runtimeInEnv := os.Getenv("RUNTIME") - if runtimeInEnv != "" { - runtimeCommand = runtimeInEnv - } -} - -func prepareBundle() (string, error) { - // Setup a temporary test directory - bundleDir, err := ioutil.TempDir("", "ocitest") - if err != nil { - return "", err - } - - // Untar the root fs - untarCmd := exec.Command("tar", "-xf", fmt.Sprintf("../rootfs-%s.tar.gz", runtime.GOARCH), "-C", bundleDir) - _, err = untarCmd.CombinedOutput() - if err != nil { - os.RemoveAll(bundleDir) - return "", err - } - - return bundleDir, nil -} - -func getDefaultGenerator() *generate.Generator { - g := generate.New() - g.SetRootPath(".") - g.SetProcessArgs([]string{"/runtimetest", "--path=/"}) - return &g -} - -func runtimeInsideValidate(g *generate.Generator, f preFunc) error { - bundleDir, err := prepareBundle() - if err != nil { - return err - } - - if f != nil { - if err := f(bundleDir); err != nil { - return err - } - } - - r, err := NewRuntime(runtimeCommand, bundleDir) - if err != nil { - os.RemoveAll(bundleDir) - return err - } - defer r.Clean(true) - err = r.SetConfig(g) - if err != nil { - return err - } - err = fileutils.CopyFile("../runtimetest", filepath.Join(r.BundleDir, "runtimetest")) - if err != nil { - return err - } - - r.SetID(uuid.NewV4().String()) - err = r.Create() - if err != nil { - return err - } - return r.Start() -} - -func TestValidateBasic(t *testing.T) { - g := getDefaultGenerator() - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test whether rootfs Readonly can be applied as false -func TestValidateRootFSReadWrite(t *testing.T) { - g := getDefaultGenerator() - g.SetRootReadonly(false) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test whether rootfs Readonly can be applied as true -func TestValidateRootFSReadonly(t *testing.T) { - if "windows" == runtime.GOOS { - t.Skip("skip this test on windows platform") - } - - g := getDefaultGenerator() - g.SetRootReadonly(true) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test Process -func TestValidateProcess(t *testing.T) { - g := getDefaultGenerator() - g.SetProcessCwd("/test") - g.AddProcessEnv("testa", "valuea") - g.AddProcessEnv("testb", "123") - - assert.Nil(t, runtimeInsideValidate(g, func(path string) error { - pathName := filepath.Join(path, "test") - return os.MkdirAll(pathName, 0700) - })) -} - -// Test whether Capabilites can be applied or not -func TestValidateCapabilities(t *testing.T) { - if "linux" != runtime.GOOS { - t.Skip("skip linux-specific capabilities test") - } - - g := getDefaultGenerator() - g.SetupPrivileged(true) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test whether hostname can be applied or not -func TestValidateHostname(t *testing.T) { - g := getDefaultGenerator() - g.SetHostname("hostname-specific") - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateRootfsPropagationPrivate(t *testing.T) { - t.Skip("has not been implemented yet") -} - -func TestValidateRootfsPropagationSlave(t *testing.T) { - t.Skip("has not been implemented yet") -} - -func TestValidateRootfsPropagationShared(t *testing.T) { - g := getDefaultGenerator() - g.SetupPrivileged(true) - g.SetLinuxRootPropagation("shared") - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateRootfsPropagationUnbindable(t *testing.T) { - g := getDefaultGenerator() - g.SetupPrivileged(true) - g.SetLinuxRootPropagation("unbindable") - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateLinuxDevices(t *testing.T) { - g := getDefaultGenerator() - - // add char device - cdev := rspecs.LinuxDevice{} - cdev.Path = "/dev/test1" - cdev.Type = "c" - cdev.Major = 10 - cdev.Minor = 666 - cmode := os.FileMode(int32(432)) - cdev.FileMode = &cmode - cuid := uint32(0) - cdev.UID = &cuid - cgid := uint32(0) - cdev.GID = &cgid - g.AddDevice(cdev) - // add block device - bdev := rspecs.LinuxDevice{} - bdev.Path = "/dev/test2" - bdev.Type = "b" - bdev.Major = 8 - bdev.Minor = 666 - bmode := os.FileMode(int32(432)) - bdev.FileMode = &bmode - uid := uint32(0) - bdev.UID = &uid - gid := uint32(0) - bdev.GID = &gid - g.AddDevice(bdev) - // add fifo device - pdev := rspecs.LinuxDevice{} - pdev.Path = "/dev/test3" - pdev.Type = "p" - pdev.Major = 8 - pdev.Minor = 666 - pmode := os.FileMode(int32(432)) - pdev.FileMode = &pmode - g.AddDevice(pdev) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateMaskedPaths(t *testing.T) { - g := getDefaultGenerator() - g.AddLinuxMaskedPaths("/masktest") - - assert.Nil(t, runtimeInsideValidate(g, func(path string) error { - pathName := filepath.Join(path, "masktest") - return os.MkdirAll(pathName, 0700) - })) -} - -func TestValidateROPaths(t *testing.T) { - g := getDefaultGenerator() - g.AddLinuxReadonlyPaths("readonlytest") - - assert.Nil(t, runtimeInsideValidate(g, func(path string) error { - pathName := filepath.Join(path, "readonlytest") - return os.MkdirAll(pathName, 0700) - })) -} - -func TestValidateOOMScoreAdj(t *testing.T) { - g := getDefaultGenerator() - g.SetProcessOOMScoreAdj(500) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateUIDMappings(t *testing.T) { - g := getDefaultGenerator() - g.AddLinuxUIDMapping(uint32(1000), uint32(0), uint32(3200)) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -func TestValidateGIDMappings(t *testing.T) { - g := getDefaultGenerator() - g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(3200)) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test whether mounts are correctly mounted -func TestValidateMounts(t *testing.T) { - // TODO mounts generation options have not been implemented - // will add it after 'mounts generate' done -} - -// Test whether rlimits can be applied or not -func TestValidateRlimits(t *testing.T) { - g := getDefaultGenerator() - g.AddProcessRlimits("RLIMIT_NOFILE", 1024, 1024) - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test whether sysctls can be applied or not -func TestValidateSysctls(t *testing.T) { - g := getDefaultGenerator() - g.AddLinuxSysctl("net.ipv4.ip_forward", "1") - - assert.Nil(t, runtimeInsideValidate(g, nil)) -} - -// Test Create operation -func TestValidateCreate(t *testing.T) { - g := generate.New() - g.SetRootPath(".") - g.SetProcessArgs([]string{"ls"}) - - bundleDir, err := prepareBundle() - assert.Nil(t, err) - - r, err := NewRuntime(runtimeCommand, bundleDir) - assert.Nil(t, err) - defer r.Clean(true) - - err = r.SetConfig(&g) - assert.Nil(t, err) - - containerID := uuid.NewV4().String() - cases := []struct { - id string - errExpected bool - err error - }{ - {"", false, specerror.NewError(specerror.CreateWithBundlePathAndID, fmt.Errorf("create MUST generate an error if the ID is not provided"), rspecs.Version)}, - {containerID, true, specerror.NewError(specerror.CreateNewContainer, fmt.Errorf("create MUST create a new container"), rspecs.Version)}, - {containerID, false, specerror.NewError(specerror.CreateWithUniqueID, fmt.Errorf("create MUST generate an error if the ID provided is not unique"), rspecs.Version)}, - } - - for _, c := range cases { - r.SetID(c.id) - err := r.Create() - assert.Equal(t, c.errExpected, err == nil, c.err.Error()) - - if err == nil { - state, _ := r.State() - assert.Equal(t, c.id, state.ID, c.err.Error()) - } - } -}