From 56054fc1fb0b92cb985f96467f9059e202d8095c Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Tue, 7 Apr 2020 18:49:52 -0700 Subject: Add friendlier messages for frequently encountered errors. Issue #2270 Issue #1765 PiperOrigin-RevId: 305385436 --- runsc/specutils/specutils.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runsc/specutils/specutils.go') diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index d3c2e4e78..0f4a9cf6d 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -528,3 +528,8 @@ func EnvVar(env []string, name string) (string, bool) { } return "", false } + +// FaqErrorMsg returns an error message pointing to the FAQ. +func FaqErrorMsg(anchor, msg string) string { + return fmt.Sprintf("%s; see https://gvisor.dev/faq#%s for more details", msg, anchor) +} -- cgit v1.2.3 From daf3322498b698518a3c8545ad05f790deb3848c Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Fri, 10 Apr 2020 20:31:07 -0700 Subject: Add logging message for noNewPrivileges OCI option. noNewPrivileges is ignored if set to false since gVisor assumes that PR_SET_NO_NEW_PRIVS is always enabled. PiperOrigin-RevId: 305991947 --- pkg/sentry/kernel/task_identity.go | 2 +- pkg/sentry/syscalls/linux/sys_prctl.go | 4 ++-- runsc/specutils/specutils.go | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'runsc/specutils/specutils.go') diff --git a/pkg/sentry/kernel/task_identity.go b/pkg/sentry/kernel/task_identity.go index ce3e6ef28..0325967e4 100644 --- a/pkg/sentry/kernel/task_identity.go +++ b/pkg/sentry/kernel/task_identity.go @@ -455,7 +455,7 @@ func (t *Task) SetKeepCaps(k bool) { t.creds.Store(creds) } -// updateCredsForExec updates t.creds to reflect an execve(). +// updateCredsForExecLocked updates t.creds to reflect an execve(). // // NOTE(b/30815691): We currently do not implement privileged executables // (set-user/group-ID bits and file capabilities). This allows us to make a lot diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go index 9c6728530..f92bf8096 100644 --- a/pkg/sentry/syscalls/linux/sys_prctl.go +++ b/pkg/sentry/syscalls/linux/sys_prctl.go @@ -161,8 +161,8 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall if args[1].Int() != 1 || args[2].Int() != 0 || args[3].Int() != 0 || args[4].Int() != 0 { return 0, nil, syserror.EINVAL } - // no_new_privs is assumed to always be set. See - // kernel.Task.updateCredsForExec. + // PR_SET_NO_NEW_PRIVS is assumed to always be set. + // See kernel.Task.updateCredsForExecLocked. return 0, nil, nil case linux.PR_GET_NO_NEW_PRIVS: diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 0f4a9cf6d..837d5e238 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -92,6 +92,12 @@ func ValidateSpec(spec *specs.Spec) error { log.Warningf("AppArmor profile %q is being ignored", spec.Process.ApparmorProfile) } + // PR_SET_NO_NEW_PRIVS is assumed to always be set. + // See kernel.Task.updateCredsForExecLocked. + if !spec.Process.NoNewPrivileges { + log.Warningf("noNewPrivileges ignored. PR_SET_NO_NEW_PRIVS is assumed to always be set.") + } + // TODO(gvisor.dev/issue/510): Apply seccomp to application inside sandbox. if spec.Linux != nil && spec.Linux.Seccomp != nil { log.Warningf("Seccomp spec is being ignored") -- cgit v1.2.3 From fc53d6436776d5de052075e98f44417f04ced7e7 Mon Sep 17 00:00:00 2001 From: moricho Date: Wed, 22 Apr 2020 15:04:18 +0900 Subject: refactor and add test for bindmount Signed-off-by: moricho --- pkg/sentry/fs/filesystems.go | 4 --- runsc/boot/fs.go | 59 ++++++++++++--------------------- runsc/container/container_test.go | 22 ++++++++++++ runsc/container/multi_container_test.go | 2 +- runsc/specutils/specutils.go | 14 +++++++- 5 files changed, 57 insertions(+), 44 deletions(-) (limited to 'runsc/specutils/specutils.go') diff --git a/pkg/sentry/fs/filesystems.go b/pkg/sentry/fs/filesystems.go index d6abddfd8..084da2a8d 100644 --- a/pkg/sentry/fs/filesystems.go +++ b/pkg/sentry/fs/filesystems.go @@ -144,10 +144,6 @@ type MountSourceFlags struct { // NoExec corresponds to mount(2)'s "MS_NOEXEC" and indicates that // binaries from this file system can't be executed. NoExec bool - - // Bind corresponds to mount(2)'s MS_BIND and indicates that - // the filesystem should be bind mounted. - Bind bool } // GenericMountSourceOptions splits a string containing comma separated tokens of the diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index 78d6a0c14..4875452e2 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -63,13 +63,8 @@ const ( nonefs = "none" ) -var ( - // tmpfs has some extra supported options that we must pass through. - tmpfsAllowedOptions = []string{"mode", "uid", "gid"} - - // filesystems supported on gVisor. - supportedFilesystems = []string{bind, devpts, devtmpfs, proc, sysfs, tmpfs} -) +// tmpfs has some extra supported options that we must pass through. +var tmpfsAllowedOptions = []string{"mode", "uid", "gid"} func addOverlay(ctx context.Context, conf *Config, lower *fs.Inode, name string, lowerFlags fs.MountSourceFlags) (*fs.Inode, error) { // Upper layer uses the same flags as lower, but it must be read-write. @@ -225,7 +220,8 @@ func mountFlags(opts []string) fs.MountSourceFlags { case "noexec": mf.NoExec = true case "bind", "rbind": - mf.Bind = true + // When options include either "bind" or "rbind", + // it's converted to a 9P mount. default: log.Warningf("ignoring unknown mount option %q", o) } @@ -237,10 +233,6 @@ func isSupportedMountFlag(fstype, opt string) bool { switch opt { case "rw", "ro", "noatime", "noexec": return true - case "bind", "rbind": - if fstype == nonefs || !specutils.ContainsStr(supportedFilesystems, fstype) { - return true - } } if fstype == tmpfs { ok, err := parseMountOption(opt, tmpfsAllowedOptions...) @@ -767,17 +759,6 @@ func (c *containerMounter) createRootMount(ctx context.Context, conf *Config) (* return rootInode, nil } -// getBindMountNameAndOptions retrieves the fsName, opts, and useOverlay values -// used for bind mounts. -func (c *containerMounter) getBindMountNameAndOptions(conf *Config, m specs.Mount) (string, []string, bool) { - fd := c.fds.remove() - fsName := "9p" - opts := p9MountOptions(fd, c.getMountAccessType(m)) - // If configured, add overlay to all writable mounts. - useOverlay := conf.Overlay && !mountFlags(m.Options).ReadOnly - return fsName, opts, useOverlay -} - // getMountNameAndOptions retrieves the fsName, opts, and useOverlay values // used for mounts. func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) (string, []string, bool, error) { @@ -787,17 +768,20 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) ( useOverlay bool ) + for _, opt := range m.Options { + // When options include either "bind" or "rbind", this behaves as + // bind mount even if the mount type is equal to a filesystem supported + // on runsc. + if opt == "bind" || opt == "rbind" { + m.Type = bind + break + } + } + switch m.Type { case devpts, devtmpfs, proc, sysfs: fsName = m.Type case nonefs: - for _, opt := range m.Options { - if opt == "bind" || opt == "rbind" { - fsName, opts, useOverlay = c.getBindMountNameAndOptions(conf, m) - return fsName, opts, useOverlay, nil - } - } - fsName = sysfs case tmpfs: fsName = m.Type @@ -807,16 +791,15 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) ( if err != nil { return "", nil, false, err } + case bind: - fsName, opts, useOverlay = c.getBindMountNameAndOptions(conf, m) - default: - for _, opt := range m.Options { - if opt == "bind" || opt == "rbind" { - fsName, opts, useOverlay = c.getBindMountNameAndOptions(conf, m) - return fsName, opts, useOverlay, nil - } - } + fd := c.fds.remove() + fsName = "9p" + opts = p9MountOptions(fd, c.getMountAccessType(m)) + // If configured, add overlay to all writable mounts. + useOverlay = conf.Overlay && !mountFlags(m.Options).ReadOnly + default: log.Warningf("ignoring unknown filesystem type %q", m.Type) } return fsName, opts, useOverlay, nil diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 3ff89f38c..c963c2153 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1523,6 +1523,28 @@ func TestReadonlyMount(t *testing.T) { } } +func TestBindMountByOption(t *testing.T) { + for _, conf := range configs(t, overlay) { + t.Logf("Running test with conf: %+v", conf) + + dir, err := ioutil.TempDir(testutil.TmpDir(), "bind-mount") + spec := testutil.NewSpecWithArgs("/bin/touch", path.Join(dir, "file")) + if err != nil { + t.Fatalf("ioutil.TempDir() failed: %v", err) + } + spec.Mounts = append(spec.Mounts, specs.Mount{ + Destination: dir, + Source: dir, + Type: "none", + Options: []string{"rw", "bind"}, + }) + + if err := run(spec, conf); err != nil { + t.Fatalf("error running sandbox: %v", err) + } + } +} + // TestAbbreviatedIDs checks that runsc supports using abbreviated container // IDs in place of full IDs. func TestAbbreviatedIDs(t *testing.T) { diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index e3704b453..f6861b1dd 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -1394,7 +1394,7 @@ func TestMultiContainerSharedMountUnsupportedOptions(t *testing.T) { Destination: "/mydir/test", Source: "/some/dir", Type: "tmpfs", - Options: []string{"rw", "rbind", "relatime"}, + Options: []string{"rw", "relatime"}, } podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 837d5e238..202518b58 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -311,7 +311,19 @@ func capsFromNames(names []string, skipSet map[linux.Capability]struct{}) (auth. // Is9PMount returns true if the given mount can be mounted as an external gofer. func Is9PMount(m specs.Mount) bool { - return m.Type == "bind" && m.Source != "" && IsSupportedDevMount(m) + var isBind bool + switch m.Type { + case "bind": + isBind = true + default: + for _, opt := range m.Options { + if opt == "bind" || opt == "rbind" { + isBind = true + break + } + } + } + return isBind && m.Source != "" && IsSupportedDevMount(m) } // IsSupportedDevMount returns true if the mount is a supported /dev mount. -- cgit v1.2.3 From a8c1b326600983abeabd41cbfa0c32ff84cf475a Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 28 May 2020 12:24:37 -0700 Subject: Automated rollback of changelist 309082540 PiperOrigin-RevId: 313636920 --- runsc/boot/fs.go | 19 ------------------- runsc/boot/vfs.go | 6 +----- runsc/container/container_test.go | 22 ---------------------- runsc/container/multi_container_test.go | 2 +- runsc/specutils/specutils.go | 14 +------------- 5 files changed, 3 insertions(+), 60 deletions(-) (limited to 'runsc/specutils/specutils.go') diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index e1181271a..9cd4f97da 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -221,9 +221,6 @@ func mountFlags(opts []string) fs.MountSourceFlags { mf.NoAtime = true case "noexec": mf.NoExec = true - case "bind", "rbind": - // When options include either "bind" or "rbind", - // it's converted to a 9P mount. default: log.Warningf("ignoring unknown mount option %q", o) } @@ -770,10 +767,6 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) ( useOverlay bool ) - if isBindMount(m) { - m.Type = bind - } - switch m.Type { case devpts.Name, devtmpfs.Name, procvfs2.Name, sysvfs2.Name: fsName = m.Type @@ -801,18 +794,6 @@ func (c *containerMounter) getMountNameAndOptions(conf *Config, m specs.Mount) ( return fsName, opts, useOverlay, nil } -func isBindMount(m specs.Mount) bool { - for _, opt := range m.Options { - // When options include either "bind" or "rbind", this behaves as - // bind mount even if the mount type is equal to a filesystem supported - // on runsc. - if opt == "bind" || opt == "rbind" { - return true - } - } - return false -} - func (c *containerMounter) getMountAccessType(mount specs.Mount) FileAccessType { if hint := c.hints.findMount(mount); hint != nil { return hint.fileAccessType() diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 147c901c4..fefeeff58 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -235,7 +235,7 @@ func (c *containerMounter) prepareMountsVFS2() ([]mountAndFD, error) { fd := -1 // Only bind mounts use host FDs; see // containerMounter.getMountNameAndOptionsVFS2. - if m.Type == bind || isBindMount(m) { + if m.Type == bind { fd = c.fds.remove() } mounts = append(mounts, mountAndFD{ @@ -305,10 +305,6 @@ func (c *containerMounter) getMountNameAndOptionsVFS2(conf *Config, m *mountAndF useOverlay bool ) - if isBindMount(m.Mount) { - m.Type = bind - } - switch m.Type { case devpts.Name, devtmpfs.Name, proc.Name, sys.Name: fsName = m.Type diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 1a6d50d0d..d59a1d97e 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -1537,28 +1537,6 @@ func TestReadonlyMount(t *testing.T) { } } -func TestBindMountByOption(t *testing.T) { - for _, conf := range configs(t, overlay) { - t.Logf("Running test with conf: %+v", conf) - - dir, err := ioutil.TempDir(testutil.TmpDir(), "bind-mount") - spec := testutil.NewSpecWithArgs("/bin/touch", path.Join(dir, "file")) - if err != nil { - t.Fatalf("ioutil.TempDir() failed: %v", err) - } - spec.Mounts = append(spec.Mounts, specs.Mount{ - Destination: dir, - Source: dir, - Type: "none", - Options: []string{"rw", "bind"}, - }) - - if err := run(spec, conf); err != nil { - t.Fatalf("error running sandbox: %v", err) - } - } -} - // TestAbbreviatedIDs checks that runsc supports using abbreviated container // IDs in place of full IDs. func TestAbbreviatedIDs(t *testing.T) { diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index f6861b1dd..e3704b453 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -1394,7 +1394,7 @@ func TestMultiContainerSharedMountUnsupportedOptions(t *testing.T) { Destination: "/mydir/test", Source: "/some/dir", Type: "tmpfs", - Options: []string{"rw", "relatime"}, + Options: []string{"rw", "rbind", "relatime"}, } podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 202518b58..837d5e238 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -311,19 +311,7 @@ func capsFromNames(names []string, skipSet map[linux.Capability]struct{}) (auth. // Is9PMount returns true if the given mount can be mounted as an external gofer. func Is9PMount(m specs.Mount) bool { - var isBind bool - switch m.Type { - case "bind": - isBind = true - default: - for _, opt := range m.Options { - if opt == "bind" || opt == "rbind" { - isBind = true - break - } - } - } - return isBind && m.Source != "" && IsSupportedDevMount(m) + return m.Type == "bind" && m.Source != "" && IsSupportedDevMount(m) } // IsSupportedDevMount returns true if the mount is a supported /dev mount. -- cgit v1.2.3 From f7418e21590e271302a3c375323950c209ce5ced Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 28 May 2020 14:45:52 -0700 Subject: Move Cleanup to its own package PiperOrigin-RevId: 313663382 --- pkg/cleanup/BUILD | 17 +++++++++ pkg/cleanup/cleanup.go | 60 ++++++++++++++++++++++++++++++ pkg/cleanup/cleanup_test.go | 66 +++++++++++++++++++++++++++++++++ runsc/cgroup/BUILD | 2 +- runsc/cgroup/cgroup.go | 4 +- runsc/container/BUILD | 2 + runsc/container/container.go | 7 ++-- runsc/container/multi_container_test.go | 26 ++++--------- runsc/fsgofer/BUILD | 2 +- runsc/fsgofer/fsgofer.go | 8 ++-- runsc/sandbox/BUILD | 1 + runsc/sandbox/sandbox.go | 3 +- runsc/specutils/specutils.go | 30 --------------- test/root/BUILD | 1 + test/root/crictl_test.go | 27 +++++--------- test/root/oom_score_adj_test.go | 30 ++++----------- 16 files changed, 186 insertions(+), 100 deletions(-) create mode 100644 pkg/cleanup/BUILD create mode 100644 pkg/cleanup/cleanup.go create mode 100644 pkg/cleanup/cleanup_test.go (limited to 'runsc/specutils/specutils.go') diff --git a/pkg/cleanup/BUILD b/pkg/cleanup/BUILD new file mode 100644 index 000000000..5c34b9872 --- /dev/null +++ b/pkg/cleanup/BUILD @@ -0,0 +1,17 @@ +load("//tools:defs.bzl", "go_library", "go_test") + +package(licenses = ["notice"]) + +go_library( + name = "cleanup", + srcs = ["cleanup.go"], + visibility = ["//:sandbox"], + deps = [ + ], +) + +go_test( + name = "cleanup_test", + srcs = ["cleanup_test.go"], + library = ":cleanup", +) diff --git a/pkg/cleanup/cleanup.go b/pkg/cleanup/cleanup.go new file mode 100644 index 000000000..14a05f076 --- /dev/null +++ b/pkg/cleanup/cleanup.go @@ -0,0 +1,60 @@ +// 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 cleanup provides utilities to clean "stuff" on defers. +package cleanup + +// Cleanup allows defers to be aborted when cleanup needs to happen +// conditionally. Usage: +// cu := cleanup.Make(func() { f.Close() }) +// defer cu.Clean() // failure before release is called will close the file. +// ... +// cu.Add(func() { f2.Close() }) // Adds another cleanup function +// ... +// cu.Release() // on success, aborts closing the file. +// return f +type Cleanup struct { + cleaners []func() +} + +// Make creates a new Cleanup object. +func Make(f func()) Cleanup { + return Cleanup{cleaners: []func(){f}} +} + +// Add adds a new function to be called on Clean(). +func (c *Cleanup) Add(f func()) { + c.cleaners = append(c.cleaners, f) +} + +// Clean calls all cleanup functions in reverse order. +func (c *Cleanup) Clean() { + clean(c.cleaners) + c.cleaners = nil +} + +// Release releases the cleanup from its duties, i.e. cleanup functions are not +// called after this point. Returns a function that calls all registered +// functions in case the caller has use for them. +func (c *Cleanup) Release() func() { + old := c.cleaners + c.cleaners = nil + return func() { clean(old) } +} + +func clean(cleaners []func()) { + for i := len(cleaners) - 1; i >= 0; i-- { + cleaners[i]() + } +} diff --git a/pkg/cleanup/cleanup_test.go b/pkg/cleanup/cleanup_test.go new file mode 100644 index 000000000..ab3d9ed95 --- /dev/null +++ b/pkg/cleanup/cleanup_test.go @@ -0,0 +1,66 @@ +// 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 cleanup + +import "testing" + +func testCleanupHelper(clean, cleanAdd *bool, release bool) func() { + cu := Make(func() { + *clean = true + }) + cu.Add(func() { + *cleanAdd = true + }) + defer cu.Clean() + if release { + return cu.Release() + } + return nil +} + +func TestCleanup(t *testing.T) { + clean := false + cleanAdd := false + testCleanupHelper(&clean, &cleanAdd, false) + if !clean { + t.Fatalf("cleanup function was not called.") + } + if !cleanAdd { + t.Fatalf("added cleanup function was not called.") + } +} + +func TestRelease(t *testing.T) { + clean := false + cleanAdd := false + cleaner := testCleanupHelper(&clean, &cleanAdd, true) + + // Check that clean was not called after release. + if clean { + t.Fatalf("cleanup function was called.") + } + if cleanAdd { + t.Fatalf("added cleanup function was called.") + } + + // Call the cleaner function and check that both cleanup functions are called. + cleaner() + if !clean { + t.Fatalf("cleanup function was not called.") + } + if !cleanAdd { + t.Fatalf("added cleanup function was not called.") + } +} diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD index d4c7bdfbb..c087e1a3c 100644 --- a/runsc/cgroup/BUILD +++ b/runsc/cgroup/BUILD @@ -7,8 +7,8 @@ go_library( srcs = ["cgroup.go"], visibility = ["//:sandbox"], deps = [ + "//pkg/cleanup", "//pkg/log", - "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", ], diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go index 19c8b0db6..ef01820ef 100644 --- a/runsc/cgroup/cgroup.go +++ b/runsc/cgroup/cgroup.go @@ -31,8 +31,8 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/runsc/specutils" ) const ( @@ -246,7 +246,7 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error { // The Cleanup object cleans up partially created cgroups when an error occurs. // Errors occuring during cleanup itself are ignored. - clean := specutils.MakeCleanup(func() { _ = c.Uninstall() }) + clean := cleanup.Make(func() { _ = c.Uninstall() }) defer clean.Clean() for key, cfg := range controllers { diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 46154df60..9a856d65c 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -16,6 +16,7 @@ go_library( ], deps = [ "//pkg/abi/linux", + "//pkg/cleanup", "//pkg/log", "//pkg/sentry/control", "//pkg/sentry/sighandling", @@ -53,6 +54,7 @@ go_test( deps = [ "//pkg/abi/linux", "//pkg/bits", + "//pkg/cleanup", "//pkg/log", "//pkg/sentry/control", "//pkg/sentry/kernel", diff --git a/runsc/container/container.go b/runsc/container/container.go index 8539f252d..6d297d0df 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -31,6 +31,7 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/control" "gvisor.dev/gvisor/pkg/sentry/sighandling" @@ -293,7 +294,7 @@ func New(conf *boot.Config, args Args) (*Container, error) { } // The Cleanup object cleans up partially created containers when an error // occurs. Any errors occurring during cleanup itself are ignored. - cu := specutils.MakeCleanup(func() { _ = c.Destroy() }) + cu := cleanup.Make(func() { _ = c.Destroy() }) defer cu.Clean() // Lock the container metadata file to prevent concurrent creations of @@ -402,7 +403,7 @@ func (c *Container) Start(conf *boot.Config) error { if err := c.Saver.lock(); err != nil { return err } - unlock := specutils.MakeCleanup(func() { c.Saver.unlock() }) + unlock := cleanup.Make(func() { c.Saver.unlock() }) defer unlock.Clean() if err := c.requireStatus("start", Created); err != nil { @@ -506,7 +507,7 @@ func Run(conf *boot.Config, args Args) (syscall.WaitStatus, error) { } // Clean up partially created container if an error occurs. // Any errors returned by Destroy() itself are ignored. - cu := specutils.MakeCleanup(func() { + cu := cleanup.Make(func() { c.Destroy() }) defer cu.Clean() diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index e3704b453..dc825abd9 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -27,6 +27,7 @@ import ( "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" @@ -64,29 +65,16 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*C panic("conf.RootDir not set. Call testutil.SetupRootDir() to set.") } - var ( - containers []*Container - cleanups []func() - ) - cleanups = append(cleanups, func() { - for _, c := range containers { - c.Destroy() - } - }) - cleanupAll := func() { - for _, c := range cleanups { - c() - } - } - localClean := specutils.MakeCleanup(cleanupAll) - defer localClean.Clean() + 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) } - cleanups = append(cleanups, cleanup) + cu.Add(cleanup) args := Args{ ID: ids[i], @@ -97,6 +85,7 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*C 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 { @@ -104,8 +93,7 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*C } } - localClean.Release() - return containers, cleanupAll, nil + return containers, cu.Release(), nil } type execDesc struct { diff --git a/runsc/fsgofer/BUILD b/runsc/fsgofer/BUILD index 64a406ae2..1036b0630 100644 --- a/runsc/fsgofer/BUILD +++ b/runsc/fsgofer/BUILD @@ -13,12 +13,12 @@ go_library( visibility = ["//runsc:__subpackages__"], deps = [ "//pkg/abi/linux", + "//pkg/cleanup", "//pkg/fd", "//pkg/log", "//pkg/p9", "//pkg/sync", "//pkg/syserr", - "//runsc/specutils", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index 1942f50d7..edc239013 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -33,11 +33,11 @@ import ( "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/fd" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/p9" "gvisor.dev/gvisor/pkg/sync" - "gvisor.dev/gvisor/runsc/specutils" ) const ( @@ -439,7 +439,7 @@ func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid if err != nil { return nil, nil, p9.QID{}, 0, extractErrno(err) } - cu := specutils.MakeCleanup(func() { + cu := cleanup.Make(func() { child.Close() // Best effort attempt to remove the file in case of failure. if err := syscall.Unlinkat(l.file.FD(), name); err != nil { @@ -480,7 +480,7 @@ func (l *localFile) Mkdir(name string, perm p9.FileMode, uid p9.UID, gid p9.GID) if err := syscall.Mkdirat(l.file.FD(), name, uint32(perm.Permissions())); err != nil { return p9.QID{}, extractErrno(err) } - cu := specutils.MakeCleanup(func() { + cu := cleanup.Make(func() { // Best effort attempt to remove the dir in case of failure. if err := unix.Unlinkat(l.file.FD(), name, unix.AT_REMOVEDIR); err != nil { log.Warningf("error unlinking dir %q after failure: %v", path.Join(l.hostPath, name), err) @@ -864,7 +864,7 @@ func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9. if err := unix.Symlinkat(target, l.file.FD(), newName); err != nil { return p9.QID{}, extractErrno(err) } - cu := specutils.MakeCleanup(func() { + cu := cleanup.Make(func() { // Best effort attempt to remove the symlink in case of failure. if err := syscall.Unlinkat(l.file.FD(), newName); err != nil { log.Warningf("error unlinking file %q after failure: %v", path.Join(l.hostPath, newName), err) diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index c95d50294..035dcd3e3 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -13,6 +13,7 @@ go_library( "//runsc:__subpackages__", ], deps = [ + "//pkg/cleanup", "//pkg/control/client", "//pkg/control/server", "//pkg/log", diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index e4ec16e2f..6e1a2af25 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -30,6 +30,7 @@ import ( "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/syndtr/gocapability/capability" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/control/client" "gvisor.dev/gvisor/pkg/control/server" "gvisor.dev/gvisor/pkg/log" @@ -119,7 +120,7 @@ func New(conf *boot.Config, args *Args) (*Sandbox, error) { s := &Sandbox{ID: args.ID, Cgroup: args.Cgroup} // The Cleanup object cleans up partially created sandboxes when an error // occurs. Any errors occurring during cleanup itself are ignored. - c := specutils.MakeCleanup(func() { + c := cleanup.Make(func() { err := s.destroy() log.Warningf("error destroying sandbox: %v", err) }) diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 837d5e238..f1fa573c5 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -444,36 +444,6 @@ func ContainsStr(strs []string, str string) bool { return false } -// Cleanup allows defers to be aborted when cleanup needs to happen -// conditionally. Usage: -// c := MakeCleanup(func() { f.Close() }) -// defer c.Clean() // any failure before release is called will close the file. -// ... -// c.Release() // on success, aborts closing the file and return it. -// return f -type Cleanup struct { - clean func() -} - -// MakeCleanup creates a new Cleanup object. -func MakeCleanup(f func()) Cleanup { - return Cleanup{clean: f} -} - -// Clean calls the cleanup function. -func (c *Cleanup) Clean() { - if c.clean != nil { - c.clean() - c.clean = nil - } -} - -// Release releases the cleanup from its duties, i.e. cleanup function is not -// called after this point. -func (c *Cleanup) Release() { - c.clean = nil -} - // RetryEintr retries the function until an error different than EINTR is // returned. func RetryEintr(f func() (uintptr, uintptr, error)) (uintptr, uintptr, error) { diff --git a/test/root/BUILD b/test/root/BUILD index 639e293e3..a9e91ccd6 100644 --- a/test/root/BUILD +++ b/test/root/BUILD @@ -33,6 +33,7 @@ go_test( ], visibility = ["//:sandbox"], deps = [ + "//pkg/cleanup", "//pkg/test/criutil", "//pkg/test/dockerutil", "//pkg/test/testutil", diff --git a/test/root/crictl_test.go b/test/root/crictl_test.go index 85007dcce..c138e02dc 100644 --- a/test/root/crictl_test.go +++ b/test/root/crictl_test.go @@ -30,10 +30,10 @@ import ( "testing" "time" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/test/criutil" "gvisor.dev/gvisor/pkg/test/dockerutil" "gvisor.dev/gvisor/pkg/test/testutil" - "gvisor.dev/gvisor/runsc/specutils" ) // Tests for crictl have to be run as root (rather than in a user namespace) @@ -272,27 +272,20 @@ disabled_plugins = ["restart"] // * Runs containerd and waits for it to reach a "ready" state for testing. // * Returns a cleanup function that should be called at the end of the test. func setup(t *testing.T) (*criutil.Crictl, func(), error) { - var cleanups []func() - cleanupFunc := func() { - for i := len(cleanups) - 1; i >= 0; i-- { - cleanups[i]() - } - } - cleanup := specutils.MakeCleanup(cleanupFunc) - defer cleanup.Clean() - // Create temporary containerd root and state directories, and a socket // via which crictl and containerd communicate. containerdRoot, err := ioutil.TempDir(testutil.TmpDir(), "containerd-root") if err != nil { t.Fatalf("failed to create containerd root: %v", err) } - cleanups = append(cleanups, func() { os.RemoveAll(containerdRoot) }) + cu := cleanup.Make(func() { os.RemoveAll(containerdRoot) }) + defer cu.Clean() + containerdState, err := ioutil.TempDir(testutil.TmpDir(), "containerd-state") if err != nil { t.Fatalf("failed to create containerd state: %v", err) } - cleanups = append(cleanups, func() { os.RemoveAll(containerdState) }) + cu.Add(func() { os.RemoveAll(containerdState) }) sockAddr := filepath.Join(testutil.TmpDir(), "containerd-test.sock") // We rewrite a configuration. This is based on the current docker @@ -305,7 +298,7 @@ func setup(t *testing.T) (*criutil.Crictl, func(), error) { if err != nil { t.Fatalf("failed to write containerd config") } - cleanups = append(cleanups, configCleanup) + cu.Add(configCleanup) // Start containerd. cmd := exec.Command(getContainerd(), @@ -321,7 +314,8 @@ func setup(t *testing.T) (*criutil.Crictl, func(), error) { stdout := &bytes.Buffer{} cmd.Stderr = io.MultiWriter(startupW, stderr) cmd.Stdout = io.MultiWriter(startupW, stdout) - cleanups = append(cleanups, func() { + cu.Add(func() { + // Log output in case of failure. t.Logf("containerd stdout: %s", stdout.String()) t.Logf("containerd stderr: %s", stderr.String()) }) @@ -338,15 +332,14 @@ func setup(t *testing.T) (*criutil.Crictl, func(), error) { // Kill must be the last cleanup (as it will be executed first). cc := criutil.NewCrictl(t, sockAddr) - cleanups = append(cleanups, func() { + cu.Add(func() { cc.CleanUp() // Remove tmp files, etc. if err := testutil.KillCommand(cmd); err != nil { log.Printf("error killing containerd: %v", err) } }) - cleanup.Release() - return cc, cleanupFunc, nil + return cc, cu.Release(), nil } // httpGet GETs the contents of a file served from a pod on port 80. diff --git a/test/root/oom_score_adj_test.go b/test/root/oom_score_adj_test.go index 9a3cecd97..4243eb59e 100644 --- a/test/root/oom_score_adj_test.go +++ b/test/root/oom_score_adj_test.go @@ -20,6 +20,7 @@ import ( "testing" specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/test/testutil" "gvisor.dev/gvisor/runsc/container" "gvisor.dev/gvisor/runsc/specutils" @@ -324,40 +325,26 @@ func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) { } func startContainers(t *testing.T, specs []*specs.Spec, ids []string) ([]*container.Container, func(), error) { - var ( - containers []*container.Container - cleanups []func() - ) - cleanups = append(cleanups, func() { - for _, c := range containers { - c.Destroy() - } - }) - cleanupAll := func() { - for _, c := range cleanups { - c() - } - } - localClean := specutils.MakeCleanup(cleanupAll) - defer localClean.Clean() + var containers []*container.Container // All containers must share the same root. - rootDir, cleanup, err := testutil.SetupRootDir() + rootDir, clean, err := testutil.SetupRootDir() if err != nil { t.Fatalf("error creating root dir: %v", err) } - cleanups = append(cleanups, cleanup) + cu := cleanup.Make(clean) + defer cu.Clean() // Point this to from the configuration. conf := testutil.TestConfig(t) conf.RootDir = rootDir for i, spec := range specs { - bundleDir, cleanup, err := testutil.SetupBundleDir(spec) + bundleDir, clean, err := testutil.SetupBundleDir(spec) if err != nil { return nil, nil, fmt.Errorf("error setting up bundle: %v", err) } - cleanups = append(cleanups, cleanup) + cu.Add(clean) args := container.Args{ ID: ids[i], @@ -375,6 +362,5 @@ func startContainers(t *testing.T, specs []*specs.Spec, ids []string) ([]*contai } } - localClean.Release() - return containers, cleanupAll, nil + return containers, cu.Release(), nil } -- cgit v1.2.3 From bae1475603b03a38726da743430c761fb36ee338 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 16 Jun 2020 10:50:29 -0700 Subject: Print spec as json when --debug is enabled The previous format skipped many important structs that are pointers, especially for cgroups. Change to print as json, removing parts of the spec that are not relevant. Also removed debug message from gofer that can be very noisy when directories are large. PiperOrigin-RevId: 316713267 --- runsc/fsgofer/fsgofer.go | 2 -- runsc/specutils/BUILD | 1 + runsc/specutils/specutils.go | 40 ++++++++++++++++++++++++++-------------- 3 files changed, 27 insertions(+), 16 deletions(-) (limited to 'runsc/specutils/specutils.go') diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index edc239013..74977c313 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -175,8 +175,6 @@ func (a *attachPoint) makeQID(stat syscall.Stat_t) p9.QID { log.Warningf("first 8 bytes of host inode id %x will be truncated to construct virtual inode id", stat.Ino) } ino := uint64(dev)<<56 | maskedIno - log.Debugf("host inode %x on device %x mapped to virtual inode %x", stat.Ino, stat.Dev, ino) - return p9.QID{ Type: p9.FileMode(stat.Mode).QIDType(), Path: ino, diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 4ccd77f63..62d4f5113 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -17,6 +17,7 @@ go_library( "//pkg/log", "//pkg/sentry/kernel/auth", "@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", diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index f1fa573c5..5015c3a84 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -29,6 +29,7 @@ import ( "time" "github.com/cenkalti/backoff" + "github.com/mohae/deepcopy" specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/bits" @@ -44,20 +45,31 @@ var ExePath = "/proc/self/exe" var Version = specs.Version // LogSpec logs the spec in a human-friendly way. -func LogSpec(spec *specs.Spec) { - log.Debugf("Spec: %+v", spec) - log.Debugf("Spec.Hooks: %+v", spec.Hooks) - log.Debugf("Spec.Linux: %+v", spec.Linux) - if spec.Linux != nil && spec.Linux.Resources != nil { - res := spec.Linux.Resources - log.Debugf("Spec.Linux.Resources.Memory: %+v", res.Memory) - log.Debugf("Spec.Linux.Resources.CPU: %+v", res.CPU) - log.Debugf("Spec.Linux.Resources.BlockIO: %+v", res.BlockIO) - log.Debugf("Spec.Linux.Resources.Network: %+v", res.Network) - } - log.Debugf("Spec.Process: %+v", spec.Process) - log.Debugf("Spec.Root: %+v", spec.Root) - log.Debugf("Spec.Mounts: %+v", spec.Mounts) +func LogSpec(orig *specs.Spec) { + if !log.IsLogging(log.Debug) { + return + } + + // Strip down parts of the spec that are not interesting. + spec := deepcopy.Copy(orig).(*specs.Spec) + if spec.Process != nil { + spec.Process.Capabilities = nil + } + if spec.Linux != nil { + spec.Linux.Seccomp = nil + spec.Linux.MaskedPaths = nil + spec.Linux.ReadonlyPaths = nil + if spec.Linux.Resources != nil { + spec.Linux.Resources.Devices = nil + } + } + + out, err := json.MarshalIndent(spec, "", " ") + if err != nil { + log.Debugf("Failed to marshal spec: %v", err) + return + } + log.Debugf("Spec:\n%s", out) } // ValidateSpec validates that the spec is compatible with runsc. -- cgit v1.2.3