diff --git a/executor/oci/spec.go b/executor/oci/spec.go index fe0b03c4a1c1..f4cc072312fa 100644 --- a/executor/oci/spec.go +++ b/executor/oci/spec.go @@ -201,8 +201,8 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou return nil, nil, err } s.Mounts = append(s.Mounts, specs.Mount{ - Destination: m.Dest, - Type: mount.Type, + Destination: getAbsPath(m.Dest), // for Windows only + Type: getMountType(mount.Type), Source: mount.Source, Options: mount.Options, }) @@ -243,6 +243,10 @@ type submounts struct { func (s *submounts) subMount(m mount.Mount, subPath string) (mount.Mount, error) { if path.Join("/", subPath) == "/" { + // for Windows, the mounting by HCS doesn't go through + // WCIFS, hence we have to give the direct path of the + // mount, which is in the /Files subdirectory. + m.Source = getCompleteSourcePath(m.Source) return m, nil } if s.m == nil { diff --git a/executor/oci/spec_unix.go b/executor/oci/spec_unix.go new file mode 100644 index 000000000000..1810731d5a03 --- /dev/null +++ b/executor/oci/spec_unix.go @@ -0,0 +1,18 @@ +//go:build !windows + +package oci + +// no effect for non-Windows +func getMountType(mType string) string { + return mType +} + +// no effect for non-Windows +func getAbsPath(p string) string { + return p +} + +// no effect for non-Windows +func getCompleteSourcePath(p string) string { + return p +} diff --git a/executor/oci/spec_windows.go b/executor/oci/spec_windows.go index d3059c3034f4..5f69ce5c3528 100644 --- a/executor/oci/spec_windows.go +++ b/executor/oci/spec_windows.go @@ -110,3 +110,22 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) { m.Source = src return m, func() error { return nil }, nil } + +func getMountType(_ string) string { + // HCS shim doesn't expect a named type + // for the mount. + return "" +} + +// Returns an absolute path, starting with the +// default drive letter (C:). HCS Shim expects an absolute path. +func getAbsPath(p string) string { + return filepath.Join("C:\\", p) +} + +// For Windows, the mounting by HCS doesn't go through +// WCIFS, hence we have to give the direct path of the +// mount, which is in the /Files subdirectory. +func getCompleteSourcePath(p string) string { + return filepath.Join(p, "Files") +} diff --git a/frontend/dockerfile/dockerfile_mount_test.go b/frontend/dockerfile/dockerfile_mount_test.go index 0423145eabd5..27cac73c9bbf 100644 --- a/frontend/dockerfile/dockerfile_mount_test.go +++ b/frontend/dockerfile/dockerfile_mount_test.go @@ -36,13 +36,21 @@ func init() { } func testMountContext(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + + ` FROM busybox RUN --mount=target=/context [ "$(cat /context/testfile)" == "contents0" ] -`) +`, + ` +FROM nanoserver +USER ContainerAdministrator +RUN --mount=target=/context cmd /V:on /C "set /p tfcontent= out/foo @@ -174,7 +199,20 @@ RUN --mount=from=build,src=out,target=/out,rw touch /out/bar && cat /dev/urandom from scratch COPY --from=second /unique /unique -`) +`, + ` +FROM nanoserver AS build +COPY cachebust / +RUN mkdir out && echo foo> out\foo + +FROM nanoserver AS second +USER ContainerAdministrator +RUN --mount=from=build,src=out,target=/out,rw echo %RANDOM% > \unique + +FROM nanoserver +COPY --from=second /unique /unique +`, + )) dir := integration.Tmpdir( t, @@ -261,15 +299,23 @@ RUN --mount=type=cache,target=/mycache,uid=1001,gid=1002,mode=0751 [ "$(stat -c } func testCacheMountDefaultID(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox RUN --mount=type=cache,target=/mycache touch /mycache/foo RUN --mount=type=cache,target=/mycache2 [ ! -f /mycache2/foo ] RUN --mount=type=cache,target=/mycache [ -f /mycache/foo ] -`) +`, + ` +FROM nanoserver +USER ContainerAdministrator +RUN --mount=type=cache,target=/mycache echo hello > \mycache\foo +RUN --mount=type=cache,target=/mycache2 IF NOT EXIST C:\mycache2\foo (exit 0) ELSE (exit 1) +RUN --mount=type=cache,target=/mycache dir \mycache\foo +`, + )) dir := integration.Tmpdir( t, @@ -290,15 +336,23 @@ RUN --mount=type=cache,target=/mycache [ -f /mycache/foo ] } func testMountEnvVar(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox ENV SOME_PATH=/mycache RUN --mount=type=cache,target=/mycache touch /mycache/foo RUN --mount=type=cache,target=$SOME_PATH [ -f $SOME_PATH/foo ] -`) +`, + ` +FROM nanoserver +USER ContainerAdministrator +ENV SOME_PATH=mycache +RUN --mount=type=cache,target=/mycache echo hello > \mycache\foo +RUN --mount=type=cache,target=/$SOME_PATH dir %SOME_PATH%\foo +`, + )) dir := integration.Tmpdir( t, @@ -319,15 +373,23 @@ RUN --mount=type=cache,target=$SOME_PATH [ -f $SOME_PATH/foo ] } func testMountArg(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox ARG MNT_TYPE=cache RUN --mount=type=$MNT_TYPE,target=/mycache2 touch /mycache2/foo RUN --mount=type=cache,target=/mycache2 [ -f /mycache2/foo ] -`) +`, + ` +FROM nanoserver +USER ContainerAdministrator +ARG MNT_TYPE=cache +RUN --mount=type=$MNT_TYPE,target=/mycache2 echo hello > \mycache2\foo +RUN --mount=type=cache,target=/mycache2 dir \mycache2\foo +`, + )) dir := integration.Tmpdir( t, @@ -348,10 +410,10 @@ RUN --mount=type=cache,target=/mycache2 [ -f /mycache2/foo ] } func testMountEnvAcrossStages(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox as stage1 ENV MNT_ID=mycache @@ -361,7 +423,19 @@ RUN --mount=type=$MNT_TYPE2,id=$MNT_ID,target=/cbacba [ -f /cbacba/foo ] FROM stage1 RUN --mount=type=$MNT_TYPE2,id=$MNT_ID,target=/whatever [ -f /whatever/foo ] -`) +`, + ` +FROM nanoserver AS stage1 +USER ContainerAdministrator +ENV MNT_ID=mycache +ENV MNT_TYPE2=cache +RUN --mount=type=cache,id=mycache,target=/abcabc echo 1 > \abcabc\foo +RUN --mount=type=$MNT_TYPE2,id=$MNT_ID,target=/cbacba dir \cbacba\foo + +FROM stage1 +RUN --mount=type=$MNT_TYPE2,id=$MNT_ID,target=/whatever dir \whatever\foo +`, + )) dir := integration.Tmpdir( t, @@ -382,17 +456,27 @@ RUN --mount=type=$MNT_TYPE2,id=$MNT_ID,target=/whatever [ -f /whatever/foo ] } func testMountMetaArg(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` ARG META_PATH=/tmp/meta FROM busybox ARG META_PATH RUN --mount=type=cache,id=mycache,target=/tmp/meta touch /tmp/meta/foo RUN --mount=type=cache,id=mycache,target=$META_PATH [ -f /tmp/meta/foo ] -`) +`, + ` +ARG META_PATH=/tmp/meta + +FROM nanoserver +USER ContainerAdministrator +ARG META_PATH +RUN --mount=type=cache,id=mycache,target=/tmp/meta echo 1 > \tmp\meta\foo +RUN --mount=type=cache,id=mycache,target=$META_PATH dir \tmp\meta\foo +`, + )) dir := integration.Tmpdir( t, @@ -413,17 +497,28 @@ RUN --mount=type=cache,id=mycache,target=$META_PATH [ -f /tmp/meta/foo ] } func testMountFromError(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox as test RUN touch /tmp/test FROM busybox ENV ttt=test RUN --mount=from=$ttt,type=cache,target=/tmp ls -`) +`, + ` +FROM nanoserver AS test +RUN mkdir \tmp +RUN echo \tmp\test + +FROM nanoserver +USER ContainerAdministrator +ENV ttt=test +RUN --mount=from=$ttt,type=cache,target=/tmp dir +`, + )) dir := integration.Tmpdir( t, @@ -487,17 +582,29 @@ COPY --from=base /tmpfssize / // moby/buildkit#4123 func testMountDuplicate(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM busybox AS base RUN --mount=source=.,target=/tmp/test \ --mount=source=b.txt,target=/tmp/b.txt \ cat /tmp/test/a.txt /tmp/b.txt > /combined.txt FROM scratch COPY --from=base /combined.txt / -`) +`, + ` +FROM nanoserver AS base +USER ContainerAdministrator +RUN --mount=source=.,target=/tmp/test \ + --mount=source=b.txt,target=/tmp/b.txt \ + type \tmp\test\a.txt \tmp\b.txt > \combined.txt + +FROM nanoserver +USER ContainerAdministrator +COPY --from=base /combined.txt / +`, + )) c, err := client.New(sb.Context(), sb.Address()) require.NoError(t, err) @@ -540,10 +647,10 @@ COPY --from=base /combined.txt / // moby/buildkit#5566 func testCacheMountParallel(t *testing.T, sb integration.Sandbox) { - integration.SkipOnPlatform(t, "windows") f := getFrontend(t, sb) - dockerfile := []byte(` + dockerfile := []byte(integration.UnixOrWindows( + ` FROM alpine AS b1 RUN --mount=type=cache,target=/foo/bar --mount=type=cache,target=/foo/bar/baz echo 1 @@ -553,7 +660,21 @@ RUN --mount=type=cache,target=/foo/bar --mount=type=cache,target=/foo/bar/baz ec FROM scratch COPY --from=b1 /etc/passwd p1 COPY --from=b2 /etc/passwd p2 -`) +`, + ` +FROM nanoserver AS b1 +USER ContainerAdministrator +RUN --mount=type=cache,target=/foo/bar --mount=type=cache,target=/foo/bar/baz echo 1 + +FROM nanoserver AS b2 +USER ContainerAdministrator +RUN --mount=type=cache,target=/foo/bar --mount=type=cache,target=/foo/bar/baz echo 2 + +FROM nanoserver +COPY --from=b1 /License.txt p1 +COPY --from=b2 /License.txt p2 +`, + )) dir := integration.Tmpdir( t,