github.com/MaximeAubanel/moby@v1.13.1/volume/volume_test.go (about) 1 package volume 2 3 import ( 4 "io/ioutil" 5 "os" 6 "runtime" 7 "strings" 8 "testing" 9 10 "github.com/docker/docker/api/types/mount" 11 ) 12 13 func TestParseMountRaw(t *testing.T) { 14 var ( 15 valid []string 16 invalid map[string]string 17 ) 18 19 if runtime.GOOS == "windows" { 20 valid = []string{ 21 `d:\`, 22 `d:`, 23 `d:\path`, 24 `d:\path with space`, 25 // TODO Windows post TP5 - readonly support `d:\pathandmode:ro`, 26 `c:\:d:\`, 27 `c:\windows\:d:`, 28 `c:\windows:d:\s p a c e`, 29 `c:\windows:d:\s p a c e:RW`, 30 `c:\program files:d:\s p a c e i n h o s t d i r`, 31 `0123456789name:d:`, 32 `MiXeDcAsEnAmE:d:`, 33 `name:D:`, 34 `name:D::rW`, 35 `name:D::RW`, 36 // TODO Windows post TP5 - readonly support `name:D::RO`, 37 `c:/:d:/forward/slashes/are/good/too`, 38 // TODO Windows post TP5 - readonly support `c:/:d:/including with/spaces:ro`, 39 `c:\Windows`, // With capital 40 `c:\Program Files (x86)`, // With capitals and brackets 41 } 42 invalid = map[string]string{ 43 ``: "invalid volume specification: ", 44 `.`: "invalid volume specification: ", 45 `..\`: "invalid volume specification: ", 46 `c:\:..\`: "invalid volume specification: ", 47 `c:\:d:\:xyzzy`: "invalid volume specification: ", 48 `c:`: "cannot be `c:`", 49 `c:\`: "cannot be `c:`", 50 `c:\notexist:d:`: `source path does not exist`, 51 `c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`, 52 `name<:d:`: `invalid volume specification`, 53 `name>:d:`: `invalid volume specification`, 54 `name::d:`: `invalid volume specification`, 55 `name":d:`: `invalid volume specification`, 56 `name\:d:`: `invalid volume specification`, 57 `name*:d:`: `invalid volume specification`, 58 `name|:d:`: `invalid volume specification`, 59 `name?:d:`: `invalid volume specification`, 60 `name/:d:`: `invalid volume specification`, 61 `d:\pathandmode:rw`: `invalid volume specification`, 62 `con:d:`: `cannot be a reserved word for Windows filenames`, 63 `PRN:d:`: `cannot be a reserved word for Windows filenames`, 64 `aUx:d:`: `cannot be a reserved word for Windows filenames`, 65 `nul:d:`: `cannot be a reserved word for Windows filenames`, 66 `com1:d:`: `cannot be a reserved word for Windows filenames`, 67 `com2:d:`: `cannot be a reserved word for Windows filenames`, 68 `com3:d:`: `cannot be a reserved word for Windows filenames`, 69 `com4:d:`: `cannot be a reserved word for Windows filenames`, 70 `com5:d:`: `cannot be a reserved word for Windows filenames`, 71 `com6:d:`: `cannot be a reserved word for Windows filenames`, 72 `com7:d:`: `cannot be a reserved word for Windows filenames`, 73 `com8:d:`: `cannot be a reserved word for Windows filenames`, 74 `com9:d:`: `cannot be a reserved word for Windows filenames`, 75 `lpt1:d:`: `cannot be a reserved word for Windows filenames`, 76 `lpt2:d:`: `cannot be a reserved word for Windows filenames`, 77 `lpt3:d:`: `cannot be a reserved word for Windows filenames`, 78 `lpt4:d:`: `cannot be a reserved word for Windows filenames`, 79 `lpt5:d:`: `cannot be a reserved word for Windows filenames`, 80 `lpt6:d:`: `cannot be a reserved word for Windows filenames`, 81 `lpt7:d:`: `cannot be a reserved word for Windows filenames`, 82 `lpt8:d:`: `cannot be a reserved word for Windows filenames`, 83 `lpt9:d:`: `cannot be a reserved word for Windows filenames`, 84 `c:\windows\system32\ntdll.dll`: `Only directories can be mapped on this platform`, 85 } 86 87 } else { 88 valid = []string{ 89 "/home", 90 "/home:/home", 91 "/home:/something/else", 92 "/with space", 93 "/home:/with space", 94 "relative:/absolute-path", 95 "hostPath:/containerPath:ro", 96 "/hostPath:/containerPath:rw", 97 "/rw:/ro", 98 } 99 invalid = map[string]string{ 100 "": "invalid volume specification", 101 "./": "mount path must be absolute", 102 "../": "mount path must be absolute", 103 "/:../": "mount path must be absolute", 104 "/:path": "mount path must be absolute", 105 ":": "invalid volume specification", 106 "/tmp:": "invalid volume specification", 107 ":test": "invalid volume specification", 108 ":/test": "invalid volume specification", 109 "tmp:": "invalid volume specification", 110 ":test:": "invalid volume specification", 111 "::": "invalid volume specification", 112 ":::": "invalid volume specification", 113 "/tmp:::": "invalid volume specification", 114 ":/tmp::": "invalid volume specification", 115 "/path:rw": "invalid volume specification", 116 "/path:ro": "invalid volume specification", 117 "/rw:rw": "invalid volume specification", 118 "path:ro": "invalid volume specification", 119 "/path:/path:sw": `invalid mode`, 120 "/path:/path:rwz": `invalid mode`, 121 } 122 } 123 124 for _, path := range valid { 125 if _, err := ParseMountRaw(path, "local"); err != nil { 126 t.Fatalf("ParseMountRaw(`%q`) should succeed: error %q", path, err) 127 } 128 } 129 130 for path, expectedError := range invalid { 131 if mp, err := ParseMountRaw(path, "local"); err == nil { 132 t.Fatalf("ParseMountRaw(`%q`) should have failed validation. Err '%v' - MP: %v", path, err, mp) 133 } else { 134 if !strings.Contains(err.Error(), expectedError) { 135 t.Fatalf("ParseMountRaw(`%q`) error should contain %q, got %v", path, expectedError, err.Error()) 136 } 137 } 138 } 139 } 140 141 // testParseMountRaw is a structure used by TestParseMountRawSplit for 142 // specifying test cases for the ParseMountRaw() function. 143 type testParseMountRaw struct { 144 bind string 145 driver string 146 expDest string 147 expSource string 148 expName string 149 expDriver string 150 expRW bool 151 fail bool 152 } 153 154 func TestParseMountRawSplit(t *testing.T) { 155 var cases []testParseMountRaw 156 if runtime.GOOS == "windows" { 157 cases = []testParseMountRaw{ 158 {`c:\:d:`, "local", `d:`, `c:\`, ``, "", true, false}, 159 {`c:\:d:\`, "local", `d:\`, `c:\`, ``, "", true, false}, 160 // TODO Windows post TP5 - Add readonly support {`c:\:d:\:ro`, "local", `d:\`, `c:\`, ``, "", false, false}, 161 {`c:\:d:\:rw`, "local", `d:\`, `c:\`, ``, "", true, false}, 162 {`c:\:d:\:foo`, "local", `d:\`, `c:\`, ``, "", false, true}, 163 {`name:d::rw`, "local", `d:`, ``, `name`, "local", true, false}, 164 {`name:d:`, "local", `d:`, ``, `name`, "local", true, false}, 165 // TODO Windows post TP5 - Add readonly support {`name:d::ro`, "local", `d:`, ``, `name`, "local", false, false}, 166 {`name:c:`, "", ``, ``, ``, "", true, true}, 167 {`driver/name:c:`, "", ``, ``, ``, "", true, true}, 168 } 169 } else { 170 cases = []testParseMountRaw{ 171 {"/tmp:/tmp1", "", "/tmp1", "/tmp", "", "", true, false}, 172 {"/tmp:/tmp2:ro", "", "/tmp2", "/tmp", "", "", false, false}, 173 {"/tmp:/tmp3:rw", "", "/tmp3", "/tmp", "", "", true, false}, 174 {"/tmp:/tmp4:foo", "", "", "", "", "", false, true}, 175 {"name:/named1", "", "/named1", "", "name", "", true, false}, 176 {"name:/named2", "external", "/named2", "", "name", "external", true, false}, 177 {"name:/named3:ro", "local", "/named3", "", "name", "local", false, false}, 178 {"local/name:/tmp:rw", "", "/tmp", "", "local/name", "", true, false}, 179 {"/tmp:tmp", "", "", "", "", "", true, true}, 180 } 181 } 182 183 for i, c := range cases { 184 t.Logf("case %d", i) 185 m, err := ParseMountRaw(c.bind, c.driver) 186 if c.fail { 187 if err == nil { 188 t.Fatalf("Expected error, was nil, for spec %s\n", c.bind) 189 } 190 continue 191 } 192 193 if m == nil || err != nil { 194 t.Fatalf("ParseMountRaw failed for spec '%s', driver '%s', error '%v'", c.bind, c.driver, err.Error()) 195 continue 196 } 197 198 if m.Destination != c.expDest { 199 t.Fatalf("Expected destination '%s, was %s', for spec '%s'", c.expDest, m.Destination, c.bind) 200 } 201 202 if m.Source != c.expSource { 203 t.Fatalf("Expected source '%s', was '%s', for spec '%s'", c.expSource, m.Source, c.bind) 204 } 205 206 if m.Name != c.expName { 207 t.Fatalf("Expected name '%s', was '%s' for spec '%s'", c.expName, m.Name, c.bind) 208 } 209 210 if m.Driver != c.expDriver { 211 t.Fatalf("Expected driver '%s', was '%s', for spec '%s'", c.expDriver, m.Driver, c.bind) 212 } 213 214 if m.RW != c.expRW { 215 t.Fatalf("Expected RW '%v', was '%v' for spec '%s'", c.expRW, m.RW, c.bind) 216 } 217 } 218 } 219 220 func TestParseMountSpec(t *testing.T) { 221 type c struct { 222 input mount.Mount 223 expected MountPoint 224 } 225 testDir, err := ioutil.TempDir("", "test-mount-config") 226 if err != nil { 227 t.Fatal(err) 228 } 229 defer os.RemoveAll(testDir) 230 231 cases := []c{ 232 {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}}, 233 {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true}}, 234 {mount.Mount{Type: mount.TypeBind, Source: testDir + string(os.PathSeparator), Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}}, 235 {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath}}, 236 {mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: DefaultCopyMode}}, 237 {mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath + string(os.PathSeparator)}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: DefaultCopyMode}}, 238 } 239 240 for i, c := range cases { 241 t.Logf("case %d", i) 242 mp, err := ParseMountSpec(c.input) 243 if err != nil { 244 t.Fatal(err) 245 } 246 247 if c.expected.Type != mp.Type { 248 t.Fatalf("Expected mount types to match. Expected: '%s', Actual: '%s'", c.expected.Type, mp.Type) 249 } 250 if c.expected.Destination != mp.Destination { 251 t.Fatalf("Expected mount destination to match. Expected: '%s', Actual: '%s'", c.expected.Destination, mp.Destination) 252 } 253 if c.expected.Source != mp.Source { 254 t.Fatalf("Expected mount source to match. Expected: '%s', Actual: '%s'", c.expected.Source, mp.Source) 255 } 256 if c.expected.RW != mp.RW { 257 t.Fatalf("Expected mount writable to match. Expected: '%v', Actual: '%v'", c.expected.RW, mp.RW) 258 } 259 if c.expected.Propagation != mp.Propagation { 260 t.Fatalf("Expected mount propagation to match. Expected: '%v', Actual: '%s'", c.expected.Propagation, mp.Propagation) 261 } 262 if c.expected.Driver != mp.Driver { 263 t.Fatalf("Expected mount driver to match. Expected: '%v', Actual: '%s'", c.expected.Driver, mp.Driver) 264 } 265 if c.expected.CopyData != mp.CopyData { 266 t.Fatalf("Expected mount copy data to match. Expected: '%v', Actual: '%v'", c.expected.CopyData, mp.CopyData) 267 } 268 } 269 }