|
@@ -1,22 +1,13 @@
|
|
package mounts // import "github.com/docker/docker/volume/mounts"
|
|
package mounts // import "github.com/docker/docker/volume/mounts"
|
|
|
|
|
|
import (
|
|
import (
|
|
- "errors"
|
|
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
"os"
|
|
"os"
|
|
- "runtime"
|
|
|
|
- "strings"
|
|
|
|
"testing"
|
|
"testing"
|
|
|
|
|
|
"github.com/docker/docker/api/types/mount"
|
|
"github.com/docker/docker/api/types/mount"
|
|
- "gotest.tools/v3/assert"
|
|
|
|
)
|
|
)
|
|
|
|
|
|
-type parseMountRawTestSet struct {
|
|
|
|
- valid []string
|
|
|
|
- invalid map[string]string
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
type mockFiProvider struct{}
|
|
type mockFiProvider struct{}
|
|
|
|
|
|
func (mockFiProvider) fileInfo(path string) (exists, isDir bool, err error) {
|
|
func (mockFiProvider) fileInfo(path string) (exists, isDir bool, err error) {
|
|
@@ -41,363 +32,25 @@ func (mockFiProvider) fileInfo(path string) (exists, isDir bool, err error) {
|
|
return false, false, nil
|
|
return false, false, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func TestParseMountRaw(t *testing.T) {
|
|
|
|
-
|
|
|
|
- previousProvider := currentFileInfoProvider
|
|
|
|
- defer func() { currentFileInfoProvider = previousProvider }()
|
|
|
|
- currentFileInfoProvider = mockFiProvider{}
|
|
|
|
- windowsSet := parseMountRawTestSet{
|
|
|
|
- valid: []string{
|
|
|
|
- `d:\`,
|
|
|
|
- `d:`,
|
|
|
|
- `d:\path`,
|
|
|
|
- `d:\path with space`,
|
|
|
|
- `c:\:d:\`,
|
|
|
|
- `c:\windows\:d:`,
|
|
|
|
- `c:\windows:d:\s p a c e`,
|
|
|
|
- `c:\windows:d:\s p a c e:RW`,
|
|
|
|
- `c:\program files:d:\s p a c e i n h o s t d i r`,
|
|
|
|
- `0123456789name:d:`,
|
|
|
|
- `MiXeDcAsEnAmE:d:`,
|
|
|
|
- `name:D:`,
|
|
|
|
- `name:D::rW`,
|
|
|
|
- `name:D::RW`,
|
|
|
|
- `name:D::RO`,
|
|
|
|
- `c:/:d:/forward/slashes/are/good/too`,
|
|
|
|
- `c:/:d:/including with/spaces:ro`,
|
|
|
|
- `c:\Windows`, // With capital
|
|
|
|
- `c:\Program Files (x86)`, // With capitals and brackets
|
|
|
|
- `\\?\c:\windows\:d:`, // Long path handling (source)
|
|
|
|
- `c:\windows\:\\?\d:\`, // Long path handling (target)
|
|
|
|
- `\\.\pipe\foo:\\.\pipe\foo`, // named pipe
|
|
|
|
- `//./pipe/foo://./pipe/foo`, // named pipe forward slashes
|
|
|
|
- },
|
|
|
|
- invalid: map[string]string{
|
|
|
|
- ``: "invalid volume specification: ",
|
|
|
|
- `.`: "invalid volume specification: ",
|
|
|
|
- `..\`: "invalid volume specification: ",
|
|
|
|
- `c:\:..\`: "invalid volume specification: ",
|
|
|
|
- `c:\:d:\:xyzzy`: "invalid volume specification: ",
|
|
|
|
- `c:`: "cannot be `c:`",
|
|
|
|
- `c:\`: "cannot be `c:`",
|
|
|
|
- `c:\notexist:d:`: `source path does not exist: c:\notexist`,
|
|
|
|
- `c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`,
|
|
|
|
- `name<:d:`: `invalid volume specification`,
|
|
|
|
- `name>:d:`: `invalid volume specification`,
|
|
|
|
- `name::d:`: `invalid volume specification`,
|
|
|
|
- `name":d:`: `invalid volume specification`,
|
|
|
|
- `name\:d:`: `invalid volume specification`,
|
|
|
|
- `name*:d:`: `invalid volume specification`,
|
|
|
|
- `name|:d:`: `invalid volume specification`,
|
|
|
|
- `name?:d:`: `invalid volume specification`,
|
|
|
|
- `name/:d:`: `invalid volume specification`,
|
|
|
|
- `d:\pathandmode:rw`: `invalid volume specification`,
|
|
|
|
- `d:\pathandmode:ro`: `invalid volume specification`,
|
|
|
|
- `con:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `PRN:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `aUx:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `nul:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com1:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com2:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com3:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com4:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com5:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com6:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com7:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com8:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com9:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt1:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt2:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt3:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt4:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt5:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt6:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt7:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt8:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt9:d:`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `c:\windows\system32\ntdll.dll`: `Only directories can be mapped on this platform`,
|
|
|
|
- `\\.\pipe\foo:c:\pipe`: `'c:\pipe' is not a valid pipe path`,
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
- lcowSet := parseMountRawTestSet{
|
|
|
|
- valid: []string{
|
|
|
|
- `/foo`,
|
|
|
|
- `/foo/`,
|
|
|
|
- `/foo bar`,
|
|
|
|
- `c:\:/foo`,
|
|
|
|
- `c:\windows\:/foo`,
|
|
|
|
- `c:\windows:/s p a c e`,
|
|
|
|
- `c:\windows:/s p a c e:RW`,
|
|
|
|
- `c:\program files:/s p a c e i n h o s t d i r`,
|
|
|
|
- `0123456789name:/foo`,
|
|
|
|
- `MiXeDcAsEnAmE:/foo`,
|
|
|
|
- `name:/foo`,
|
|
|
|
- `name:/foo:rW`,
|
|
|
|
- `name:/foo:RW`,
|
|
|
|
- `name:/foo:RO`,
|
|
|
|
- `c:/:/forward/slashes/are/good/too`,
|
|
|
|
- `c:/:/including with/spaces:ro`,
|
|
|
|
- `/Program Files (x86)`, // With capitals and brackets
|
|
|
|
- },
|
|
|
|
- invalid: map[string]string{
|
|
|
|
- ``: "invalid volume specification: ",
|
|
|
|
- `.`: "invalid volume specification: ",
|
|
|
|
- `c:`: "invalid volume specification: ",
|
|
|
|
- `c:\`: "invalid volume specification: ",
|
|
|
|
- `../`: "invalid volume specification: ",
|
|
|
|
- `c:\:../`: "invalid volume specification: ",
|
|
|
|
- `c:\:/foo:xyzzy`: "invalid volume specification: ",
|
|
|
|
- `/`: "destination can't be '/'",
|
|
|
|
- `/..`: "destination can't be '/'",
|
|
|
|
- `c:\notexist:/foo`: `source path does not exist: c:\notexist`,
|
|
|
|
- `c:\windows\system32\ntdll.dll:/foo`: `source path must be a directory`,
|
|
|
|
- `name<:/foo`: `invalid volume specification`,
|
|
|
|
- `name>:/foo`: `invalid volume specification`,
|
|
|
|
- `name::/foo`: `invalid volume specification`,
|
|
|
|
- `name":/foo`: `invalid volume specification`,
|
|
|
|
- `name\:/foo`: `invalid volume specification`,
|
|
|
|
- `name*:/foo`: `invalid volume specification`,
|
|
|
|
- `name|:/foo`: `invalid volume specification`,
|
|
|
|
- `name?:/foo`: `invalid volume specification`,
|
|
|
|
- `name/:/foo`: `invalid volume specification`,
|
|
|
|
- `/foo:rw`: `invalid volume specification`,
|
|
|
|
- `/foo:ro`: `invalid volume specification`,
|
|
|
|
- `con:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `PRN:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `aUx:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `nul:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com1:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com2:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com3:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com4:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com5:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com6:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com7:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com8:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `com9:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt1:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt2:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt3:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt4:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt5:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt6:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt7:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt8:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `lpt9:/foo`: `cannot be a reserved word for Windows filenames`,
|
|
|
|
- `\\.\pipe\foo:/foo`: `Linux containers on Windows do not support named pipe mounts`,
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
- linuxSet := parseMountRawTestSet{
|
|
|
|
- valid: []string{
|
|
|
|
- "/home",
|
|
|
|
- "/home:/home",
|
|
|
|
- "/home:/something/else",
|
|
|
|
- "/with space",
|
|
|
|
- "/home:/with space",
|
|
|
|
- "relative:/absolute-path",
|
|
|
|
- "hostPath:/containerPath:ro",
|
|
|
|
- "/hostPath:/containerPath:rw",
|
|
|
|
- "/rw:/ro",
|
|
|
|
- "/hostPath:/containerPath:shared",
|
|
|
|
- "/hostPath:/containerPath:rshared",
|
|
|
|
- "/hostPath:/containerPath:slave",
|
|
|
|
- "/hostPath:/containerPath:rslave",
|
|
|
|
- "/hostPath:/containerPath:private",
|
|
|
|
- "/hostPath:/containerPath:rprivate",
|
|
|
|
- "/hostPath:/containerPath:ro,shared",
|
|
|
|
- "/hostPath:/containerPath:ro,slave",
|
|
|
|
- "/hostPath:/containerPath:ro,private",
|
|
|
|
- "/hostPath:/containerPath:ro,z,shared",
|
|
|
|
- "/hostPath:/containerPath:ro,Z,slave",
|
|
|
|
- "/hostPath:/containerPath:Z,ro,slave",
|
|
|
|
- "/hostPath:/containerPath:slave,Z,ro",
|
|
|
|
- "/hostPath:/containerPath:Z,slave,ro",
|
|
|
|
- "/hostPath:/containerPath:slave,ro,Z",
|
|
|
|
- "/hostPath:/containerPath:rslave,ro,Z",
|
|
|
|
- "/hostPath:/containerPath:ro,rshared,Z",
|
|
|
|
- "/hostPath:/containerPath:ro,Z,rprivate",
|
|
|
|
- },
|
|
|
|
- invalid: map[string]string{
|
|
|
|
- "": "invalid volume specification",
|
|
|
|
- "./": "mount path must be absolute",
|
|
|
|
- "../": "mount path must be absolute",
|
|
|
|
- "/:../": "mount path must be absolute",
|
|
|
|
- "/:path": "mount path must be absolute",
|
|
|
|
- ":": "invalid volume specification",
|
|
|
|
- "/tmp:": "invalid volume specification",
|
|
|
|
- ":test": "invalid volume specification",
|
|
|
|
- ":/test": "invalid volume specification",
|
|
|
|
- "tmp:": "invalid volume specification",
|
|
|
|
- ":test:": "invalid volume specification",
|
|
|
|
- "::": "invalid volume specification",
|
|
|
|
- ":::": "invalid volume specification",
|
|
|
|
- "/tmp:::": "invalid volume specification",
|
|
|
|
- ":/tmp::": "invalid volume specification",
|
|
|
|
- "/path:rw": "invalid volume specification",
|
|
|
|
- "/path:ro": "invalid volume specification",
|
|
|
|
- "/rw:rw": "invalid volume specification",
|
|
|
|
- "path:ro": "invalid volume specification",
|
|
|
|
- "/path:/path:sw": `invalid mode`,
|
|
|
|
- "/path:/path:rwz": `invalid mode`,
|
|
|
|
- "/path:/path:ro,rshared,rslave": `invalid mode`,
|
|
|
|
- "/path:/path:ro,z,rshared,rslave": `invalid mode`,
|
|
|
|
- "/path:shared": "invalid volume specification",
|
|
|
|
- "/path:slave": "invalid volume specification",
|
|
|
|
- "/path:private": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:shared": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:rshared": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:slave": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:rslave": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:private": "invalid volume specification",
|
|
|
|
- "name:/absolute-path:rprivate": "invalid volume specification",
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- linParser := &linuxParser{}
|
|
|
|
- winParser := &windowsParser{}
|
|
|
|
- lcowParser := &lcowParser{}
|
|
|
|
- tester := func(parser Parser, set parseMountRawTestSet) {
|
|
|
|
- for _, path := range set.valid {
|
|
|
|
- if _, err := parser.ParseMountRaw(path, "local"); err != nil {
|
|
|
|
- t.Errorf("ParseMountRaw(`%q`) should succeed: error %q", path, err)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for path, expectedError := range set.invalid {
|
|
|
|
- if mp, err := parser.ParseMountRaw(path, "local"); err == nil {
|
|
|
|
- t.Errorf("ParseMountRaw(`%q`) should have failed validation. Err '%v' - MP: %v", path, err, mp)
|
|
|
|
- } else {
|
|
|
|
- if !strings.Contains(err.Error(), expectedError) {
|
|
|
|
- t.Errorf("ParseMountRaw(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tester(linParser, linuxSet)
|
|
|
|
- tester(winParser, windowsSet)
|
|
|
|
- tester(lcowParser, lcowSet)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// testParseMountRaw is a structure used by TestParseMountRawSplit for
|
|
|
|
-// specifying test cases for the ParseMountRaw() function.
|
|
|
|
-type testParseMountRaw struct {
|
|
|
|
- bind string
|
|
|
|
- driver string
|
|
|
|
- expType mount.Type
|
|
|
|
- expDest string
|
|
|
|
- expSource string
|
|
|
|
- expName string
|
|
|
|
- expDriver string
|
|
|
|
- expRW bool
|
|
|
|
- fail bool
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func TestParseMountRawSplit(t *testing.T) {
|
|
|
|
- previousProvider := currentFileInfoProvider
|
|
|
|
- defer func() { currentFileInfoProvider = previousProvider }()
|
|
|
|
- currentFileInfoProvider = mockFiProvider{}
|
|
|
|
- windowsCases := []testParseMountRaw{
|
|
|
|
- {`c:\:d:`, "local", mount.TypeBind, `d:`, `c:\`, ``, "", true, false},
|
|
|
|
- {`c:\:d:\`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
|
|
|
|
- {`c:\:d:\:ro`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, false},
|
|
|
|
- {`c:\:d:\:rw`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
|
|
|
|
- {`c:\:d:\:foo`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, true},
|
|
|
|
- {`name:d::rw`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
|
|
|
|
- {`name:d:`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
|
|
|
|
- {`name:d::ro`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", false, false},
|
|
|
|
- {`name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
|
|
|
|
- {`driver/name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
|
|
|
|
- {`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, false},
|
|
|
|
- {`\\.\pipe\foo:c:\foo\bar`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
|
|
|
|
- {`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
|
|
|
|
- }
|
|
|
|
- lcowCases := []testParseMountRaw{
|
|
|
|
- {`c:\:/foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
|
|
|
|
- {`c:\:/foo:ro`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, false},
|
|
|
|
- {`c:\:/foo:rw`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
|
|
|
|
- {`c:\:/foo:foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, true},
|
|
|
|
- {`name:/foo:rw`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
|
|
|
|
- {`name:/foo`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
|
|
|
|
- {`name:/foo:ro`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", false, false},
|
|
|
|
- {`name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
|
|
|
|
- {`driver/name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
|
|
|
|
- {`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, true},
|
|
|
|
- {`\\.\pipe\foo:/data`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
|
|
|
|
- {`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
|
|
|
|
- }
|
|
|
|
- linuxCases := []testParseMountRaw{
|
|
|
|
- {"/tmp:/tmp1", "", mount.TypeBind, "/tmp1", "/tmp", "", "", true, false},
|
|
|
|
- {"/tmp:/tmp2:ro", "", mount.TypeBind, "/tmp2", "/tmp", "", "", false, false},
|
|
|
|
- {"/tmp:/tmp3:rw", "", mount.TypeBind, "/tmp3", "/tmp", "", "", true, false},
|
|
|
|
- {"/tmp:/tmp4:foo", "", mount.TypeBind, "", "", "", "", false, true},
|
|
|
|
- {"name:/named1", "", mount.TypeVolume, "/named1", "", "name", "", true, false},
|
|
|
|
- {"name:/named2", "external", mount.TypeVolume, "/named2", "", "name", "external", true, false},
|
|
|
|
- {"name:/named3:ro", "local", mount.TypeVolume, "/named3", "", "name", "local", false, false},
|
|
|
|
- {"local/name:/tmp:rw", "", mount.TypeVolume, "/tmp", "", "local/name", "", true, false},
|
|
|
|
- {"/tmp:tmp", "", mount.TypeBind, "", "", "", "", true, true},
|
|
|
|
- }
|
|
|
|
- linParser := &linuxParser{}
|
|
|
|
- winParser := &windowsParser{}
|
|
|
|
- lcowParser := &lcowParser{}
|
|
|
|
- tester := func(parser Parser, cases []testParseMountRaw) {
|
|
|
|
- for i, c := range cases {
|
|
|
|
- t.Logf("case %d", i)
|
|
|
|
- m, err := parser.ParseMountRaw(c.bind, c.driver)
|
|
|
|
- if c.fail {
|
|
|
|
- if err == nil {
|
|
|
|
- t.Errorf("Expected error, was nil, for spec %s\n", c.bind)
|
|
|
|
- }
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m == nil || err != nil {
|
|
|
|
- t.Errorf("ParseMountRaw failed for spec '%s', driver '%s', error '%v'", c.bind, c.driver, err)
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m.Destination != c.expDest {
|
|
|
|
- t.Errorf("Expected destination '%s, was %s', for spec '%s'", c.expDest, m.Destination, c.bind)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m.Source != c.expSource {
|
|
|
|
- t.Errorf("Expected source '%s', was '%s', for spec '%s'", c.expSource, m.Source, c.bind)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m.Name != c.expName {
|
|
|
|
- t.Errorf("Expected name '%s', was '%s' for spec '%s'", c.expName, m.Name, c.bind)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m.Driver != c.expDriver {
|
|
|
|
- t.Errorf("Expected driver '%s', was '%s', for spec '%s'", c.expDriver, m.Driver, c.bind)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if m.RW != c.expRW {
|
|
|
|
- t.Errorf("Expected RW '%v', was '%v' for spec '%s'", c.expRW, m.RW, c.bind)
|
|
|
|
- }
|
|
|
|
- if m.Type != c.expType {
|
|
|
|
- t.Fatalf("Expected type '%s', was '%s', for spec '%s'", c.expType, m.Type, c.bind)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+// always returns the configured error
|
|
|
|
+// this is used to test error handling
|
|
|
|
+type mockFiProviderWithError struct{ err error }
|
|
|
|
|
|
- tester(linParser, linuxCases)
|
|
|
|
- tester(winParser, windowsCases)
|
|
|
|
- tester(lcowParser, lcowCases)
|
|
|
|
|
|
+func (m mockFiProviderWithError) fileInfo(path string) (bool, bool, error) {
|
|
|
|
+ return false, false, m.err
|
|
}
|
|
}
|
|
|
|
|
|
func TestParseMountSpec(t *testing.T) {
|
|
func TestParseMountSpec(t *testing.T) {
|
|
- type c struct {
|
|
|
|
- input mount.Mount
|
|
|
|
- expected MountPoint
|
|
|
|
- }
|
|
|
|
testDir, err := ioutil.TempDir("", "test-mount-config")
|
|
testDir, err := ioutil.TempDir("", "test-mount-config")
|
|
if err != nil {
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
defer os.RemoveAll(testDir)
|
|
defer os.RemoveAll(testDir)
|
|
parser := NewParser()
|
|
parser := NewParser()
|
|
- cases := []c{
|
|
|
|
|
|
+ cases := []struct {
|
|
|
|
+ input mount.Mount
|
|
|
|
+ expected MountPoint
|
|
|
|
+ }{
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: parser.DefaultPropagationMode()}},
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: parser.DefaultPropagationMode()}},
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
|
|
{mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
|
|
@@ -437,48 +90,3 @@ func TestParseMountSpec(t *testing.T) {
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
-// always returns the configured error
|
|
|
|
-// this is used to test error handling
|
|
|
|
-type mockFiProviderWithError struct{ err error }
|
|
|
|
-
|
|
|
|
-func (m mockFiProviderWithError) fileInfo(path string) (bool, bool, error) {
|
|
|
|
- return false, false, m.err
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TestParseMountSpecBindWithFileinfoError makes sure that the parser returns
|
|
|
|
-// the error produced by the fileinfo provider.
|
|
|
|
-//
|
|
|
|
-// Some extra context for the future in case of changes and possible wtf are we
|
|
|
|
-// testing this for:
|
|
|
|
-//
|
|
|
|
-// Currently this "fileInfoProvider" returns (bool, bool, error)
|
|
|
|
-// The 1st bool is "does this path exist"
|
|
|
|
-// The 2nd bool is "is this path a dir"
|
|
|
|
-// Then of course the error is an error.
|
|
|
|
-//
|
|
|
|
-// The issue is the parser was ignoring the error and only looking at the
|
|
|
|
-// "does this path exist" boolean, which is always false if there is an error.
|
|
|
|
-// Then the error returned to the caller was a (slightly, maybe) friendlier
|
|
|
|
-// error string than what comes from `os.Stat`
|
|
|
|
-// So ...the caller was always getting an error saying the path doesn't exist
|
|
|
|
-// even if it does exist but got some other error (like a permission error).
|
|
|
|
-// This is confusing to users.
|
|
|
|
-func TestParseMountSpecBindWithFileinfoError(t *testing.T) {
|
|
|
|
- previousProvider := currentFileInfoProvider
|
|
|
|
- defer func() { currentFileInfoProvider = previousProvider }()
|
|
|
|
-
|
|
|
|
- testErr := errors.New("some crazy error")
|
|
|
|
- currentFileInfoProvider = &mockFiProviderWithError{err: testErr}
|
|
|
|
-
|
|
|
|
- p := "/bananas"
|
|
|
|
- if runtime.GOOS == "windows" {
|
|
|
|
- p = `c:\bananas`
|
|
|
|
- }
|
|
|
|
- m := mount.Mount{Type: mount.TypeBind, Source: p, Target: p}
|
|
|
|
-
|
|
|
|
- parser := NewParser()
|
|
|
|
-
|
|
|
|
- _, err := parser.ParseMountSpec(m)
|
|
|
|
- assert.ErrorContains(t, err, "some crazy error")
|
|
|
|
-}
|
|
|