diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 89f7a46aeec2..d0ed815b2505 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -122,6 +122,7 @@ var fileOpTests = []integration.Test{ testWorkdirExists, testWorkdirCopyIgnoreRelative, testCopyFollowAllSymlinks, + testDockerfileAddChownExpand, } // Tests that depend on the `security.*` entitlements @@ -2258,6 +2259,42 @@ ADD *.tar /dest require.Equal(t, "content1", string(dt)) } +func testDockerfileAddChownExpand(t *testing.T, sb integration.Sandbox) { + f := getFrontend(t, sb) + isFileOp := getFileOp(t, sb) + + dockerfile := []byte(` +FROM busybox +ARG group +ENV owner 1000 +ADD --chown=${owner}:${group} foo / +RUN [ "$(stat -c "%u %G" /foo)" == "1000 nogroup" ] +`) + + dir, err := tmpdir( + fstest.CreateFile("Dockerfile", dockerfile, 0600), + fstest.CreateFile("foo", []byte(`foo-contents`), 0600), + ) + require.NoError(t, err) + defer os.RemoveAll(dir) + + c, err := client.New(context.TODO(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + _, err = f.Solve(context.TODO(), c, client.SolveOpt{ + FrontendAttrs: map[string]string{ + "build-arg:BUILDKIT_DISABLE_FILEOP": strconv.FormatBool(!isFileOp), + "build-arg:group": "nogroup", + }, + LocalDirs: map[string]string{ + builder.DefaultLocalNameDockerfile: dir, + builder.DefaultLocalNameContext: dir, + }, + }, nil) + require.NoError(t, err) +} + func testSymlinkDestination(t *testing.T, sb integration.Sandbox) { skipDockerd(t, sb) f := getFrontend(t, sb) diff --git a/frontend/dockerfile/instructions/commands.go b/frontend/dockerfile/instructions/commands.go index bcd495fd6ca0..2580c9602798 100644 --- a/frontend/dockerfile/instructions/commands.go +++ b/frontend/dockerfile/instructions/commands.go @@ -192,6 +192,11 @@ type AddCommand struct { // Expand variables func (c *AddCommand) Expand(expander SingleWordExpander) error { + expandedChown, err := expander(c.Chown) + if err != nil { + return err + } + c.Chown = expandedChown return expandSliceInPlace(c.SourcesAndDest, expander) }