Skip to content

Commit e42889d

Browse files
committed
dockerfile: add support for non-octal COPY --chmod in labs
Signed-off-by: Tonis Tiigi <[email protected]>
1 parent c7db68a commit e42889d

File tree

5 files changed

+147
-5
lines changed

5 files changed

+147
-5
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,12 +1363,21 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
13631363

13641364
var mode *llb.ChmodOpt
13651365
if cfg.chmod != "" {
1366+
mode = &llb.ChmodOpt{}
13661367
p, err := strconv.ParseUint(cfg.chmod, 8, 32)
1367-
if err != nil || p > 0o7777 {
1368-
return errors.Errorf("invalid chmod parameter: '%v'. it should be octal string and between 0 and 07777", cfg.chmod)
1368+
nonOctalErr := errors.Errorf("invalid chmod parameter: '%v'. it should be octal string and between 0 and 07777", cfg.chmod)
1369+
if err == nil {
1370+
if p > 0o7777 {
1371+
return nonOctalErr
1372+
}
1373+
mode.Mode = os.FileMode(p)
1374+
} else {
1375+
if featureCopyChmodNonOctalEnabled {
1376+
mode.ModeStr = cfg.chmod
1377+
} else {
1378+
return nonOctalErr
1379+
}
13691380
}
1370-
perm := os.FileMode(p)
1371-
mode = &llb.ChmodOpt{Mode: perm}
13721381
}
13731382

13741383
if cfg.checksum != "" {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build !dfcopychmodnonoctal
2+
// +build !dfcopychmodnonoctal
3+
4+
package dockerfile2llb
5+
6+
var featureCopyChmodNonOctalEnabled = false
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build dfcopychmodnonoctal
2+
// +build dfcopychmodnonoctal
3+
4+
package dockerfile2llb
5+
6+
var featureCopyChmodNonOctalEnabled = true
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//go:build dfcopychmodnonoctal
2+
// +build dfcopychmodnonoctal
3+
4+
package dockerfile
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
10+
"github.com/containerd/continuity/fs/fstest"
11+
"github.com/moby/buildkit/client"
12+
"github.com/moby/buildkit/frontend/dockerui"
13+
"github.com/moby/buildkit/util/testutil/integration"
14+
"github.com/stretchr/testify/require"
15+
"github.com/tonistiigi/fsutil"
16+
)
17+
18+
func init() {
19+
allTests = append(allTests, integration.TestFuncs(testChmodNonOctal)...)
20+
}
21+
22+
func testChmodNonOctal(t *testing.T, sb integration.Sandbox) {
23+
integration.SkipOnPlatform(t, "windows")
24+
f := getFrontend(t, sb)
25+
26+
tcases := []struct {
27+
src string
28+
dst string
29+
mode string
30+
isDir bool
31+
}{
32+
{
33+
src: "file",
34+
dst: "f1",
35+
mode: "go-w",
36+
},
37+
{
38+
src: "file",
39+
dst: "f2",
40+
mode: "u=rw,g=r,o=r",
41+
}, {
42+
src: "file",
43+
dst: "f3",
44+
mode: "a+X",
45+
},
46+
{
47+
src: "dir",
48+
dst: "d1",
49+
mode: "go-w",
50+
isDir: true,
51+
},
52+
{
53+
src: "dir",
54+
dst: "d2",
55+
mode: "u+rw,g+r,o-x,o+w",
56+
isDir: true,
57+
},
58+
{
59+
src: "dir",
60+
dst: "d3",
61+
mode: "a+X",
62+
isDir: true,
63+
},
64+
}
65+
66+
expectedCommands := ""
67+
copyCommands := ""
68+
verifyCommands := ""
69+
70+
for _, tc := range tcases {
71+
if tc.isDir {
72+
// create nested input dir because COPY copies directory contents
73+
expectedCommands += "RUN mkdir -p /input/dirs/" + tc.dst + " && cp -a /input/" + tc.src + " /input/dirs/" + tc.dst + "/" + tc.dst + "\n"
74+
expectedCommands += "RUN cp -a /input/dirs/" + tc.dst + "/. /expected/ && chmod " + tc.mode + " /expected/" + tc.dst + "\n"
75+
copyCommands += "COPY --from=base --chmod=" + tc.mode + " /input/dirs/" + tc.dst + " /\n"
76+
} else {
77+
expectedCommands += "RUN cp -a /input/" + tc.src + " /expected/" + tc.dst + " && chmod " + tc.mode + " /expected/" + tc.dst + "\n"
78+
copyCommands += "COPY --from=base --chmod=" + tc.mode + " /input/" + tc.src + " /" + tc.dst + "\n"
79+
}
80+
verifyCommands += "RUN [ \"$(stat -c %A /actual/" + tc.dst + ")\" = \"$(stat -c %A /expected/" + tc.dst + ")\" ]\n"
81+
}
82+
83+
dockerfile := []byte(fmt.Sprintf(`
84+
FROM alpine as base
85+
RUN <<eot
86+
set -ex
87+
mkdir /input
88+
touch /input/file
89+
chmod 666 /input/file
90+
mkdir /input/dir
91+
chmod 124 /input/dir
92+
mkdir /expected
93+
eot
94+
%s
95+
96+
FROM scratch as result
97+
%s
98+
99+
FROM base
100+
COPY --from=result / /actual/
101+
%s
102+
103+
`, expectedCommands, copyCommands, verifyCommands))
104+
105+
dir := integration.Tmpdir(
106+
t,
107+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
108+
)
109+
110+
c, err := client.New(sb.Context(), sb.Address())
111+
require.NoError(t, err)
112+
defer c.Close()
113+
114+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
115+
LocalMounts: map[string]fsutil.FS{
116+
dockerui.DefaultLocalNameDockerfile: dir,
117+
dockerui.DefaultLocalNameContext: dir,
118+
},
119+
}, nil)
120+
require.NoError(t, err)
121+
}

frontend/dockerfile/release/labs/tags

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dfrunsecurity dfparents dfexcludepatterns
1+
dfrunsecurity dfparents dfexcludepatterns dfchmodnonoctal

0 commit comments

Comments
 (0)