diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 7c2a36e32..52bb09c25 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -469,7 +469,8 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error { // Set up the restore environment. ctx := k.SupervisorContext() - mntr := newContainerMounter(&cm.l.root, cm.l.k, cm.l.mountHints, cm.l.productName, o.SandboxID) + // TODO(b/298078576): Need to process hints here probably + mntr := newContainerMounter(&cm.l.root, cm.l.k, cm.l.mountHints, cm.l.sharedMounts, cm.l.productName, o.SandboxID) ctx, err = mntr.configureRestore(ctx) if err != nil { return fmt.Errorf("configuring filesystem restore: %v", err) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 7e6689cad..200a3369e 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -158,6 +158,10 @@ type Loader struct { // apply to the entire pod. mountHints *PodMountHints + // sharedMountKey holds VFS mounts that may be shared between containers + // within the same pod. It is mapped by mount source. + sharedMounts map[string]*vfs.Mount + // productName is the value to show in // /sys/devices/virtual/dmi/id/product_name. productName string @@ -932,12 +936,12 @@ func (l *Loader) createContainerProcess(root bool, cid string, info *containerIn } l.startGoferMonitor(cid, int32(info.goferFDs[0].FD())) - mntr := newContainerMounter(info, l.k, l.mountHints, l.productName, l.sandboxID) if root { - if err := mntr.processHints(info.conf, info.procArgs.Credentials); err != nil { + if err := l.processHints(info.conf, info.procArgs.Credentials); err != nil { return nil, nil, err } } + mntr := newContainerMounter(info, l.k, l.mountHints, l.sharedMounts, l.productName, l.sandboxID) if err := setupContainerVFS(ctx, info, mntr, &info.procArgs); err != nil { return nil, nil, err } diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index 48d273872..e462357c8 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -475,10 +475,10 @@ func TestCreateMountNamespace(t *testing.T) { defer l.Destroy() defer loaderCleanup() - mntr := newContainerMounter(&l.root, l.k, l.mountHints, "", l.sandboxID) - if err := mntr.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil { + if err := l.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil { t.Fatalf("failed process hints: %v", err) } + mntr := newContainerMounter(&l.root, l.k, l.mountHints, l.sharedMounts, "", l.sandboxID) ctx := l.k.SupervisorContext() creds := auth.NewRootCredentials(l.root.procArgs.Credentials.UserNamespace) diff --git a/runsc/boot/mount_hints.go b/runsc/boot/mount_hints.go index f1e8cc73a..e07ff2627 100644 --- a/runsc/boot/mount_hints.go +++ b/runsc/boot/mount_hints.go @@ -21,7 +21,6 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" - "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/specutils" ) @@ -29,11 +28,11 @@ import ( // MountPrefix is the annotation prefix for mount hints. const MountPrefix = "dev.gvisor.spec.mount." -// shareType indicates who can access/mutate the volume contents. -type shareType int +// ShareType indicates who can access/mutate the volume contents. +type ShareType int const ( - invalid shareType = iota + invalid ShareType = iota // container shareType indicates that the mount is used by a single // container. There are no external observers. @@ -48,7 +47,7 @@ const ( shared ) -func (s shareType) String() string { +func (s ShareType) String() string { switch s { case invalid: return "invalid" @@ -63,14 +62,14 @@ func (s shareType) String() string { } } -// lifecycleType indicates whether creation/deletion of the volume is tied to +// LifecycleType indicates whether creation/deletion of the volume is tied to // the pod or container's lifecycle. -type lifecycleType int +type LifecycleType int const ( // sharedLife indicates that the volume's lifecycle is not tied to the pod. // The volume persists beyond the pod's life. This is the safe default. - sharedLife lifecycleType = iota + sharedLife LifecycleType = iota // podLife indicates that the volume's lifecycle is tied to the pod's // lifecycle. The volume is destroyed with the pod. @@ -81,7 +80,7 @@ const ( containerLife ) -func (o lifecycleType) String() string { +func (o LifecycleType) String() string { switch o { case sharedLife: return "shared" @@ -96,7 +95,7 @@ func (o lifecycleType) String() string { // PodMountHints contains a collection of mountHints for the pod. type PodMountHints struct { - mounts map[string]*MountHint + Mounts map[string]*MountHint `json:"mounts"` } // NewPodMountHints instantiates PodMountHints using spec. @@ -116,7 +115,7 @@ func NewPodMountHints(spec *specs.Spec) (*PodMountHints, error) { } mnt := mnts[name] if mnt == nil { - mnt = &MountHint{name: name} + mnt = &MountHint{Name: name} mnts[name] = mnt } if err := mnt.setField(parts[1], v); err != nil { @@ -127,8 +126,8 @@ func NewPodMountHints(spec *specs.Spec) (*PodMountHints, error) { // Validate all the parsed hints. for name, m := range mnts { - log.Infof("Mount annotation found, name: %s, source: %q, type: %s, share: %v", name, m.mount.Source, m.mount.Type, m.share) - if m.share == invalid || len(m.mount.Source) == 0 || len(m.mount.Type) == 0 { + log.Infof("Mount annotation found, name: %s, source: %q, type: %s, share: %v", name, m.Mount.Source, m.Mount.Type, m.Share) + if m.Share == invalid || len(m.Mount.Source) == 0 || len(m.Mount.Type) == 0 { log.Warningf("ignoring mount annotations for %q because of missing required field(s)", name) delete(mnts, name) continue @@ -136,27 +135,24 @@ func NewPodMountHints(spec *specs.Spec) (*PodMountHints, error) { // Check for duplicate mount sources. for name2, m2 := range mnts { - if name != name2 && m.mount.Source == m2.mount.Source { - return nil, fmt.Errorf("mounts %q and %q have the same mount source %q", m.name, m2.name, m.mount.Source) + if name != name2 && m.Mount.Source == m2.Mount.Source { + return nil, fmt.Errorf("mounts %q and %q have the same mount source %q", m.Name, m2.Name, m.Mount.Source) } } } - return &PodMountHints{mounts: mnts}, nil + return &PodMountHints{Mounts: mnts}, nil } // MountHint represents extra information about mounts that are provided via // annotations. They can override mount type, and provide sharing information // so that mounts can be correctly shared inside the pod. +// It is part of the sandbox.Sandbox struct, so it must be serializable. type MountHint struct { - name string - share shareType - mount specs.Mount - lifecycle lifecycleType - - // vfsMount is the master mount for the volume. For mounts with 'pod' share - // the master volume is bind mounted inside the containers. - vfsMount *vfs.Mount + Name string `json:"name"` + Share ShareType `json:"share"` + Mount specs.Mount `json:"mount"` + Lifecycle LifecycleType `json:"lifecycle"` } func (m *MountHint) setField(key, val string) error { @@ -165,13 +161,13 @@ func (m *MountHint) setField(key, val string) error { if len(val) == 0 { return fmt.Errorf("source cannot be empty") } - m.mount.Source = val + m.Mount.Source = val case "type": return m.setType(val) case "share": return m.setShare(val) case "options": - m.mount.Options = specutils.FilterMountOptions(strings.Split(val, ",")) + m.Mount.Options = specutils.FilterMountOptions(strings.Split(val, ",")) case "lifecycle": return m.setLifecycle(val) default: @@ -183,7 +179,7 @@ func (m *MountHint) setField(key, val string) error { func (m *MountHint) setType(val string) error { switch val { case tmpfs.Name, Bind: - m.mount.Type = val + m.Mount.Type = val default: return fmt.Errorf("invalid type %q", val) } @@ -193,11 +189,11 @@ func (m *MountHint) setType(val string) error { func (m *MountHint) setShare(val string) error { switch val { case container.String(): - m.share = container + m.Share = container case pod.String(): - m.share = pod + m.Share = pod case shared.String(): - m.share = shared + m.Share = shared default: return fmt.Errorf("invalid share value %q", val) } @@ -207,11 +203,11 @@ func (m *MountHint) setShare(val string) error { func (m *MountHint) setLifecycle(val string) error { switch val { case containerLife.String(): - m.lifecycle = containerLife + m.Lifecycle = containerLife case podLife.String(): - m.lifecycle = podLife + m.Lifecycle = podLife case sharedLife.String(): - m.lifecycle = sharedLife + m.Lifecycle = sharedLife default: return fmt.Errorf("invalid lifecycle %q", val) } @@ -223,21 +219,21 @@ func (m *MountHint) setLifecycle(val string) error { func (m *MountHint) shouldShareMount() bool { // TODO(b/142076984): Only support tmpfs for now. Bind mounts require a // common gofer to mount all shared volumes. - return m.mount.Type == tmpfs.Name && m.share == pod + return m.Mount.Type == tmpfs.Name && m.Share == pod } // ShouldOverlay returns true if this mount should be overlaid. func (m *MountHint) ShouldOverlay() bool { // TODO(b/142076984): Only support share=container for now. Once shared gofer // support is added, we can overlay shared bind mounts too. - return m.mount.Type == Bind && m.share == container && m.lifecycle != sharedLife + return m.Mount.Type == Bind && m.Share == container && m.Lifecycle != sharedLife } // checkCompatible verifies that shared mount is compatible with master. // Master options must be the same or less restrictive than the container mount, // e.g. master can be 'rw' while container mounts as 'ro'. func (m *MountHint) checkCompatible(replica *specs.Mount) error { - masterOpts := ParseMountOptions(m.mount.Options) + masterOpts := ParseMountOptions(m.Mount.Options) replicaOpts := ParseMountOptions(replica.Options) if masterOpts.ReadOnly && !replicaOpts.ReadOnly { @@ -254,13 +250,13 @@ func (m *MountHint) checkCompatible(replica *specs.Mount) error { // Precondition: m.mount.Type == Bind. func (m *MountHint) fileAccessType() config.FileAccessType { - if m.share == shared { + if m.Share == shared { return config.FileAccessShared } if m.shouldShareMount() { return config.FileAccessExclusive } - if m.share == container { + if m.Share == container { return config.FileAccessExclusive } return config.FileAccessShared @@ -268,8 +264,8 @@ func (m *MountHint) fileAccessType() config.FileAccessType { // FindMount finds the MountHint that applies to this mount. func (p *PodMountHints) FindMount(mount *specs.Mount) *MountHint { - for _, m := range p.mounts { - if m.mount.Source == mount.Source { + for _, m := range p.Mounts { + if m.Mount.Source == mount.Source { return m } } diff --git a/runsc/boot/mount_hints_test.go b/runsc/boot/mount_hints_test.go index bc8a59a1c..682e03f36 100644 --- a/runsc/boot/mount_hints_test.go +++ b/runsc/boot/mount_hints_test.go @@ -41,38 +41,38 @@ func TestPodMountHintsHappy(t *testing.T) { } // Check that fields were set correctly. - mount1 := podHints.mounts["mount1"] - if want := "mount1"; want != mount1.name { - t.Errorf("mount1 name, want: %q, got: %q", want, mount1.name) + mount1 := podHints.Mounts["mount1"] + if want := "mount1"; want != mount1.Name { + t.Errorf("mount1 name, want: %q, got: %q", want, mount1.Name) } - if want := "foo"; want != mount1.mount.Source { - t.Errorf("mount1 source, want: %q, got: %q", want, mount1.mount.Source) + if want := "foo"; want != mount1.Mount.Source { + t.Errorf("mount1 source, want: %q, got: %q", want, mount1.Mount.Source) } - if want := "tmpfs"; want != mount1.mount.Type { - t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Type) + if want := "tmpfs"; want != mount1.Mount.Type { + t.Errorf("mount1 type, want: %q, got: %q", want, mount1.Mount.Type) } - if want := pod; want != mount1.share { - t.Errorf("mount1 type, want: %q, got: %q", want, mount1.share) + if want := pod; want != mount1.Share { + t.Errorf("mount1 type, want: %q, got: %q", want, mount1.Share) } - if want := []string(nil); !reflect.DeepEqual(want, mount1.mount.Options) { - t.Errorf("mount1 type, want: %q, got: %q", want, mount1.mount.Options) + if want := []string(nil); !reflect.DeepEqual(want, mount1.Mount.Options) { + t.Errorf("mount1 type, want: %q, got: %q", want, mount1.Mount.Options) } - mount2 := podHints.mounts["mount2"] - if want := "mount2"; want != mount2.name { - t.Errorf("mount2 name, want: %q, got: %q", want, mount2.name) + mount2 := podHints.Mounts["mount2"] + if want := "mount2"; want != mount2.Name { + t.Errorf("mount2 name, want: %q, got: %q", want, mount2.Name) } - if want := "bar"; want != mount2.mount.Source { - t.Errorf("mount2 source, want: %q, got: %q", want, mount2.mount.Source) + if want := "bar"; want != mount2.Mount.Source { + t.Errorf("mount2 source, want: %q, got: %q", want, mount2.Mount.Source) } - if want := "bind"; want != mount2.mount.Type { - t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Type) + if want := "bind"; want != mount2.Mount.Type { + t.Errorf("mount2 type, want: %q, got: %q", want, mount2.Mount.Type) } - if want := container; want != mount2.share { - t.Errorf("mount2 type, want: %q, got: %q", want, mount2.share) + if want := container; want != mount2.Share { + t.Errorf("mount2 type, want: %q, got: %q", want, mount2.Share) } - if want := []string{"rw", "private"}; !reflect.DeepEqual(want, mount2.mount.Options) { - t.Errorf("mount2 type, want: %q, got: %q", want, mount2.mount.Options) + if want := []string{"rw", "private"}; !reflect.DeepEqual(want, mount2.Mount.Options) { + t.Errorf("mount2 type, want: %q, got: %q", want, mount2.Mount.Options) } } @@ -161,7 +161,7 @@ func TestPodMountHintsIgnore(t *testing.T) { if err != nil { t.Errorf("newPodMountHints() failed: %v", err) } else if podHints != nil { - if hint, ok := podHints.mounts["mount1"]; ok { + if hint, ok := podHints.Mounts["mount1"]; ok { t.Errorf("hint was provided when it should have been omitted: %+v", hint) } } @@ -182,9 +182,9 @@ func TestIgnoreInvalidMountOptions(t *testing.T) { if err != nil { t.Fatalf("newPodMountHints failed: %v", err) } - mount1 := podHints.mounts["mount1"] - if want := []string{"rw", "private"}; !reflect.DeepEqual(want, mount1.mount.Options) { - t.Errorf("mount2 type, want: %q, got: %q", want, mount1.mount.Options) + mount1 := podHints.Mounts["mount1"] + if want := []string{"rw", "private"}; !reflect.DeepEqual(want, mount1.Mount.Options) { + t.Errorf("mount2 type, want: %q, got: %q", want, mount1.Mount.Options) } } @@ -233,7 +233,7 @@ func TestHintsCheckCompatible(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - master := MountHint{mount: specs.Mount{Options: tc.masterOpts}} + master := MountHint{Mount: specs.Mount{Options: tc.masterOpts}} replica := specs.Mount{Options: tc.replicaOpts} if err := master.checkCompatible(&replica); err != nil { if !strings.Contains(err.Error(), tc.err) { diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index f933541d7..bafbb16d2 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -388,8 +388,13 @@ type containerMounter struct { k *kernel.Kernel + // hints is the set of pod mount hints for the sandbox. hints *PodMountHints + // sharedMounts is a map of shared mounts that can be reused across + // containers. + sharedMounts map[string]*vfs.Mount + // productName is the value to show in // /sys/devices/virtual/dmi/id/product_name. productName string @@ -398,7 +403,7 @@ type containerMounter struct { sandboxID string } -func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *PodMountHints, productName string, sandboxID string) *containerMounter { +func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *PodMountHints, sharedMounts map[string]*vfs.Mount, productName string, sandboxID string) *containerMounter { return &containerMounter{ root: info.spec.Root, mounts: compileMounts(info.spec, info.conf), @@ -407,6 +412,7 @@ func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *PodMountH overlayMediums: info.overlayMediums, k: k, hints: hints, + sharedMounts: sharedMounts, productName: productName, sandboxID: sandboxID, } @@ -419,7 +425,7 @@ func (c *containerMounter) checkDispenser() error { return nil } -func (c *containerMounter) getMountAccessType(conf *config.Config, mount *specs.Mount, hint *MountHint) config.FileAccessType { +func getMountAccessType(conf *config.Config, mount *specs.Mount, hint *MountHint) config.FileAccessType { if hint != nil { return hint.fileAccessType() } @@ -668,9 +674,13 @@ func (c *containerMounter) mountSubmounts(ctx context.Context, conf *config.Conf ) if submount.hint != nil && submount.hint.shouldShareMount() { - mnt, err = c.mountSharedSubmount(ctx, conf, mns, creds, submount.mount, submount.hint) + sharedMount, ok := c.sharedMounts[submount.hint.Mount.Source] + if !ok { + return fmt.Errorf("shared mount %q not found", submount.hint.Name) + } + mnt, err = c.mountSharedSubmount(ctx, conf, mns, creds, submount.mount, submount.hint, sharedMount) if err != nil { - return fmt.Errorf("mount shared mount %q to %q: %v", submount.hint.name, submount.mount.Destination, err) + return fmt.Errorf("mount shared mount %q to %q: %v", submount.hint.Name, submount.mount.Destination, err) } } else { mnt, err = c.mountSubmount(ctx, conf, mns, creds, submount) @@ -752,7 +762,7 @@ func (c *containerMounter) prepareMounts() ([]mountInfo, error) { } func (c *containerMounter) mountSubmount(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, submount *mountInfo) (*vfs.Mount, error) { - fsName, opts, err := c.getMountNameAndOptions(conf, submount) + fsName, opts, err := getMountNameAndOptions(conf, submount, c.productName) if err != nil { return nil, fmt.Errorf("mountOptions failed: %w", err) } @@ -793,7 +803,7 @@ func (c *containerMounter) mountSubmount(ctx context.Context, conf *config.Confi // getMountNameAndOptions retrieves the fsName, opts, and useOverlay values // used for mounts. -func (c *containerMounter) getMountNameAndOptions(conf *config.Config, m *mountInfo) (string, *vfs.MountOptions, error) { +func getMountNameAndOptions(conf *config.Config, m *mountInfo, productName string) (string, *vfs.MountOptions, error) { fsName := m.mount.Type var ( data []string @@ -810,8 +820,8 @@ func (c *containerMounter) getMountNameAndOptions(conf *config.Config, m *mountI case sys.Name: sysData := &sys.InternalData{EnableAccelSysfs: conf.TPUProxy} - if len(c.productName) > 0 { - sysData.ProductName = c.productName + if len(productName) > 0 { + sysData.ProductName = productName } internalData = sysData @@ -828,7 +838,7 @@ func (c *containerMounter) getMountNameAndOptions(conf *config.Config, m *mountI // Check that an FD was provided to fails fast. return "", nil, fmt.Errorf("gofer mount requires a connection FD") } - data = goferMountData(m.fd, c.getMountAccessType(conf, m.mount, m.hint), conf) + data = goferMountData(m.fd, getMountAccessType(conf, m.mount, m.hint), conf) internalData = gofer.InternalFilesystemOptions{ UniqueID: m.mount.Destination, } @@ -961,55 +971,62 @@ func (c *containerMounter) mountTmp(ctx context.Context, conf *config.Config, cr } // processHints processes annotations that container hints about how volumes -// should be mounted (e.g. a volume shared between containers). It must be -// called for the root container only. -func (c *containerMounter) processHints(conf *config.Config, creds *auth.Credentials) error { - ctx := c.k.SupervisorContext() - for _, hint := range c.hints.mounts { +// should be mounted (e.g. a volume shared between containers). +// Precondition: Must be only called once during the loader sequence +// for the root container. +// Postcondition: Initialized l.sharedMounts on success. +func (l *Loader) processHints(conf *config.Config, creds *auth.Credentials) error { + ctx := l.k.SupervisorContext() + var sharedMounts map[string]*vfs.Mount + for _, hint := range l.mountHints.Mounts { if !hint.shouldShareMount() { continue } - log.Infof("Mounting master of shared mount %q from %q type %q", hint.name, hint.mount.Source, hint.mount.Type) - mnt, err := c.mountSharedMaster(ctx, conf, hint, creds) + log.Infof("Mounting master of shared mount %q from %q type %q", hint.Name, hint.Mount.Source, hint.Mount.Type) + mnt, err := l.mountSharedMaster(ctx, conf, hint, creds) if err != nil { - return fmt.Errorf("mounting shared master %q: %v", hint.name, err) + return fmt.Errorf("mounting shared master %q: %v", hint.Name, err) } - hint.vfsMount = mnt + if sharedMounts == nil { + sharedMounts = make(map[string]*vfs.Mount) + } + sharedMounts[hint.Mount.Source] = mnt } + l.sharedMounts = sharedMounts return nil } // mountSharedMaster mounts the master of a volume that is shared among // containers in a pod. -func (c *containerMounter) mountSharedMaster(ctx context.Context, conf *config.Config, hint *MountHint, creds *auth.Credentials) (*vfs.Mount, error) { +func (l *Loader) mountSharedMaster(ctx context.Context, conf *config.Config, hint *MountHint, creds *auth.Credentials) (*vfs.Mount, error) { // Map mount type to filesystem name, and parse out the options that we are // capable of dealing with. - mntInfo := newNonGoferMountInfo(&hint.mount) - fsName, opts, err := c.getMountNameAndOptions(conf, mntInfo) + mntInfo := newNonGoferMountInfo(&hint.Mount) + fsName, opts, err := getMountNameAndOptions(conf, mntInfo, l.productName) if err != nil { return nil, err } if len(fsName) == 0 { - return nil, fmt.Errorf("mount type not supported %q", hint.mount.Type) + return nil, fmt.Errorf("mount type not supported %q", hint.Mount.Type) } - return c.k.VFS().MountDisconnected(ctx, creds, "", fsName, opts) + return l.k.VFS().MountDisconnected(ctx, creds, "", fsName, opts) } // mountSharedSubmount binds mount to a previously mounted volume that is shared // among containers in the same pod. -func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, mount *specs.Mount, source *MountHint) (*vfs.Mount, error) { - if err := source.checkCompatible(mount); err != nil { +func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config.Config, mns *vfs.MountNamespace, creds *auth.Credentials, mount *specs.Mount, srcHint *MountHint, srcMount *vfs.Mount) (*vfs.Mount, error) { + if err := srcHint.checkCompatible(mount); err != nil { return nil, err } // Ignore data and useOverlay because these were already applied to // the master mount. - _, opts, err := c.getMountNameAndOptions(conf, newNonGoferMountInfo(mount)) + _, opts, err := getMountNameAndOptions(conf, newNonGoferMountInfo(mount), c.productName) if err != nil { return nil, err } - newMnt := c.k.VFS().NewDisconnectedMount(source.vfsMount.Filesystem(), source.vfsMount.Root(), opts) + newMnt := c.k.VFS().NewDisconnectedMount(srcMount.Filesystem(), srcMount.Root(), opts) defer newMnt.DecRef(ctx) root := mns.Root(ctx) @@ -1027,7 +1044,7 @@ func (c *containerMounter) mountSharedSubmount(ctx context.Context, conf *config if err := c.k.VFS().ConnectMountAt(ctx, creds, newMnt, target); err != nil { return nil, err } - log.Infof("Mounted %q type shared bind to %q", mount.Destination, source.name) + log.Infof("Mounted %q type shared bind to %q", mount.Destination, srcHint.Name) return newMnt, nil } diff --git a/runsc/boot/vfs_test.go b/runsc/boot/vfs_test.go index 477eb9473..96b5d5d40 100644 --- a/runsc/boot/vfs_test.go +++ b/runsc/boot/vfs_test.go @@ -71,11 +71,10 @@ func TestGetMountAccessType(t *testing.T) { if err != nil { t.Fatalf("newPodMountHints failed: %v", err) } - mounter := containerMounter{hints: podHints} conf := &config.Config{FileAccessMounts: config.FileAccessShared} mnt := &specs.Mount{Source: source} - if got := mounter.getMountAccessType(conf, mnt, podHints.FindMount(mnt)); got != tst.want { - t.Errorf("getMountAccessType(), want: %v, got: %v", tst.want, got) + if got := getMountAccessType(conf, mnt, podHints.FindMount(mnt)); got != tst.want { + t.Errorf("getMountAccessType(), got: %v, want: %v", got, tst.want) } }) } diff --git a/runsc/container/container.go b/runsc/container/container.go index 00b3fca9d..5bc214277 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -926,7 +926,7 @@ func (c *Container) createOverlayFilestore(conf config.Overlay2, mountSrc string func (c *Container) createOverlayFilestoreInSelf(mountSrc string) (*os.File, boot.OverlayMedium, error) { mountSrcInfo, err := os.Stat(mountSrc) if err != nil { - return nil, boot.NoOverlay, fmt.Errorf("failed to stat mount %q to see if it were a dirctory: %v", mountSrc, err) + return nil, boot.NoOverlay, fmt.Errorf("failed to stat mount %q to see if it were a directory: %v", mountSrc, err) } if !mountSrcInfo.IsDir() { log.Warningf("overlay2 self medium is only supported for directory mounts, but mount %q is not a directory, falling back to memory", mountSrc)