mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Compare commits
15 Commits
pull-reque
...
pull-reque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
699608902b | ||
|
|
d4b331fbbb | ||
|
|
f3b730c805 | ||
|
|
1cfaef4b01 | ||
|
|
76b6d4d38f | ||
|
|
4523b2e35d | ||
|
|
d757f6e68c | ||
|
|
5d5166cbb6 | ||
|
|
3df59b955a | ||
|
|
33280cd2b2 | ||
|
|
3306d5081e | ||
|
|
7c3ab75d08 | ||
|
|
71985df972 | ||
|
|
4255d73d89 | ||
|
|
9bdb74aec2 |
@@ -69,47 +69,47 @@ func TestInstall(t *testing.T) {
|
||||
cdiEnabled: true,
|
||||
expectedCdiSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
kind: example.com/class
|
||||
devices:
|
||||
- name: all
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia0
|
||||
hostPath: /host/driver/root/dev/nvidia0
|
||||
- path: /dev/nvidiactl
|
||||
hostPath: /host/driver/root/dev/nvidiactl
|
||||
- path: /dev/nvidia-caps-imex-channels/channel0
|
||||
hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel0
|
||||
- path: /dev/nvidia-caps-imex-channels/channel1
|
||||
hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel1
|
||||
- path: /dev/nvidia-caps-imex-channels/channel2047
|
||||
hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel2047
|
||||
containerEdits:
|
||||
env:
|
||||
- NVIDIA_VISIBLE_DEVICES=void
|
||||
hooks:
|
||||
- args:
|
||||
- hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
args:
|
||||
- nvidia-cdi-hook
|
||||
- create-symlinks
|
||||
- --link
|
||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||
hookName: createContainer
|
||||
- hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
- args:
|
||||
args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
mounts:
|
||||
- containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
hostPath: /host/driver/root/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
- hostPath: /host/driver/root/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
options:
|
||||
- ro
|
||||
- nosuid
|
||||
- nodev
|
||||
- bind
|
||||
devices:
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /host/driver/root/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
- hostPath: /host/driver/root/dev/nvidiactl
|
||||
path: /dev/nvidiactl
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel0
|
||||
path: /dev/nvidia-caps-imex-channels/channel0
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel1
|
||||
path: /dev/nvidia-caps-imex-channels/channel1
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel2047
|
||||
path: /dev/nvidia-caps-imex-channels/channel2047
|
||||
name: all
|
||||
kind: example.com/class
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -141,6 +141,9 @@ swarm-resource = ""
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
@@ -202,6 +205,9 @@ swarm-resource = ""
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
@@ -266,6 +272,9 @@ swarm-resource = ""
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
@@ -327,6 +336,9 @@ swarm-resource = ""
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
@@ -410,6 +422,9 @@ swarm-resource = ""
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
@@ -65,53 +65,53 @@ func TestGenerateSpec(t *testing.T) {
|
||||
},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
kind: example.com/device
|
||||
devices:
|
||||
- name: "0"
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia0
|
||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
- name: all
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia0
|
||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidiactl
|
||||
path: /dev/nvidiactl
|
||||
env:
|
||||
- NVIDIA_VISIBLE_DEVICES=void
|
||||
deviceNodes:
|
||||
- path: /dev/nvidiactl
|
||||
hostPath: {{ .driverRoot }}/dev/nvidiactl
|
||||
hooks:
|
||||
- args:
|
||||
- hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
args:
|
||||
- nvidia-cdi-hook
|
||||
- create-symlinks
|
||||
- --link
|
||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||
hookName: createContainer
|
||||
- hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
args:
|
||||
- nvidia-cdi-hook
|
||||
- enable-cuda-compat
|
||||
- --host-driver-version=999.88.77
|
||||
hookName: createContainer
|
||||
- hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
mounts:
|
||||
- containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
hostPath: {{ .driverRoot }}/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
- hostPath: {{ .driverRoot }}/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
options:
|
||||
- ro
|
||||
- nosuid
|
||||
- nodev
|
||||
- bind
|
||||
devices:
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
name: "0"
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
name: all
|
||||
kind: example.com/device
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -145,21 +145,9 @@ func (m allPossible) getGPUDeviceNodes(gpu int) []deviceNode {
|
||||
// getNVCapDeviceNodes generates a list of cap device nodes for a given GPU.
|
||||
func (m allPossible) getNVCapDeviceNodes(gpu int) []deviceNode {
|
||||
var selectedCapMinors []nvcaps.MigMinor
|
||||
for gi := 0; ; gi++ {
|
||||
giCap := nvcaps.NewGPUInstanceCap(gpu, gi)
|
||||
giMinor, exist := m.migCaps[giCap]
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
selectedCapMinors = append(selectedCapMinors, giMinor)
|
||||
for ci := 0; ; ci++ {
|
||||
ciCap := nvcaps.NewComputeInstanceCap(gpu, gi, ci)
|
||||
ciMinor, exist := m.migCaps[ciCap]
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
selectedCapMinors = append(selectedCapMinors, ciMinor)
|
||||
}
|
||||
|
||||
for _, capMinors := range m.migCaps.FilterForGPU(nvcaps.Index(gpu)) {
|
||||
selectedCapMinors = append(selectedCapMinors, capMinors)
|
||||
}
|
||||
|
||||
var deviceNodes []deviceNode
|
||||
|
||||
4
go.mod
4
go.mod
@@ -14,8 +14,8 @@ require (
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/sys v0.31.0
|
||||
tags.cncf.io/container-device-interface v0.8.1
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0
|
||||
tags.cncf.io/container-device-interface v1.0.0
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
8
go.sum
8
go.sum
@@ -92,7 +92,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.8.1 h1:c0jN4Mt6781jD67NdPajmZlD1qrqQyov/Xfoab37lj0=
|
||||
tags.cncf.io/container-device-interface v0.8.1/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
||||
tags.cncf.io/container-device-interface v1.0.0 h1:fbwPQiWZNpXUb9Os6t6JW52rsOppTFUbeJOpNtN1TmI=
|
||||
tags.cncf.io/container-device-interface v1.0.0/go.mod h1:mmi2aRGmOjK/6NR3TXjLpEIarOJ9qwgZjQ3nTIRwAaA=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8=
|
||||
tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ=
|
||||
|
||||
@@ -121,6 +121,9 @@ func GetDefault() (*Config, error) {
|
||||
AnnotationPrefixes: []string{cdi.AnnotationPrefix},
|
||||
SpecDirs: cdi.DefaultSpecDirs,
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
|
||||
@@ -74,6 +74,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -102,6 +105,7 @@ func TestGetConfig(t *testing.T) {
|
||||
"nvidia-container-runtime.modes.cdi.annotation-prefixes = [\"cdi.k8s.io/\", \"example.vendor.com/\",]",
|
||||
"nvidia-container-runtime.modes.cdi.spec-dirs = [\"/except/etc/cdi\", \"/not/var/run/cdi\",]",
|
||||
"nvidia-container-runtime.modes.csv.mount-spec-path = \"/not/etc/nvidia-container-runtime/host-files-for-container.d\"",
|
||||
"nvidia-container-runtime.modes.jit-cdi.load-kernel-modules = [\"foo\"]",
|
||||
"nvidia-container-runtime-hook.path = \"/foo/bar/nvidia-container-runtime-hook\"",
|
||||
"nvidia-ctk.path = \"/foo/bar/nvidia-ctk\"",
|
||||
},
|
||||
@@ -134,6 +138,9 @@ func TestGetConfig(t *testing.T) {
|
||||
"/not/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -178,6 +185,9 @@ func TestGetConfig(t *testing.T) {
|
||||
"/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -213,6 +223,8 @@ func TestGetConfig(t *testing.T) {
|
||||
"spec-dirs = [\"/except/etc/cdi\", \"/not/var/run/cdi\",]",
|
||||
"[nvidia-container-runtime.modes.csv]",
|
||||
"mount-spec-path = \"/not/etc/nvidia-container-runtime/host-files-for-container.d\"",
|
||||
"[nvidia-container-runtime.modes.jit-cdi]",
|
||||
"load-kernel-modules = [\"foo\"]",
|
||||
"[nvidia-container-runtime-hook]",
|
||||
"path = \"/foo/bar/nvidia-container-runtime-hook\"",
|
||||
"[nvidia-ctk]",
|
||||
@@ -247,6 +259,9 @@ func TestGetConfig(t *testing.T) {
|
||||
"/not/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -283,6 +298,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -322,6 +340,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
|
||||
@@ -29,8 +29,9 @@ type RuntimeConfig struct {
|
||||
|
||||
// modesConfig defines (optional) per-mode configs
|
||||
type modesConfig struct {
|
||||
CSV csvModeConfig `toml:"csv"`
|
||||
CDI cdiModeConfig `toml:"cdi"`
|
||||
CSV csvModeConfig `toml:"csv"`
|
||||
CDI cdiModeConfig `toml:"cdi"`
|
||||
JitCDI jitCDIModeConfig `toml:"jit-cdi"`
|
||||
}
|
||||
|
||||
type cdiModeConfig struct {
|
||||
@@ -45,3 +46,11 @@ type cdiModeConfig struct {
|
||||
type csvModeConfig struct {
|
||||
MountSpecPath string `toml:"mount-spec-path"`
|
||||
}
|
||||
|
||||
type jitCDIModeConfig struct {
|
||||
// LoadKernelModules defines the names of the kernel modules that should be
|
||||
// loaded before generating a just-in-time CDI specification.
|
||||
// The module names must start with `nvidia` and if no modules are specified
|
||||
// no kernel modules are loaded.
|
||||
LoadKernelModules []string `toml:"load-kernel-modules"`
|
||||
}
|
||||
|
||||
@@ -74,6 +74,9 @@ spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "nvidia-container-runtime-hook"
|
||||
skip-mode-detection = false
|
||||
|
||||
@@ -45,7 +45,7 @@ func New(opts ...Option) Devices {
|
||||
type Option func(*builder)
|
||||
|
||||
// WithDeviceToMajor specifies an explicit device name to major number map.
|
||||
func WithDeviceToMajor(deviceToMajor map[string]int) Option {
|
||||
func WithDeviceToMajor(deviceToMajor map[string]uint32) Option {
|
||||
return func(b *builder) {
|
||||
b.asMap = make(devices)
|
||||
for name, major := range deviceToMajor {
|
||||
|
||||
@@ -45,7 +45,7 @@ const (
|
||||
type Name string
|
||||
|
||||
// Major represents a device major as specified under /proc/devices
|
||||
type Major int
|
||||
type Major uint32
|
||||
|
||||
// Devices represents the set of devices under /proc/devices
|
||||
//
|
||||
@@ -130,8 +130,8 @@ func nvidiaDeviceFrom(reader io.Reader) (Devices, error) {
|
||||
return nvidiaDevices, nil
|
||||
}
|
||||
|
||||
func devicesFrom(reader io.Reader) map[string]int {
|
||||
allDevices := make(map[string]int)
|
||||
func devicesFrom(reader io.Reader) map[string]uint32 {
|
||||
allDevices := make(map[string]uint32)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
device, major, err := processProcDeviceLine(scanner.Text())
|
||||
@@ -143,11 +143,11 @@ func devicesFrom(reader io.Reader) map[string]int {
|
||||
return allDevices
|
||||
}
|
||||
|
||||
func processProcDeviceLine(line string) (string, int, error) {
|
||||
func processProcDeviceLine(line string) (string, uint32, error) {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
|
||||
var name string
|
||||
var major int
|
||||
var major uint32
|
||||
|
||||
n, _ := fmt.Sscanf(trimmed, "%d %s", &major, &name)
|
||||
if n == 2 {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNvidiaDevices(t *testing.T) {
|
||||
perDriverDeviceMaps := map[string]map[string]int{
|
||||
perDriverDeviceMaps := map[string]map[string]uint32{
|
||||
"pre550": {
|
||||
"nvidia-frontend": 195,
|
||||
"nvidia-nvlink": 234,
|
||||
@@ -100,7 +100,7 @@ func TestProcessDeviceFileLine(t *testing.T) {
|
||||
testCases := []struct {
|
||||
line string
|
||||
name string
|
||||
major int
|
||||
major uint32
|
||||
err bool
|
||||
}{
|
||||
{"", "", 0, true},
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvmodules"
|
||||
)
|
||||
|
||||
// Driver represents a filesystem in which a set of drivers or devices is defined.
|
||||
@@ -125,3 +128,20 @@ func xdgDataDirs() []string {
|
||||
|
||||
return []string{"/usr/local/share", "/usr/share"}
|
||||
}
|
||||
|
||||
// LoadKmods loads the specified kernel modules in the driver root.
|
||||
// Errors in loading a module do not prevent other modules from being attempted.
|
||||
func (r *Driver) LoadKernelModules(moduleNames ...string) error {
|
||||
modules := nvmodules.New(
|
||||
nvmodules.WithLogger(r.logger),
|
||||
nvmodules.WithRoot(r.Root),
|
||||
)
|
||||
|
||||
var errs error
|
||||
for _, moduleName := range moduleNames {
|
||||
if err := modules.Load(moduleName); err != nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to load kernel module %q: %w", moduleName, err))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -22,11 +22,15 @@ import (
|
||||
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier/cdi"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvdevices"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
||||
)
|
||||
@@ -34,7 +38,7 @@ import (
|
||||
// NewCDIModifier creates an OCI spec modifier that determines the modifications to make based on the
|
||||
// CDI specifications available on the system. The NVIDIA_VISIBLE_DEVICES environment variable is
|
||||
// used to select the devices to include.
|
||||
func NewCDIModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec) (oci.SpecModifier, error) {
|
||||
func NewCDIModifier(logger logger.Interface, cfg *config.Config, driver *root.Driver, ociSpec oci.Spec) (oci.SpecModifier, error) {
|
||||
devices, err := getDevicesFromSpec(logger, ociSpec, cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get required devices from OCI specification: %v", err)
|
||||
@@ -50,7 +54,7 @@ func NewCDIModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spe
|
||||
return nil, fmt.Errorf("requesting a CDI device with vendor 'runtime.nvidia.com' is not supported when requesting other CDI devices")
|
||||
}
|
||||
if len(automaticDevices) > 0 {
|
||||
automaticModifier, err := newAutomaticCDISpecModifier(logger, cfg, automaticDevices)
|
||||
automaticModifier, err := newAutomaticCDISpecModifier(logger, cfg, driver, automaticDevices)
|
||||
if err == nil {
|
||||
return automaticModifier, nil
|
||||
}
|
||||
@@ -163,9 +167,9 @@ func filterAutomaticDevices(devices []string) []string {
|
||||
return automatic
|
||||
}
|
||||
|
||||
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
|
||||
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (oci.SpecModifier, error) {
|
||||
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
|
||||
spec, err := generateAutomaticCDISpec(logger, cfg, devices)
|
||||
spec, err := generateAutomaticCDISpec(logger, cfg, driver, devices)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate CDI spec: %w", err)
|
||||
}
|
||||
@@ -180,7 +184,7 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, de
|
||||
return cdiModifier, nil
|
||||
}
|
||||
|
||||
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devices []string) (spec.Interface, error) {
|
||||
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (spec.Interface, error) {
|
||||
cdilib, err := nvcdi.New(
|
||||
nvcdi.WithLogger(logger),
|
||||
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
||||
@@ -192,12 +196,19 @@ func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devic
|
||||
return nil, fmt.Errorf("failed to construct CDI library: %w", err)
|
||||
}
|
||||
|
||||
identifiers := []string{}
|
||||
// TODO: Consider moving this into the nvcdi API.
|
||||
if err := driver.LoadKernelModules(cfg.NVIDIAContainerRuntimeConfig.Modes.JitCDI.LoadKernelModules...); err != nil {
|
||||
logger.Warningf("Ignoring error(s) loading kernel modules: %v", err)
|
||||
}
|
||||
|
||||
var identifiers []string
|
||||
for _, device := range devices {
|
||||
_, _, id := parser.ParseDevice(device)
|
||||
identifiers = append(identifiers, id)
|
||||
}
|
||||
|
||||
tryCreateDeviceNodes(logger, driver, identifiers...)
|
||||
|
||||
deviceSpecs, err := cdilib.GetDeviceSpecsByID(identifiers...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get CDI device specs: %w", err)
|
||||
@@ -215,3 +226,27 @@ func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devic
|
||||
spec.WithClass("gpu"),
|
||||
)
|
||||
}
|
||||
|
||||
func tryCreateDeviceNodes(logger logger.Interface, driver *root.Driver, identifiers ...string) {
|
||||
devices, err := nvdevices.New(
|
||||
nvdevices.WithLogger(logger),
|
||||
nvdevices.WithDevRoot(driver.Root),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Warningf("Failed to create devices library: %v", err)
|
||||
return
|
||||
}
|
||||
if err := devices.CreateNVIDIAControlDevices(); err != nil {
|
||||
logger.Warningf("Failed to create control devices: %v", err)
|
||||
}
|
||||
if err := devices.CreateNVIDIACapsControlDeviceNodes(); err != nil {
|
||||
logger.Warningf("Failed to create nvidia-caps control devices: %v", err)
|
||||
}
|
||||
|
||||
for _, id := range identifiers {
|
||||
identifier := device.Identifier(id)
|
||||
if err := devices.CreateDeviceNodes(identifier); err != nil {
|
||||
logger.Warningf("Error creating device nodes for %v: %v", identifier, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,20 @@ const (
|
||||
nvcapsDevicePath = "/dev/nvidia-caps"
|
||||
)
|
||||
|
||||
// MigMinor represents the minor number of a MIG device
|
||||
type MigMinor int
|
||||
// An Index represents a gpu, ci, or gi index.
|
||||
// We use uint32 as this typically maps to a device minor number.
|
||||
type Index uint32
|
||||
|
||||
// MigCap represents the path to a MIG cap file
|
||||
// MigMinor represents the minor number of a MIG device
|
||||
type MigMinor Index
|
||||
|
||||
// MigCap represents the path to a MIG cap file.
|
||||
// These are listed in /proc/driver/nvidia-caps/mig-minors and have one of the
|
||||
// follown forms:
|
||||
// - config
|
||||
// - monitor
|
||||
// - gpu{{ .gpuIndex }}/gi{{ .gi }}/access
|
||||
// - gpu{{ .gpuIndex }}/gi{{ .gi }}/ci {{ .ci }}/access
|
||||
type MigCap string
|
||||
|
||||
// MigCaps stores a map of MIG cap file paths to MIG minors
|
||||
@@ -47,16 +57,41 @@ type MigCaps map[MigCap]MigMinor
|
||||
|
||||
// NewGPUInstanceCap creates a MigCap for the specified MIG GPU instance.
|
||||
// A GPU instance is uniquely defined by the GPU minor number and GI instance ID.
|
||||
func NewGPUInstanceCap(gpu, gi int) MigCap {
|
||||
func NewGPUInstanceCap[T uint32 | int | Index](gpu, gi T) MigCap {
|
||||
return MigCap(fmt.Sprintf("gpu%d/gi%d/access", gpu, gi))
|
||||
}
|
||||
|
||||
// NewComputeInstanceCap creates a MigCap for the specified MIG Compute instance.
|
||||
// A GPU instance is uniquely defined by the GPU minor number, GI instance ID, and CI instance ID.
|
||||
func NewComputeInstanceCap(gpu, gi, ci int) MigCap {
|
||||
func NewComputeInstanceCap[T uint32 | int | Index](gpu, gi, ci T) MigCap {
|
||||
return MigCap(fmt.Sprintf("gpu%d/gi%d/ci%d/access", gpu, gi, ci))
|
||||
}
|
||||
|
||||
// FilterForGPU limits the MIG Caps to those associated with a particular GPU.
|
||||
func (m MigCaps) FilterForGPU(gpu Index) MigCaps {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
filtered := make(MigCaps)
|
||||
for gi := Index(0); ; gi++ {
|
||||
giCap := NewGPUInstanceCap(gpu, gi)
|
||||
giMinor, exist := m[giCap]
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
filtered[giCap] = giMinor
|
||||
for ci := Index(0); ; ci++ {
|
||||
ciCap := NewComputeInstanceCap(gpu, gi, ci)
|
||||
ciMinor, exist := m[ciCap]
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
filtered[ciCap] = ciMinor
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// GetCapDevicePath returns the path to the cap device for the specified cap.
|
||||
// An error is returned if the cap is invalid.
|
||||
func (m MigCaps) GetCapDevicePath(cap MigCap) (string, error) {
|
||||
@@ -113,7 +148,7 @@ func processMigMinorsLine(line string) (MigCap, MigMinor, error) {
|
||||
return "", 0, fmt.Errorf("invalid MIG minors line: '%v'", line)
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(parts[1])
|
||||
minor, err := strconv.ParseUint(parts[1], 10, 32)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("error reading MIG minor from '%v': %v", line, err)
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ensure, that SpecMock does implement Spec.
|
||||
|
||||
@@ -77,7 +77,7 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
|
||||
mode := info.ResolveAutoMode(logger, cfg.NVIDIAContainerRuntimeConfig.Mode, image)
|
||||
// We update the mode here so that we can continue passing just the config to other functions.
|
||||
cfg.NVIDIAContainerRuntimeConfig.Mode = mode
|
||||
modeModifier, err := newModeModifier(logger, mode, cfg, ociSpec, image)
|
||||
modeModifier, err := newModeModifier(logger, mode, cfg, driver, ociSpec, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,14 +107,14 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
|
||||
return modifiers, nil
|
||||
}
|
||||
|
||||
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, ociSpec oci.Spec, image image.CUDA) (oci.SpecModifier, error) {
|
||||
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, driver *root.Driver, ociSpec oci.Spec, image image.CUDA) (oci.SpecModifier, error) {
|
||||
switch mode {
|
||||
case "legacy":
|
||||
return modifier.NewStableRuntimeModifier(logger, cfg.NVIDIAContainerRuntimeHookConfig.Path), nil
|
||||
case "csv":
|
||||
return modifier.NewCSVModifier(logger, cfg, image)
|
||||
case "cdi":
|
||||
return modifier.NewCDIModifier(logger, cfg, ociSpec)
|
||||
return modifier.NewCDIModifier(logger, cfg, driver, ociSpec)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid runtime mode: %v", cfg.NVIDIAContainerRuntimeConfig.Mode)
|
||||
|
||||
122
internal/system/nvdevices/control-device-nodes.go
Normal file
122
internal/system/nvdevices/control-device-nodes.go
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvdevices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info/proc/devices"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
)
|
||||
|
||||
// A controlDeviceNode represents an NVIDIA devices node for control or meta devices.
|
||||
// Such device nodes are typically required regardless of which GPU is being accessed.
|
||||
type controlDeviceNode string
|
||||
|
||||
func (c controlDeviceNode) path() string {
|
||||
return filepath.Join("dev", string(c))
|
||||
}
|
||||
|
||||
// CreateNVIDIAControlDevices creates the NVIDIA control device nodes at the configured devRoot.
|
||||
func (m *Interface) CreateNVIDIAControlDevices() error {
|
||||
controlNodes := []controlDeviceNode{"nvidiactl", "nvidia-modeset", "nvidia-uvm", "nvidia-uvm-tools"}
|
||||
for _, node := range controlNodes {
|
||||
if err := m.createControlDeviceNode(node); err != nil {
|
||||
return fmt.Errorf("failed to create device node %s: %w", node, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNVIDIACapsControlDeviceNodes creates the nvidia-caps control device nodes at the configured devRoot.
|
||||
func (m *Interface) CreateNVIDIACapsControlDeviceNodes() error {
|
||||
capsMajor, exists := m.Get("nvidia-caps")
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs error
|
||||
for _, migCap := range []nvcaps.MigCap{"config", "monitor"} {
|
||||
migMinor, exists := m.migCaps[migCap]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
deviceNodePath := migMinor.DevicePath()
|
||||
if err := m.createDeviceNode(deviceNodePath, capsMajor, uint32(migMinor)); err != nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to create nvidia-caps device node %v: %w", deviceNodePath, err))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// createControlDeviceNode creates the specified NVIDIA device node at the configured devRoot.
|
||||
func (m *Interface) createControlDeviceNode(node controlDeviceNode) error {
|
||||
if !strings.HasPrefix(string(node), "nvidia") {
|
||||
return fmt.Errorf("invalid device node %q: %w", node, errInvalidDeviceNode)
|
||||
}
|
||||
|
||||
major, err := m.controlDeviceNodeMajor(node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine major: %w", err)
|
||||
}
|
||||
|
||||
minor, err := m.controlDeviceNodeMinor(node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine minor: %w", err)
|
||||
}
|
||||
|
||||
return m.createDeviceNode(node.path(), major, minor)
|
||||
}
|
||||
|
||||
// controlDeviceNodeMajor returns the major number for the specified NVIDIA control device node.
|
||||
// If the device node is not supported, an error is returned.
|
||||
func (m *Interface) controlDeviceNodeMajor(node controlDeviceNode) (devices.Major, error) {
|
||||
var valid bool
|
||||
var major devices.Major
|
||||
switch node {
|
||||
case "nvidia-uvm", "nvidia-uvm-tools":
|
||||
major, valid = m.Get(devices.NVIDIAUVM)
|
||||
case "nvidia-modeset", "nvidiactl":
|
||||
major, valid = m.Get(devices.NVIDIAGPU)
|
||||
}
|
||||
|
||||
if valid {
|
||||
return major, nil
|
||||
}
|
||||
|
||||
return 0, errInvalidDeviceNode
|
||||
}
|
||||
|
||||
// controlDeviceNodeMinor returns the minor number for the specified NVIDIA control device node.
|
||||
// If the device node is not supported, an error is returned.
|
||||
func (m *Interface) controlDeviceNodeMinor(node controlDeviceNode) (uint32, error) {
|
||||
switch node {
|
||||
case "nvidia-modeset":
|
||||
return devices.NVIDIAModesetMinor, nil
|
||||
case "nvidia-uvm-tools":
|
||||
return devices.NVIDIAUVMToolsMinor, nil
|
||||
case "nvidia-uvm":
|
||||
return devices.NVIDIAUVMMinor, nil
|
||||
case "nvidiactl":
|
||||
return devices.NVIDIACTLMinor, nil
|
||||
}
|
||||
|
||||
return 0, errInvalidDeviceNode
|
||||
}
|
||||
@@ -22,8 +22,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info/proc/devices"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
)
|
||||
|
||||
var errInvalidDeviceNode = errors.New("invalid device node")
|
||||
@@ -38,6 +41,8 @@ type Interface struct {
|
||||
// devRoot is the root directory where device nodes are expected to exist.
|
||||
devRoot string
|
||||
|
||||
migCaps nvcaps.MigCaps
|
||||
|
||||
mknoder
|
||||
}
|
||||
|
||||
@@ -62,6 +67,14 @@ func New(opts ...Option) (*Interface, error) {
|
||||
i.Devices = devices
|
||||
}
|
||||
|
||||
if i.migCaps == nil {
|
||||
migCaps, err := nvcaps.NewMigCaps()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load MIG caps: %w", err)
|
||||
}
|
||||
i.migCaps = migCaps
|
||||
}
|
||||
|
||||
if i.dryRun {
|
||||
i.mknoder = &mknodLogger{i.logger}
|
||||
} else {
|
||||
@@ -70,77 +83,40 @@ func New(opts ...Option) (*Interface, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// CreateNVIDIAControlDevices creates the NVIDIA control device nodes at the configured devRoot.
|
||||
func (m *Interface) CreateNVIDIAControlDevices() error {
|
||||
controlNodes := []string{"nvidiactl", "nvidia-modeset", "nvidia-uvm", "nvidia-uvm-tools"}
|
||||
for _, node := range controlNodes {
|
||||
err := m.CreateNVIDIADevice(node)
|
||||
// CreateDeviceNodes creates the device nodes for a device with the specified identifier.
|
||||
// A list of created device nodes are returned and an error.
|
||||
func (m *Interface) CreateDeviceNodes(id device.Identifier) error {
|
||||
switch {
|
||||
case id.IsGpuIndex():
|
||||
gpuIndex, err := toIndex(string(id))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create device node %s: %w", node, err)
|
||||
return fmt.Errorf("invalid GPU index: %v", id)
|
||||
}
|
||||
return m.createGPUDeviceNode(gpuIndex)
|
||||
case id.IsMigIndex():
|
||||
indices := strings.Split(string(id), ":")
|
||||
if len(indices) != 2 {
|
||||
return fmt.Errorf("invalid MIG index %v", id)
|
||||
}
|
||||
gpuIndex, err := toIndex(indices[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid parent index %v: %w", indices[0], err)
|
||||
}
|
||||
if err := m.createGPUDeviceNode(gpuIndex); err != nil {
|
||||
return fmt.Errorf("failed to create parent device node: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNVIDIADevice creates the specified NVIDIA device node at the configured devRoot.
|
||||
func (m *Interface) CreateNVIDIADevice(node string) error {
|
||||
node = filepath.Base(node)
|
||||
if !strings.HasPrefix(node, "nvidia") {
|
||||
return fmt.Errorf("invalid device node %q: %w", node, errInvalidDeviceNode)
|
||||
return m.createMigDeviceNodes(gpuIndex)
|
||||
case id.IsGpuUUID(), id.IsMigUUID(), id == "all":
|
||||
return m.createAllGPUDeviceNodes()
|
||||
default:
|
||||
return fmt.Errorf("invalid device identifier: %v", id)
|
||||
}
|
||||
|
||||
major, err := m.Major(node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine major: %w", err)
|
||||
}
|
||||
|
||||
minor, err := m.Minor(node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine minor: %w", err)
|
||||
}
|
||||
|
||||
return m.createDeviceNode(filepath.Join("dev", node), int(major), int(minor))
|
||||
}
|
||||
|
||||
// createDeviceNode creates the specified device node with the require major and minor numbers.
|
||||
// If a devRoot is configured, this is prepended to the path.
|
||||
func (m *Interface) createDeviceNode(path string, major int, minor int) error {
|
||||
func (m *Interface) createDeviceNode(path string, major devices.Major, minor uint32) error {
|
||||
path = filepath.Join(m.devRoot, path)
|
||||
return m.Mknode(path, major, minor)
|
||||
}
|
||||
|
||||
// Major returns the major number for the specified NVIDIA device node.
|
||||
// If the device node is not supported, an error is returned.
|
||||
func (m *Interface) Major(node string) (int64, error) {
|
||||
var valid bool
|
||||
var major devices.Major
|
||||
switch node {
|
||||
case "nvidia-uvm", "nvidia-uvm-tools":
|
||||
major, valid = m.Get(devices.NVIDIAUVM)
|
||||
case "nvidia-modeset", "nvidiactl":
|
||||
major, valid = m.Get(devices.NVIDIAGPU)
|
||||
}
|
||||
|
||||
if valid {
|
||||
return int64(major), nil
|
||||
}
|
||||
|
||||
return 0, errInvalidDeviceNode
|
||||
}
|
||||
|
||||
// Minor returns the minor number for the specified NVIDIA device node.
|
||||
// If the device node is not supported, an error is returned.
|
||||
func (m *Interface) Minor(node string) (int64, error) {
|
||||
switch node {
|
||||
case "nvidia-modeset":
|
||||
return devices.NVIDIAModesetMinor, nil
|
||||
case "nvidia-uvm-tools":
|
||||
return devices.NVIDIAUVMToolsMinor, nil
|
||||
case "nvidia-uvm":
|
||||
return devices.NVIDIAUVMMinor, nil
|
||||
case "nvidiactl":
|
||||
return devices.NVIDIACTLMinor, nil
|
||||
}
|
||||
|
||||
return 0, errInvalidDeviceNode
|
||||
return m.Mknode(path, uint32(major), minor)
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
nvidiaDevices := devices.New(
|
||||
devices.WithDeviceToMajor(map[string]int{
|
||||
devices.WithDeviceToMajor(map[string]uint32{
|
||||
"nvidia-frontend": 195,
|
||||
"nvidia-uvm": 243,
|
||||
}),
|
||||
)
|
||||
nvidia550Devices := devices.New(
|
||||
devices.WithDeviceToMajor(map[string]int{
|
||||
devices.WithDeviceToMajor(map[string]uint32{
|
||||
"nvidia": 195,
|
||||
"nvidia-uvm": 243,
|
||||
}),
|
||||
@@ -52,8 +52,8 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
expectedError error
|
||||
expectedCalls []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}
|
||||
}{
|
||||
{
|
||||
@@ -63,8 +63,8 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
mknodeError: nil,
|
||||
expectedCalls: []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}{
|
||||
{"/dev/nvidiactl", 195, 255},
|
||||
{"/dev/nvidia-modeset", 195, 254},
|
||||
@@ -79,8 +79,8 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
mknodeError: nil,
|
||||
expectedCalls: []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}{
|
||||
{"/dev/nvidiactl", 195, 255},
|
||||
{"/dev/nvidia-modeset", 195, 254},
|
||||
@@ -95,8 +95,8 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
mknodeError: nil,
|
||||
expectedCalls: []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}{
|
||||
{"/some/root/dev/nvidiactl", 195, 255},
|
||||
{"/some/root/dev/nvidia-modeset", 195, 254},
|
||||
@@ -112,8 +112,8 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
// We expect the first call to this to fail, and the rest to be skipped
|
||||
expectedCalls: []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}{
|
||||
{"/dev/nvidiactl", 195, 255},
|
||||
},
|
||||
@@ -132,7 +132,7 @@ func TestCreateControlDevices(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
mknode := &mknoderMock{
|
||||
MknodeFunc: func(string, int, int) error {
|
||||
MknodeFunc: func(string, uint32, uint32) error {
|
||||
return tc.mknodeError
|
||||
},
|
||||
}
|
||||
|
||||
88
internal/system/nvdevices/gpu-device-nodes.go
Normal file
88
internal/system/nvdevices/gpu-device-nodes.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvdevices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvpci"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info/proc/devices"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
)
|
||||
|
||||
type gpuIndex nvcaps.Index
|
||||
|
||||
func toIndex(index string) (gpuIndex, error) {
|
||||
i, err := strconv.ParseUint(index, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return gpuIndex(i), nil
|
||||
}
|
||||
|
||||
func (m *Interface) createGPUDeviceNode(gpu gpuIndex) error {
|
||||
major, exists := m.Get(devices.NVIDIAGPU)
|
||||
if !exists {
|
||||
return fmt.Errorf("failed to determine device major; nvidia kernel module may not be loaded")
|
||||
}
|
||||
|
||||
deviceNodePath := fmt.Sprintf("/dev/nvidia%d", gpu)
|
||||
if err := m.createDeviceNode(deviceNodePath, major, uint32(gpu)); err != nil {
|
||||
return fmt.Errorf("failed to create device node %v: %w", deviceNodePath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Interface) createMigDeviceNodes(gpu gpuIndex) error {
|
||||
capsMajor, exists := m.Get("nvidia-caps")
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
var errs error
|
||||
for _, capsDeviceMinor := range m.migCaps.FilterForGPU(nvcaps.Index(gpu)) {
|
||||
capDevicePath := capsDeviceMinor.DevicePath()
|
||||
err := m.createDeviceNode(capDevicePath, capsMajor, uint32(capsDeviceMinor))
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to create %v: %w", capDevicePath, err))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (m *Interface) createAllGPUDeviceNodes() error {
|
||||
gpus, err := nvpci.New(
|
||||
nvpci.WithPCIDevicesRoot(filepath.Join(m.devRoot, nvpci.PCIDevicesRoot)),
|
||||
nvpci.WithLogger(m.logger),
|
||||
).GetGPUs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get GPU information from PCI: %w", err)
|
||||
}
|
||||
|
||||
count := gpuIndex(len(gpus))
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs error
|
||||
for gpuIndex := gpuIndex(0); gpuIndex < count; gpuIndex++ {
|
||||
errs = errors.Join(errs, m.createGPUDeviceNode(gpuIndex))
|
||||
errs = errors.Join(errs, m.createMigDeviceNodes(gpuIndex))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
@@ -25,16 +25,16 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
//go:generate moq -stub -out mknod_mock.go . mknoder
|
||||
//go:generate moq -fmt=goimports -rm -stub -out mknod_mock.go . mknoder
|
||||
type mknoder interface {
|
||||
Mknode(string, int, int) error
|
||||
Mknode(string, uint32, uint32) error
|
||||
}
|
||||
|
||||
type mknodLogger struct {
|
||||
logger.Interface
|
||||
}
|
||||
|
||||
func (m *mknodLogger) Mknode(path string, major, minor int) error {
|
||||
func (m *mknodLogger) Mknode(path string, major uint32, minor uint32) error {
|
||||
m.Infof("Running: mknod --mode=0666 %s c %d %d", path, major, minor)
|
||||
return nil
|
||||
}
|
||||
@@ -43,7 +43,7 @@ type mknodUnix struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
func (m *mknodUnix) Mknode(path string, major, minor int) error {
|
||||
func (m *mknodUnix) Mknode(path string, major uint32, minor uint32) error {
|
||||
// TODO: Ensure that the existing device node has the correct properties.
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
m.logger.Infof("Skipping: %s already exists", path)
|
||||
@@ -52,7 +52,7 @@ func (m *mknodUnix) Mknode(path string, major, minor int) error {
|
||||
return fmt.Errorf("failed to stat %s: %v", path, err)
|
||||
}
|
||||
|
||||
err := unix.Mknod(path, unix.S_IFCHR, int(unix.Mkdev(uint32(major), uint32(minor))))
|
||||
err := unix.Mknod(path, unix.S_IFCHR, int(unix.Mkdev(major, minor)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ var _ mknoder = &mknoderMock{}
|
||||
//
|
||||
// // make and configure a mocked mknoder
|
||||
// mockedmknoder := &mknoderMock{
|
||||
// MknodeFunc: func(s string, n1 int, n2 int) error {
|
||||
// MknodeFunc: func(s string, v1 uint32, v2 uint32) error {
|
||||
// panic("mock out the Mknode method")
|
||||
// },
|
||||
// }
|
||||
@@ -28,7 +28,7 @@ var _ mknoder = &mknoderMock{}
|
||||
// }
|
||||
type mknoderMock struct {
|
||||
// MknodeFunc mocks the Mknode method.
|
||||
MknodeFunc func(s string, n1 int, n2 int) error
|
||||
MknodeFunc func(s string, v1 uint32, v2 uint32) error
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
@@ -36,25 +36,25 @@ type mknoderMock struct {
|
||||
Mknode []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
// N1 is the n1 argument value.
|
||||
N1 int
|
||||
// N2 is the n2 argument value.
|
||||
N2 int
|
||||
// V1 is the v1 argument value.
|
||||
V1 uint32
|
||||
// V2 is the v2 argument value.
|
||||
V2 uint32
|
||||
}
|
||||
}
|
||||
lockMknode sync.RWMutex
|
||||
}
|
||||
|
||||
// Mknode calls MknodeFunc.
|
||||
func (mock *mknoderMock) Mknode(s string, n1 int, n2 int) error {
|
||||
func (mock *mknoderMock) Mknode(s string, v1 uint32, v2 uint32) error {
|
||||
callInfo := struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}{
|
||||
S: s,
|
||||
N1: n1,
|
||||
N2: n2,
|
||||
V1: v1,
|
||||
V2: v2,
|
||||
}
|
||||
mock.lockMknode.Lock()
|
||||
mock.calls.Mknode = append(mock.calls.Mknode, callInfo)
|
||||
@@ -65,7 +65,7 @@ func (mock *mknoderMock) Mknode(s string, n1 int, n2 int) error {
|
||||
)
|
||||
return errOut
|
||||
}
|
||||
return mock.MknodeFunc(s, n1, n2)
|
||||
return mock.MknodeFunc(s, v1, v2)
|
||||
}
|
||||
|
||||
// MknodeCalls gets all the calls that were made to Mknode.
|
||||
@@ -74,13 +74,13 @@ func (mock *mknoderMock) Mknode(s string, n1 int, n2 int) error {
|
||||
// len(mockedmknoder.MknodeCalls())
|
||||
func (mock *mknoderMock) MknodeCalls() []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
N1 int
|
||||
N2 int
|
||||
V1 uint32
|
||||
V2 uint32
|
||||
}
|
||||
mock.lockMknode.RLock()
|
||||
calls = mock.calls.Mknode
|
||||
|
||||
@@ -39,26 +39,26 @@ func TestImexMode(t *testing.T) {
|
||||
|
||||
expectedSpec := `---
|
||||
cdiVersion: 0.5.0
|
||||
kind: nvidia.com/imex-channel
|
||||
devices:
|
||||
- name: "0"
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia-caps-imex-channels/channel0
|
||||
hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel0
|
||||
- name: "1"
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia-caps-imex-channels/channel1
|
||||
hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel1
|
||||
- name: "2047"
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- path: /dev/nvidia-caps-imex-channels/channel2047
|
||||
hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel2047
|
||||
containerEdits:
|
||||
env:
|
||||
- NVIDIA_VISIBLE_DEVICES=void
|
||||
devices:
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel0
|
||||
path: /dev/nvidia-caps-imex-channels/channel0
|
||||
name: "0"
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel1
|
||||
path: /dev/nvidia-caps-imex-channels/channel1
|
||||
name: "1"
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .hostRoot }}/dev/nvidia-caps-imex-channels/channel2047
|
||||
path: /dev/nvidia-caps-imex-channels/channel2047
|
||||
name: "2047"
|
||||
kind: nvidia.com/imex-channel
|
||||
`
|
||||
expectedSpec = strings.ReplaceAll(expectedSpec, "{{ .hostRoot }}", hostRoot)
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
package nvcdi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ensure, that nvmlUUIDerMock does implement nvmlUUIDer.
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
cdi "tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
|
||||
)
|
||||
|
||||
@@ -28,6 +28,18 @@ import (
|
||||
)
|
||||
|
||||
func TestSpec(t *testing.T) {
|
||||
minimalSpec := &specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Name: "one",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"DEVICE_FOO=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
options []Option
|
||||
@@ -35,23 +47,17 @@ func TestSpec(t *testing.T) {
|
||||
transform transform.Transformer
|
||||
expectedSpec string
|
||||
}{
|
||||
{
|
||||
description: "default options return empty spec",
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.3.0
|
||||
containerEdits: {}
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "version is overridden",
|
||||
options: []Option{WithVersion("0.5.0")},
|
||||
options: []Option{WithVersion("0.8.0"), WithRawSpec(minimalSpec)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits: {}
|
||||
devices: null
|
||||
cdiVersion: 0.8.0
|
||||
kind: nvidia.com/gpu
|
||||
devices:
|
||||
- name: one
|
||||
containerEdits:
|
||||
env:
|
||||
- DEVICE_FOO=bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -60,18 +66,24 @@ kind: nvidia.com/gpu
|
||||
&specs.Spec{
|
||||
Version: "0.5.0",
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Name: "one",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"DEVICE_FOO=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
devices:
|
||||
- name: one
|
||||
containerEdits:
|
||||
env:
|
||||
- DEVICE_FOO=bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -79,18 +91,24 @@ kind: nvidia.com/gpu
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Name: "one",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"DEVICE_FOO=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.3.0
|
||||
containerEdits:
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
devices:
|
||||
- name: one
|
||||
containerEdits:
|
||||
env:
|
||||
- DEVICE_FOO=bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -98,8 +116,15 @@ kind: nvidia.com/gpu
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Name: "one",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"DEVICE_FOO=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/some/dev/dev0",
|
||||
@@ -111,14 +136,16 @@ kind: nvidia.com/gpu
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
kind: nvidia.com/gpu
|
||||
devices:
|
||||
- name: one
|
||||
containerEdits:
|
||||
env:
|
||||
- DEVICE_FOO=bar
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /some/dev/dev0
|
||||
path: /dev/dev0
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
- path: /dev/dev0
|
||||
hostPath: /some/dev/dev0
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -126,8 +153,15 @@ kind: nvidia.com/gpu
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Name: "one",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"DEVICE_FOO=bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/some/dev/dev0",
|
||||
@@ -147,14 +181,16 @@ kind: nvidia.com/gpu
|
||||
),
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
kind: nvidia.com/gpu
|
||||
devices:
|
||||
- name: one
|
||||
containerEdits:
|
||||
env:
|
||||
- DEVICE_FOO=bar
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /dev/dev0
|
||||
path: /dev/dev0
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
- path: /dev/dev0
|
||||
hostPath: /dev/dev0
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
2
third_party/libnvidia-container
vendored
2
third_party/libnvidia-container
vendored
Submodule third_party/libnvidia-container updated: f23e5e55ea...95d3e86522
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -94,12 +94,12 @@ gopkg.in/yaml.v3
|
||||
# sigs.k8s.io/yaml v1.3.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# tags.cncf.io/container-device-interface v0.8.1
|
||||
# tags.cncf.io/container-device-interface v1.0.0
|
||||
## explicit; go 1.20
|
||||
tags.cncf.io/container-device-interface/internal/validation
|
||||
tags.cncf.io/container-device-interface/internal/validation/k8s
|
||||
tags.cncf.io/container-device-interface/pkg/cdi
|
||||
tags.cncf.io/container-device-interface/pkg/parser
|
||||
# tags.cncf.io/container-device-interface/specs-go v0.8.0
|
||||
# tags.cncf.io/container-device-interface/specs-go v1.0.0
|
||||
## explicit; go 1.19
|
||||
tags.cncf.io/container-device-interface/specs-go
|
||||
|
||||
4
vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
generated
vendored
4
vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
generated
vendored
@@ -71,7 +71,7 @@ func ParseAnnotations(annotations map[string]string) ([]string, []string, error)
|
||||
continue
|
||||
}
|
||||
for _, d := range strings.Split(value, ",") {
|
||||
if !IsQualifiedName(d) {
|
||||
if !parser.IsQualifiedName(d) {
|
||||
return nil, nil, fmt.Errorf("invalid CDI device name %q", d)
|
||||
}
|
||||
devices = append(devices, d)
|
||||
@@ -130,7 +130,7 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
|
||||
func AnnotationValue(devices []string) (string, error) {
|
||||
value, sep := "", ""
|
||||
for _, d := range devices {
|
||||
if _, _, _, err := ParseQualifiedName(d); err != nil {
|
||||
if _, _, _, err := parser.ParseQualifiedName(d); err != nil {
|
||||
return "", err
|
||||
}
|
||||
value += sep + d
|
||||
|
||||
47
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
generated
vendored
47
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
generated
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -116,7 +117,7 @@ func (c *Cache) configure(options ...Option) {
|
||||
c.watch.setup(c.specDirs, c.dirErrors)
|
||||
c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
|
||||
}
|
||||
c.refresh()
|
||||
_ = c.refresh() // we record but ignore errors
|
||||
}
|
||||
|
||||
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
||||
@@ -222,7 +223,8 @@ func (c *Cache) refreshIfRequired(force bool) (bool, error) {
|
||||
|
||||
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
||||
// returns any unresolvable devices and an error if injection fails for
|
||||
// any of the devices.
|
||||
// any of the devices. Might trigger a cache refresh, in which case any
|
||||
// errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) {
|
||||
var unresolved []string
|
||||
|
||||
@@ -233,7 +235,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
edits := &ContainerEdits{}
|
||||
specs := map[*Spec]struct{}{}
|
||||
@@ -335,24 +337,27 @@ func (c *Cache) RemoveSpec(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDevice returns the cached device for the given qualified name.
|
||||
// GetDevice returns the cached device for the given qualified name. Might trigger
|
||||
// a cache refresh, in which case any errors encountered can be obtained using
|
||||
// GetErrors().
|
||||
func (c *Cache) GetDevice(device string) *Device {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
return c.devices[device]
|
||||
}
|
||||
|
||||
// ListDevices lists all cached devices by qualified name.
|
||||
// ListDevices lists all cached devices by qualified name. Might trigger a cache
|
||||
// refresh, in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListDevices() []string {
|
||||
var devices []string
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
for name := range c.devices {
|
||||
devices = append(devices, name)
|
||||
@@ -362,14 +367,15 @@ func (c *Cache) ListDevices() []string {
|
||||
return devices
|
||||
}
|
||||
|
||||
// ListVendors lists all vendors known to the cache.
|
||||
// ListVendors lists all vendors known to the cache. Might trigger a cache refresh,
|
||||
// in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListVendors() []string {
|
||||
var vendors []string
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
for vendor := range c.specs {
|
||||
vendors = append(vendors, vendor)
|
||||
@@ -379,7 +385,8 @@ func (c *Cache) ListVendors() []string {
|
||||
return vendors
|
||||
}
|
||||
|
||||
// ListClasses lists all device classes known to the cache.
|
||||
// ListClasses lists all device classes known to the cache. Might trigger a cache
|
||||
// refresh, in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) ListClasses() []string {
|
||||
var (
|
||||
cmap = map[string]struct{}{}
|
||||
@@ -389,7 +396,7 @@ func (c *Cache) ListClasses() []string {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
for _, specs := range c.specs {
|
||||
for _, spec := range specs {
|
||||
@@ -404,12 +411,13 @@ func (c *Cache) ListClasses() []string {
|
||||
return classes
|
||||
}
|
||||
|
||||
// GetVendorSpecs returns all specs for the given vendor.
|
||||
// GetVendorSpecs returns all specs for the given vendor. Might trigger a cache
|
||||
// refresh, in which case any errors encountered can be obtained using GetErrors().
|
||||
func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.refreshIfRequired(false)
|
||||
_, _ = c.refreshIfRequired(false) // we record but ignore errors
|
||||
|
||||
return c.specs[vendor]
|
||||
}
|
||||
@@ -522,6 +530,13 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
|
||||
if watch == nil {
|
||||
return
|
||||
}
|
||||
|
||||
eventMask := fsnotify.Rename | fsnotify.Remove | fsnotify.Write
|
||||
// On macOS, we also need to watch for Create events.
|
||||
if runtime.GOOS == "darwin" {
|
||||
eventMask |= fsnotify.Create
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watch.Events:
|
||||
@@ -529,10 +544,10 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
|
||||
return
|
||||
}
|
||||
|
||||
if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
|
||||
if (event.Op & eventMask) == 0 {
|
||||
continue
|
||||
}
|
||||
if event.Op == fsnotify.Write {
|
||||
if event.Op == fsnotify.Write || event.Op == fsnotify.Create {
|
||||
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
|
||||
continue
|
||||
}
|
||||
@@ -544,7 +559,7 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
|
||||
} else {
|
||||
w.update(dirErrors)
|
||||
}
|
||||
refresh()
|
||||
_ = refresh()
|
||||
m.Unlock()
|
||||
|
||||
case _, ok := <-watch.Errors:
|
||||
|
||||
26
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_darwin.go
generated
vendored
Normal file
26
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
/*
|
||||
Copyright © 2021 The CDI Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cdi
|
||||
|
||||
import "syscall"
|
||||
|
||||
func osSync() {
|
||||
_ = syscall.Sync()
|
||||
}
|
||||
4
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go
generated
vendored
4
vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
//go:build !windows && !darwin
|
||||
// +build !windows,!darwin
|
||||
|
||||
/*
|
||||
Copyright © 2021 The CDI Authors
|
||||
|
||||
27
vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
generated
vendored
27
vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
ocigen "github.com/opencontainers/runtime-tools/generate"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
cdi "tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -64,7 +64,7 @@ var (
|
||||
// to all OCI Specs where at least one devices from the CDI Spec
|
||||
// is injected.
|
||||
type ContainerEdits struct {
|
||||
*specs.ContainerEdits
|
||||
*cdi.ContainerEdits
|
||||
}
|
||||
|
||||
// Apply edits to the given OCI Spec. Updates the OCI Spec in place.
|
||||
@@ -205,7 +205,7 @@ func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits {
|
||||
e = &ContainerEdits{}
|
||||
}
|
||||
if e.ContainerEdits == nil {
|
||||
e.ContainerEdits = &specs.ContainerEdits{}
|
||||
e.ContainerEdits = &cdi.ContainerEdits{}
|
||||
}
|
||||
|
||||
e.Env = append(e.Env, o.Env...)
|
||||
@@ -259,7 +259,7 @@ func ValidateEnv(env []string) error {
|
||||
|
||||
// DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
|
||||
type DeviceNode struct {
|
||||
*specs.DeviceNode
|
||||
*cdi.DeviceNode
|
||||
}
|
||||
|
||||
// Validate a CDI Spec DeviceNode.
|
||||
@@ -289,7 +289,7 @@ func (d *DeviceNode) Validate() error {
|
||||
|
||||
// Hook is a CDI Spec Hook wrapper, used for validating hooks.
|
||||
type Hook struct {
|
||||
*specs.Hook
|
||||
*cdi.Hook
|
||||
}
|
||||
|
||||
// Validate a hook.
|
||||
@@ -308,7 +308,7 @@ func (h *Hook) Validate() error {
|
||||
|
||||
// Mount is a CDI Mount wrapper, used for validating mounts.
|
||||
type Mount struct {
|
||||
*specs.Mount
|
||||
*cdi.Mount
|
||||
}
|
||||
|
||||
// Validate a mount.
|
||||
@@ -325,13 +325,13 @@ func (m *Mount) Validate() error {
|
||||
// IntelRdt is a CDI IntelRdt wrapper.
|
||||
// This is used for validation and conversion to OCI specifications.
|
||||
type IntelRdt struct {
|
||||
*specs.IntelRdt
|
||||
*cdi.IntelRdt
|
||||
}
|
||||
|
||||
// ValidateIntelRdt validates the IntelRdt configuration.
|
||||
//
|
||||
// Deprecated: ValidateIntelRdt is deprecated use IntelRdt.Validate() instead.
|
||||
func ValidateIntelRdt(i *specs.IntelRdt) error {
|
||||
func ValidateIntelRdt(i *cdi.IntelRdt) error {
|
||||
return (&IntelRdt{i}).Validate()
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ func ensureOCIHooks(spec *oci.Spec) {
|
||||
func sortMounts(specgen *ocigen.Generator) {
|
||||
mounts := specgen.Mounts()
|
||||
specgen.ClearMounts()
|
||||
sort.Sort(orderedMounts(mounts))
|
||||
sort.Stable(orderedMounts(mounts))
|
||||
specgen.Config.Mounts = mounts
|
||||
}
|
||||
|
||||
@@ -375,14 +375,7 @@ func (m orderedMounts) Len() int {
|
||||
// mount indexed by parameter 1 is less than that of the mount indexed by
|
||||
// parameter 2. Used in sorting.
|
||||
func (m orderedMounts) Less(i, j int) bool {
|
||||
ip, jp := m.parts(i), m.parts(j)
|
||||
if ip < jp {
|
||||
return true
|
||||
}
|
||||
if jp < ip {
|
||||
return false
|
||||
}
|
||||
return m[i].Destination < m[j].Destination
|
||||
return m.parts(i) < m.parts(j)
|
||||
}
|
||||
|
||||
// Swap swaps two items in an array of mounts. Used in sorting
|
||||
|
||||
2
vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
generated
vendored
2
vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
generated
vendored
@@ -67,7 +67,7 @@ func (d *Device) edits() *ContainerEdits {
|
||||
|
||||
// Validate the device.
|
||||
func (d *Device) validate() error {
|
||||
if err := ValidateDeviceName(d.Name); err != nil {
|
||||
if err := parser.ValidateDeviceName(d.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
name := d.Name
|
||||
|
||||
51
vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
generated
vendored
51
vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
generated
vendored
@@ -35,25 +35,11 @@
|
||||
// available and instantiated the first time it is referenced directly
|
||||
// or indirectly. The most frequently used cache functions are available
|
||||
// as identically named package level functions which operate on the
|
||||
// default cache instance. Moreover, the registry also operates on the
|
||||
// same default cache. We plan to deprecate the registry and eventually
|
||||
// remove it in a future release.
|
||||
//
|
||||
// # CDI Registry
|
||||
//
|
||||
// Note: the Registry and its related interfaces are deprecated and will
|
||||
// be removed in a future version. Please use the default cache and its
|
||||
// related package-level function instead.
|
||||
//
|
||||
// The primary interface to interact with CDI devices is the Registry. It
|
||||
// is essentially a cache of all Specs and devices discovered in standard
|
||||
// CDI directories on the host. The registry has two main functionality,
|
||||
// injecting devices into an OCI Spec and refreshing the cache of CDI
|
||||
// Specs and devices.
|
||||
// default cache instance.
|
||||
//
|
||||
// # Device Injection
|
||||
//
|
||||
// Using the Registry one can inject CDI devices into a container with code
|
||||
// Using the Cache one can inject CDI devices into a container with code
|
||||
// similar to the following snippet:
|
||||
//
|
||||
// import (
|
||||
@@ -63,13 +49,14 @@
|
||||
// log "github.com/sirupsen/logrus"
|
||||
//
|
||||
// "tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
// oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// )
|
||||
//
|
||||
// func injectCDIDevices(spec *oci.Spec, devices []string) error {
|
||||
// func injectCDIDevices(spec *specs.Spec, devices []string) error {
|
||||
// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
|
||||
//
|
||||
// unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
|
||||
// cache := cdi.GetDefaultCache()
|
||||
// unresolved, err := cache.InjectDevices(spec, devices)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("CDI device injection failed: %w", err)
|
||||
// }
|
||||
@@ -106,17 +93,17 @@
|
||||
// log "github.com/sirupsen/logrus"
|
||||
//
|
||||
// "tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
// oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// "github.com/opencontainers/runtime-spec/specs-go"
|
||||
// )
|
||||
//
|
||||
// func injectCDIDevices(spec *oci.Spec, devices []string) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// func injectCDIDevices(spec *specs.Spec, devices []string) error {
|
||||
// cache := cdi.GetDefaultCache()
|
||||
//
|
||||
// if err := registry.Refresh(); err != nil {
|
||||
// if err := cache.Refresh(); err != nil {
|
||||
// // Note:
|
||||
// // It is up to the implementation to decide whether
|
||||
// // to abort injection on errors. A failed Refresh()
|
||||
// // does not necessarily render the registry unusable.
|
||||
// // does not necessarily render the cache unusable.
|
||||
// // For instance, a parse error in a Spec file for
|
||||
// // vendor A does not have any effect on devices of
|
||||
// // vendor B...
|
||||
@@ -125,7 +112,7 @@
|
||||
//
|
||||
// log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
|
||||
//
|
||||
// unresolved, err := registry.InjectDevices(spec, devices)
|
||||
// unresolved, err := cache.InjectDevices(spec, devices)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("CDI device injection failed: %w", err)
|
||||
// }
|
||||
@@ -192,7 +179,7 @@
|
||||
// )
|
||||
//
|
||||
// func generateDeviceSpecs() error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// cache := specs.GetDefaultCache()
|
||||
// spec := &specs.Spec{
|
||||
// Version: specs.CurrentVersion,
|
||||
// Kind: vendor+"/"+class,
|
||||
@@ -210,7 +197,7 @@
|
||||
// return fmt.Errorf("failed to generate Spec name: %w", err)
|
||||
// }
|
||||
//
|
||||
// return registry.SpecDB().WriteSpec(spec, specName)
|
||||
// return cache.WriteSpec(spec, specName)
|
||||
// }
|
||||
//
|
||||
// Similarly, generating and later cleaning up transient Spec files can be
|
||||
@@ -229,7 +216,7 @@
|
||||
// )
|
||||
//
|
||||
// func generateTransientSpec(ctr Container) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// cache := specs.GetDefaultCache()
|
||||
// devices := getContainerDevs(ctr, vendor, class)
|
||||
// spec := &specs.Spec{
|
||||
// Version: specs.CurrentVersion,
|
||||
@@ -257,21 +244,21 @@
|
||||
// return fmt.Errorf("failed to generate Spec name: %w", err)
|
||||
// }
|
||||
//
|
||||
// return registry.SpecDB().WriteSpec(spec, specName)
|
||||
// return cache.WriteSpec(spec, specName)
|
||||
// }
|
||||
//
|
||||
// func removeTransientSpec(ctr Container) error {
|
||||
// registry := cdi.GetRegistry()
|
||||
// cache := specs.GetDefaultCache()
|
||||
// transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
|
||||
// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
|
||||
//
|
||||
// return registry.SpecDB().RemoveSpec(specName)
|
||||
// return cache.RemoveSpec(specName)
|
||||
// }
|
||||
//
|
||||
// # CDI Spec Validation
|
||||
//
|
||||
// This package performs both syntactic and semantic validation of CDI
|
||||
// Spec file data when a Spec file is loaded via the registry or using
|
||||
// Spec file data when a Spec file is loaded via the cache or using
|
||||
// the ReadSpec API function. As part of the semantic verification, the
|
||||
// Spec file is verified against the CDI Spec JSON validation schema.
|
||||
//
|
||||
|
||||
113
vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
generated
vendored
113
vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
generated
vendored
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 The CDI Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cdi
|
||||
|
||||
import (
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
// QualifiedName returns the qualified name for a device.
|
||||
// The syntax for a qualified device names is
|
||||
//
|
||||
// "<vendor>/<class>=<name>".
|
||||
//
|
||||
// A valid vendor and class name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
|
||||
//
|
||||
// A valid device name may contain the following runes:
|
||||
//
|
||||
// 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
|
||||
//
|
||||
// Deprecated: use parser.QualifiedName instead
|
||||
func QualifiedName(vendor, class, name string) string {
|
||||
return parser.QualifiedName(vendor, class, name)
|
||||
}
|
||||
|
||||
// IsQualifiedName tests if a device name is qualified.
|
||||
//
|
||||
// Deprecated: use parser.IsQualifiedName instead
|
||||
func IsQualifiedName(device string) bool {
|
||||
return parser.IsQualifiedName(device)
|
||||
}
|
||||
|
||||
// ParseQualifiedName splits a qualified name into device vendor, class,
|
||||
// and name. If the device fails to parse as a qualified name, or if any
|
||||
// of the split components fail to pass syntax validation, vendor and
|
||||
// class are returned as empty, together with the verbatim input as the
|
||||
// name and an error describing the reason for failure.
|
||||
//
|
||||
// Deprecated: use parser.ParseQualifiedName instead
|
||||
func ParseQualifiedName(device string) (string, string, string, error) {
|
||||
return parser.ParseQualifiedName(device)
|
||||
}
|
||||
|
||||
// ParseDevice tries to split a device name into vendor, class, and name.
|
||||
// If this fails, for instance in the case of unqualified device names,
|
||||
// ParseDevice returns an empty vendor and class together with name set
|
||||
// to the verbatim input.
|
||||
//
|
||||
// Deprecated: use parser.ParseDevice instead
|
||||
func ParseDevice(device string) (string, string, string) {
|
||||
return parser.ParseDevice(device)
|
||||
}
|
||||
|
||||
// ParseQualifier splits a device qualifier into vendor and class.
|
||||
// The syntax for a device qualifier is
|
||||
//
|
||||
// "<vendor>/<class>"
|
||||
//
|
||||
// If parsing fails, an empty vendor and the class set to the
|
||||
// verbatim input is returned.
|
||||
//
|
||||
// Deprecated: use parser.ParseQualifier instead
|
||||
func ParseQualifier(kind string) (string, string) {
|
||||
return parser.ParseQualifier(kind)
|
||||
}
|
||||
|
||||
// ValidateVendorName checks the validity of a vendor name.
|
||||
// A vendor name may contain the following ASCII characters:
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, and dot ('_', '-', and '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateVendorName instead
|
||||
func ValidateVendorName(vendor string) error {
|
||||
return parser.ValidateVendorName(vendor)
|
||||
}
|
||||
|
||||
// ValidateClassName checks the validity of class name.
|
||||
// A class name may contain the following ASCII characters:
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, and dot ('_', '-', and '.')
|
||||
//
|
||||
// Deprecated: use parser.ValidateClassName instead
|
||||
func ValidateClassName(class string) error {
|
||||
return parser.ValidateClassName(class)
|
||||
}
|
||||
|
||||
// ValidateDeviceName checks the validity of a device name.
|
||||
// A device name may contain the following ASCII characters:
|
||||
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
|
||||
// - digits ('0'-'9')
|
||||
// - underscore, dash, dot, colon ('_', '-', '.', ':')
|
||||
//
|
||||
// Deprecated: use parser.ValidateDeviceName instead
|
||||
func ValidateDeviceName(name string) error {
|
||||
return parser.ValidateDeviceName(name)
|
||||
}
|
||||
178
vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
generated
vendored
178
vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
generated
vendored
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 The CDI Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cdi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
cdi "tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
// Registry keeps a cache of all CDI Specs installed or generated on
|
||||
// the host. Registry is the primary interface clients should use to
|
||||
// interact with CDI.
|
||||
//
|
||||
// The most commonly used Registry functions are for refreshing the
|
||||
// registry and injecting CDI devices into an OCI Spec.
|
||||
//
|
||||
// Deprecated: Registry is deprecated and will be removed in a future
|
||||
// version. Please update your code to use the corresponding package-
|
||||
// level functions Configure(), Refresh(), InjectDevices(), GetErrors(),
|
||||
// and GetDefaultCache().
|
||||
type Registry interface {
|
||||
RegistryResolver
|
||||
RegistryRefresher
|
||||
DeviceDB() RegistryDeviceDB
|
||||
SpecDB() RegistrySpecDB
|
||||
}
|
||||
|
||||
// RegistryRefresher is the registry interface for refreshing the
|
||||
// cache of CDI Specs and devices.
|
||||
//
|
||||
// Configure reconfigures the registry with the given options.
|
||||
//
|
||||
// Refresh rescans all CDI Spec directories and updates the
|
||||
// state of the cache to reflect any changes. It returns any
|
||||
// errors encountered during the refresh.
|
||||
//
|
||||
// GetErrors returns all errors encountered for any of the scanned
|
||||
// Spec files during the last cache refresh.
|
||||
//
|
||||
// GetSpecDirectories returns the set up CDI Spec directories
|
||||
// currently in use. The directories are returned in the scan
|
||||
// order of Refresh().
|
||||
//
|
||||
// GetSpecDirErrors returns any errors related to the configured
|
||||
// Spec directories.
|
||||
//
|
||||
// Deprecated: RegistryRefresher is deprecated and will be removed
|
||||
// in a future version. Please use the default cache and its related
|
||||
// package-level functions instead.
|
||||
type RegistryRefresher interface {
|
||||
Configure(...Option) error
|
||||
Refresh() error
|
||||
GetErrors() map[string][]error
|
||||
GetSpecDirectories() []string
|
||||
GetSpecDirErrors() map[string]error
|
||||
}
|
||||
|
||||
// RegistryResolver is the registry interface for injecting CDI
|
||||
// devices into an OCI Spec.
|
||||
//
|
||||
// InjectDevices takes an OCI Spec and injects into it a set of
|
||||
// CDI devices given by qualified name. It returns the names of
|
||||
// any unresolved devices and an error if injection fails.
|
||||
//
|
||||
// Deprecated: RegistryRefresher is deprecated and will be removed
|
||||
// in a future version. Please use the default cache and its related
|
||||
// package-level functions instead.
|
||||
type RegistryResolver interface {
|
||||
InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
|
||||
}
|
||||
|
||||
// RegistryDeviceDB is the registry interface for querying devices.
|
||||
//
|
||||
// GetDevice returns the CDI device for the given qualified name. If
|
||||
// the device is not GetDevice returns nil.
|
||||
//
|
||||
// ListDevices returns a slice with the names of qualified device
|
||||
// known. The returned slice is sorted.
|
||||
//
|
||||
// Deprecated: RegistryDeviceDB is deprecated and will be removed
|
||||
// in a future version. Please use the default cache and its related
|
||||
// package-level functions instead.
|
||||
// and will be removed in a future version. Please use the default
|
||||
// cache and its related package-level functions instead.
|
||||
type RegistryDeviceDB interface {
|
||||
GetDevice(device string) *Device
|
||||
ListDevices() []string
|
||||
}
|
||||
|
||||
// RegistrySpecDB is the registry interface for querying CDI Specs.
|
||||
//
|
||||
// ListVendors returns a slice with all vendors known. The returned
|
||||
// slice is sorted.
|
||||
//
|
||||
// ListClasses returns a slice with all classes known. The returned
|
||||
// slice is sorted.
|
||||
//
|
||||
// GetVendorSpecs returns a slice of all Specs for the vendor.
|
||||
//
|
||||
// GetSpecErrors returns any errors for the Spec encountered during
|
||||
// the last cache refresh.
|
||||
//
|
||||
// WriteSpec writes the Spec with the given content and name to the
|
||||
// last Spec directory.
|
||||
//
|
||||
// Deprecated: RegistrySpecDB is deprecated and will be removed
|
||||
// in a future version. Please use the default cache and its related
|
||||
// package-level functions instead.
|
||||
type RegistrySpecDB interface {
|
||||
ListVendors() []string
|
||||
ListClasses() []string
|
||||
GetVendorSpecs(vendor string) []*Spec
|
||||
GetSpecErrors(*Spec) []error
|
||||
WriteSpec(raw *cdi.Spec, name string) error
|
||||
RemoveSpec(name string) error
|
||||
}
|
||||
|
||||
type registry struct {
|
||||
*Cache
|
||||
}
|
||||
|
||||
var _ Registry = ®istry{}
|
||||
|
||||
var (
|
||||
reg *registry
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// GetRegistry returns the CDI registry. If any options are given, those
|
||||
// are applied to the registry.
|
||||
//
|
||||
// Deprecated: GetRegistry is deprecated and will be removed in a future
|
||||
// version. Please use the default cache and its related package-level
|
||||
// functions instead.
|
||||
func GetRegistry(options ...Option) Registry {
|
||||
initOnce.Do(func() {
|
||||
reg = ®istry{GetDefaultCache()}
|
||||
})
|
||||
if len(options) > 0 {
|
||||
// We don't care about errors here
|
||||
_ = reg.Configure(options...)
|
||||
}
|
||||
return reg
|
||||
}
|
||||
|
||||
// DeviceDB returns the registry interface for querying devices.
|
||||
//
|
||||
// Deprecated: DeviceDB is deprecated and will be removed in a future
|
||||
// version. Please use the default cache and its related package-level
|
||||
// functions instead.
|
||||
func (r *registry) DeviceDB() RegistryDeviceDB {
|
||||
return r
|
||||
}
|
||||
|
||||
// SpecDB returns the registry interface for querying Specs.
|
||||
//
|
||||
// Deprecated: SpecDB is deprecated and will be removed in a future
|
||||
// version. Please use the default cache and its related package-level
|
||||
// functions instead.
|
||||
func (r *registry) SpecDB() RegistrySpecDB {
|
||||
return r
|
||||
}
|
||||
3
vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
generated
vendored
3
vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
generated
vendored
@@ -35,8 +35,7 @@ var (
|
||||
// While altering this variable changes the package defaults,
|
||||
// the preferred way of overriding the default directories is
|
||||
// to use a WithSpecDirs options. Otherwise the change is only
|
||||
// effective if it takes place before creating the Registry or
|
||||
// other Cache instances.
|
||||
// effective if it takes place before creating the cache instance.
|
||||
DefaultSpecDirs = []string{DefaultStaticDir, DefaultDynamicDir}
|
||||
// ErrStopScan can be returned from a ScanSpecFunc to stop the scan.
|
||||
ErrStopScan = errors.New("stop Spec scan")
|
||||
|
||||
57
vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
generated
vendored
57
vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
generated
vendored
@@ -25,9 +25,11 @@ import (
|
||||
"sync"
|
||||
|
||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||
orderedyaml "gopkg.in/yaml.v2"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"tags.cncf.io/container-device-interface/internal/validation"
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
cdi "tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
@@ -36,9 +38,13 @@ const (
|
||||
defaultSpecExt = ".yaml"
|
||||
)
|
||||
|
||||
type validator interface {
|
||||
Validate(*cdi.Spec) error
|
||||
}
|
||||
|
||||
var (
|
||||
// Externally set CDI Spec validation function.
|
||||
specValidator func(*cdi.Spec) error
|
||||
specValidator validator
|
||||
validatorLock sync.RWMutex
|
||||
)
|
||||
|
||||
@@ -105,7 +111,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
|
||||
spec.path += defaultSpecExt
|
||||
}
|
||||
|
||||
spec.vendor, spec.class = ParseQualifier(spec.Kind)
|
||||
spec.vendor, spec.class = parser.ParseQualifier(spec.Kind)
|
||||
|
||||
if spec.devices, err = spec.validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid CDI Spec: %w", err)
|
||||
@@ -130,7 +136,7 @@ func (s *Spec) write(overwrite bool) error {
|
||||
}
|
||||
|
||||
if filepath.Ext(s.path) == ".yaml" {
|
||||
data, err = yaml.Marshal(s.Spec)
|
||||
data, err = orderedyaml.Marshal(s.Spec)
|
||||
data = append([]byte("---\n"), data...)
|
||||
} else {
|
||||
data, err = json.Marshal(s.Spec)
|
||||
@@ -200,24 +206,21 @@ func (s *Spec) edits() *ContainerEdits {
|
||||
return &ContainerEdits{&s.ContainerEdits}
|
||||
}
|
||||
|
||||
// MinimumRequiredVersion determines the minimum spec version for the input spec.
|
||||
// Deprecated: use cdi.MinimumRequiredVersion instead
|
||||
func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
|
||||
return cdi.MinimumRequiredVersion(spec)
|
||||
}
|
||||
|
||||
// Validate the Spec.
|
||||
func (s *Spec) validate() (map[string]*Device, error) {
|
||||
if err := validateVersion(s.Version); err != nil {
|
||||
if err := cdi.ValidateVersion(s.Spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minVersion, err := MinimumRequiredVersion(s.Spec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine minimum required version: %v", err)
|
||||
}
|
||||
if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
|
||||
return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
|
||||
}
|
||||
|
||||
if err := ValidateVendorName(s.vendor); err != nil {
|
||||
if err := parser.ValidateVendorName(s.vendor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ValidateClassName(s.class); err != nil {
|
||||
if err := parser.ValidateClassName(s.class); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ValidateSpecAnnotations(s.Kind, s.Annotations); err != nil {
|
||||
@@ -238,17 +241,11 @@ func (s *Spec) validate() (map[string]*Device, error) {
|
||||
}
|
||||
devices[d.Name] = dev
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// validateVersion checks whether the specified spec version is supported.
|
||||
func validateVersion(version string) error {
|
||||
if !validSpecVersions.isValidVersion(version) {
|
||||
return fmt.Errorf("invalid version %q", version)
|
||||
if len(devices) == 0 {
|
||||
return nil, fmt.Errorf("invalid spec, no devices")
|
||||
}
|
||||
|
||||
return nil
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// ParseSpec parses CDI Spec data into a raw CDI Spec.
|
||||
@@ -264,13 +261,13 @@ func ParseSpec(data []byte) (*cdi.Spec, error) {
|
||||
// SetSpecValidator sets a CDI Spec validator function. This function
|
||||
// is used for extra CDI Spec content validation whenever a Spec file
|
||||
// loaded (using ReadSpec() or written (using WriteSpec()).
|
||||
func SetSpecValidator(fn func(*cdi.Spec) error) {
|
||||
func SetSpecValidator(v validator) {
|
||||
validatorLock.Lock()
|
||||
defer validatorLock.Unlock()
|
||||
specValidator = fn
|
||||
specValidator = v
|
||||
}
|
||||
|
||||
// validateSpec validates the Spec using the extneral validator.
|
||||
// validateSpec validates the Spec using the external validator.
|
||||
func validateSpec(raw *cdi.Spec) error {
|
||||
validatorLock.RLock()
|
||||
defer validatorLock.RUnlock()
|
||||
@@ -278,7 +275,7 @@ func validateSpec(raw *cdi.Spec) error {
|
||||
if specValidator == nil {
|
||||
return nil
|
||||
}
|
||||
err := specValidator(raw)
|
||||
err := specValidator.Validate(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Spec validation failed: %w", err)
|
||||
}
|
||||
@@ -328,7 +325,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string {
|
||||
// the Spec does not contain a valid vendor or class, it returns
|
||||
// an empty name and a non-nil error.
|
||||
func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
|
||||
vendor, class := ParseQualifier(raw.Kind)
|
||||
vendor, class := parser.ParseQualifier(raw.Kind)
|
||||
if vendor == "" {
|
||||
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
|
||||
}
|
||||
@@ -342,7 +339,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
|
||||
// If the Spec does not contain a valid vendor or class, it returns an
|
||||
// an empty name and a non-nil error.
|
||||
func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) {
|
||||
vendor, class := ParseQualifier(raw.Kind)
|
||||
vendor, class := parser.ParseQualifier(raw.Kind)
|
||||
if vendor == "" {
|
||||
return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
|
||||
}
|
||||
|
||||
79
vendor/tags.cncf.io/container-device-interface/specs-go/config.go
generated
vendored
79
vendor/tags.cncf.io/container-device-interface/specs-go/config.go
generated
vendored
@@ -2,72 +2,71 @@ package specs
|
||||
|
||||
import "os"
|
||||
|
||||
// CurrentVersion is the current version of the Spec.
|
||||
const CurrentVersion = "0.8.0"
|
||||
|
||||
// Spec is the base configuration for CDI
|
||||
type Spec struct {
|
||||
Version string `json:"cdiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Version string `json:"cdiVersion" yaml:"cdiVersion"`
|
||||
Kind string `json:"kind" yaml:"kind"`
|
||||
// Annotations add meta information per CDI spec. Note these are CDI-specific and do not affect container metadata.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
Devices []Device `json:"devices"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits,omitempty"`
|
||||
// Added in v0.6.0.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
Devices []Device `json:"devices" yaml:"devices"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits,omitempty" yaml:"containerEdits,omitempty"`
|
||||
}
|
||||
|
||||
// Device is a "Device" a container runtime can add to a container
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
// Annotations add meta information per device. Note these are CDI-specific and do not affect container metadata.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits"`
|
||||
// Added in v0.6.0.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
ContainerEdits ContainerEdits `json:"containerEdits" yaml:"containerEdits"`
|
||||
}
|
||||
|
||||
// ContainerEdits are edits a container runtime must make to the OCI spec to expose the device.
|
||||
type ContainerEdits struct {
|
||||
Env []string `json:"env,omitempty"`
|
||||
DeviceNodes []*DeviceNode `json:"deviceNodes,omitempty"`
|
||||
Hooks []*Hook `json:"hooks,omitempty"`
|
||||
Mounts []*Mount `json:"mounts,omitempty"`
|
||||
IntelRdt *IntelRdt `json:"intelRdt,omitempty"`
|
||||
AdditionalGIDs []uint32 `json:"additionalGids,omitempty"`
|
||||
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
|
||||
DeviceNodes []*DeviceNode `json:"deviceNodes,omitempty" yaml:"deviceNodes,omitempty"`
|
||||
Hooks []*Hook `json:"hooks,omitempty" yaml:"hooks,omitempty"`
|
||||
Mounts []*Mount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||
IntelRdt *IntelRdt `json:"intelRdt,omitempty" yaml:"intelRdt,omitempty"` // Added in v0.7.0
|
||||
AdditionalGIDs []uint32 `json:"additionalGids,omitempty" yaml:"additionalGids,omitempty"` // Added in v0.7.0
|
||||
}
|
||||
|
||||
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
||||
type DeviceNode struct {
|
||||
Path string `json:"path"`
|
||||
HostPath string `json:"hostPath,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Major int64 `json:"major,omitempty"`
|
||||
Minor int64 `json:"minor,omitempty"`
|
||||
FileMode *os.FileMode `json:"fileMode,omitempty"`
|
||||
Permissions string `json:"permissions,omitempty"`
|
||||
UID *uint32 `json:"uid,omitempty"`
|
||||
GID *uint32 `json:"gid,omitempty"`
|
||||
Path string `json:"path" yaml:"path"`
|
||||
HostPath string `json:"hostPath,omitempty" yaml:"hostPath,omitempty"` // Added in v0.5.0
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Major int64 `json:"major,omitempty" yaml:"major,omitempty"`
|
||||
Minor int64 `json:"minor,omitempty" yaml:"minor,omitempty"`
|
||||
FileMode *os.FileMode `json:"fileMode,omitempty" yaml:"fileMode,omitempty"`
|
||||
Permissions string `json:"permissions,omitempty" yaml:"permissions,omitempty"`
|
||||
UID *uint32 `json:"uid,omitempty" yaml:"uid,omitempty"`
|
||||
GID *uint32 `json:"gid,omitempty" yaml:"gid,omitempty"`
|
||||
}
|
||||
|
||||
// Mount represents a mount that needs to be added to the OCI spec.
|
||||
type Mount struct {
|
||||
HostPath string `json:"hostPath"`
|
||||
ContainerPath string `json:"containerPath"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
HostPath string `json:"hostPath" yaml:"hostPath"`
|
||||
ContainerPath string `json:"containerPath" yaml:"containerPath"`
|
||||
Options []string `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"` // Added in v0.4.0
|
||||
}
|
||||
|
||||
// Hook represents a hook that needs to be added to the OCI spec.
|
||||
type Hook struct {
|
||||
HookName string `json:"hookName"`
|
||||
Path string `json:"path"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
Env []string `json:"env,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
HookName string `json:"hookName" yaml:"hookName"`
|
||||
Path string `json:"path" yaml:"path"`
|
||||
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
|
||||
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty" yaml:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// IntelRdt describes the Linux IntelRdt parameters to set in the OCI spec.
|
||||
type IntelRdt struct {
|
||||
ClosID string `json:"closID,omitempty"`
|
||||
L3CacheSchema string `json:"l3CacheSchema,omitempty"`
|
||||
MemBwSchema string `json:"memBwSchema,omitempty"`
|
||||
EnableCMT bool `json:"enableCMT,omitempty"`
|
||||
EnableMBM bool `json:"enableMBM,omitempty"`
|
||||
ClosID string `json:"closID,omitempty" yaml:"closID,omitempty"`
|
||||
L3CacheSchema string `json:"l3CacheSchema,omitempty" yaml:"l3CacheSchema,omitempty"`
|
||||
MemBwSchema string `json:"memBwSchema,omitempty" yaml:"memBwSchema,omitempty"`
|
||||
EnableCMT bool `json:"enableCMT,omitempty" yaml:"enableCMT,omitempty"`
|
||||
EnableMBM bool `json:"enableMBM,omitempty" yaml:"enableMBM,omitempty"`
|
||||
}
|
||||
|
||||
56
vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
generated
vendored
56
vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
generated
vendored
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright © 2021 The CDI Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package specs
|
||||
|
||||
import "errors"
|
||||
|
||||
// errDeprecated is returned for the ToOCI functions below.
|
||||
// This should provide better guidance for user when migrating from the API
|
||||
// below to the APIs provided in the cdi package.
|
||||
var errDeprecated = errors.New("deprecated; Use cdi package functions instead")
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec Hook for this Hook.
|
||||
//
|
||||
// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Hook.toOCI
|
||||
// and made private.
|
||||
func (h *Hook) ToOCI() error {
|
||||
return errDeprecated
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec Mount for this Mount.
|
||||
//
|
||||
// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Mount.toOCI
|
||||
// and made private.
|
||||
func (m *Mount) ToOCI() error {
|
||||
return errDeprecated
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
|
||||
//
|
||||
// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.DeviceNode.toOCI
|
||||
// and made private.
|
||||
func (d *DeviceNode) ToOCI() error {
|
||||
return errDeprecated
|
||||
}
|
||||
|
||||
// ToOCI returns the opencontainers runtime Spec LinuxIntelRdt for this IntelRdt config.
|
||||
//
|
||||
// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.IntelRdt.toOCI
|
||||
// and made private.
|
||||
func (i *IntelRdt) ToOCI() error {
|
||||
return errDeprecated
|
||||
}
|
||||
@@ -14,20 +14,18 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cdi
|
||||
package specs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
cdi "tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
const (
|
||||
// CurrentVersion is the current version of the CDI Spec.
|
||||
CurrentVersion = cdi.CurrentVersion
|
||||
// CurrentVersion is the current version of the Spec.
|
||||
CurrentVersion = "1.0.0"
|
||||
|
||||
// vCurrent is the current version as a semver-comparable type
|
||||
vCurrent version = "v" + CurrentVersion
|
||||
@@ -41,6 +39,7 @@ const (
|
||||
v060 version = "v0.6.0"
|
||||
v070 version = "v0.7.0"
|
||||
v080 version = "v0.8.0"
|
||||
v100 version = "v1.0.0"
|
||||
|
||||
// vEarliest is the earliest supported version of the CDI specification
|
||||
vEarliest version = v030
|
||||
@@ -58,10 +57,29 @@ var validSpecVersions = requiredVersionMap{
|
||||
v060: requiresV060,
|
||||
v070: requiresV070,
|
||||
v080: requiresV080,
|
||||
v100: requiresV100,
|
||||
}
|
||||
|
||||
// ValidateVersion checks whether the specified spec version is valid.
|
||||
// In addition to checking whether the spec version is in the set of known versions,
|
||||
// the spec is inspected to determine whether the features used are available in specified
|
||||
// version.
|
||||
func ValidateVersion(spec *Spec) error {
|
||||
if !validSpecVersions.isValidVersion(spec.Version) {
|
||||
return fmt.Errorf("invalid version %q", spec.Version)
|
||||
}
|
||||
minVersion, err := MinimumRequiredVersion(spec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not determine minimum required version: %w", err)
|
||||
}
|
||||
if newVersion(minVersion).isGreaterThan(newVersion(spec.Version)) {
|
||||
return fmt.Errorf("the spec version must be at least v%v", minVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumRequiredVersion determines the minimum spec version for the input spec.
|
||||
func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
|
||||
func MinimumRequiredVersion(spec *Spec) (string, error) {
|
||||
minVersion := validSpecVersions.requiredVersion(spec)
|
||||
return minVersion.String(), nil
|
||||
}
|
||||
@@ -80,17 +98,17 @@ func (v version) String() string {
|
||||
return strings.TrimPrefix(string(v), "v")
|
||||
}
|
||||
|
||||
// IsGreaterThan checks with a version is greater than the specified version.
|
||||
func (v version) IsGreaterThan(o version) bool {
|
||||
// isGreaterThan checks with a version is greater than the specified version.
|
||||
func (v version) isGreaterThan(o version) bool {
|
||||
return semver.Compare(string(v), string(o)) > 0
|
||||
}
|
||||
|
||||
// IsLatest checks whether the version is the latest supported version
|
||||
func (v version) IsLatest() bool {
|
||||
// isLatest checks whether the version is the latest supported version
|
||||
func (v version) isLatest() bool {
|
||||
return v == vCurrent
|
||||
}
|
||||
|
||||
type requiredFunc func(*cdi.Spec) bool
|
||||
type requiredFunc func(*Spec) bool
|
||||
|
||||
type requiredVersionMap map[version]requiredFunc
|
||||
|
||||
@@ -103,18 +121,18 @@ func (r requiredVersionMap) isValidVersion(specVersion string) bool {
|
||||
}
|
||||
|
||||
// requiredVersion returns the minimum version required for the given spec
|
||||
func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
|
||||
func (r requiredVersionMap) requiredVersion(spec *Spec) version {
|
||||
minVersion := vEarliest
|
||||
|
||||
for v, isRequired := range validSpecVersions {
|
||||
if isRequired == nil {
|
||||
continue
|
||||
}
|
||||
if isRequired(spec) && v.IsGreaterThan(minVersion) {
|
||||
if isRequired(spec) && v.isGreaterThan(minVersion) {
|
||||
minVersion = v
|
||||
}
|
||||
// If we have already detected the latest version then no later version could be detected
|
||||
if minVersion.IsLatest() {
|
||||
if minVersion.isLatest() {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -122,15 +140,22 @@ func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
|
||||
return minVersion
|
||||
}
|
||||
|
||||
// requiresV100 returns true if the spec uses v1.0.0 features.
|
||||
// Since the v1.0.0 spec bump was due to moving the minimum version checks to
|
||||
// the spec package, there are no explicit spec changes.
|
||||
func requiresV100(_ *Spec) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// requiresV080 returns true if the spec uses v0.8.0 features.
|
||||
// Since the v0.8.0 spec bump was due to the removed .ToOCI functions on the
|
||||
// spec types, there are explicit spec changes.
|
||||
func requiresV080(_ *cdi.Spec) bool {
|
||||
// spec types, there are no explicit spec changes.
|
||||
func requiresV080(_ *Spec) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// requiresV070 returns true if the spec uses v0.7.0 features
|
||||
func requiresV070(spec *cdi.Spec) bool {
|
||||
func requiresV070(spec *Spec) bool {
|
||||
if spec.ContainerEdits.IntelRdt != nil {
|
||||
return true
|
||||
}
|
||||
@@ -153,7 +178,7 @@ func requiresV070(spec *cdi.Spec) bool {
|
||||
}
|
||||
|
||||
// requiresV060 returns true if the spec uses v0.6.0 features
|
||||
func requiresV060(spec *cdi.Spec) bool {
|
||||
func requiresV060(spec *Spec) bool {
|
||||
// The v0.6.0 spec allows annotations to be specified at a spec level
|
||||
for range spec.Annotations {
|
||||
return true
|
||||
@@ -167,23 +192,20 @@ func requiresV060(spec *cdi.Spec) bool {
|
||||
}
|
||||
|
||||
// The v0.6.0 spec allows dots "." in Kind name label (class)
|
||||
vendor, class := parser.ParseQualifier(spec.Kind)
|
||||
if vendor != "" {
|
||||
if strings.ContainsRune(class, '.') {
|
||||
return true
|
||||
}
|
||||
if !strings.Contains(spec.Kind, "/") {
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
class := strings.SplitN(spec.Kind, "/", 2)[1]
|
||||
return strings.Contains(class, ".")
|
||||
}
|
||||
|
||||
// requiresV050 returns true if the spec uses v0.5.0 features
|
||||
func requiresV050(spec *cdi.Spec) bool {
|
||||
var edits []*cdi.ContainerEdits
|
||||
func requiresV050(spec *Spec) bool {
|
||||
var edits []*ContainerEdits
|
||||
|
||||
for _, d := range spec.Devices {
|
||||
// The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter
|
||||
if len(d.Name) > 0 && !parser.IsLetter(rune(d.Name[0])) {
|
||||
// The v0.5.0 spec allowed device name to start with a digit
|
||||
if len(d.Name) > 0 && '0' <= d.Name[0] && d.Name[0] <= '9' {
|
||||
return true
|
||||
}
|
||||
edits = append(edits, &d.ContainerEdits)
|
||||
@@ -202,8 +224,8 @@ func requiresV050(spec *cdi.Spec) bool {
|
||||
}
|
||||
|
||||
// requiresV040 returns true if the spec uses v0.4.0 features
|
||||
func requiresV040(spec *cdi.Spec) bool {
|
||||
var edits []*cdi.ContainerEdits
|
||||
func requiresV040(spec *Spec) bool {
|
||||
var edits []*ContainerEdits
|
||||
|
||||
for _, d := range spec.Devices {
|
||||
edits = append(edits, &d.ContainerEdits)
|
||||
Reference in New Issue
Block a user