Skip to content

Commit 6667434

Browse files
committed
cdi: support optional devices
Signed-off-by: CrazyMax <[email protected]>
1 parent 943c3ad commit 6667434

File tree

10 files changed

+454
-389
lines changed

10 files changed

+454
-389
lines changed

client/client_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11027,8 +11027,9 @@ devices:
1102711027
st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
1102811028
}
1102911029

11030-
run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice("vendor1.com/device=foo"))
11031-
run(`sh -c 'env|sort | tee bar.env'`, llb.AddCDIDevice("vendor2.com/device=bar"))
11030+
run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=foo")))
11031+
run(`sh -c 'env|sort | tee bar.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor2.com/device=bar")))
11032+
run(`ls`, llb.AddCDIDevice(llb.CDIDeviceName("vendor3.com/device=baz"), llb.CDIDeviceOptional))
1103211033

1103311034
def, err := st.Marshal(sb.Context())
1103411035
require.NoError(t, err)

client/llb/exec.go

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type ExecOp struct {
6060
isValidated bool
6161
secrets []SecretInfo
6262
ssh []SSHInfo
63+
cdiDevices []CDIDeviceInfo
6364
}
6465

6566
func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Output {
@@ -267,21 +268,6 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []
267268
Security: security,
268269
}
269270

270-
cdiDevices, err := getCDIDevice(e.base)(ctx, c)
271-
if err != nil {
272-
return "", nil, nil, nil, err
273-
}
274-
if len(cdiDevices) > 0 {
275-
addCap(&e.constraints, pb.CapExecMetaCDI)
276-
cd := make([]*pb.CDIDevice, len(cdiDevices))
277-
for i, d := range cdiDevices {
278-
cd[i] = &pb.CDIDevice{
279-
Name: d.Name,
280-
}
281-
}
282-
peo.CdiDevices = cd
283-
}
284-
285271
if network != NetModeSandbox {
286272
addCap(&e.constraints, pb.CapExecMetaNetwork)
287273
}
@@ -337,6 +323,18 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []
337323
addCap(&e.constraints, pb.CapExecMountSSH)
338324
}
339325

326+
if len(e.cdiDevices) > 0 {
327+
addCap(&e.constraints, pb.CapExecMetaCDI)
328+
cd := make([]*pb.CDIDevice, len(e.cdiDevices))
329+
for i, d := range e.cdiDevices {
330+
cd[i] = &pb.CDIDevice{
331+
Name: d.Name,
332+
Optional: d.Optional,
333+
}
334+
}
335+
peo.CdiDevices = cd
336+
}
337+
340338
if e.constraints.Platform == nil {
341339
p, err := getPlatform(e.base)(ctx, c)
342340
if err != nil {
@@ -640,12 +638,41 @@ func AddUlimit(name UlimitName, soft int64, hard int64) RunOption {
640638
})
641639
}
642640

643-
func AddCDIDevice(name string) RunOption {
641+
func AddCDIDevice(opts ...CDIDeviceOption) RunOption {
644642
return runOptionFunc(func(ei *ExecInfo) {
645-
ei.State = ei.State.AddCDIDevice(name)
643+
c := &CDIDeviceInfo{}
644+
for _, opt := range opts {
645+
opt.SetCDIDeviceOption(c)
646+
}
647+
ei.CDIDevices = append(ei.CDIDevices, *c)
648+
})
649+
}
650+
651+
type CDIDeviceOption interface {
652+
SetCDIDeviceOption(*CDIDeviceInfo)
653+
}
654+
655+
type cdiDeviceOptionFunc func(*CDIDeviceInfo)
656+
657+
func (fn cdiDeviceOptionFunc) SetCDIDeviceOption(ci *CDIDeviceInfo) {
658+
fn(ci)
659+
}
660+
661+
func CDIDeviceName(name string) CDIDeviceOption {
662+
return cdiDeviceOptionFunc(func(ci *CDIDeviceInfo) {
663+
ci.Name = name
646664
})
647665
}
648666

667+
var CDIDeviceOptional = cdiDeviceOptionFunc(func(ci *CDIDeviceInfo) {
668+
ci.Optional = true
669+
})
670+
671+
type CDIDeviceInfo struct {
672+
Name string
673+
Optional bool
674+
}
675+
649676
func ValidExitCodes(codes ...int) RunOption {
650677
return runOptionFunc(func(ei *ExecInfo) {
651678
ei.State = validExitCodes(codes...)(ei.State)
@@ -837,6 +864,7 @@ type ExecInfo struct {
837864
ProxyEnv *ProxyEnv
838865
Secrets []SecretInfo
839866
SSH []SSHInfo
867+
CDIDevices []CDIDeviceInfo
840868
}
841869

842870
type MountInfo struct {

client/llb/meta.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ var (
2424
keyExtraHost = contextKeyT("llb.exec.extrahost")
2525
keyHostname = contextKeyT("llb.exec.hostname")
2626
keyUlimit = contextKeyT("llb.exec.ulimit")
27-
keyDevice = contextKeyT("llb.exec.device")
2827
keyCgroupParent = contextKeyT("llb.exec.cgroup.parent")
2928
keyUser = contextKeyT("llb.exec.user")
3029
keyValidExitCodes = contextKeyT("llb.exec.validexitcodes")
@@ -306,33 +305,6 @@ func getUlimit(s State) func(context.Context, *Constraints) ([]*pb.Ulimit, error
306305
}
307306
}
308307

309-
func cdiDevice(name string) StateOption {
310-
return func(s State) State {
311-
return s.withValue(keyDevice, func(ctx context.Context, c *Constraints) (interface{}, error) {
312-
v, err := getCDIDevice(s)(ctx, c)
313-
if err != nil {
314-
return nil, err
315-
}
316-
return append(v, &pb.CDIDevice{
317-
Name: name,
318-
}), nil
319-
})
320-
}
321-
}
322-
323-
func getCDIDevice(s State) func(context.Context, *Constraints) ([]*pb.CDIDevice, error) {
324-
return func(ctx context.Context, c *Constraints) ([]*pb.CDIDevice, error) {
325-
v, err := s.getValue(keyDevice)(ctx, c)
326-
if err != nil {
327-
return nil, err
328-
}
329-
if v != nil {
330-
return v.([]*pb.CDIDevice), nil
331-
}
332-
return nil, nil
333-
}
334-
}
335-
336308
func cgroupParent(cp string) StateOption {
337309
return func(s State) State {
338310
return s.WithValue(keyCgroupParent, cp)

client/llb/state.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ func (s State) Run(ro ...RunOption) ExecState {
295295
}
296296
exec.secrets = ei.Secrets
297297
exec.ssh = ei.SSH
298+
exec.cdiDevices = ei.CDIDevices
298299

299300
return ExecState{
300301
State: s.WithOutput(exec.Output()),
@@ -476,12 +477,6 @@ func (s State) AddUlimit(name UlimitName, soft int64, hard int64) State {
476477
return ulimit(name, soft, hard)(s)
477478
}
478479

479-
// AddCDIDevice sets the fully qualified CDI device name.
480-
// https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md
481-
func (s State) AddCDIDevice(name string) State {
482-
return cdiDevice(name)(s)
483-
}
484-
485480
// WithCgroupParent sets the parent cgroup for any containers created from this state.
486481
// This is useful when you want to apply resource constraints to a group of containers.
487482
// Cgroups are Linux specific and only applies to containers created from this state such as via `[State.Run]`

executor/oci/spec_linux.go

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -157,29 +157,43 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt
157157
if len(devices) == 0 {
158158
return nil, nil
159159
}
160-
var dd []string
161-
for _, d := range devices {
162-
if d == nil {
163-
continue
164-
}
165-
if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil {
166-
return nil, errors.Wrapf(err, "invalid CDI device name %s", d.Name)
167-
}
168-
dd = append(dd, d.Name)
169-
}
170160

171-
withCDIDevices := func(devices ...string) oci.SpecOpts {
161+
withCDIDevices := func(devices []*pb.CDIDevice) oci.SpecOpts {
172162
return func(ctx context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error {
173-
if len(devices) == 0 {
174-
return nil
175-
}
176163
if err := manager.Refresh(); err != nil {
177164
bklog.G(ctx).Warnf("CDI registry refresh failed: %v", err)
178165
}
179-
bklog.G(ctx).Debugf("Injecting CDI devices %v", devices)
180-
if _, err := manager.InjectDevices(s, devices...); err != nil {
166+
167+
registeredDevices := manager.ListDevices()
168+
isDeviceRegistered := func(device *pb.CDIDevice) bool {
169+
for _, name := range registeredDevices {
170+
if device.Name == name {
171+
return true
172+
}
173+
}
174+
return false
175+
}
176+
177+
var dd []string
178+
for _, d := range devices {
179+
if d == nil {
180+
continue
181+
}
182+
if _, _, _, err := parser.ParseQualifiedName(d.Name); err != nil {
183+
return errors.Wrapf(err, "invalid CDI device name %s", d.Name)
184+
}
185+
if !isDeviceRegistered(d) && d.Optional {
186+
bklog.G(ctx).Warnf("Optional CDI device %q is not registered", d.Name)
187+
continue
188+
}
189+
dd = append(dd, d.Name)
190+
}
191+
192+
bklog.G(ctx).Debugf("Injecting CDI devices %v", dd)
193+
if _, err := manager.InjectDevices(s, dd...); err != nil {
181194
return errors.Wrapf(err, "CDI device injection failed")
182195
}
196+
183197
// One crucial thing to keep in mind is that CDI device injection
184198
// might add OCI Spec environment variables, hooks, and mounts as
185199
// well. Therefore, it is important that none of the corresponding
@@ -189,7 +203,7 @@ func generateCDIOpts(manager *cdi.Cache, devices []*pb.CDIDevice) ([]oci.SpecOpt
189203
}
190204

191205
return []oci.SpecOpts{
192-
withCDIDevices(dd...),
206+
withCDIDevices(devices),
193207
}, nil
194208
}
195209

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,10 +1305,15 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
13051305
}
13061306
}
13071307

1308-
// TODO: use entitlements
13091308
if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaCDI) == nil {
1310-
for _, u := range dopt.devices {
1311-
opt = append(opt, llb.AddCDIDevice(u.Name))
1309+
for _, device := range dopt.devices {
1310+
deviceOpts := []llb.CDIDeviceOption{
1311+
llb.CDIDeviceName(device.Name),
1312+
}
1313+
if device.Optional {
1314+
deviceOpts = append(deviceOpts, llb.CDIDeviceOptional)
1315+
}
1316+
opt = append(opt, llb.AddCDIDevice(deviceOpts...))
13121317
}
13131318
runDevices, err := dispatchRunDevices(c)
13141319
if err != nil {

frontend/dockerfile/dockerfile2llb/convert_rundevice.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func dispatchRunDevices(c *instructions.RunCommand) ([]llb.RunOption, error) {
1111
var out []llb.RunOption
1212
devices := instructions.GetDevices(c)
1313
for _, device := range devices {
14-
out = append(out, llb.AddCDIDevice(device))
14+
out = append(out, llb.AddCDIDevice(llb.CDIDeviceName(device), llb.CDIDeviceOptional))
1515
}
1616
return out, nil
1717
}

0 commit comments

Comments
 (0)