Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.11.1
github.com/urfave/cli-altsrc/v3 v3.1.0
github.com/urfave/cli/v3 v3.5.0
golang.org/x/mod v0.29.0
github.com/urfave/cli/v3 v3.6.0
golang.org/x/mod v0.30.0
golang.org/x/sys v0.38.0
tags.cncf.io/container-device-interface v1.0.1
tags.cncf.io/container-device-interface/specs-go v1.0.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli-altsrc/v3 v3.1.0 h1:6E5+kXeAWmRxXlPgdEVf9VqVoTJ2MJci0UMpUi/w/bA=
github.com/urfave/cli-altsrc/v3 v3.1.0/go.mod h1:VcWVTGXcL3nrXUDJZagHAeUX702La3PKeWav7KpISqA=
github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw=
github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/urfave/cli/v3 v3.6.0 h1:oIdArVjkdIXHWg3iqxgmqwQGC8NM0JtdgwQAj2sRwFo=
github.com/urfave/cli/v3 v3.6.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
53 changes: 34 additions & 19 deletions internal/modifier/cdi.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,39 +166,25 @@ func filterAutomaticDevices(devices []string) []string {
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)

perModeIdentifiers := make(map[string][]string)
perModeDeviceClass := map[string]string{"auto": automaticDeviceClass}
uniqueModes := []string{"auto"}
seen := make(map[string]bool)
for _, device := range devices {
mode, id := getModeIdentifier(device)
logger.Debugf("Mapped %v to %v: %v", device, mode, id)
if !seen[mode] {
uniqueModes = append(uniqueModes, mode)
seen[mode] = true
}
if id != "" {
perModeIdentifiers[mode] = append(perModeIdentifiers[mode], id)
}
}
cdiModeIdentifiers := cdiModeIdentfiersFromDevices(devices...)

logger.Debugf("Per-mode identifiers: %v", perModeIdentifiers)
logger.Debugf("Per-mode identifiers: %v", cdiModeIdentifiers)
var modifiers oci.SpecModifiers
for _, mode := range uniqueModes {
for _, mode := range cdiModeIdentifiers.modes {
cdilib, err := nvcdi.New(
nvcdi.WithLogger(logger),
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
nvcdi.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
nvcdi.WithVendor(automaticDeviceVendor),
nvcdi.WithClass(perModeDeviceClass[mode]),
nvcdi.WithClass(cdiModeIdentifiers.deviceClassByMode[mode]),
nvcdi.WithMode(mode),
nvcdi.WithFeatureFlags(cfg.NVIDIAContainerRuntimeConfig.Modes.JitCDI.NVCDIFeatureFlags...),
)
if err != nil {
return nil, fmt.Errorf("failed to construct CDI library for mode %q: %w", mode, err)
}

spec, err := cdilib.GetSpec(perModeIdentifiers[mode]...)
spec, err := cdilib.GetSpec(cdiModeIdentifiers.idsByMode[mode]...)
if err != nil {
return nil, fmt.Errorf("failed to generate CDI spec for mode %q: %w", mode, err)
}
Expand All @@ -217,6 +203,35 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, de
return modifiers, nil
}

type cdiModeIdentifiers struct {
modes []string
idsByMode map[string][]string
deviceClassByMode map[string]string
}

func cdiModeIdentfiersFromDevices(devices ...string) *cdiModeIdentifiers {
perModeIdentifiers := make(map[string][]string)
perModeDeviceClass := map[string]string{"auto": automaticDeviceClass}
var uniqueModes []string
seen := make(map[string]bool)
for _, device := range devices {
mode, id := getModeIdentifier(device)
if !seen[mode] {
uniqueModes = append(uniqueModes, mode)
seen[mode] = true
}
if id != "" {
perModeIdentifiers[mode] = append(perModeIdentifiers[mode], id)
}
}

return &cdiModeIdentifiers{
modes: uniqueModes,
idsByMode: perModeIdentifiers,
deviceClassByMode: perModeDeviceClass,
}
}

func getModeIdentifier(device string) (string, string) {
if !strings.HasPrefix(device, "mode=") {
return "auto", strings.TrimPrefix(device, automaticDevicePrefix)
Expand Down
83 changes: 83 additions & 0 deletions internal/modifier/cdi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,86 @@ func TestDeviceRequests(t *testing.T) {
})
}
}

func Test_cdiModeIdentfiersFromDevices(t *testing.T) {
testCases := []struct {
description string
devices []string
expected *cdiModeIdentifiers
}{
{
description: "empty device list",
devices: []string{},
expected: &cdiModeIdentifiers{
modes: nil,
idsByMode: map[string][]string{},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "single automatic device",
devices: []string{"0"},
expected: &cdiModeIdentifiers{
modes: []string{"auto"},
idsByMode: map[string][]string{"auto": {"0"}},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "multiple automatic devices",
devices: []string{"0", "1"},
expected: &cdiModeIdentifiers{
modes: []string{"auto"},
idsByMode: map[string][]string{"auto": {"0", "1"}},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "device with explicit mode",
devices: []string{"mode=gds,id=foo"},
expected: &cdiModeIdentifiers{
modes: []string{"gds"},
idsByMode: map[string][]string{"gds": {"foo"}},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "mixed auto and explicit",
devices: []string{"0", "mode=gds,id=foo", "mode=gdrcopy,id=bar"},
expected: &cdiModeIdentifiers{
modes: []string{"auto", "gds", "gdrcopy"},
idsByMode: map[string][]string{
"auto": {"0"},
"gds": {"foo"},
"gdrcopy": {"bar"},
},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "device with only mode, no id",
devices: []string{"mode=nvswitch"},
expected: &cdiModeIdentifiers{
modes: []string{"nvswitch"},
idsByMode: map[string][]string{},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
{
description: "duplicate modes",
devices: []string{"mode=gds,id=x", "mode=gds,id=y", "mode=gds"},
expected: &cdiModeIdentifiers{
modes: []string{"gds"},
idsByMode: map[string][]string{"gds": {"x", "y"}},
deviceClassByMode: map[string]string{"auto": "gpu"},
},
},
}

for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
result := cdiModeIdentfiersFromDevices(tc.devices...)
require.EqualValues(t, tc.expected, result)
})
}
}
1 change: 1 addition & 0 deletions tests/e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ GINKGO_BIN := $(CURDIR)/bin/ginkgo
# - nvidia-container-cli
# - docker
# - nvidia-cdi-refresh
# - containerd
GINKGO_FOCUS ?=

test: $(GINKGO_BIN)
Expand Down
10 changes: 10 additions & 0 deletions tests/e2e/nvidia-container-toolkit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
Expect(err).ToNot(HaveOccurred())
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda-12.9/compat/"))
})

It("should create a single ld.so.conf.d config file", func(ctx context.Context) {
lsout, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all nvcr.io/nvidia/cuda:12.9.0-base-ubi8 bash -c \"ls -l /etc/ld.so.conf.d/00-compat-*.conf\"")
Expect(err).ToNot(HaveOccurred())
Expect(lsout).To(WithTransform(
func(s string) []string {
return strings.Split(strings.TrimSpace(s), "\n")
}, HaveLen(1),
))
})
})

When("Disabling device node creation", Ordered, func() {
Expand Down
Loading