diff --git a/CHANGELOG.md b/CHANGELOG.md index 44b74bb0..d2d515f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Create file in `etc/ld.so.conf.d` with permissions `644` to support non-root containers. * Generate CDI specification files with `644` permissions to allow rootless applications (e.g. podman) * Add `nvidia-ctk cdi list` command to show the known CDI devices. +* Add support for generating merged devices (e.g. `all` device) to the nvcdi API. ## v1.13.1 diff --git a/cmd/nvidia-ctk/cdi/generate/generate.go b/cmd/nvidia-ctk/cdi/generate/generate.go index 02518f56..7d27685a 100644 --- a/cmd/nvidia-ctk/cdi/generate/generate.go +++ b/cmd/nvidia-ctk/cdi/generate/generate.go @@ -230,28 +230,16 @@ func (m command) generateSpec(opts *options) (spec.Interface, error) { return nil, fmt.Errorf("failed to create edits common for entities: %v", err) } - spec, err := spec.New( + return spec.New( spec.WithVendor(opts.vendor), spec.WithClass(opts.class), spec.WithDeviceSpecs(deviceSpecs), spec.WithEdits(*commonEdits.ContainerEdits), spec.WithFormat(opts.format), + spec.WithMergedDeviceOptions( + transform.WithName(allDeviceName), + transform.WithSkipIfExists(true), + ), spec.WithPermissions(0644), ) - if err != nil { - return nil, fmt.Errorf("failed to create CDI spec: %v", err) - } - - addAllDevice, err := transform.NewMergedDevice( - transform.WithName(allDeviceName), - transform.WithSkipIfExists(true), - ) - if err != nil { - return nil, fmt.Errorf("failed to create merged device: %v", err) - } - if err := addAllDevice.Transform(spec.Raw()); err != nil { - return nil, fmt.Errorf("failed to add merged device: %v", err) - } - - return spec, nil } diff --git a/pkg/nvcdi/lib.go b/pkg/nvcdi/lib.go index d692b9ff..fd5e2e54 100644 --- a/pkg/nvcdi/lib.go +++ b/pkg/nvcdi/lib.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" "github.com/sirupsen/logrus" "gitlab.com/nvidia/cloud-native/go-nvlib/pkg/nvlib/device" "gitlab.com/nvidia/cloud-native/go-nvlib/pkg/nvlib/info" @@ -31,6 +32,8 @@ type wrapper struct { vendor string class string + + mergedDeviceOptions []transform.MergedDeviceOption } type nvcdilib struct { @@ -46,6 +49,8 @@ type nvcdilib struct { class string infolib info.Interface + + mergedDeviceOptions []transform.MergedDeviceOption } // New creates a new nvcdi library @@ -106,9 +111,10 @@ func New(opts ...Option) (Interface, error) { } w := wrapper{ - Interface: lib, - vendor: l.vendor, - class: l.class, + Interface: lib, + vendor: l.vendor, + class: l.class, + mergedDeviceOptions: l.mergedDeviceOptions, } return &w, nil } @@ -130,8 +136,8 @@ func (l *wrapper) GetSpec() (spec.Interface, error) { spec.WithEdits(*edits.ContainerEdits), spec.WithVendor(l.vendor), spec.WithClass(l.class), + spec.WithMergedDeviceOptions(l.mergedDeviceOptions...), ) - } // resolveMode resolves the mode for CDI spec generation based on the current system. diff --git a/pkg/nvcdi/options.go b/pkg/nvcdi/options.go index 19aef93c..254c6e0e 100644 --- a/pkg/nvcdi/options.go +++ b/pkg/nvcdi/options.go @@ -17,6 +17,7 @@ package nvcdi import ( + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" "github.com/sirupsen/logrus" "gitlab.com/nvidia/cloud-native/go-nvlib/pkg/nvlib/device" "gitlab.com/nvidia/cloud-native/go-nvlib/pkg/nvml" @@ -87,3 +88,11 @@ func WithClass(class string) Option { o.class = class } } + +// WithMergedDeviceOptions sets the merged device options for the library +// If these are not set, no merged device will be generated. +func WithMergedDeviceOptions(opts ...transform.MergedDeviceOption) Option { + return func(o *nvcdilib) { + o.mergedDeviceOptions = opts + } +} diff --git a/pkg/nvcdi/spec/builder.go b/pkg/nvcdi/spec/builder.go index a0158132..0a867a55 100644 --- a/pkg/nvcdi/spec/builder.go +++ b/pkg/nvcdi/spec/builder.go @@ -33,8 +33,10 @@ type builder struct { deviceSpecs []specs.Device edits specs.ContainerEdits format string - noSimplify bool - permissions os.FileMode + + mergedDeviceOptions []transform.MergedDeviceOption + noSimplify bool + permissions os.FileMode } // newBuilder creates a new spec builder with the supplied options @@ -95,6 +97,16 @@ func (o *builder) Build() (*spec, error) { } } + if len(o.mergedDeviceOptions) > 0 { + merge, err := transform.NewMergedDevice(o.mergedDeviceOptions...) + if err != nil { + return nil, fmt.Errorf("failed to create merged device transformer: %v", err) + } + if err := merge.Transform(raw); err != nil { + return nil, fmt.Errorf("failed to merge devices: %v", err) + } + } + s := spec{ Spec: raw, format: o.format, @@ -169,3 +181,10 @@ func WithPermissions(permissions os.FileMode) Option { o.permissions = permissions } } + +// WithMergedDeviceOptions sets the options for generating a merged device. +func WithMergedDeviceOptions(opts ...transform.MergedDeviceOption) Option { + return func(o *builder) { + o.mergedDeviceOptions = opts + } +}