Skip to content

Commit f5c38aa

Browse files
committed
dockerfile: device flag support
Signed-off-by: CrazyMax <[email protected]>
1 parent 0fe26ed commit f5c38aa

File tree

4 files changed

+128
-1
lines changed

4 files changed

+128
-1
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1305,11 +1305,16 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
13051305
}
13061306
}
13071307

1308-
// TODO: use entitlements and put this on labs
1308+
// TODO: use entitlements
13091309
if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaCDI) == nil {
13101310
for _, u := range dopt.devices {
13111311
opt = append(opt, llb.AddCDIDevice(u.Name))
13121312
}
1313+
runDevices, err := dispatchRunDevices(c)
1314+
if err != nil {
1315+
return err
1316+
}
1317+
opt = append(opt, runDevices...)
13131318
}
13141319

13151320
shlex := *dopt.shlex
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dockerfile2llb
2+
3+
// TODO: move in labs with dfrundevice tag
4+
5+
import (
6+
"github.com/moby/buildkit/client/llb"
7+
"github.com/moby/buildkit/frontend/dockerfile/instructions"
8+
)
9+
10+
func dispatchRunDevices(c *instructions.RunCommand) ([]llb.RunOption, error) {
11+
var out []llb.RunOption
12+
devices := instructions.GetDevices(c)
13+
for _, device := range devices {
14+
out = append(out, llb.AddCDIDevice(device))
15+
}
16+
return out, nil
17+
}

frontend/dockerfile/dockerfile_rundevice_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
var deviceTests = integration.TestFuncs(
1717
testDeviceEnv,
18+
testDeviceRunEnv,
1819
)
1920

2021
func testDeviceEnv(t *testing.T, sb integration.Sandbox) {
@@ -74,3 +75,58 @@ COPY --from=base /foo.env /
7475
require.NoError(t, err)
7576
require.Contains(t, string(dt), `FOO=injected`)
7677
}
78+
79+
func testDeviceRunEnv(t *testing.T, sb integration.Sandbox) {
80+
if sb.Rootless() {
81+
t.SkipNow()
82+
}
83+
84+
integration.SkipOnPlatform(t, "windows")
85+
f := getFrontend(t, sb)
86+
87+
require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
88+
cdiVersion: "0.3.0"
89+
kind: "vendor1.com/device"
90+
devices:
91+
- name: foo
92+
containerEdits:
93+
env:
94+
- FOO=injected
95+
`), 0600))
96+
97+
dockerfile := []byte(`
98+
FROM busybox AS base
99+
RUN --device=vendor1.com/device=foo env|sort | tee foo.env
100+
FROM scratch
101+
COPY --from=base /foo.env /
102+
`)
103+
104+
dir := integration.Tmpdir(
105+
t,
106+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
107+
)
108+
109+
c, err := client.New(sb.Context(), sb.Address())
110+
require.NoError(t, err)
111+
defer c.Close()
112+
113+
destDir := t.TempDir()
114+
115+
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
116+
LocalMounts: map[string]fsutil.FS{
117+
dockerui.DefaultLocalNameDockerfile: dir,
118+
dockerui.DefaultLocalNameContext: dir,
119+
},
120+
Exports: []client.ExportEntry{
121+
{
122+
Type: client.ExporterLocal,
123+
OutputDir: destDir,
124+
},
125+
},
126+
}, nil)
127+
require.NoError(t, err)
128+
129+
dt, err := os.ReadFile(filepath.Join(destDir, "foo.env"))
130+
require.NoError(t, err)
131+
require.Contains(t, string(dt), `FOO=injected`)
132+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package instructions
2+
3+
import (
4+
"github.com/pkg/errors"
5+
)
6+
7+
var deviceKey = "dockerfile/run/device"
8+
9+
func init() {
10+
parseRunPreHooks = append(parseRunPreHooks, runDevicePreHook)
11+
parseRunPostHooks = append(parseRunPostHooks, runDevicePostHook)
12+
}
13+
14+
func runDevicePreHook(cmd *RunCommand, req parseRequest) error {
15+
st := &deviceState{}
16+
st.flag = req.flags.AddStrings("device")
17+
cmd.setExternalValue(deviceKey, st)
18+
return nil
19+
}
20+
21+
func runDevicePostHook(cmd *RunCommand, req parseRequest) error {
22+
return setDeviceState(cmd)
23+
}
24+
25+
func setDeviceState(cmd *RunCommand) error {
26+
st := getDeviceState(cmd)
27+
if st == nil {
28+
return errors.Errorf("no device state")
29+
}
30+
st.names = st.flag.StringValues
31+
return nil
32+
}
33+
34+
func getDeviceState(cmd *RunCommand) *deviceState {
35+
v := cmd.getExternalValue(deviceKey)
36+
if v == nil {
37+
return nil
38+
}
39+
return v.(*deviceState)
40+
}
41+
42+
func GetDevices(cmd *RunCommand) []string {
43+
return getDeviceState(cmd).names
44+
}
45+
46+
type deviceState struct {
47+
flag *Flag
48+
names []string
49+
}

0 commit comments

Comments
 (0)