mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Remove restriction on anonymous read-only volumes
Restriction on anonymouse read-only volumes is currently preventing the use of pre-populated volumes that should be accessed in a read-only manner in a container (e.g. an NFS volume containing data to be processed or served). According to @neersighted the restriction may have originally been put in place with the assumption that pre-populated volumes would be exposed as a named volume by the volume driver. In practice, NFS volumes are mounted using the docker `local` driver by supplying driver opts. Example that fails when `readonly` is specified but works without: ``` docker run --rm -it \ --mount 'readonly,type=volume,dst=/data/dest,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/export/some-share,"volume-opt=o=nfsvers=4,addr=some.server"' \ debian ``` Fixes #45297 Signed-off-by: Shane St Savage <shane@axds.co>
This commit is contained in:
@@ -112,9 +112,6 @@ func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSour
|
||||
return &errMountConfig{mnt, errInvalidSubpath}
|
||||
}
|
||||
}
|
||||
if mnt.ReadOnly && anonymousVolume {
|
||||
return &errMountConfig{mnt, errors.New("must not set ReadOnly mode when using anonymous volumes")}
|
||||
}
|
||||
case mount.TypeTmpfs:
|
||||
if mnt.BindOptions != nil {
|
||||
return &errMountConfig{mnt, errExtraField("BindOptions")}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -264,6 +265,89 @@ func TestLinuxParseMountRawSplit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinuxValidateMounts(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
tests := []struct {
|
||||
doc string
|
||||
mount mount.Mount
|
||||
expected *MountPoint
|
||||
expErr string
|
||||
}{
|
||||
{
|
||||
doc: "read-write",
|
||||
mount: mount.Mount{
|
||||
Source: tmpDir,
|
||||
Target: "/tmp1",
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
expected: &MountPoint{
|
||||
Source: tmpDir,
|
||||
Destination: "/tmp1",
|
||||
RW: true,
|
||||
Type: mount.TypeBind,
|
||||
Propagation: "rprivate",
|
||||
Spec: mount.Mount{
|
||||
Source: tmpDir,
|
||||
Target: "/tmp1",
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "read-only",
|
||||
mount: mount.Mount{
|
||||
Target: "/data/anonymous-read-only-volume",
|
||||
ReadOnly: true,
|
||||
Type: mount.TypeVolume,
|
||||
},
|
||||
expected: &MountPoint{
|
||||
Destination: "/data/anonymous-read-only-volume",
|
||||
Type: mount.TypeVolume,
|
||||
CopyData: true,
|
||||
Spec: mount.Mount{
|
||||
Target: "/data/anonymous-read-only-volume",
|
||||
ReadOnly: true,
|
||||
Type: mount.TypeVolume,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "invalid source path",
|
||||
mount: mount.Mount{
|
||||
Source: filepath.Join(tmpDir, "no-such-path"),
|
||||
Target: "/data/anonymous-read-only-volume",
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
expErr: `invalid mount config for type "bind": bind source path does not exist: ` + filepath.Join(tmpDir, "no-such-path"),
|
||||
},
|
||||
{
|
||||
doc: "invalid mount type",
|
||||
mount: mount.Mount{
|
||||
Target: "/data/invalid-type",
|
||||
Type: "invalid",
|
||||
},
|
||||
expErr: `invalid mount config for type "invalid": mount type unknown`,
|
||||
},
|
||||
}
|
||||
|
||||
parser := NewLinuxParser()
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
m, err := parser.ParseMountSpec(tc.mount)
|
||||
if tc.expErr != "" {
|
||||
assert.Check(t, is.Nil(m))
|
||||
assert.Check(t, is.Error(err, tc.expErr))
|
||||
return
|
||||
}
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(*m, *tc.expected, cmpopts.IgnoreUnexported(MountPoint{})))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLinuxParseMountSpecBindWithFileinfoError makes sure that the parser returns
|
||||
// the error produced by the fileinfo provider.
|
||||
//
|
||||
|
||||
@@ -270,10 +270,6 @@ func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, additionalValid
|
||||
}
|
||||
}
|
||||
|
||||
if anonymousVolume && mnt.ReadOnly {
|
||||
return &errMountConfig{mnt, errors.New("must not set ReadOnly mode when using anonymous volumes")}
|
||||
}
|
||||
|
||||
if mnt.Source != "" {
|
||||
if err := p.ValidateVolumeName(mnt.Source); err != nil {
|
||||
return &errMountConfig{mnt, err}
|
||||
|
||||
@@ -294,6 +294,81 @@ func TestWindowsParseMountRawSplit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowsValidateMounts(t *testing.T) {
|
||||
tests := []struct {
|
||||
mount mount.Mount
|
||||
expected *MountPoint
|
||||
expErr string
|
||||
}{
|
||||
{
|
||||
mount: mount.Mount{
|
||||
Source: `c:\`,
|
||||
Target: `d:\mount`,
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
expected: &MountPoint{
|
||||
Source: `c:\`,
|
||||
Destination: `d:\mount`,
|
||||
RW: true,
|
||||
Type: mount.TypeBind,
|
||||
Spec: mount.Mount{
|
||||
Source: `c:\`,
|
||||
Target: `d:\mount`,
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
mount: mount.Mount{
|
||||
Target: `c:/data/anonymous-read-only-volume`,
|
||||
ReadOnly: true,
|
||||
Type: mount.TypeVolume,
|
||||
},
|
||||
expected: &MountPoint{
|
||||
Destination: `c:\data\anonymous-read-only-volume`,
|
||||
Type: mount.TypeVolume,
|
||||
Spec: mount.Mount{
|
||||
Target: `c:/data/anonymous-read-only-volume`,
|
||||
ReadOnly: true,
|
||||
Type: mount.TypeVolume,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
mount: mount.Mount{
|
||||
Source: "c:/bad/path",
|
||||
Target: "d:/data/anonymous-read-only-volume",
|
||||
Type: mount.TypeBind,
|
||||
},
|
||||
expErr: `invalid mount config for type "bind": bind source path does not exist: c:/bad/path`,
|
||||
},
|
||||
{
|
||||
mount: mount.Mount{
|
||||
Target: "d:/data/invalid-type",
|
||||
Type: "invalid",
|
||||
},
|
||||
expErr: `invalid mount config for type "invalid": mount type unknown`,
|
||||
},
|
||||
}
|
||||
|
||||
parser := NewWindowsParser()
|
||||
if p, ok := parser.(*windowsParser); ok {
|
||||
p.fi = mockFiProvider{}
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
m, err := parser.ParseMountSpec(tc.mount)
|
||||
if tc.expErr != "" {
|
||||
assert.Check(t, is.Nil(m))
|
||||
assert.Check(t, is.Error(err, tc.expErr))
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(*m, *tc.expected, cmpopts.IgnoreUnexported(MountPoint{})))
|
||||
}
|
||||
}
|
||||
|
||||
// TestWindowsParseMountSpecBindWithFileinfoError makes sure that the parser returns
|
||||
// the error produced by the fileinfo provider.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user