Browse code

Windows: Volume integration tests

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2015/09/24 08:04:51
Showing 8 changed files
... ...
@@ -595,7 +595,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist
595 595
 
596 596
 			repoInfo, err := registry.ParseRepositoryInfo(repo)
597 597
 			if err != nil {
598
-				return nil, nil, fmt.Errorf("unable to parse repository info: %v", err)
598
+				return nil, nil, fmt.Errorf("unable to parse repository info %q: %v", repo, err)
599 599
 			}
600 600
 
601 601
 			ref := registry.ParseReference(tag)
... ...
@@ -3,7 +3,7 @@ package main
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"net/http"
6
-	"path"
6
+	"path/filepath"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9 9
 	"github.com/docker/docker/pkg/integration/checker"
... ...
@@ -11,8 +11,12 @@ import (
11 11
 )
12 12
 
13 13
 func (s *DockerSuite) TestVolumesApiList(c *check.C) {
14
-	testRequires(c, DaemonIsLinux)
15
-	dockerCmd(c, "run", "-d", "-v", "/foo", "busybox")
14
+	prefix := ""
15
+	if daemonPlatform == "windows" {
16
+		prefix = "c:"
17
+		testRequires(c, WindowsDaemonSupportsVolumes)
18
+	}
19
+	dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "busybox")
16 20
 
17 21
 	status, b, err := sockRequest("GET", "/volumes", nil)
18 22
 	c.Assert(err, checker.IsNil)
... ...
@@ -25,7 +29,9 @@ func (s *DockerSuite) TestVolumesApiList(c *check.C) {
25 25
 }
26 26
 
27 27
 func (s *DockerSuite) TestVolumesApiCreate(c *check.C) {
28
-	testRequires(c, DaemonIsLinux)
28
+	if daemonPlatform == "windows" {
29
+		testRequires(c, WindowsDaemonSupportsVolumes)
30
+	}
29 31
 	config := types.VolumeCreateRequest{
30 32
 		Name: "test",
31 33
 	}
... ...
@@ -37,12 +43,16 @@ func (s *DockerSuite) TestVolumesApiCreate(c *check.C) {
37 37
 	err = json.Unmarshal(b, &vol)
38 38
 	c.Assert(err, checker.IsNil)
39 39
 
40
-	c.Assert(path.Base(path.Dir(vol.Mountpoint)), checker.Equals, config.Name)
40
+	c.Assert(filepath.Base(filepath.Dir(vol.Mountpoint)), checker.Equals, config.Name)
41 41
 }
42 42
 
43 43
 func (s *DockerSuite) TestVolumesApiRemove(c *check.C) {
44
-	testRequires(c, DaemonIsLinux)
45
-	dockerCmd(c, "run", "-d", "-v", "/foo", "--name=test", "busybox")
44
+	prefix := ""
45
+	if daemonPlatform == "windows" {
46
+		testRequires(c, WindowsDaemonSupportsVolumes)
47
+		prefix = "c:"
48
+	}
49
+	dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "--name=test", "busybox")
46 50
 
47 51
 	status, b, err := sockRequest("GET", "/volumes", nil)
48 52
 	c.Assert(err, checker.IsNil)
... ...
@@ -65,7 +75,9 @@ func (s *DockerSuite) TestVolumesApiRemove(c *check.C) {
65 65
 }
66 66
 
67 67
 func (s *DockerSuite) TestVolumesApiInspect(c *check.C) {
68
-	testRequires(c, DaemonIsLinux)
68
+	if daemonPlatform == "windows" {
69
+		testRequires(c, WindowsDaemonSupportsVolumes)
70
+	}
69 71
 	config := types.VolumeCreateRequest{
70 72
 		Name: "test",
71 73
 	}
... ...
@@ -425,7 +425,7 @@ func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) {
425 425
 }
426 426
 
427 427
 func (s *DockerSuite) TestRunExecDir(c *check.C) {
428
-	testRequires(c, SameHostDaemon)
428
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
429 429
 
430 430
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
431 431
 	id := strings.TrimSpace(out)
... ...
@@ -489,8 +489,7 @@ func (s *DockerSuite) TestRunExecDir(c *check.C) {
489 489
 }
490 490
 
491 491
 func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) {
492
-	testRequires(c, SameHostDaemon)
493
-
492
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
494 493
 	for _, fn := range []string{"resolv.conf", "hosts"} {
495 494
 		deleteAllContainers()
496 495
 
... ...
@@ -223,15 +223,29 @@ func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
223 223
 
224 224
 // Regression test for #4979
225 225
 func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
226
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
227
-	// not support volumes
228
-	testRequires(c, DaemonIsLinux)
229
-	out, exitCode := dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")
226
+
227
+	var (
228
+		out      string
229
+		exitCode int
230
+	)
231
+
232
+	// Create a file in a volume
233
+	if daemonPlatform == "windows" {
234
+		testRequires(c, WindowsDaemonSupportsVolumes)
235
+		out, exitCode = dockerCmd(c, "run", "--name", "test-data", "--volume", `c:\some\dir`, WindowsBaseImage, `cmd /c echo hello > c:\some\dir\file`)
236
+	} else {
237
+		out, exitCode = dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")
238
+	}
230 239
 	if exitCode != 0 {
231 240
 		c.Fatal("1", out, exitCode)
232 241
 	}
233 242
 
234
-	out, exitCode = dockerCmd(c, "run", "--volumes-from", "test-data", "busybox", "cat", "/some/dir/file")
243
+	// Read the file from another container using --volumes-from to access the volume in the second container
244
+	if daemonPlatform == "windows" {
245
+		out, exitCode = dockerCmd(c, "run", "--volumes-from", "test-data", WindowsBaseImage, `cmd /c type c:\some\dir\file`)
246
+	} else {
247
+		out, exitCode = dockerCmd(c, "run", "--volumes-from", "test-data", "busybox", "cat", "/some/dir/file")
248
+	}
235 249
 	if exitCode != 0 {
236 250
 		c.Fatal("2", out, exitCode)
237 251
 	}
... ...
@@ -240,9 +254,17 @@ func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
240 240
 // Volume path is a symlink which also exists on the host, and the host side is a file not a dir
241 241
 // But the volume call is just a normal volume, not a bind mount
242 242
 func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
243
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
244
-	// not support volumes
245
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NativeExecDriver)
243
+	var (
244
+		dockerFile    string
245
+		containerPath string
246
+		cmd           string
247
+	)
248
+	if daemonPlatform == "windows" {
249
+		testRequires(c, SameHostDaemon, WindowsDaemonSupportsVolumes)
250
+	} else {
251
+		testRequires(c, SameHostDaemon, NativeExecDriver)
252
+	}
253
+
246 254
 	name := "test-volume-symlink"
247 255
 
248 256
 	dir, err := ioutil.TempDir("", name)
... ...
@@ -257,52 +279,84 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
257 257
 	}
258 258
 	f.Close()
259 259
 
260
-	dockerFile := fmt.Sprintf("FROM busybox\nRUN mkdir -p %s\nRUN ln -s %s /test", dir, dir)
260
+	if daemonPlatform == "windows" {
261
+		dockerFile = fmt.Sprintf("FROM %s\nRUN mkdir %s\nRUN mklink /D c:\\test %s", WindowsBaseImage, dir, dir)
262
+		containerPath = `c:\test\test`
263
+		cmd = "tasklist"
264
+	} else {
265
+		dockerFile = fmt.Sprintf("FROM busybox\nRUN mkdir -p %s\nRUN ln -s %s /test", dir, dir)
266
+		containerPath = "/test/test"
267
+		cmd = "true"
268
+	}
261 269
 	if _, err := buildImage(name, dockerFile, false); err != nil {
262 270
 		c.Fatal(err)
263 271
 	}
264 272
 
265
-	dockerCmd(c, "run", "-v", "/test/test", name)
273
+	dockerCmd(c, "run", "-v", containerPath, name, cmd)
266 274
 }
267 275
 
268 276
 func (s *DockerSuite) TestRunVolumesMountedAsReadonly(c *check.C) {
269
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
270
-	// not support volumes
277
+	// TODO Windows (Post TP4): This test cannot run on a Windows daemon as
278
+	// Windows does not support read-only bind mounts.
271 279
 	testRequires(c, DaemonIsLinux)
272 280
 	if _, code, err := dockerCmdWithError("run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile"); err == nil || code == 0 {
273 281
 		c.Fatalf("run should fail because volume is ro: exit code %d", code)
274 282
 	}
275 283
 }
276 284
 
277
-func (s *DockerSuite) TestRunVolumesFromInReadonlyMode(c *check.C) {
278
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
279
-	// not support volumes
285
+func (s *DockerSuite) TestRunVolumesFromInReadonlyModeFails(c *check.C) {
286
+	// TODO Windows (Post TP4): This test cannot run on a Windows daemon as
287
+	// Windows does not support read-only bind mounts. Modified for when ro is supported.
280 288
 	testRequires(c, DaemonIsLinux)
281
-	dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "true")
289
+	var (
290
+		volumeDir string
291
+		fileInVol string
292
+	)
293
+	if daemonPlatform == "windows" {
294
+		testRequires(c, WindowsDaemonSupportsVolumes)
295
+		volumeDir = `c:/test` // Forward-slash as using busybox
296
+		fileInVol = `c:/test/file`
297
+	} else {
298
+		testRequires(c, DaemonIsLinux)
299
+		volumeDir = "/test"
300
+		fileInVol = `/test/file`
301
+	}
302
+	dockerCmd(c, "run", "--name", "parent", "-v", volumeDir, "busybox", "true")
282 303
 
283
-	if _, code, err := dockerCmdWithError("run", "--volumes-from", "parent:ro", "busybox", "touch", "/test/file"); err == nil || code == 0 {
304
+	if _, code, err := dockerCmdWithError("run", "--volumes-from", "parent:ro", "busybox", "touch", fileInVol); err == nil || code == 0 {
284 305
 		c.Fatalf("run should fail because volume is ro: exit code %d", code)
285 306
 	}
286 307
 }
287 308
 
288 309
 // Regression test for #1201
289 310
 func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *check.C) {
290
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
291
-	// not support volumes
292
-	testRequires(c, DaemonIsLinux)
293
-	dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "true")
294
-	dockerCmd(c, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file")
311
+	var (
312
+		volumeDir string
313
+		fileInVol string
314
+	)
315
+	if daemonPlatform == "windows" {
316
+		testRequires(c, WindowsDaemonSupportsVolumes)
317
+		volumeDir = `c:/test` // Forward-slash as using busybox
318
+		fileInVol = `c:/test/file`
319
+	} else {
320
+		testRequires(c, DaemonIsLinux)
321
+		volumeDir = "/test"
322
+		fileInVol = "/test/file"
323
+	}
324
+
325
+	dockerCmd(c, "run", "--name", "parent", "-v", volumeDir, "busybox", "true")
326
+	dockerCmd(c, "run", "--volumes-from", "parent:rw", "busybox", "touch", fileInVol)
295 327
 
296
-	if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", "/test/file"); err == nil || !strings.Contains(out, "invalid mode: bar") {
297
-		c.Fatalf("running --volumes-from foo:bar should have failed with invalid mode: %q", out)
328
+	if out, _, err := dockerCmdWithError("run", "--volumes-from", "parent:bar", "busybox", "touch", fileInVol); err == nil || !strings.Contains(out, "invalid mode: bar") {
329
+		c.Fatalf("running --volumes-from parent:bar should have failed with invalid mount mode: %q", out)
298 330
 	}
299 331
 
300
-	dockerCmd(c, "run", "--volumes-from", "parent", "busybox", "touch", "/test/file")
332
+	dockerCmd(c, "run", "--volumes-from", "parent", "busybox", "touch", fileInVol)
301 333
 }
302 334
 
303 335
 func (s *DockerSuite) TestVolumesFromGetsProperMode(c *check.C) {
304
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
305
-	// not support volumes
336
+	// TODO Windows: This test cannot yet run on a Windows daemon as Windows does
337
+	// not support read-only bind mounts as at TP4
306 338
 	testRequires(c, DaemonIsLinux)
307 339
 	dockerCmd(c, "run", "--name", "parent", "-v", "/test:/test:ro", "busybox", "true")
308 340
 
... ...
@@ -321,36 +375,54 @@ func (s *DockerSuite) TestVolumesFromGetsProperMode(c *check.C) {
321 321
 
322 322
 // Test for GH#10618
323 323
 func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
324
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
325
-	// not support volumes
326
-	testRequires(c, DaemonIsLinux)
327
-	mountstr1 := randomTmpDirPath("test1", daemonPlatform) + ":/someplace"
328
-	mountstr2 := randomTmpDirPath("test2", daemonPlatform) + ":/someplace"
324
+	path1 := randomTmpDirPath("test1", daemonPlatform)
325
+	path2 := randomTmpDirPath("test2", daemonPlatform)
326
+
327
+	someplace := ":/someplace"
328
+	if daemonPlatform == "windows" {
329
+		// Windows requires that the source directory exists before calling HCS
330
+		testRequires(c, SameHostDaemon, WindowsDaemonSupportsVolumes)
331
+		someplace = `:c:\someplace`
332
+		if err := os.MkdirAll(path1, 0755); err != nil {
333
+			c.Fatalf("Failed to create %s: %q", path1, err)
334
+		}
335
+		defer os.RemoveAll(path1)
336
+		if err := os.MkdirAll(path2, 0755); err != nil {
337
+			c.Fatalf("Failed to create %s: %q", path1, err)
338
+		}
339
+		defer os.RemoveAll(path2)
340
+	}
341
+	mountstr1 := path1 + someplace
342
+	mountstr2 := path2 + someplace
329 343
 
330 344
 	if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil {
331 345
 		c.Fatal("Expected error about duplicate volume definitions")
332 346
 	} else {
333 347
 		if !strings.Contains(out, "Duplicate bind mount") {
334
-			c.Fatalf("Expected 'duplicate volume' error, got %v", err)
348
+			c.Fatalf("Expected 'duplicate volume' error, got %v", out)
335 349
 		}
336 350
 	}
337 351
 }
338 352
 
339 353
 // Test for #1351
340 354
 func (s *DockerSuite) TestRunApplyVolumesFromBeforeVolumes(c *check.C) {
341
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
342
-	// not support volumes
343
-	testRequires(c, DaemonIsLinux)
344
-	dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "touch", "/test/foo")
345
-	dockerCmd(c, "run", "--volumes-from", "parent", "-v", "/test", "busybox", "cat", "/test/foo")
355
+	prefix := ""
356
+	if daemonPlatform == "windows" {
357
+		testRequires(c, WindowsDaemonSupportsVolumes)
358
+		prefix = `c:`
359
+	}
360
+	dockerCmd(c, "run", "--name", "parent", "-v", prefix+"/test", "busybox", "touch", prefix+"/test/foo")
361
+	dockerCmd(c, "run", "--volumes-from", "parent", "-v", prefix+"/test", "busybox", "cat", prefix+"/test/foo")
346 362
 }
347 363
 
348 364
 func (s *DockerSuite) TestRunMultipleVolumesFrom(c *check.C) {
349
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
350
-	// not support volumes
351
-	testRequires(c, DaemonIsLinux)
352
-	dockerCmd(c, "run", "--name", "parent1", "-v", "/test", "busybox", "touch", "/test/foo")
353
-	dockerCmd(c, "run", "--name", "parent2", "-v", "/other", "busybox", "touch", "/other/bar")
365
+	prefix := ""
366
+	if daemonPlatform == "windows" {
367
+		testRequires(c, WindowsDaemonSupportsVolumes)
368
+		prefix = `c:`
369
+	}
370
+	dockerCmd(c, "run", "--name", "parent1", "-v", prefix+"/test", "busybox", "touch", prefix+"/test/foo")
371
+	dockerCmd(c, "run", "--name", "parent2", "-v", prefix+"/other", "busybox", "touch", prefix+"/other/bar")
354 372
 	dockerCmd(c, "run", "--volumes-from", "parent1", "--volumes-from", "parent2", "busybox", "sh", "-c", "cat /test/foo && cat /other/bar")
355 373
 }
356 374
 
... ...
@@ -375,17 +447,18 @@ func (s *DockerSuite) TestRunVerifyContainerID(c *check.C) {
375 375
 
376 376
 // Test that creating a container with a volume doesn't crash. Regression test for #995.
377 377
 func (s *DockerSuite) TestRunCreateVolume(c *check.C) {
378
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
379
-	// not support volumes
380
-	testRequires(c, DaemonIsLinux)
381
-	dockerCmd(c, "run", "-v", "/var/lib/data", "busybox", "true")
378
+	prefix := ""
379
+	if daemonPlatform == "windows" {
380
+		testRequires(c, WindowsDaemonSupportsVolumes)
381
+		prefix = `c:`
382
+	}
383
+	dockerCmd(c, "run", "-v", prefix+"/var/lib/data", "busybox", "true")
382 384
 }
383 385
 
384 386
 // Test that creating a volume with a symlink in its path works correctly. Test for #5152.
385 387
 // Note that this bug happens only with symlinks with a target that starts with '/'.
386 388
 func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) {
387
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
388
-	// not support volumes
389
+	// Cannot run on Windows as relies on Linux-specific functionality (sh -c mount...)
389 390
 	testRequires(c, DaemonIsLinux)
390 391
 	image := "docker-test-createvolumewithsymlink"
391 392
 
... ...
@@ -421,27 +494,39 @@ func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) {
421 421
 
422 422
 // Tests that a volume path that has a symlink exists in a container mounting it with `--volumes-from`.
423 423
 func (s *DockerSuite) TestRunVolumesFromSymlinkPath(c *check.C) {
424
-	// TODO Windows: This test cannot run on a Windows daemon as Windows does
425
-	// not support volumes
426
-	testRequires(c, DaemonIsLinux)
424
+	if daemonPlatform == "windows" {
425
+		testRequires(c, WindowsDaemonSupportsVolumes)
426
+	}
427
+
427 428
 	name := "docker-test-volumesfromsymlinkpath"
429
+	prefix := ""
430
+	dfContents := `FROM busybox
431
+		RUN ln -s home /foo
432
+		VOLUME ["/foo/bar"]`
433
+
434
+	if daemonPlatform == "windows" {
435
+		prefix = `c:`
436
+		dfContents = `FROM ` + WindowsBaseImage + `
437
+	    RUN mkdir c:\home
438
+		RUN mklink /D c:\foo c:\home 
439
+		VOLUME ["c:/foo/bar"]
440
+		ENTRYPOINT c:\windows\system32\cmd.exe`
441
+	}
428 442
 
429 443
 	buildCmd := exec.Command(dockerBinary, "build", "-t", name, "-")
430
-	buildCmd.Stdin = strings.NewReader(`FROM busybox
431
-		RUN ln -s home /foo
432
-		VOLUME ["/foo/bar"]`)
444
+	buildCmd.Stdin = strings.NewReader(dfContents)
433 445
 	buildCmd.Dir = workingDirectory
434 446
 	err := buildCmd.Run()
435 447
 	if err != nil {
436 448
 		c.Fatalf("could not build 'docker-test-volumesfromsymlinkpath': %v", err)
437 449
 	}
438 450
 
439
-	_, exitCode, err := dockerCmdWithError("run", "--name", "test-volumesfromsymlinkpath", name)
451
+	out, exitCode, err := dockerCmdWithError("run", "--name", "test-volumesfromsymlinkpath", name)
440 452
 	if err != nil || exitCode != 0 {
441
-		c.Fatalf("[run] (volume) err: %v, exitcode: %d", err, exitCode)
453
+		c.Fatalf("[run] (volume) err: %v, exitcode: %d, out: %s", err, exitCode, out)
442 454
 	}
443 455
 
444
-	_, exitCode, err = dockerCmdWithError("run", "--volumes-from", "test-volumesfromsymlinkpath", "busybox", "sh", "-c", "ls /foo | grep -q bar")
456
+	_, exitCode, err = dockerCmdWithError("run", "--volumes-from", "test-volumesfromsymlinkpath", "busybox", "sh", "-c", "ls "+prefix+"/foo | grep -q bar")
445 457
 	if err != nil || exitCode != 0 {
446 458
 		c.Fatalf("[run] err: %v, exitcode: %d", err, exitCode)
447 459
 	}
... ...
@@ -544,6 +629,15 @@ func (s *DockerSuite) TestRunUserNotFound(c *check.C) {
544 544
 }
545 545
 
546 546
 func (s *DockerSuite) TestRunTwoConcurrentContainers(c *check.C) {
547
+	// TODO Windows. There are two bugs in TP4 which means this test cannot
548
+	// be reliably enabled. The first is a race condition where sometimes
549
+	// HCS CreateComputeSystem() will fail "Invalid class string". #4985252 and
550
+	// #4493430.
551
+	//
552
+	// The second, which is seen more readily by increasing the number of concurrent
553
+	// containers to 5 or more, is that CSRSS hangs. This may fixed in the TP4 ZDP.
554
+	// #4898773.
555
+	testRequires(c, DaemonIsLinux)
547 556
 	sleepTime := "2"
548 557
 	if daemonPlatform == "windows" {
549 558
 		sleepTime = "5" // Make more reliable on Windows
... ...
@@ -1005,14 +1099,24 @@ func (s *DockerSuite) TestRunRootWorkdir(c *check.C) {
1005 1005
 }
1006 1006
 
1007 1007
 func (s *DockerSuite) TestRunAllowBindMountingRoot(c *check.C) {
1008
-	testRequires(c, DaemonIsLinux)
1009
-	dockerCmd(c, "run", "-v", "/:/host", "busybox", "ls", "/host")
1008
+	if daemonPlatform == "windows" {
1009
+		testRequires(c, WindowsDaemonSupportsVolumes)
1010
+		// Windows busybox will fail with Permission Denied on items such as pagefile.sys
1011
+		dockerCmd(c, "run", "-v", `c:\:c:\host`, WindowsBaseImage, "cmd", "-c", "dir", `c:\host`)
1012
+	} else {
1013
+		dockerCmd(c, "run", "-v", "/:/host", "busybox", "ls", "/host")
1014
+	}
1010 1015
 }
1011 1016
 
1012 1017
 func (s *DockerSuite) TestRunDisallowBindMountingRootToRoot(c *check.C) {
1013
-	// Not applicable on Windows as Windows does not support volumes
1014
-	testRequires(c, DaemonIsLinux)
1015
-	out, _, err := dockerCmdWithError("run", "-v", "/:/", "busybox", "ls", "/host")
1018
+	mount := "/:/"
1019
+	targetDir := "/host"
1020
+	if daemonPlatform == "windows" {
1021
+		testRequires(c, WindowsDaemonSupportsVolumes)
1022
+		mount = `c:\:c\`
1023
+		targetDir = "c:/host" // Forward slash as using busybox
1024
+	}
1025
+	out, _, err := dockerCmdWithError("run", "-v", mount, "busybox", "ls", targetDir)
1016 1026
 	if err == nil {
1017 1027
 		c.Fatal(out, err)
1018 1028
 	}
... ...
@@ -1466,7 +1570,7 @@ func (s *DockerSuite) TestRunState(c *check.C) {
1466 1466
 
1467 1467
 // Test for #1737
1468 1468
 func (s *DockerSuite) TestRunCopyVolumeUidGid(c *check.C) {
1469
-	// Not applicable on Windows as it does not support volumes, uid or gid
1469
+	// Not applicable on Windows as it does not support uid or gid in this way
1470 1470
 	testRequires(c, DaemonIsLinux)
1471 1471
 	name := "testrunvolumesuidgid"
1472 1472
 	_, err := buildImage(name,
... ...
@@ -1489,7 +1593,8 @@ func (s *DockerSuite) TestRunCopyVolumeUidGid(c *check.C) {
1489 1489
 
1490 1490
 // Test for #1582
1491 1491
 func (s *DockerSuite) TestRunCopyVolumeContent(c *check.C) {
1492
-	// Not applicable on Windows as it does not support volumes
1492
+	// TODO Windows, post TP4. Windows does not yet support volume functionality
1493
+	// that copies from the image to the volume.
1493 1494
 	testRequires(c, DaemonIsLinux)
1494 1495
 	name := "testruncopyvolumecontent"
1495 1496
 	_, err := buildImage(name,
... ...
@@ -1712,10 +1817,12 @@ func (s *DockerSuite) TestRunEntrypoint(c *check.C) {
1712 1712
 }
1713 1713
 
1714 1714
 func (s *DockerSuite) TestRunBindMounts(c *check.C) {
1715
-	// /tmp gets permission denied
1716
-	testRequires(c, NotUserNamespace)
1717
-	// Cannot run on Windows as Windows does not support volumes
1718
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
1715
+	testRequires(c, SameHostDaemon)
1716
+	if daemonPlatform == "windows" {
1717
+		testRequires(c, WindowsDaemonSupportsVolumes)
1718
+	} else {
1719
+		testRequires(c, DaemonIsLinux, NotUserNamespace)
1720
+	}
1719 1721
 
1720 1722
 	tmpDir, err := ioutil.TempDir("", "docker-test-container")
1721 1723
 	if err != nil {
... ...
@@ -1725,14 +1832,21 @@ func (s *DockerSuite) TestRunBindMounts(c *check.C) {
1725 1725
 	defer os.RemoveAll(tmpDir)
1726 1726
 	writeFile(path.Join(tmpDir, "touch-me"), "", c)
1727 1727
 
1728
-	// Test reading from a read-only bind mount
1729
-	out, _ := dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox", "ls", "/tmp")
1730
-	if !strings.Contains(out, "touch-me") {
1731
-		c.Fatal("Container failed to read from bind mount")
1728
+	// TODO Windows Post TP4. Windows does not yet support :ro binds
1729
+	if daemonPlatform != "windows" {
1730
+		// Test reading from a read-only bind mount
1731
+		out, _ := dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox", "ls", "/tmp")
1732
+		if !strings.Contains(out, "touch-me") {
1733
+			c.Fatal("Container failed to read from bind mount")
1734
+		}
1732 1735
 	}
1733 1736
 
1734 1737
 	// test writing to bind mount
1735
-	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "busybox", "touch", "/tmp/holla")
1738
+	if daemonPlatform == "windows" {
1739
+		dockerCmd(c, "run", "-v", fmt.Sprintf(`%s:c:\tmp:rw`, tmpDir), "busybox", "touch", "c:/tmp/holla")
1740
+	} else {
1741
+		dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "busybox", "touch", "/tmp/holla")
1742
+	}
1736 1743
 
1737 1744
 	readFile(path.Join(tmpDir, "holla"), c) // Will fail if the file doesn't exist
1738 1745
 
... ...
@@ -1742,12 +1856,15 @@ func (s *DockerSuite) TestRunBindMounts(c *check.C) {
1742 1742
 		c.Fatal("Container bind mounted illegal directory")
1743 1743
 	}
1744 1744
 
1745
-	// test mount a file
1746
-	dockerCmd(c, "run", "-v", fmt.Sprintf("%s/holla:/tmp/holla:rw", tmpDir), "busybox", "sh", "-c", "echo -n 'yotta' > /tmp/holla")
1747
-	content := readFile(path.Join(tmpDir, "holla"), c) // Will fail if the file doesn't exist
1748
-	expected := "yotta"
1749
-	if content != expected {
1750
-		c.Fatalf("Output should be %q, actual out: %q", expected, content)
1745
+	// Windows does not (and likely never will) support mounting a single file
1746
+	if daemonPlatform != "windows" {
1747
+		// test mount a file
1748
+		dockerCmd(c, "run", "-v", fmt.Sprintf("%s/holla:/tmp/holla:rw", tmpDir), "busybox", "sh", "-c", "echo -n 'yotta' > /tmp/holla")
1749
+		content := readFile(path.Join(tmpDir, "holla"), c) // Will fail if the file doesn't exist
1750
+		expected := "yotta"
1751
+		if content != expected {
1752
+			c.Fatalf("Output should be %q, actual out: %q", expected, content)
1753
+		}
1751 1754
 	}
1752 1755
 }
1753 1756
 
... ...
@@ -1806,16 +1923,11 @@ func (s *DockerSuite) TestRunCidFileCheckIDLength(c *check.C) {
1806 1806
 }
1807 1807
 
1808 1808
 func (s *DockerSuite) TestRunSetMacAddress(c *check.C) {
1809
-	// TODO Windows. Test modified to be theoretically Windows compatible,
1810
-	// but the version of busybox being used on Windows doesn't handle
1811
-	// sh -c ipconfig -all (or /all). It ignores the *all bit. This could
1812
-	// be a bug in busybox.
1813
-	testRequires(c, DaemonIsLinux)
1814 1809
 	mac := "12:34:56:78:9a:bc"
1815
-
1816 1810
 	var out string
1817 1811
 	if daemonPlatform == "windows" {
1818 1812
 		out, _ = dockerCmd(c, "run", "-i", "--rm", fmt.Sprintf("--mac-address=%s", mac), "busybox", "sh", "-c", "ipconfig /all | grep 'Physical Address' | awk '{print $12}'")
1813
+		mac = strings.Replace(strings.ToUpper(mac), ":", "-", -1) // To Windows-style MACs
1819 1814
 	} else {
1820 1815
 		out, _ = dockerCmd(c, "run", "-i", "--rm", fmt.Sprintf("--mac-address=%s", mac), "busybox", "/bin/sh", "-c", "ip link show eth0 | tail -1 | awk '{print $2}'")
1821 1816
 	}
... ...
@@ -1912,10 +2024,13 @@ func (s *DockerSuite) TestRunAllocatePortInReservedRange(c *check.C) {
1912 1912
 
1913 1913
 // Regression test for #7792
1914 1914
 func (s *DockerSuite) TestRunMountOrdering(c *check.C) {
1915
-	// tmp gets permission denied
1916
-	testRequires(c, NotUserNamespace)
1917
-	// Not applicable on Windows as Windows does not support volumes
1918
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
1915
+	// TODO Windows: Post TP4. Updated, but Windows does not support nested mounts currently.
1916
+	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
1917
+	prefix := ""
1918
+	if daemonPlatform == "windows" {
1919
+		testRequires(c, WindowsDaemonSupportsVolumes)
1920
+		prefix = "c:"
1921
+	}
1919 1922
 
1920 1923
 	tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test")
1921 1924
 	if err != nil {
... ...
@@ -1948,20 +2063,23 @@ func (s *DockerSuite) TestRunMountOrdering(c *check.C) {
1948 1948
 	}
1949 1949
 
1950 1950
 	dockerCmd(c, "run",
1951
-		"-v", fmt.Sprintf("%s:/tmp", tmpDir),
1952
-		"-v", fmt.Sprintf("%s:/tmp/foo", fooDir),
1953
-		"-v", fmt.Sprintf("%s:/tmp/tmp2", tmpDir2),
1954
-		"-v", fmt.Sprintf("%s:/tmp/tmp2/foo", fooDir),
1951
+		"-v", fmt.Sprintf("%s:"+prefix+"/tmp", tmpDir),
1952
+		"-v", fmt.Sprintf("%s:"+prefix+"/tmp/foo", fooDir),
1953
+		"-v", fmt.Sprintf("%s:"+prefix+"/tmp/tmp2", tmpDir2),
1954
+		"-v", fmt.Sprintf("%s:"+prefix+"/tmp/tmp2/foo", fooDir),
1955 1955
 		"busybox:latest", "sh", "-c",
1956
-		"ls /tmp/touch-me && ls /tmp/foo/touch-me && ls /tmp/tmp2/touch-me && ls /tmp/tmp2/foo/touch-me")
1956
+		"ls "+prefix+"/tmp/touch-me && ls "+prefix+"/tmp/foo/touch-me && ls "+prefix+"/tmp/tmp2/touch-me && ls "+prefix+"/tmp/tmp2/foo/touch-me")
1957 1957
 }
1958 1958
 
1959 1959
 // Regression test for https://github.com/docker/docker/issues/8259
1960 1960
 func (s *DockerSuite) TestRunReuseBindVolumeThatIsSymlink(c *check.C) {
1961
-	// /tmp gets permission denied
1962
-	testRequires(c, NotUserNamespace)
1963 1961
 	// Not applicable on Windows as Windows does not support volumes
1964
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
1962
+	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
1963
+	prefix := ""
1964
+	if daemonPlatform == "windows" {
1965
+		testRequires(c, WindowsDaemonSupportsVolumes)
1966
+		prefix = "c:"
1967
+	}
1965 1968
 
1966 1969
 	tmpDir, err := ioutil.TempDir(os.TempDir(), "testlink")
1967 1970
 	if err != nil {
... ...
@@ -1976,16 +2094,17 @@ func (s *DockerSuite) TestRunReuseBindVolumeThatIsSymlink(c *check.C) {
1976 1976
 	defer os.RemoveAll(linkPath)
1977 1977
 
1978 1978
 	// Create first container
1979
-	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
1979
+	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:"+prefix+"/tmp/test", linkPath), "busybox", "ls", prefix+"/tmp/test")
1980 1980
 
1981 1981
 	// Create second container with same symlinked path
1982 1982
 	// This will fail if the referenced issue is hit with a "Volume exists" error
1983
-	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
1983
+	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:"+prefix+"/tmp/test", linkPath), "busybox", "ls", prefix+"/tmp/test")
1984 1984
 }
1985 1985
 
1986 1986
 //GH#10604: Test an "/etc" volume doesn't overlay special bind mounts in container
1987 1987
 func (s *DockerSuite) TestRunCreateVolumeEtc(c *check.C) {
1988
-	// Not applicable on Windows as Windows does not support volumes
1988
+	// While Windows supports volumes, it does not support --add-host hence
1989
+	// this test is not applicable on Windows.
1989 1990
 	testRequires(c, DaemonIsLinux)
1990 1991
 	out, _ := dockerCmd(c, "run", "--dns=127.0.0.1", "-v", "/etc", "busybox", "cat", "/etc/resolv.conf")
1991 1992
 	if !strings.Contains(out, "nameserver 127.0.0.1") {
... ...
@@ -2005,7 +2124,8 @@ func (s *DockerSuite) TestRunCreateVolumeEtc(c *check.C) {
2005 2005
 }
2006 2006
 
2007 2007
 func (s *DockerSuite) TestVolumesNoCopyData(c *check.C) {
2008
-	// Not applicable on Windows as Windows does not support volumes
2008
+	// TODO Windows (Post TP4). Windows does not support volumes which
2009
+	// are pre-populated such as is built in the dockerfile used in this test.
2009 2010
 	testRequires(c, DaemonIsLinux)
2010 2011
 	if _, err := buildImage("dataimage",
2011 2012
 		`FROM busybox
... ...
@@ -2041,37 +2161,42 @@ func (s *DockerSuite) TestRunNoOutputFromPullInStdout(c *check.C) {
2041 2041
 }
2042 2042
 
2043 2043
 func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
2044
-	// Not applicable on Windows as Windows does not support volumes
2045
-	testRequires(c, DaemonIsLinux)
2044
+	prefix := ""
2045
+	slash := `/`
2046
+	if daemonPlatform == "windows" {
2047
+		testRequires(c, WindowsDaemonSupportsVolumes)
2048
+		prefix = "c:"
2049
+		slash = `\`
2050
+	}
2046 2051
 	if _, err := buildImage("run_volumes_clean_paths",
2047 2052
 		`FROM busybox
2048
-		VOLUME /foo/`,
2053
+		VOLUME `+prefix+`/foo/`,
2049 2054
 		true); err != nil {
2050 2055
 		c.Fatal(err)
2051 2056
 	}
2052 2057
 
2053
-	dockerCmd(c, "run", "-v", "/foo", "-v", "/bar/", "--name", "dark_helmet", "run_volumes_clean_paths")
2058
+	dockerCmd(c, "run", "-v", prefix+"/foo", "-v", prefix+"/bar/", "--name", "dark_helmet", "run_volumes_clean_paths")
2054 2059
 
2055
-	out, err := inspectMountSourceField("dark_helmet", "/foo/")
2060
+	out, err := inspectMountSourceField("dark_helmet", prefix+slash+"foo"+slash)
2056 2061
 	if err != errMountNotFound {
2057
-		c.Fatalf("Found unexpected volume entry for '/foo/' in volumes\n%q", out)
2062
+		c.Fatalf("Found unexpected volume entry for '%s/foo/' in volumes\n%q", prefix, out)
2058 2063
 	}
2059 2064
 
2060
-	out, err = inspectMountSourceField("dark_helmet", "/foo")
2065
+	out, err = inspectMountSourceField("dark_helmet", prefix+slash+`foo`)
2061 2066
 	c.Assert(err, check.IsNil)
2062
-	if !strings.Contains(out, volumesConfigPath) {
2063
-		c.Fatalf("Volume was not defined for /foo\n%q", out)
2067
+	if !strings.Contains(strings.ToLower(out), strings.ToLower(volumesConfigPath)) {
2068
+		c.Fatalf("Volume was not defined for %s/foo\n%q", prefix, out)
2064 2069
 	}
2065 2070
 
2066
-	out, err = inspectMountSourceField("dark_helmet", "/bar/")
2071
+	out, err = inspectMountSourceField("dark_helmet", prefix+slash+"bar"+slash)
2067 2072
 	if err != errMountNotFound {
2068
-		c.Fatalf("Found unexpected volume entry for '/bar/' in volumes\n%q", out)
2073
+		c.Fatalf("Found unexpected volume entry for '%s/bar/' in volumes\n%q", prefix, out)
2069 2074
 	}
2070 2075
 
2071
-	out, err = inspectMountSourceField("dark_helmet", "/bar")
2076
+	out, err = inspectMountSourceField("dark_helmet", prefix+slash+"bar")
2072 2077
 	c.Assert(err, check.IsNil)
2073
-	if !strings.Contains(out, volumesConfigPath) {
2074
-		c.Fatalf("Volume was not defined for /bar\n%q", out)
2078
+	if !strings.Contains(strings.ToLower(out), strings.ToLower(volumesConfigPath)) {
2079
+		c.Fatalf("Volume was not defined for %s/bar\n%q", prefix, out)
2075 2080
 	}
2076 2081
 }
2077 2082
 
... ...
@@ -2409,7 +2534,7 @@ func (s *DockerSuite) TestRunNonLocalMacAddress(c *check.C) {
2409 2409
 	if daemonPlatform == "windows" {
2410 2410
 		cmd = "ipconfig /all"
2411 2411
 		image = WindowsBaseImage
2412
-		expected = strings.Replace(addr, ":", "-", -1)
2412
+		expected = strings.Replace(strings.ToUpper(addr), ":", "-", -1)
2413 2413
 
2414 2414
 	}
2415 2415
 
... ...
@@ -2614,11 +2739,13 @@ func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithAddHostFlag(c *check
2614 2614
 }
2615 2615
 
2616 2616
 func (s *DockerSuite) TestRunVolumesFromRestartAfterRemoved(c *check.C) {
2617
-	// TODO Windows. Not applicable on Windows which does not support volumes.
2618
-	// This may be possible to add in the future.
2619
-	testRequires(c, DaemonIsLinux)
2620
-	dockerCmd(c, "run", "-d", "--name", "voltest", "-v", "/foo", "busybox")
2621
-	dockerCmd(c, "run", "-d", "--name", "restarter", "--volumes-from", "voltest", "busybox", "top")
2617
+	prefix := ""
2618
+	if daemonPlatform == "windows" {
2619
+		prefix = "c:"
2620
+		testRequires(c, WindowsDaemonSupportsVolumes)
2621
+	}
2622
+	dockerCmd(c, "run", "-d", "--name", "voltest", "-v", prefix+"/foo", "busybox", "sleep", "60")
2623
+	dockerCmd(c, "run", "-d", "--name", "restarter", "--volumes-from", "voltest", "busybox", "sleep", "60")
2622 2624
 
2623 2625
 	// Remove the main volume container and restart the consuming container
2624 2626
 	dockerCmd(c, "rm", "-f", "voltest")
... ...
@@ -2844,19 +2971,31 @@ func (s *DockerSuite) TestRunCapAddCHOWN(c *check.C) {
2844 2844
 
2845 2845
 // https://github.com/docker/docker/pull/14498
2846 2846
 func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *check.C) {
2847
-	// Not applicable on Windows as volumes are not supported on Winodws
2848
-	testRequires(c, DaemonIsLinux)
2849
-	dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "true")
2850
-	dockerCmd(c, "run", "--volumes-from", "parent:ro", "--name", "test-volumes-1", "busybox", "true")
2847
+	// TODO Windows post TP4. Enable the read-only bits once they are
2848
+	// supported on the platform.
2849
+	prefix := ""
2850
+	slash := `/`
2851
+	if daemonPlatform == "windows" {
2852
+		testRequires(c, WindowsDaemonSupportsVolumes)
2853
+		prefix = "c:"
2854
+		slash = `\`
2855
+	}
2856
+
2857
+	dockerCmd(c, "run", "--name", "parent", "-v", prefix+"/test", "busybox", "true")
2858
+	if daemonPlatform != "windows" {
2859
+		dockerCmd(c, "run", "--volumes-from", "parent:ro", "--name", "test-volumes-1", "busybox", "true")
2860
+	}
2851 2861
 	dockerCmd(c, "run", "--volumes-from", "parent:rw", "--name", "test-volumes-2", "busybox", "true")
2852 2862
 
2853
-	mRO, err := inspectMountPoint("test-volumes-1", "/test")
2854
-	c.Assert(err, check.IsNil)
2855
-	if mRO.RW {
2856
-		c.Fatalf("Expected RO volume was RW")
2863
+	if daemonPlatform != "windows" {
2864
+		mRO, err := inspectMountPoint("test-volumes-1", prefix+slash+"test")
2865
+		c.Assert(err, check.IsNil)
2866
+		if mRO.RW {
2867
+			c.Fatalf("Expected RO volume was RW")
2868
+		}
2857 2869
 	}
2858 2870
 
2859
-	mRW, err := inspectMountPoint("test-volumes-2", "/test")
2871
+	mRW, err := inspectMountPoint("test-volumes-2", prefix+slash+"test")
2860 2872
 	c.Assert(err, check.IsNil)
2861 2873
 	if !mRW.RW {
2862 2874
 		c.Fatalf("Expected RW volume was RO")
... ...
@@ -3189,14 +3328,20 @@ func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *check.C) {
3189 3189
 }
3190 3190
 
3191 3191
 func (s *DockerSuite) TestRunNamedVolume(c *check.C) {
3192
-	// TODO Windows: This may be possible to modify once Windows supports volumes
3192
+	prefix := ""
3193
+	slash := `/`
3194
+	if daemonPlatform == "windows" {
3195
+		testRequires(c, WindowsDaemonSupportsVolumes)
3196
+		prefix = "c:"
3197
+		slash = `\`
3198
+	}
3193 3199
 	testRequires(c, DaemonIsLinux)
3194
-	dockerCmd(c, "run", "--name=test", "-v", "testing:/foo", "busybox", "sh", "-c", "echo hello > /foo/bar")
3200
+	dockerCmd(c, "run", "--name=test", "-v", "testing:"+prefix+slash+"foo", "busybox", "sh", "-c", "echo hello > "+prefix+"/foo/bar")
3195 3201
 
3196
-	out, _ := dockerCmd(c, "run", "--volumes-from", "test", "busybox", "sh", "-c", "cat /foo/bar")
3202
+	out, _ := dockerCmd(c, "run", "--volumes-from", "test", "busybox", "sh", "-c", "cat "+prefix+"/foo/bar")
3197 3203
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
3198 3204
 
3199
-	out, _ = dockerCmd(c, "run", "-v", "testing:/foo", "busybox", "sh", "-c", "cat /foo/bar")
3205
+	out, _ = dockerCmd(c, "run", "-v", "testing:"+prefix+slash+"foo", "busybox", "sh", "-c", "cat "+prefix+"/foo/bar")
3200 3206
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
3201 3207
 }
3202 3208
 
... ...
@@ -3598,6 +3743,8 @@ func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
3598 3598
 }
3599 3599
 
3600 3600
 func (s *DockerSuite) TestRunWrongCpusetCpusFlagValue(c *check.C) {
3601
+	// TODO Windows: This needs validation (error out) in the daemon.
3602
+	testRequires(c, DaemonIsLinux)
3601 3603
 	out, _, err := dockerCmdWithError("run", "--cpuset-cpus", "1-10,11--", "busybox", "true")
3602 3604
 	c.Assert(err, check.NotNil)
3603 3605
 	expected := "Error response from daemon: Invalid value 1-10,11-- for cpuset cpus.\n"
... ...
@@ -3605,6 +3752,8 @@ func (s *DockerSuite) TestRunWrongCpusetCpusFlagValue(c *check.C) {
3605 3605
 }
3606 3606
 
3607 3607
 func (s *DockerSuite) TestRunWrongCpusetMemsFlagValue(c *check.C) {
3608
+	// TODO Windows: This needs validation (error out) in the daemon.
3609
+	testRequires(c, DaemonIsLinux)
3608 3610
 	out, _, err := dockerCmdWithError("run", "--cpuset-mems", "1-42--", "busybox", "true")
3609 3611
 	c.Assert(err, check.NotNil)
3610 3612
 	expected := "Error response from daemon: Invalid value 1-42-- for cpuset mems.\n"
... ...
@@ -11,7 +11,9 @@ import (
11 11
 )
12 12
 
13 13
 func (s *DockerSuite) TestVolumeCliCreate(c *check.C) {
14
-	testRequires(c, DaemonIsLinux)
14
+	if daemonPlatform == "windows" {
15
+		testRequires(c, WindowsDaemonSupportsVolumes)
16
+	}
15 17
 	dockerCmd(c, "volume", "create")
16 18
 
17 19
 	_, err := runCommand(exec.Command(dockerBinary, "volume", "create", "-d", "nosuchdriver"))
... ...
@@ -23,6 +25,9 @@ func (s *DockerSuite) TestVolumeCliCreate(c *check.C) {
23 23
 }
24 24
 
25 25
 func (s *DockerSuite) TestVolumeCliCreateOptionConflict(c *check.C) {
26
+	if daemonPlatform == "windows" {
27
+		testRequires(c, WindowsDaemonSupportsVolumes)
28
+	}
26 29
 	dockerCmd(c, "volume", "create", "--name=test")
27 30
 	out, _, err := dockerCmdWithError("volume", "create", "--name", "test", "--driver", "nosuchdriver")
28 31
 	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
... ...
@@ -35,7 +40,9 @@ func (s *DockerSuite) TestVolumeCliCreateOptionConflict(c *check.C) {
35 35
 }
36 36
 
37 37
 func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
38
-	testRequires(c, DaemonIsLinux)
38
+	if daemonPlatform == "windows" {
39
+		testRequires(c, WindowsDaemonSupportsVolumes)
40
+	}
39 41
 	c.Assert(
40 42
 		exec.Command(dockerBinary, "volume", "inspect", "doesntexist").Run(),
41 43
 		check.Not(check.IsNil),
... ...
@@ -53,33 +60,40 @@ func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
53 53
 }
54 54
 
55 55
 func (s *DockerSuite) TestVolumeCliLs(c *check.C) {
56
-	testRequires(c, DaemonIsLinux)
56
+	prefix := ""
57
+	if daemonPlatform == "windows" {
58
+		prefix = "c:"
59
+		testRequires(c, WindowsDaemonSupportsVolumes)
60
+	}
57 61
 	out, _ := dockerCmd(c, "volume", "create")
58 62
 	id := strings.TrimSpace(out)
59 63
 
60 64
 	dockerCmd(c, "volume", "create", "--name", "test")
61
-	dockerCmd(c, "run", "-v", "/foo", "busybox", "ls", "/")
65
+	dockerCmd(c, "run", "-v", prefix+"/foo", "busybox", "ls", "/")
62 66
 
63 67
 	out, _ = dockerCmd(c, "volume", "ls")
64 68
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
65 69
 	c.Assert(len(outArr), check.Equals, 4, check.Commentf("\n%s", out))
66 70
 
67
-	// Since there is no guarentee of ordering of volumes, we just make sure the names are in the output
71
+	// Since there is no guarantee of ordering of volumes, we just make sure the names are in the output
68 72
 	c.Assert(strings.Contains(out, id+"\n"), check.Equals, true)
69 73
 	c.Assert(strings.Contains(out, "test\n"), check.Equals, true)
70 74
 }
71 75
 
72 76
 func (s *DockerSuite) TestVolumeCliLsFilterDangling(c *check.C) {
73
-	testRequires(c, DaemonIsLinux)
74
-
77
+	prefix := ""
78
+	if daemonPlatform == "windows" {
79
+		prefix = "c:"
80
+		testRequires(c, WindowsDaemonSupportsVolumes)
81
+	}
75 82
 	dockerCmd(c, "volume", "create", "--name", "testnotinuse1")
76 83
 	dockerCmd(c, "volume", "create", "--name", "testisinuse1")
77 84
 	dockerCmd(c, "volume", "create", "--name", "testisinuse2")
78 85
 
79 86
 	// Make sure both "created" (but not started), and started
80 87
 	// containers are included in reference counting
81
-	dockerCmd(c, "run", "--name", "volume-test1", "-v", "testisinuse1:/foo", "busybox", "true")
82
-	dockerCmd(c, "create", "--name", "volume-test2", "-v", "testisinuse2:/foo", "busybox", "true")
88
+	dockerCmd(c, "run", "--name", "volume-test1", "-v", "testisinuse1:"+prefix+"/foo", "busybox", "true")
89
+	dockerCmd(c, "create", "--name", "volume-test2", "-v", "testisinuse2:"+prefix+"/foo", "busybox", "true")
83 90
 
84 91
 	out, _ := dockerCmd(c, "volume", "ls")
85 92
 
... ...
@@ -104,7 +118,11 @@ func (s *DockerSuite) TestVolumeCliLsFilterDangling(c *check.C) {
104 104
 }
105 105
 
106 106
 func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
107
-	testRequires(c, DaemonIsLinux)
107
+	prefix := ""
108
+	if daemonPlatform == "windows" {
109
+		prefix = "c:"
110
+		testRequires(c, WindowsDaemonSupportsVolumes)
111
+	}
108 112
 	out, _ := dockerCmd(c, "volume", "create")
109 113
 	id := strings.TrimSpace(out)
110 114
 
... ...
@@ -117,7 +135,7 @@ func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
117 117
 	c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
118 118
 
119 119
 	volumeID := "testing"
120
-	dockerCmd(c, "run", "-v", volumeID+":/foo", "--name=test", "busybox", "sh", "-c", "echo hello > /foo/bar")
120
+	dockerCmd(c, "run", "-v", volumeID+":"+prefix+"/foo", "--name=test", "busybox", "sh", "-c", "echo hello > /foo/bar")
121 121
 	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "volume", "rm", "testing"))
122 122
 	c.Assert(
123 123
 		err,
... ...
@@ -130,7 +148,7 @@ func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
130 130
 	dockerCmd(c, "volume", "inspect", volumeID)
131 131
 	dockerCmd(c, "rm", "-f", "test")
132 132
 
133
-	out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":/foo", "busybox", "sh", "-c", "cat /foo/bar")
133
+	out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":"+prefix+"/foo", "busybox", "sh", "-c", "cat /foo/bar")
134 134
 	c.Assert(strings.TrimSpace(out), check.Equals, "hello", check.Commentf("volume data was removed"))
135 135
 	dockerCmd(c, "rm", "test2")
136 136
 
... ...
@@ -34,6 +34,12 @@ var (
34 34
 	// a version call to the daemon and examining the response header.
35 35
 	daemonPlatform string
36 36
 
37
+	// windowsDaemonKV is used on Windows to distinguish between different
38
+	// versions. This is necessary to enable certain tests based on whether
39
+	// the platform supports it. For example, Windows Server 2016 TP3 does
40
+	// not support volumes, but TP4 does.
41
+	windowsDaemonKV int
42
+
37 43
 	// daemonDefaultImage is the name of the default image to use when running
38 44
 	// tests. This is platform dependent.
39 45
 	daemonDefaultImage string
... ...
@@ -637,6 +637,29 @@ func init() {
637 637
 	if daemonPlatform != "linux" && daemonPlatform != "windows" {
638 638
 		panic("Cannot run tests against platform: " + daemonPlatform)
639 639
 	}
640
+
641
+	// On Windows, extract out the version as we need to make selective
642
+	// decisions during integration testing as and when features are implemented.
643
+	if daemonPlatform == "windows" {
644
+		if body, err := ioutil.ReadAll(res.Body); err == nil {
645
+			var server types.Version
646
+			if err := json.Unmarshal(body, &server); err == nil {
647
+				// eg in "10.0 10550 (10550.1000.amd64fre.branch.date-time)" we want 10550
648
+				windowsDaemonKV, _ = strconv.Atoi(strings.Split(server.KernelVersion, " ")[1])
649
+			}
650
+		}
651
+	}
652
+
653
+	// Now we know the daemon platform, can set paths used by tests.
654
+	if daemonPlatform == "windows" {
655
+		dockerBasePath = `c:\programdata\docker`
656
+		volumesConfigPath = dockerBasePath + `\volumes`
657
+		containerStoragePath = dockerBasePath + `\containers`
658
+	} else {
659
+		dockerBasePath = "/var/lib/docker"
660
+		volumesConfigPath = dockerBasePath + "/volumes"
661
+		containerStoragePath = dockerBasePath + "/containers"
662
+	}
640 663
 }
641 664
 
642 665
 func deleteAllImages() error {
... ...
@@ -29,6 +29,18 @@ var (
29 29
 		func() bool { return daemonPlatform == "windows" },
30 30
 		"Test requires a Windows daemon",
31 31
 	}
32
+	WindowsDaemonSupportsVolumes = testRequirement{
33
+		func() bool {
34
+			if daemonPlatform != "windows" {
35
+				return false
36
+			}
37
+			if windowsDaemonKV == 0 {
38
+				panic("windowsDaemonKV is not set")
39
+			}
40
+			return windowsDaemonKV >= 10559
41
+		},
42
+		"Test requires a Windows daemon that supports volumes",
43
+	}
32 44
 	DaemonIsLinux = testRequirement{
33 45
 		func() bool { return daemonPlatform == "linux" },
34 46
 		"Test requires a Linux daemon",