From 8bf1d785afa6a8752d3246ee68b5184c6200ba5a Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 11 Dec 2024 22:55:39 -0800 Subject: [PATCH] dockerfile: fix onbuild propagation for child stages Signed-off-by: Tonis Tiigi --- frontend/dockerfile/dockerfile2llb/convert.go | 2 + frontend/dockerfile/dockerfile2llb/image.go | 1 + frontend/dockerfile/dockerfile_test.go | 91 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index e0e49dd047b6..bf2647ceb0e2 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -1065,6 +1065,8 @@ func (ds *dispatchState) init() { ds.state = ds.base.state ds.platform = ds.base.platform ds.image = clone(ds.base.image) + // onbuild triggers to not carry over from base stage + ds.image.Config.OnBuild = nil ds.baseImg = cloneX(ds.base.baseImg) // Utilize the same path index as our base image so we propagate // the paths we use back to the base image. diff --git a/frontend/dockerfile/dockerfile2llb/image.go b/frontend/dockerfile/dockerfile2llb/image.go index b6b589e77654..0f10542afc4f 100644 --- a/frontend/dockerfile/dockerfile2llb/image.go +++ b/frontend/dockerfile/dockerfile2llb/image.go @@ -12,6 +12,7 @@ func clone(src dockerspec.DockerOCIImage) dockerspec.DockerOCIImage { img.Config.Env = append([]string{}, src.Config.Env...) img.Config.Cmd = append([]string{}, src.Config.Cmd...) img.Config.Entrypoint = append([]string{}, src.Config.Entrypoint...) + img.Config.OnBuild = append([]string{}, src.Config.OnBuild...) return img } diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 8fc3b4ad7307..833d6ce9ee9f 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -127,6 +127,7 @@ var allTests = integration.TestFuncs( testEnvEmptyFormatting, testCacheMultiPlatformImportExport, testOnBuildCleared, + testOnBuildWithChildStage, testOnBuildInheritedStageRun, testOnBuildInheritedStageWithFrom, testOnBuildNewDeps, @@ -5013,6 +5014,96 @@ ONBUILD RUN mkdir \out && echo 11>> \out\foo require.Equal(t, integration.UnixOrWindows("11", "11\r\n"), string(dt)) } +// testOnBuildWithChildStage tests that ONBUILD rules from the parent image do +// not run again if another stage inherits from current stage. +// moby/buildkit#5578 +func testOnBuildWithChildStage(t *testing.T, sb integration.Sandbox) { + integration.SkipOnPlatform(t, "windows") + workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush) + f := getFrontend(t, sb) + + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + + dockerfile := []byte(` +FROM busybox +ONBUILD RUN mkdir -p /out && echo -n yes >> /out/didrun +`) + + dir := integration.Tmpdir( + t, + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + c, err := client.New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + target := registry + "/buildkit/testonbuildstage:base" + + _, err = f.Solve(sb.Context(), c, client.SolveOpt{ + Exports: []client.ExportEntry{ + { + Type: client.ExporterImage, + Attrs: map[string]string{ + "push": "true", + "name": target, + }, + }, + }, + LocalMounts: map[string]fsutil.FS{ + dockerui.DefaultLocalNameDockerfile: dir, + dockerui.DefaultLocalNameContext: dir, + }, + }, nil) + require.NoError(t, err) + + dockerfile = []byte(fmt.Sprintf(` +FROM %s AS base +RUN [ -f /out/didrun ] && touch /step1 +RUN rm /out/didrun +RUN [ ! -f /out/didrun ] && touch /step2 + +FROM base AS child +RUN [ ! -f /out/didrun ] && touch /step3 + +FROM scratch +COPY --from=child /step* / + `, target)) + + dir = integration.Tmpdir( + t, + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + destDir := t.TempDir() + + _, err = f.Solve(sb.Context(), c, client.SolveOpt{ + Exports: []client.ExportEntry{ + { + Type: client.ExporterLocal, + OutputDir: destDir, + }, + }, + LocalMounts: map[string]fsutil.FS{ + dockerui.DefaultLocalNameDockerfile: dir, + dockerui.DefaultLocalNameContext: dir, + }, + }, nil) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(destDir, "step1")) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(destDir, "step2")) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(destDir, "step3")) + require.NoError(t, err) +} + func testOnBuildNamedContext(t *testing.T, sb integration.Sandbox) { integration.SkipOnPlatform(t, "windows") workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter, workers.FeatureOCILayout)