mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Make PodMountHints more serializable.
Before this CL, `PodMountHints` serialized to nothing, so it was an empty `{}`
in the container state file. However, `runsc start` needs to read this from
the state file when starting new subcontainers that have mounts of their own.
This CL exports `PodMountHints.Mounts` and most fields of `MountHint`, so that
the serialized view of `PodMountHints` contains enough data for `runsc start`
to do its thing.
PiperOrigin-RevId: 561259523
This commit is contained in:
committed by
gVisor bot
parent
268b43d1b2
commit
8326bcecfc
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+36
-40
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+45
-28
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user