瀏覽代碼

Merge pull request #315 from thaJeztah/19.03_backport_fix_copy_on_windows

[19.03 backport] Builder: fix "COPY --from" to non-existing directory on Windows [ENGCORE-935]
Andrew Hsu 5 年之前
父節點
當前提交
c7139be62b

+ 4 - 2
builder/dockerfile/copy.go

@@ -556,13 +556,15 @@ func copyFile(archiver Archiver, source, dest *copyEndpoint, identity *idtools.I
 			return errors.Wrapf(err, "failed to create new directory")
 			return errors.Wrapf(err, "failed to create new directory")
 		}
 		}
 	} else {
 	} else {
+		// Normal containers
 		if identity == nil {
 		if identity == nil {
-			if err := os.MkdirAll(filepath.Dir(dest.path), 0755); err != nil {
+			// Use system.MkdirAll here, which is a custom version of os.MkdirAll
+			// modified for use on Windows to handle volume GUID paths (\\?\{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\path\)
+			if err := system.MkdirAll(filepath.Dir(dest.path), 0755, ""); err != nil {
 				return err
 				return err
 			}
 			}
 		} else {
 		} else {
 			if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, *identity); err != nil {
 			if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, *identity); err != nil {
-				// Normal containers
 				return errors.Wrapf(err, "failed to create new directory")
 				return errors.Wrapf(err, "failed to create new directory")
 			}
 			}
 		}
 		}

+ 1 - 0
hack/make.ps1

@@ -343,6 +343,7 @@ Function Run-IntegrationTests() {
         Write-Host "Running $($PWD.Path)"
         Write-Host "Running $($PWD.Path)"
         $pinfo = New-Object System.Diagnostics.ProcessStartInfo
         $pinfo = New-Object System.Diagnostics.ProcessStartInfo
         $pinfo.FileName = "$($PWD.Path)\test.exe"
         $pinfo.FileName = "$($PWD.Path)\test.exe"
+        $pinfo.WorkingDirectory = "$($PWD.Path)"
         $pinfo.RedirectStandardError = $true
         $pinfo.RedirectStandardError = $true
         $pinfo.UseShellExecute = $false
         $pinfo.UseShellExecute = $false
         $pinfo.Arguments = $env:INTEGRATION_TESTFLAGS
         $pinfo.Arguments = $env:INTEGRATION_TESTFLAGS

+ 52 - 0
integration/build/build_test.go

@@ -135,6 +135,58 @@ func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) {
 	}
 	}
 }
 }
 
 
+// TestBuildMultiStageCopy verifies that copying between stages works correctly.
+//
+// Regression test for docker/for-win#4349, ENGCORE-935, where creating the target
+// directory failed on Windows, because `os.MkdirAll()` was called with a volume
+// GUID path (\\?\Volume{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\newdir\hello}),
+// which currently isn't supported by Golang.
+func TestBuildMultiStageCopy(t *testing.T) {
+	ctx := context.Background()
+
+	dockerfile, err := ioutil.ReadFile("testdata/Dockerfile." + t.Name())
+	assert.NilError(t, err)
+
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(string(dockerfile)))
+	defer source.Close()
+
+	apiclient := testEnv.APIClient()
+
+	for _, target := range []string{"copy_to_root", "copy_to_newdir", "copy_to_newdir_nested", "copy_to_existingdir", "copy_to_newsubdir"} {
+		t.Run(target, func(t *testing.T) {
+			imgName := strings.ToLower(t.Name())
+
+			resp, err := apiclient.ImageBuild(
+				ctx,
+				source.AsTarReader(t),
+				types.ImageBuildOptions{
+					Remove:      true,
+					ForceRemove: true,
+					Target:      target,
+					Tags:        []string{imgName},
+				},
+			)
+			assert.NilError(t, err)
+
+			out := bytes.NewBuffer(nil)
+			assert.NilError(t, err)
+			_, err = io.Copy(out, resp.Body)
+			_ = resp.Body.Close()
+			if err != nil {
+				t.Log(out)
+			}
+			assert.NilError(t, err)
+
+			// verify the image was successfully built
+			_, _, err = apiclient.ImageInspectWithRaw(ctx, imgName)
+			if err != nil {
+				t.Log(out)
+			}
+			assert.NilError(t, err)
+		})
+	}
+}
+
 func TestBuildMultiStageParentConfig(t *testing.T) {
 func TestBuildMultiStageParentConfig(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.35"), "broken in earlier versions")
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.35"), "broken in earlier versions")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")

+ 20 - 0
integration/build/testdata/Dockerfile.TestBuildMultiStageCopy

@@ -0,0 +1,20 @@
+FROM busybox AS base
+RUN mkdir existingdir
+
+FROM base AS source
+RUN echo "Hello World" > /hello
+
+FROM base AS copy_to_root
+COPY --from=source /hello /hello
+
+FROM base AS copy_to_newdir
+COPY --from=source /hello /newdir/hello
+
+FROM base AS copy_to_newdir_nested
+COPY --from=source /hello /newdir/newsubdir/hello
+
+FROM base AS copy_to_existingdir
+COPY --from=source /hello /existingdir/hello
+
+FROM base AS copy_to_newsubdir
+COPY --from=source /hello /existingdir/newsubdir/hello