volume_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. package volume
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "runtime"
  6. "strings"
  7. "testing"
  8. "github.com/docker/docker/api/types/mount"
  9. )
  10. type parseMountRawTestSet struct {
  11. valid []string
  12. invalid map[string]string
  13. }
  14. func TestConvertTmpfsOptions(t *testing.T) {
  15. type testCase struct {
  16. opt mount.TmpfsOptions
  17. readOnly bool
  18. expectedSubstrings []string
  19. unexpectedSubstrings []string
  20. }
  21. cases := []testCase{
  22. {
  23. opt: mount.TmpfsOptions{SizeBytes: 1024 * 1024, Mode: 0700},
  24. readOnly: false,
  25. expectedSubstrings: []string{"size=1m", "mode=700"},
  26. unexpectedSubstrings: []string{"ro"},
  27. },
  28. {
  29. opt: mount.TmpfsOptions{},
  30. readOnly: true,
  31. expectedSubstrings: []string{"ro"},
  32. unexpectedSubstrings: []string{},
  33. },
  34. }
  35. p := &linuxParser{}
  36. for _, c := range cases {
  37. data, err := p.ConvertTmpfsOptions(&c.opt, c.readOnly)
  38. if err != nil {
  39. t.Fatalf("could not convert %+v (readOnly: %v) to string: %v",
  40. c.opt, c.readOnly, err)
  41. }
  42. t.Logf("data=%q", data)
  43. for _, s := range c.expectedSubstrings {
  44. if !strings.Contains(data, s) {
  45. t.Fatalf("expected substring: %s, got %v (case=%+v)", s, data, c)
  46. }
  47. }
  48. for _, s := range c.unexpectedSubstrings {
  49. if strings.Contains(data, s) {
  50. t.Fatalf("unexpected substring: %s, got %v (case=%+v)", s, data, c)
  51. }
  52. }
  53. }
  54. }
  55. type mockFiProvider struct{}
  56. func (mockFiProvider) fileInfo(path string) (exists, isDir bool, err error) {
  57. dirs := map[string]struct{}{
  58. `c:\`: {},
  59. `c:\windows\`: {},
  60. `c:\windows`: {},
  61. `c:\program files`: {},
  62. `c:\Windows`: {},
  63. `c:\Program Files (x86)`: {},
  64. `\\?\c:\windows\`: {},
  65. }
  66. files := map[string]struct{}{
  67. `c:\windows\system32\ntdll.dll`: {},
  68. }
  69. if _, ok := dirs[path]; ok {
  70. return true, true, nil
  71. }
  72. if _, ok := files[path]; ok {
  73. return true, false, nil
  74. }
  75. return false, false, nil
  76. }
  77. func TestParseMountRaw(t *testing.T) {
  78. previousProvider := currentFileInfoProvider
  79. defer func() { currentFileInfoProvider = previousProvider }()
  80. currentFileInfoProvider = mockFiProvider{}
  81. windowsSet := parseMountRawTestSet{
  82. valid: []string{
  83. `d:\`,
  84. `d:`,
  85. `d:\path`,
  86. `d:\path with space`,
  87. `c:\:d:\`,
  88. `c:\windows\:d:`,
  89. `c:\windows:d:\s p a c e`,
  90. `c:\windows:d:\s p a c e:RW`,
  91. `c:\program files:d:\s p a c e i n h o s t d i r`,
  92. `0123456789name:d:`,
  93. `MiXeDcAsEnAmE:d:`,
  94. `name:D:`,
  95. `name:D::rW`,
  96. `name:D::RW`,
  97. `name:D::RO`,
  98. `c:/:d:/forward/slashes/are/good/too`,
  99. `c:/:d:/including with/spaces:ro`,
  100. `c:\Windows`, // With capital
  101. `c:\Program Files (x86)`, // With capitals and brackets
  102. `\\?\c:\windows\:d:`, // Long path handling (source)
  103. `c:\windows\:\\?\d:\`, // Long path handling (target)
  104. `\\.\pipe\foo:\\.\pipe\foo`, // named pipe
  105. `//./pipe/foo://./pipe/foo`, // named pipe forward slashes
  106. },
  107. invalid: map[string]string{
  108. ``: "invalid volume specification: ",
  109. `.`: "invalid volume specification: ",
  110. `..\`: "invalid volume specification: ",
  111. `c:\:..\`: "invalid volume specification: ",
  112. `c:\:d:\:xyzzy`: "invalid volume specification: ",
  113. `c:`: "cannot be `c:`",
  114. `c:\`: "cannot be `c:`",
  115. `c:\notexist:d:`: `source path does not exist`,
  116. `c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`,
  117. `name<:d:`: `invalid volume specification`,
  118. `name>:d:`: `invalid volume specification`,
  119. `name::d:`: `invalid volume specification`,
  120. `name":d:`: `invalid volume specification`,
  121. `name\:d:`: `invalid volume specification`,
  122. `name*:d:`: `invalid volume specification`,
  123. `name|:d:`: `invalid volume specification`,
  124. `name?:d:`: `invalid volume specification`,
  125. `name/:d:`: `invalid volume specification`,
  126. `d:\pathandmode:rw`: `invalid volume specification`,
  127. `d:\pathandmode:ro`: `invalid volume specification`,
  128. `con:d:`: `cannot be a reserved word for Windows filenames`,
  129. `PRN:d:`: `cannot be a reserved word for Windows filenames`,
  130. `aUx:d:`: `cannot be a reserved word for Windows filenames`,
  131. `nul:d:`: `cannot be a reserved word for Windows filenames`,
  132. `com1:d:`: `cannot be a reserved word for Windows filenames`,
  133. `com2:d:`: `cannot be a reserved word for Windows filenames`,
  134. `com3:d:`: `cannot be a reserved word for Windows filenames`,
  135. `com4:d:`: `cannot be a reserved word for Windows filenames`,
  136. `com5:d:`: `cannot be a reserved word for Windows filenames`,
  137. `com6:d:`: `cannot be a reserved word for Windows filenames`,
  138. `com7:d:`: `cannot be a reserved word for Windows filenames`,
  139. `com8:d:`: `cannot be a reserved word for Windows filenames`,
  140. `com9:d:`: `cannot be a reserved word for Windows filenames`,
  141. `lpt1:d:`: `cannot be a reserved word for Windows filenames`,
  142. `lpt2:d:`: `cannot be a reserved word for Windows filenames`,
  143. `lpt3:d:`: `cannot be a reserved word for Windows filenames`,
  144. `lpt4:d:`: `cannot be a reserved word for Windows filenames`,
  145. `lpt5:d:`: `cannot be a reserved word for Windows filenames`,
  146. `lpt6:d:`: `cannot be a reserved word for Windows filenames`,
  147. `lpt7:d:`: `cannot be a reserved word for Windows filenames`,
  148. `lpt8:d:`: `cannot be a reserved word for Windows filenames`,
  149. `lpt9:d:`: `cannot be a reserved word for Windows filenames`,
  150. `c:\windows\system32\ntdll.dll`: `Only directories can be mapped on this platform`,
  151. `\\.\pipe\foo:c:\pipe`: `'c:\pipe' is not a valid pipe path`,
  152. },
  153. }
  154. lcowSet := parseMountRawTestSet{
  155. valid: []string{
  156. `/foo`,
  157. `/foo/`,
  158. `/foo bar`,
  159. `c:\:/foo`,
  160. `c:\windows\:/foo`,
  161. `c:\windows:/s p a c e`,
  162. `c:\windows:/s p a c e:RW`,
  163. `c:\program files:/s p a c e i n h o s t d i r`,
  164. `0123456789name:/foo`,
  165. `MiXeDcAsEnAmE:/foo`,
  166. `name:/foo`,
  167. `name:/foo:rW`,
  168. `name:/foo:RW`,
  169. `name:/foo:RO`,
  170. `c:/:/forward/slashes/are/good/too`,
  171. `c:/:/including with/spaces:ro`,
  172. `/Program Files (x86)`, // With capitals and brackets
  173. },
  174. invalid: map[string]string{
  175. ``: "invalid volume specification: ",
  176. `.`: "invalid volume specification: ",
  177. `c:`: "invalid volume specification: ",
  178. `c:\`: "invalid volume specification: ",
  179. `../`: "invalid volume specification: ",
  180. `c:\:../`: "invalid volume specification: ",
  181. `c:\:/foo:xyzzy`: "invalid volume specification: ",
  182. `/`: "destination can't be '/'",
  183. `/..`: "destination can't be '/'",
  184. `c:\notexist:/foo`: `source path does not exist`,
  185. `c:\windows\system32\ntdll.dll:/foo`: `source path must be a directory`,
  186. `name<:/foo`: `invalid volume specification`,
  187. `name>:/foo`: `invalid volume specification`,
  188. `name::/foo`: `invalid volume specification`,
  189. `name":/foo`: `invalid volume specification`,
  190. `name\:/foo`: `invalid volume specification`,
  191. `name*:/foo`: `invalid volume specification`,
  192. `name|:/foo`: `invalid volume specification`,
  193. `name?:/foo`: `invalid volume specification`,
  194. `name/:/foo`: `invalid volume specification`,
  195. `/foo:rw`: `invalid volume specification`,
  196. `/foo:ro`: `invalid volume specification`,
  197. `con:/foo`: `cannot be a reserved word for Windows filenames`,
  198. `PRN:/foo`: `cannot be a reserved word for Windows filenames`,
  199. `aUx:/foo`: `cannot be a reserved word for Windows filenames`,
  200. `nul:/foo`: `cannot be a reserved word for Windows filenames`,
  201. `com1:/foo`: `cannot be a reserved word for Windows filenames`,
  202. `com2:/foo`: `cannot be a reserved word for Windows filenames`,
  203. `com3:/foo`: `cannot be a reserved word for Windows filenames`,
  204. `com4:/foo`: `cannot be a reserved word for Windows filenames`,
  205. `com5:/foo`: `cannot be a reserved word for Windows filenames`,
  206. `com6:/foo`: `cannot be a reserved word for Windows filenames`,
  207. `com7:/foo`: `cannot be a reserved word for Windows filenames`,
  208. `com8:/foo`: `cannot be a reserved word for Windows filenames`,
  209. `com9:/foo`: `cannot be a reserved word for Windows filenames`,
  210. `lpt1:/foo`: `cannot be a reserved word for Windows filenames`,
  211. `lpt2:/foo`: `cannot be a reserved word for Windows filenames`,
  212. `lpt3:/foo`: `cannot be a reserved word for Windows filenames`,
  213. `lpt4:/foo`: `cannot be a reserved word for Windows filenames`,
  214. `lpt5:/foo`: `cannot be a reserved word for Windows filenames`,
  215. `lpt6:/foo`: `cannot be a reserved word for Windows filenames`,
  216. `lpt7:/foo`: `cannot be a reserved word for Windows filenames`,
  217. `lpt8:/foo`: `cannot be a reserved word for Windows filenames`,
  218. `lpt9:/foo`: `cannot be a reserved word for Windows filenames`,
  219. `\\.\pipe\foo:/foo`: `Linux containers on Windows do not support named pipe mounts`,
  220. },
  221. }
  222. linuxSet := parseMountRawTestSet{
  223. valid: []string{
  224. "/home",
  225. "/home:/home",
  226. "/home:/something/else",
  227. "/with space",
  228. "/home:/with space",
  229. "relative:/absolute-path",
  230. "hostPath:/containerPath:ro",
  231. "/hostPath:/containerPath:rw",
  232. "/rw:/ro",
  233. "/hostPath:/containerPath:shared",
  234. "/hostPath:/containerPath:rshared",
  235. "/hostPath:/containerPath:slave",
  236. "/hostPath:/containerPath:rslave",
  237. "/hostPath:/containerPath:private",
  238. "/hostPath:/containerPath:rprivate",
  239. "/hostPath:/containerPath:ro,shared",
  240. "/hostPath:/containerPath:ro,slave",
  241. "/hostPath:/containerPath:ro,private",
  242. "/hostPath:/containerPath:ro,z,shared",
  243. "/hostPath:/containerPath:ro,Z,slave",
  244. "/hostPath:/containerPath:Z,ro,slave",
  245. "/hostPath:/containerPath:slave,Z,ro",
  246. "/hostPath:/containerPath:Z,slave,ro",
  247. "/hostPath:/containerPath:slave,ro,Z",
  248. "/hostPath:/containerPath:rslave,ro,Z",
  249. "/hostPath:/containerPath:ro,rshared,Z",
  250. "/hostPath:/containerPath:ro,Z,rprivate",
  251. },
  252. invalid: map[string]string{
  253. "": "invalid volume specification",
  254. "./": "mount path must be absolute",
  255. "../": "mount path must be absolute",
  256. "/:../": "mount path must be absolute",
  257. "/:path": "mount path must be absolute",
  258. ":": "invalid volume specification",
  259. "/tmp:": "invalid volume specification",
  260. ":test": "invalid volume specification",
  261. ":/test": "invalid volume specification",
  262. "tmp:": "invalid volume specification",
  263. ":test:": "invalid volume specification",
  264. "::": "invalid volume specification",
  265. ":::": "invalid volume specification",
  266. "/tmp:::": "invalid volume specification",
  267. ":/tmp::": "invalid volume specification",
  268. "/path:rw": "invalid volume specification",
  269. "/path:ro": "invalid volume specification",
  270. "/rw:rw": "invalid volume specification",
  271. "path:ro": "invalid volume specification",
  272. "/path:/path:sw": `invalid mode`,
  273. "/path:/path:rwz": `invalid mode`,
  274. "/path:/path:ro,rshared,rslave": `invalid mode`,
  275. "/path:/path:ro,z,rshared,rslave": `invalid mode`,
  276. "/path:shared": "invalid volume specification",
  277. "/path:slave": "invalid volume specification",
  278. "/path:private": "invalid volume specification",
  279. "name:/absolute-path:shared": "invalid volume specification",
  280. "name:/absolute-path:rshared": "invalid volume specification",
  281. "name:/absolute-path:slave": "invalid volume specification",
  282. "name:/absolute-path:rslave": "invalid volume specification",
  283. "name:/absolute-path:private": "invalid volume specification",
  284. "name:/absolute-path:rprivate": "invalid volume specification",
  285. },
  286. }
  287. linParser := &linuxParser{}
  288. winParser := &windowsParser{}
  289. lcowParser := &lcowParser{}
  290. tester := func(parser Parser, set parseMountRawTestSet) {
  291. for _, path := range set.valid {
  292. if _, err := parser.ParseMountRaw(path, "local"); err != nil {
  293. t.Errorf("ParseMountRaw(`%q`) should succeed: error %q", path, err)
  294. }
  295. }
  296. for path, expectedError := range set.invalid {
  297. if mp, err := parser.ParseMountRaw(path, "local"); err == nil {
  298. t.Errorf("ParseMountRaw(`%q`) should have failed validation. Err '%v' - MP: %v", path, err, mp)
  299. } else {
  300. if !strings.Contains(err.Error(), expectedError) {
  301. t.Errorf("ParseMountRaw(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
  302. }
  303. }
  304. }
  305. }
  306. tester(linParser, linuxSet)
  307. tester(winParser, windowsSet)
  308. tester(lcowParser, lcowSet)
  309. }
  310. // testParseMountRaw is a structure used by TestParseMountRawSplit for
  311. // specifying test cases for the ParseMountRaw() function.
  312. type testParseMountRaw struct {
  313. bind string
  314. driver string
  315. expType mount.Type
  316. expDest string
  317. expSource string
  318. expName string
  319. expDriver string
  320. expRW bool
  321. fail bool
  322. }
  323. func TestParseMountRawSplit(t *testing.T) {
  324. previousProvider := currentFileInfoProvider
  325. defer func() { currentFileInfoProvider = previousProvider }()
  326. currentFileInfoProvider = mockFiProvider{}
  327. windowsCases := []testParseMountRaw{
  328. {`c:\:d:`, "local", mount.TypeBind, `d:`, `c:\`, ``, "", true, false},
  329. {`c:\:d:\`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
  330. {`c:\:d:\:ro`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, false},
  331. {`c:\:d:\:rw`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
  332. {`c:\:d:\:foo`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, true},
  333. {`name:d::rw`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
  334. {`name:d:`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
  335. {`name:d::ro`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", false, false},
  336. {`name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
  337. {`driver/name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
  338. {`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, false},
  339. {`\\.\pipe\foo:c:\foo\bar`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
  340. {`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
  341. }
  342. lcowCases := []testParseMountRaw{
  343. {`c:\:/foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
  344. {`c:\:/foo:ro`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, false},
  345. {`c:\:/foo:rw`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", true, false},
  346. {`c:\:/foo:foo`, "local", mount.TypeBind, `/foo`, `c:\`, ``, "", false, true},
  347. {`name:/foo:rw`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
  348. {`name:/foo`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", true, false},
  349. {`name:/foo:ro`, "local", mount.TypeVolume, `/foo`, ``, `name`, "local", false, false},
  350. {`name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
  351. {`driver/name:/`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
  352. {`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, true},
  353. {`\\.\pipe\foo:/data`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
  354. {`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
  355. }
  356. linuxCases := []testParseMountRaw{
  357. {"/tmp:/tmp1", "", mount.TypeBind, "/tmp1", "/tmp", "", "", true, false},
  358. {"/tmp:/tmp2:ro", "", mount.TypeBind, "/tmp2", "/tmp", "", "", false, false},
  359. {"/tmp:/tmp3:rw", "", mount.TypeBind, "/tmp3", "/tmp", "", "", true, false},
  360. {"/tmp:/tmp4:foo", "", mount.TypeBind, "", "", "", "", false, true},
  361. {"name:/named1", "", mount.TypeVolume, "/named1", "", "name", "", true, false},
  362. {"name:/named2", "external", mount.TypeVolume, "/named2", "", "name", "external", true, false},
  363. {"name:/named3:ro", "local", mount.TypeVolume, "/named3", "", "name", "local", false, false},
  364. {"local/name:/tmp:rw", "", mount.TypeVolume, "/tmp", "", "local/name", "", true, false},
  365. {"/tmp:tmp", "", mount.TypeBind, "", "", "", "", true, true},
  366. }
  367. linParser := &linuxParser{}
  368. winParser := &windowsParser{}
  369. lcowParser := &lcowParser{}
  370. tester := func(parser Parser, cases []testParseMountRaw) {
  371. for i, c := range cases {
  372. t.Logf("case %d", i)
  373. m, err := parser.ParseMountRaw(c.bind, c.driver)
  374. if c.fail {
  375. if err == nil {
  376. t.Errorf("Expected error, was nil, for spec %s\n", c.bind)
  377. }
  378. continue
  379. }
  380. if m == nil || err != nil {
  381. t.Errorf("ParseMountRaw failed for spec '%s', driver '%s', error '%v'", c.bind, c.driver, err.Error())
  382. continue
  383. }
  384. if m.Destination != c.expDest {
  385. t.Errorf("Expected destination '%s, was %s', for spec '%s'", c.expDest, m.Destination, c.bind)
  386. }
  387. if m.Source != c.expSource {
  388. t.Errorf("Expected source '%s', was '%s', for spec '%s'", c.expSource, m.Source, c.bind)
  389. }
  390. if m.Name != c.expName {
  391. t.Errorf("Expected name '%s', was '%s' for spec '%s'", c.expName, m.Name, c.bind)
  392. }
  393. if m.Driver != c.expDriver {
  394. t.Errorf("Expected driver '%s', was '%s', for spec '%s'", c.expDriver, m.Driver, c.bind)
  395. }
  396. if m.RW != c.expRW {
  397. t.Errorf("Expected RW '%v', was '%v' for spec '%s'", c.expRW, m.RW, c.bind)
  398. }
  399. if m.Type != c.expType {
  400. t.Fatalf("Expected type '%s', was '%s', for spec '%s'", c.expType, m.Type, c.bind)
  401. }
  402. }
  403. }
  404. tester(linParser, linuxCases)
  405. tester(winParser, windowsCases)
  406. tester(lcowParser, lcowCases)
  407. }
  408. func TestParseMountSpec(t *testing.T) {
  409. type c struct {
  410. input mount.Mount
  411. expected MountPoint
  412. }
  413. testDir, err := ioutil.TempDir("", "test-mount-config")
  414. if err != nil {
  415. t.Fatal(err)
  416. }
  417. defer os.RemoveAll(testDir)
  418. parser := NewParser(runtime.GOOS)
  419. cases := []c{
  420. {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath, ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
  421. {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, RW: true, Propagation: parser.DefaultPropagationMode()}},
  422. {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()}},
  423. {mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath + string(os.PathSeparator), ReadOnly: true}, MountPoint{Type: mount.TypeBind, Source: testDir, Destination: testDestinationPath, Propagation: parser.DefaultPropagationMode()}},
  424. {mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()}},
  425. {mount.Mount{Type: mount.TypeVolume, Target: testDestinationPath + string(os.PathSeparator)}, MountPoint{Type: mount.TypeVolume, Destination: testDestinationPath, RW: true, CopyData: parser.DefaultCopyMode()}},
  426. }
  427. for i, c := range cases {
  428. t.Logf("case %d", i)
  429. mp, err := parser.ParseMountSpec(c.input)
  430. if err != nil {
  431. t.Error(err)
  432. }
  433. if c.expected.Type != mp.Type {
  434. t.Errorf("Expected mount types to match. Expected: '%s', Actual: '%s'", c.expected.Type, mp.Type)
  435. }
  436. if c.expected.Destination != mp.Destination {
  437. t.Errorf("Expected mount destination to match. Expected: '%s', Actual: '%s'", c.expected.Destination, mp.Destination)
  438. }
  439. if c.expected.Source != mp.Source {
  440. t.Errorf("Expected mount source to match. Expected: '%s', Actual: '%s'", c.expected.Source, mp.Source)
  441. }
  442. if c.expected.RW != mp.RW {
  443. t.Errorf("Expected mount writable to match. Expected: '%v', Actual: '%v'", c.expected.RW, mp.RW)
  444. }
  445. if c.expected.Propagation != mp.Propagation {
  446. t.Errorf("Expected mount propagation to match. Expected: '%v', Actual: '%s'", c.expected.Propagation, mp.Propagation)
  447. }
  448. if c.expected.Driver != mp.Driver {
  449. t.Errorf("Expected mount driver to match. Expected: '%v', Actual: '%s'", c.expected.Driver, mp.Driver)
  450. }
  451. if c.expected.CopyData != mp.CopyData {
  452. t.Errorf("Expected mount copy data to match. Expected: '%v', Actual: '%v'", c.expected.CopyData, mp.CopyData)
  453. }
  454. }
  455. }