| package mounts // import "github.com/docker/docker/volume/mounts" | |
| import ( | |
| "fmt" | |
| "strings" | |
| "testing" | |
| "github.com/docker/docker/api/types/mount" | |
| "gotest.tools/v3/assert" | |
| ) | |
| func TestLCOWParseMountRaw(t *testing.T) { | |
| 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`, | |
| } | |
| parser := NewLCOWParser() | |
| if p, ok := parser.(*lcowParser); ok { | |
| p.fi = mockFiProvider{} | |
| } | |
| for _, path := range valid { | |
| if _, err := parser.ParseMountRaw(path, "local"); err != nil { | |
| t.Errorf("ParseMountRaw(`%q`) should succeed: error %q", path, err) | |
| } | |
| } | |
| for path, expectedError := range 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()) | |
| } | |
| } | |
| } | |
| } | |
| func TestLCOWParseMountRawSplit(t *testing.T) { | |
| cases := []struct { | |
| bind string | |
| driver string | |
| expType mount.Type | |
| expDest string | |
| expSource string | |
| expName string | |
| expDriver string | |
| expRW bool | |
| fail bool | |
| }{ | |
| {`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}, | |
| } | |
| parser := NewLCOWParser() | |
| if p, ok := parser.(*lcowParser); ok { | |
| p.fi = mockFiProvider{} | |
| } | |
| for i, c := range cases { | |
| c := c | |
| t.Run(fmt.Sprintf("%d_%s", i, c.bind), func(t *testing.T) { | |
| m, err := parser.ParseMountRaw(c.bind, c.driver) | |
| if c.fail { | |
| assert.ErrorContains(t, err, "", "expected an error") | |
| return | |
| } | |
| assert.NilError(t, err) | |
| assert.Equal(t, m.Destination, c.expDest) | |
| assert.Equal(t, m.Source, c.expSource) | |
| assert.Equal(t, m.Name, c.expName) | |
| assert.Equal(t, m.Driver, c.expDriver) | |
| assert.Equal(t, m.RW, c.expRW) | |
| assert.Equal(t, m.Type, c.expType) | |
| }) | |
| } | |
| } |