mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Merge 5d55813fb8
into 2aadbbf22d
This commit is contained in:
commit
90614051db
@ -60,6 +60,11 @@ type options struct {
|
|||||||
files cli.StringSlice
|
files cli.StringSlice
|
||||||
ignorePatterns cli.StringSlice
|
ignorePatterns cli.StringSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spec represents the CDI spec options.
|
||||||
|
spec struct {
|
||||||
|
allowAdditionalGIDs bool
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommand constructs a generate-cdi command with the specified logger
|
// NewCommand constructs a generate-cdi command with the specified logger
|
||||||
@ -169,6 +174,11 @@ func (m command) build() *cli.Command {
|
|||||||
Usage: "Specify a pattern the CSV mount specifications.",
|
Usage: "Specify a pattern the CSV mount specifications.",
|
||||||
Destination: &opts.csv.ignorePatterns,
|
Destination: &opts.csv.ignorePatterns,
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "--allow-additional-gids",
|
||||||
|
Usage: "Allow the use of the additionalGIDs field for generated CDI specifications. Note this will generate a v0.7.0 CDI specification.",
|
||||||
|
Destination: &opts.spec.allowAdditionalGIDs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &c
|
return &c
|
||||||
@ -273,6 +283,7 @@ func (m command) generateSpec(opts *options) (spec.Interface, error) {
|
|||||||
nvcdi.WithLibrarySearchPaths(opts.librarySearchPaths.Value()),
|
nvcdi.WithLibrarySearchPaths(opts.librarySearchPaths.Value()),
|
||||||
nvcdi.WithCSVFiles(opts.csv.files.Value()),
|
nvcdi.WithCSVFiles(opts.csv.files.Value()),
|
||||||
nvcdi.WithCSVIgnorePatterns(opts.csv.ignorePatterns.Value()),
|
nvcdi.WithCSVIgnorePatterns(opts.csv.ignorePatterns.Value()),
|
||||||
|
nvcdi.WithAllowAdditionalGIDs(opts.spec.allowAdditionalGIDs),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create CDI library: %v", err)
|
return nil, fmt.Errorf("failed to create CDI library: %v", err)
|
||||||
|
@ -17,12 +17,17 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type featureName string
|
type featureName string
|
||||||
|
type feature bool
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
FeatureAllowAdditionalGIDs = featureName("allow-additional-gids")
|
||||||
|
FeatureGDRCopy = featureName("gdrcopy")
|
||||||
FeatureGDS = featureName("gds")
|
FeatureGDS = featureName("gds")
|
||||||
FeatureMOFED = featureName("mofed")
|
FeatureMOFED = featureName("mofed")
|
||||||
FeatureNVSWITCH = featureName("nvswitch")
|
FeatureNVSWITCH = featureName("nvswitch")
|
||||||
FeatureGDRCopy = featureName("gdrcopy")
|
|
||||||
|
featureEnabled feature = true
|
||||||
|
featureDisabled feature = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// features specifies a set of named features.
|
// features specifies a set of named features.
|
||||||
@ -31,10 +36,13 @@ type features struct {
|
|||||||
MOFED *feature `toml:"mofed,omitempty"`
|
MOFED *feature `toml:"mofed,omitempty"`
|
||||||
NVSWITCH *feature `toml:"nvswitch,omitempty"`
|
NVSWITCH *feature `toml:"nvswitch,omitempty"`
|
||||||
GDRCopy *feature `toml:"gdrcopy,omitempty"`
|
GDRCopy *feature `toml:"gdrcopy,omitempty"`
|
||||||
|
// AllowAdditionalGIDs triggers the additionalGIDs field in internal CDI
|
||||||
|
// specifications to be populated if required. This can be useful when
|
||||||
|
// running the container as a user id that may not have access to device
|
||||||
|
// nodes.
|
||||||
|
AllowAdditionalGIDs *feature `toml:"allow-additional-gids,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type feature bool
|
|
||||||
|
|
||||||
// IsEnabled checks whether a specified named feature is enabled.
|
// IsEnabled checks whether a specified named feature is enabled.
|
||||||
// An optional list of environments to check for feature-specific environment
|
// An optional list of environments to check for feature-specific environment
|
||||||
// variables can also be supplied.
|
// variables can also be supplied.
|
||||||
@ -44,6 +52,7 @@ func (fs features) IsEnabled(n featureName, in ...getenver) bool {
|
|||||||
FeatureMOFED: "NVIDIA_MOFED",
|
FeatureMOFED: "NVIDIA_MOFED",
|
||||||
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
|
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
|
||||||
FeatureGDRCopy: "NVIDIA_GDRCOPY",
|
FeatureGDRCopy: "NVIDIA_GDRCOPY",
|
||||||
|
FeatureAllowAdditionalGIDs: "NVIDIA_ALLOW_ADDITIONAL_GIDS",
|
||||||
}
|
}
|
||||||
|
|
||||||
envvar := featureEnvvars[n]
|
envvar := featureEnvvars[n]
|
||||||
@ -56,6 +65,8 @@ func (fs features) IsEnabled(n featureName, in ...getenver) bool {
|
|||||||
return fs.NVSWITCH.isEnabled(envvar, in...)
|
return fs.NVSWITCH.isEnabled(envvar, in...)
|
||||||
case FeatureGDRCopy:
|
case FeatureGDRCopy:
|
||||||
return fs.GDRCopy.isEnabled(envvar, in...)
|
return fs.GDRCopy.isEnabled(envvar, in...)
|
||||||
|
case FeatureAllowAdditionalGIDs:
|
||||||
|
return fs.AllowAdditionalGIDs.isEnabled(envvar, in...)
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -66,17 +77,19 @@ func (fs features) IsEnabled(n featureName, in ...getenver) bool {
|
|||||||
// associated envvar is checked in the specified getenver for the string "enabled"
|
// associated envvar is checked in the specified getenver for the string "enabled"
|
||||||
// A CUDA container / image can be passed here.
|
// A CUDA container / image can be passed here.
|
||||||
func (f *feature) isEnabled(envvar string, ins ...getenver) bool {
|
func (f *feature) isEnabled(envvar string, ins ...getenver) bool {
|
||||||
if f != nil {
|
if envvar != "" {
|
||||||
return bool(*f)
|
for _, in := range ins {
|
||||||
}
|
switch in.Getenv(envvar) {
|
||||||
if envvar == "" {
|
case "enabled", "true":
|
||||||
|
return true
|
||||||
|
case "disabled", "false":
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, in := range ins {
|
|
||||||
if in.Getenv(envvar) == "enabled" {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if f != nil {
|
||||||
|
return bool(*f)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
150
internal/config/features_test.go
Normal file
150
internal/config/features_test.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
# Copyright 2024 NVIDIA CORPORATION
|
||||||
|
#
|
||||||
|
# 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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFeatures(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
description string
|
||||||
|
features features
|
||||||
|
expected map[featureName]bool
|
||||||
|
envs []getenver
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "empty features",
|
||||||
|
expected: map[featureName]bool{
|
||||||
|
FeatureAllowAdditionalGIDs: false,
|
||||||
|
FeatureGDRCopy: false,
|
||||||
|
FeatureGDS: false,
|
||||||
|
FeatureMOFED: false,
|
||||||
|
FeatureNVSWITCH: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "envvar sets features if enabled",
|
||||||
|
expected: map[featureName]bool{
|
||||||
|
FeatureAllowAdditionalGIDs: true,
|
||||||
|
FeatureGDRCopy: false,
|
||||||
|
FeatureGDS: false,
|
||||||
|
FeatureMOFED: false,
|
||||||
|
FeatureNVSWITCH: false,
|
||||||
|
},
|
||||||
|
envs: []getenver{
|
||||||
|
mockEnver{
|
||||||
|
"NVIDIA_ALLOW_ADDITIONAL_GIDS": "enabled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "envvar sets features if true",
|
||||||
|
expected: map[featureName]bool{
|
||||||
|
FeatureAllowAdditionalGIDs: true,
|
||||||
|
FeatureGDRCopy: false,
|
||||||
|
FeatureGDS: false,
|
||||||
|
FeatureMOFED: false,
|
||||||
|
FeatureNVSWITCH: false,
|
||||||
|
},
|
||||||
|
envs: []getenver{
|
||||||
|
mockEnver{
|
||||||
|
"NVIDIA_ALLOW_ADDITIONAL_GIDS": "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "feature sets feature",
|
||||||
|
features: features{
|
||||||
|
AllowAdditionalGIDs: ptr(featureEnabled),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
for n, v := range tc.expected {
|
||||||
|
t.Run(tc.description+"-"+string(n), func(t *testing.T) {
|
||||||
|
require.EqualValues(t, v, tc.features.IsEnabled(n, tc.envs...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFeature(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
description string
|
||||||
|
feature *feature
|
||||||
|
envvar string
|
||||||
|
envs []getenver
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "nil feature is false",
|
||||||
|
feature: nil,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "feature enables",
|
||||||
|
feature: ptr(featureEnabled),
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "feature disabled",
|
||||||
|
feature: ptr(featureDisabled),
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "envvar overrides feature disabled",
|
||||||
|
feature: ptr(featureDisabled),
|
||||||
|
envvar: "FEATURE",
|
||||||
|
envs: []getenver{
|
||||||
|
mockEnver{"FEATURE": "enabled"},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "envvar overrides feature enabled",
|
||||||
|
feature: ptr(featureEnabled),
|
||||||
|
envvar: "FEATURE",
|
||||||
|
envs: []getenver{
|
||||||
|
mockEnver{"FEATURE": "disabled"},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
require.EqualValues(t, tc.expected, tc.feature.isEnabled(tc.envvar, tc.envs...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockEnver map[string]string
|
||||||
|
|
||||||
|
func (m mockEnver) Getenv(k string) string {
|
||||||
|
return m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptr[T any](x T) *T {
|
||||||
|
return &x
|
||||||
|
}
|
29
internal/edits/api.go
Normal file
29
internal/edits/api.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
# Copyright 2024 NVIDIA CORPORATION
|
||||||
|
#
|
||||||
|
# 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 edits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
|
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
EditsFromDiscoverer(discover.Discover) (*cdi.ContainerEdits, error)
|
||||||
|
SpecModifierFromDiscoverer(discover.Discover) (oci.SpecModifier, error)
|
||||||
|
}
|
@ -17,6 +17,9 @@
|
|||||||
package edits
|
package edits
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
|
||||||
@ -26,15 +29,23 @@ import (
|
|||||||
type device discover.Device
|
type device discover.Device
|
||||||
|
|
||||||
// toEdits converts a discovered device to CDI Container Edits.
|
// toEdits converts a discovered device to CDI Container Edits.
|
||||||
func (d device) toEdits() (*cdi.ContainerEdits, error) {
|
func (d device) toEdits(allowAdditionalGIDs bool) (*cdi.ContainerEdits, error) {
|
||||||
deviceNode, err := d.toSpec()
|
deviceNode, err := d.toSpec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var additionalGIDs []uint32
|
||||||
|
if allowAdditionalGIDs {
|
||||||
|
if requiredGID := d.getRequiredGID(); requiredGID != 0 {
|
||||||
|
additionalGIDs = append(additionalGIDs, requiredGID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e := cdi.ContainerEdits{
|
e := cdi.ContainerEdits{
|
||||||
ContainerEdits: &specs.ContainerEdits{
|
ContainerEdits: &specs.ContainerEdits{
|
||||||
DeviceNodes: []*specs.DeviceNode{deviceNode},
|
DeviceNodes: []*specs.DeviceNode{deviceNode},
|
||||||
|
AdditionalGIDs: additionalGIDs,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return &e, nil
|
return &e, nil
|
||||||
@ -52,6 +63,7 @@ func (d device) toSpec() (*specs.DeviceNode, error) {
|
|||||||
if hostPath == d.Path {
|
if hostPath == d.Path {
|
||||||
hostPath = ""
|
hostPath = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
s := specs.DeviceNode{
|
s := specs.DeviceNode{
|
||||||
HostPath: hostPath,
|
HostPath: hostPath,
|
||||||
Path: d.Path,
|
Path: d.Path,
|
||||||
@ -59,3 +71,29 @@ func (d device) toSpec() (*specs.DeviceNode, error) {
|
|||||||
|
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRequiredGID returns the group id of the device if the device is not world read/writable.
|
||||||
|
// If the information cannot be extracted or an error occurs, 0 is returned.
|
||||||
|
func (d device) getRequiredGID() uint32 {
|
||||||
|
path := d.HostPath
|
||||||
|
if path == "" {
|
||||||
|
path = d.Path
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat unix.Stat_t
|
||||||
|
if err := unix.Lstat(path, &stat); err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// This is only supported for char devices
|
||||||
|
if stat.Mode&unix.S_IFMT != unix.S_IFCHR {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if permissionsForOther := os.FileMode(stat.Mode).Perm(); permissionsForOther&06 == 0 {
|
||||||
|
return stat.Gid
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
@ -17,15 +17,11 @@
|
|||||||
package edits
|
package edits
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
ociSpecs "github.com/opencontainers/runtime-spec/specs-go"
|
ociSpecs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type edits struct {
|
type edits struct {
|
||||||
@ -33,58 +29,6 @@ type edits struct {
|
|||||||
logger logger.Interface
|
logger logger.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSpecEdits creates a SpecModifier that defines the required OCI spec edits (as CDI ContainerEdits) from the specified
|
|
||||||
// discoverer.
|
|
||||||
func NewSpecEdits(logger logger.Interface, d discover.Discover) (oci.SpecModifier, error) {
|
|
||||||
c, err := FromDiscoverer(d)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error constructing container edits: %v", err)
|
|
||||||
}
|
|
||||||
e := edits{
|
|
||||||
ContainerEdits: *c,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &e, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromDiscoverer creates CDI container edits for the specified discoverer.
|
|
||||||
func FromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
|
|
||||||
devices, err := d.Devices()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to discover devices: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mounts, err := d.Mounts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to discover mounts: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks, err := d.Hooks()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to discover hooks: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := NewContainerEdits()
|
|
||||||
for _, d := range devices {
|
|
||||||
edits, err := device(d).toEdits()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to created container edits for device: %v", err)
|
|
||||||
}
|
|
||||||
c.Append(edits)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range mounts {
|
|
||||||
c.Append(mount(m).toEdits())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, h := range hooks {
|
|
||||||
c.Append(hook(h).toEdits())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContainerEdits is a utility function to create a CDI ContainerEdits struct.
|
// NewContainerEdits is a utility function to create a CDI ContainerEdits struct.
|
||||||
func NewContainerEdits() *cdi.ContainerEdits {
|
func NewContainerEdits() *cdi.ContainerEdits {
|
||||||
c := cdi.ContainerEdits{
|
c := cdi.ContainerEdits{
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFromDiscovererAllowsMountsToIterate(t *testing.T) {
|
func TestFromDiscovererAllowsMountsToIterate(t *testing.T) {
|
||||||
edits, err := FromDiscoverer(discover.None{})
|
edits, err := New().EditsFromDiscoverer(discover.None{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Empty(t, edits.Mounts)
|
require.Empty(t, edits.Mounts)
|
||||||
|
92
internal/edits/lib.go
Normal file
92
internal/edits/lib.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
# Copyright 2024 NVIDIA CORPORATION
|
||||||
|
#
|
||||||
|
# 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 edits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
|
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates an Interface from the supplied options.
|
||||||
|
func New(opts ...Option) Interface {
|
||||||
|
o := &options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
if o.logger == nil {
|
||||||
|
o.logger = logger.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditsFromDiscoverer creates CDI container edits for the specified discoverer.
|
||||||
|
func (o *options) EditsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
|
||||||
|
devices, err := d.Devices()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to discover devices: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts, err := d.Mounts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to discover mounts: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, err := d.Hooks()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to discover hooks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := NewContainerEdits()
|
||||||
|
for _, d := range devices {
|
||||||
|
edits, err := device(d).toEdits(o.allowAdditionalGIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create container edits for device: %w", err)
|
||||||
|
}
|
||||||
|
c.Append(edits)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range mounts {
|
||||||
|
c.Append(mount(m).toEdits())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range hooks {
|
||||||
|
c.Append(hook(h).toEdits())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpecModifierFromDiscoverer creates a SpecModifier that defines the required OCI spec edits (as CDI ContainerEdits) from the specified
|
||||||
|
// discoverer.
|
||||||
|
func (o *options) SpecModifierFromDiscoverer(d discover.Discover) (oci.SpecModifier, error) {
|
||||||
|
c, err := o.EditsFromDiscoverer(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error constructing container edits: %w", err)
|
||||||
|
}
|
||||||
|
e := edits{
|
||||||
|
ContainerEdits: *c,
|
||||||
|
logger: o.logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e, nil
|
||||||
|
}
|
38
internal/edits/options.go
Normal file
38
internal/edits/options.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
# Copyright 2024 NVIDIA CORPORATION
|
||||||
|
#
|
||||||
|
# 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 edits
|
||||||
|
|
||||||
|
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
logger logger.Interface
|
||||||
|
allowAdditionalGIDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func WithLogger(logger logger.Interface) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.logger = logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithAllowAdditionalGIDs(allowAdditionalGIDs bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.allowAdditionalGIDs = allowAdditionalGIDs
|
||||||
|
}
|
||||||
|
}
|
30
internal/edits/resource.go
Normal file
30
internal/edits/resource.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
# Copyright 2024 NVIDIA CORPORATION
|
||||||
|
#
|
||||||
|
# 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 edits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewResource creates a CDI resource (Device) with the specified name.
|
||||||
|
func NewResource(name string, edits *cdi.ContainerEdits) specs.Device {
|
||||||
|
return specs.Device{
|
||||||
|
Name: name,
|
||||||
|
ContainerEdits: *edits.ContainerEdits,
|
||||||
|
}
|
||||||
|
}
|
@ -30,14 +30,16 @@ import (
|
|||||||
type discoverModifier struct {
|
type discoverModifier struct {
|
||||||
logger logger.Interface
|
logger logger.Interface
|
||||||
discoverer discover.Discover
|
discoverer discover.Discover
|
||||||
|
allowAdditionalGIDs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewModifierFromDiscoverer creates a modifier that applies the discovered
|
// NewModifierFromDiscoverer creates a modifier that applies the discovered
|
||||||
// modifications to an OCI spec if required by the runtime wrapper.
|
// modifications to an OCI spec if required by the runtime wrapper.
|
||||||
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oci.SpecModifier, error) {
|
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover, allowAdditionalGIDs bool) (oci.SpecModifier, error) {
|
||||||
m := discoverModifier{
|
m := discoverModifier{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
discoverer: d,
|
discoverer: d,
|
||||||
|
allowAdditionalGIDs: allowAdditionalGIDs,
|
||||||
}
|
}
|
||||||
return &m, nil
|
return &m, nil
|
||||||
}
|
}
|
||||||
@ -45,7 +47,12 @@ func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oc
|
|||||||
// Modify applies the modifications required by discoverer to the incomming OCI spec.
|
// Modify applies the modifications required by discoverer to the incomming OCI spec.
|
||||||
// These modifications are applied in-place.
|
// These modifications are applied in-place.
|
||||||
func (m discoverModifier) Modify(spec *specs.Spec) error {
|
func (m discoverModifier) Modify(spec *specs.Spec) error {
|
||||||
specEdits, err := edits.NewSpecEdits(m.logger, m.discoverer)
|
e := edits.New(
|
||||||
|
edits.WithLogger(m.logger),
|
||||||
|
edits.WithAllowAdditionalGIDs(m.allowAdditionalGIDs),
|
||||||
|
)
|
||||||
|
|
||||||
|
specEdits, err := e.SpecModifierFromDiscoverer(m.discoverer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get required container edits: %v", err)
|
return fmt.Errorf("failed to get required container edits: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func TestDiscoverModifier(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
m, err := NewModifierFromDiscoverer(logger, tc.discover)
|
m, err := NewModifierFromDiscoverer(logger, tc.discover, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = m.Modify(tc.spec)
|
err = m.Modify(tc.spec)
|
||||||
|
@ -78,5 +78,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
|||||||
discoverers = append(discoverers, d)
|
discoverers = append(discoverers, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...))
|
allowAdditionalGIDs := !cfg.Features.IsEnabled(config.FeatureAllowAdditionalGIDs, image)
|
||||||
|
|
||||||
|
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...), allowAdditionalGIDs)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,9 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
|
|||||||
drmNodes,
|
drmNodes,
|
||||||
mounts,
|
mounts,
|
||||||
)
|
)
|
||||||
return NewModifierFromDiscoverer(logger, d)
|
|
||||||
|
allowAdditionalGIDs := cfg.Features.IsEnabled(config.FeatureAllowAdditionalGIDs, image)
|
||||||
|
return NewModifierFromDiscoverer(logger, d, allowAdditionalGIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// requiresGraphicsModifier determines whether a graphics modifier is required.
|
// requiresGraphicsModifier determines whether a graphics modifier is required.
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
package oci
|
package oci
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure, that SpecMock does implement Spec.
|
// Ensure, that SpecMock does implement Spec.
|
||||||
|
@ -30,7 +30,7 @@ import (
|
|||||||
|
|
||||||
// GetGPUDeviceSpecs returns the CDI device specs for the full GPU represented by 'device'.
|
// GetGPUDeviceSpecs returns the CDI device specs for the full GPU represented by 'device'.
|
||||||
func (l *nvmllib) GetGPUDeviceSpecs(i int, d device.Device) ([]specs.Device, error) {
|
func (l *nvmllib) GetGPUDeviceSpecs(i int, d device.Device) ([]specs.Device, error) {
|
||||||
edits, err := l.GetGPUDeviceEdits(d)
|
e, err := l.GetGPUDeviceEdits(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get edits for device: %v", err)
|
return nil, fmt.Errorf("failed to get edits for device: %v", err)
|
||||||
}
|
}
|
||||||
@ -41,10 +41,7 @@ func (l *nvmllib) GetGPUDeviceSpecs(i int, d device.Device) ([]specs.Device, err
|
|||||||
return nil, fmt.Errorf("failed to get device name: %v", err)
|
return nil, fmt.Errorf("failed to get device name: %v", err)
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
spec := specs.Device{
|
spec := edits.NewResource(name, e)
|
||||||
Name: name,
|
|
||||||
ContainerEdits: *edits.ContainerEdits,
|
|
||||||
}
|
|
||||||
deviceSpecs = append(deviceSpecs, spec)
|
deviceSpecs = append(deviceSpecs, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,12 +55,7 @@ func (l *nvmllib) GetGPUDeviceEdits(d device.Device) (*cdi.ContainerEdits, error
|
|||||||
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
editsForDevice, err := edits.FromDiscoverer(device)
|
return (*nvcdilib)(l).editsFromDiscoverer(device)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create container edits for device: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return editsForDevice, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFullGPUDiscoverer creates a discoverer for the full GPU defined by the specified device.
|
// newFullGPUDiscoverer creates a discoverer for the full GPU defined by the specified device.
|
||||||
|
@ -38,22 +38,19 @@ func (l *gdslib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create GPUDirect Storage discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create GPUDirect Storage discoverer: %v", err)
|
||||||
}
|
}
|
||||||
edits, err := edits.FromDiscoverer(discoverer)
|
e, err := (*nvcdilib)(l).editsFromDiscoverer(discoverer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create container edits for GPUDirect Storage: %v", err)
|
return nil, fmt.Errorf("failed to create container edits for GPUDirect Storage: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceSpec := specs.Device{
|
deviceSpec := edits.NewResource("all", e)
|
||||||
Name: "all",
|
|
||||||
ContainerEdits: *edits.ContainerEdits,
|
|
||||||
}
|
|
||||||
|
|
||||||
return []specs.Device{deviceSpec}, nil
|
return []specs.Device{deviceSpec}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
||||||
func (l *gdslib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
func (l *gdslib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||||
return edits.FromDiscoverer(discover.None{})
|
return edits.NewContainerEdits(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSpec is unsppported for the gdslib specs.
|
// GetSpec is unsppported for the gdslib specs.
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
||||||
@ -53,7 +52,7 @@ func (l *csvlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create discoverer for CSV files: %v", err)
|
return nil, fmt.Errorf("failed to create discoverer for CSV files: %v", err)
|
||||||
}
|
}
|
||||||
e, err := edits.FromDiscoverer(d)
|
e, err := (*nvcdilib)(l).editsFromDiscoverer(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create container edits for CSV files: %v", err)
|
return nil, fmt.Errorf("failed to create container edits for CSV files: %v", err)
|
||||||
}
|
}
|
||||||
@ -64,10 +63,7 @@ func (l *csvlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
}
|
}
|
||||||
var deviceSpecs []specs.Device
|
var deviceSpecs []specs.Device
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
deviceSpec := specs.Device{
|
deviceSpec := edits.NewResource(name, e)
|
||||||
Name: name,
|
|
||||||
ContainerEdits: *e.ContainerEdits,
|
|
||||||
}
|
|
||||||
deviceSpecs = append(deviceSpecs, deviceSpec)
|
deviceSpecs = append(deviceSpecs, deviceSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,8 +72,7 @@ func (l *csvlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
|
|
||||||
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
||||||
func (l *csvlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
func (l *csvlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||||
d := discover.None{}
|
return edits.NewContainerEdits(), nil
|
||||||
return edits.FromDiscoverer(d)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGPUDeviceEdits generates a CDI specification that can be used for GPU devices
|
// GetGPUDeviceEdits generates a CDI specification that can be used for GPU devices
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ func (l *nvmllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
|||||||
return nil, fmt.Errorf("failed to create discoverer for common entities: %v", err)
|
return nil, fmt.Errorf("failed to create discoverer for common entities: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits.FromDiscoverer(common)
|
return (*nvcdilib)(l).editsFromDiscoverer(common)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDeviceSpecsByID returns the CDI device specs for the GPU(s) represented by
|
// GetDeviceSpecsByID returns the CDI device specs for the GPU(s) represented by
|
||||||
|
@ -39,15 +39,12 @@ func (l *wsllib) GetSpec() (spec.Interface, error) {
|
|||||||
// GetAllDeviceSpecs returns the device specs for all available devices.
|
// GetAllDeviceSpecs returns the device specs for all available devices.
|
||||||
func (l *wsllib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
func (l *wsllib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
||||||
device := newDXGDeviceDiscoverer(l.logger, l.devRoot)
|
device := newDXGDeviceDiscoverer(l.logger, l.devRoot)
|
||||||
deviceEdits, err := edits.FromDiscoverer(device)
|
e, err := (*nvcdilib)(l).editsFromDiscoverer(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create container edits for DXG device: %v", err)
|
return nil, fmt.Errorf("failed to create container edits for DXG device: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceSpec := specs.Device{
|
deviceSpec := edits.NewResource("all", e)
|
||||||
Name: "all",
|
|
||||||
ContainerEdits: *deviceEdits.ContainerEdits,
|
|
||||||
}
|
|
||||||
|
|
||||||
return []specs.Device{deviceSpec}, nil
|
return []specs.Device{deviceSpec}, nil
|
||||||
}
|
}
|
||||||
@ -59,7 +56,7 @@ func (l *wsllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
|||||||
return nil, fmt.Errorf("failed to create discoverer for WSL driver: %v", err)
|
return nil, fmt.Errorf("failed to create discoverer for WSL driver: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits.FromDiscoverer(driver)
|
return (*nvcdilib)(l).editsFromDiscoverer(driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGPUDeviceEdits generates a CDI specification that can be used for GPU devices
|
// GetGPUDeviceEdits generates a CDI specification that can be used for GPU devices
|
||||||
|
@ -24,6 +24,8 @@ import (
|
|||||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
|
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||||
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
|
||||||
@ -63,6 +65,8 @@ type nvcdilib struct {
|
|||||||
infolib info.Interface
|
infolib info.Interface
|
||||||
|
|
||||||
mergedDeviceOptions []transform.MergedDeviceOption
|
mergedDeviceOptions []transform.MergedDeviceOption
|
||||||
|
|
||||||
|
allowAdditionalGIDs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new nvcdi library
|
// New creates a new nvcdi library
|
||||||
@ -190,6 +194,15 @@ func (m *wrapper) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
|||||||
return edits, nil
|
return edits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editsFromDiscoverer
|
||||||
|
func (l *nvcdilib) editsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
|
||||||
|
e := edits.New(
|
||||||
|
edits.WithLogger(l.logger),
|
||||||
|
edits.WithAllowAdditionalGIDs(l.allowAdditionalGIDs),
|
||||||
|
)
|
||||||
|
return e.EditsFromDiscoverer(d)
|
||||||
|
}
|
||||||
|
|
||||||
// resolveMode resolves the mode for CDI spec generation based on the current system.
|
// resolveMode resolves the mode for CDI spec generation based on the current system.
|
||||||
func (l *nvcdilib) resolveMode() (rmode string) {
|
func (l *nvcdilib) resolveMode() (rmode string) {
|
||||||
if l.mode != ModeAuto {
|
if l.mode != ModeAuto {
|
||||||
|
@ -43,19 +43,16 @@ func (m *managementlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
edits, err := edits.FromDiscoverer(devices)
|
e, err := (*nvcdilib)(m).editsFromDiscoverer(devices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create edits from discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create edits from discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(edits.DeviceNodes) == 0 {
|
if len(e.DeviceNodes) == 0 {
|
||||||
return nil, fmt.Errorf("no NVIDIA device nodes found")
|
return nil, fmt.Errorf("no NVIDIA device nodes found")
|
||||||
}
|
}
|
||||||
|
|
||||||
device := specs.Device{
|
device := edits.NewResource("all", e)
|
||||||
Name: "all",
|
|
||||||
ContainerEdits: *edits.ContainerEdits,
|
|
||||||
}
|
|
||||||
return []specs.Device{device}, nil
|
return []specs.Device{device}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +68,7 @@ func (m *managementlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
|||||||
return nil, fmt.Errorf("failed to create driver library discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create driver library discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
edits, err := edits.FromDiscoverer(driver)
|
edits, err := (*nvcdilib)(m).editsFromDiscoverer(driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create edits from discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create edits from discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
|
|
||||||
// GetMIGDeviceSpecs returns the CDI device specs for the full GPU represented by 'device'.
|
// GetMIGDeviceSpecs returns the CDI device specs for the full GPU represented by 'device'.
|
||||||
func (l *nvmllib) GetMIGDeviceSpecs(i int, d device.Device, j int, mig device.MigDevice) ([]specs.Device, error) {
|
func (l *nvmllib) GetMIGDeviceSpecs(i int, d device.Device, j int, mig device.MigDevice) ([]specs.Device, error) {
|
||||||
edits, err := l.GetMIGDeviceEdits(d, mig)
|
e, err := l.GetMIGDeviceEdits(d, mig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get edits for device: %v", err)
|
return nil, fmt.Errorf("failed to get edits for device: %v", err)
|
||||||
}
|
}
|
||||||
@ -40,10 +40,7 @@ func (l *nvmllib) GetMIGDeviceSpecs(i int, d device.Device, j int, mig device.Mi
|
|||||||
}
|
}
|
||||||
var deviceSpecs []specs.Device
|
var deviceSpecs []specs.Device
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
spec := specs.Device{
|
spec := edits.NewResource(name, e)
|
||||||
Name: name,
|
|
||||||
ContainerEdits: *edits.ContainerEdits,
|
|
||||||
}
|
|
||||||
deviceSpecs = append(deviceSpecs, spec)
|
deviceSpecs = append(deviceSpecs, spec)
|
||||||
}
|
}
|
||||||
return deviceSpecs, nil
|
return deviceSpecs, nil
|
||||||
@ -60,7 +57,7 @@ func (l *nvmllib) GetMIGDeviceEdits(parent device.Device, mig device.MigDevice)
|
|||||||
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
editsForDevice, err := edits.FromDiscoverer(deviceNodes)
|
editsForDevice, err := (*nvcdilib)(l).editsFromDiscoverer(deviceNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create container edits for Compute Instance: %v", err)
|
return nil, fmt.Errorf("failed to create container edits for Compute Instance: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -38,22 +38,19 @@ func (l *mofedlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create MOFED discoverer: %v", err)
|
return nil, fmt.Errorf("failed to create MOFED discoverer: %v", err)
|
||||||
}
|
}
|
||||||
edits, err := edits.FromDiscoverer(discoverer)
|
e, err := (*nvcdilib)(l).editsFromDiscoverer(discoverer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create container edits for MOFED devices: %v", err)
|
return nil, fmt.Errorf("failed to create container edits for MOFED devices: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceSpec := specs.Device{
|
deviceSpec := edits.NewResource("all", e)
|
||||||
Name: "all",
|
|
||||||
ContainerEdits: *edits.ContainerEdits,
|
|
||||||
}
|
|
||||||
|
|
||||||
return []specs.Device{deviceSpec}, nil
|
return []specs.Device{deviceSpec}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
||||||
func (l *mofedlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
func (l *mofedlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||||
return edits.FromDiscoverer(discover.None{})
|
return edits.NewContainerEdits(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSpec is unsppported for the mofedlib specs.
|
// GetSpec is unsppported for the mofedlib specs.
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
package nvcdi
|
package nvcdi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure, that nvmlUUIDerMock does implement nvmlUUIDer.
|
// Ensure, that nvmlUUIDerMock does implement nvmlUUIDer.
|
||||||
|
@ -155,3 +155,11 @@ func WithLibrarySearchPaths(paths []string) Option {
|
|||||||
o.librarySearchPaths = paths
|
o.librarySearchPaths = paths
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithAllowAdditionalGIDs specifies whether a generated CDI spec can contain
|
||||||
|
// the additionaGIDs field.
|
||||||
|
func WithAllowAdditionalGIDs(allowAdditionalGIDs bool) Option {
|
||||||
|
return func(o *nvcdilib) {
|
||||||
|
o.allowAdditionalGIDs = allowAdditionalGIDs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package transform
|
package transform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,6 +52,12 @@ func (d dedupe) Transform(spec *specs.Spec) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d dedupe) transformEdits(edits *specs.ContainerEdits) error {
|
func (d dedupe) transformEdits(edits *specs.ContainerEdits) error {
|
||||||
|
additionalGIDs, err := d.deduplicateAdditionalGIDs(edits.AdditionalGIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
edits.AdditionalGIDs = additionalGIDs
|
||||||
|
|
||||||
deviceNodes, err := d.deduplicateDeviceNodes(edits.DeviceNodes)
|
deviceNodes, err := d.deduplicateDeviceNodes(edits.DeviceNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -150,3 +158,19 @@ func (d dedupe) deduplicateMounts(entities []*specs.Mount) ([]*specs.Mount, erro
|
|||||||
}
|
}
|
||||||
return mounts, nil
|
return mounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d dedupe) deduplicateAdditionalGIDs(entities []uint32) ([]uint32, error) {
|
||||||
|
seen := make(map[uint32]bool)
|
||||||
|
var additionalGIDs []uint32
|
||||||
|
for _, e := range entities {
|
||||||
|
if seen[e] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[e] = true
|
||||||
|
additionalGIDs = append(additionalGIDs, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(additionalGIDs)
|
||||||
|
|
||||||
|
return additionalGIDs, nil
|
||||||
|
}
|
||||||
|
@ -109,7 +109,6 @@ func mergeDeviceSpecs(deviceSpecs []specs.Device, mergedDeviceName string) (*spe
|
|||||||
}
|
}
|
||||||
|
|
||||||
mergedEdits := edits.NewContainerEdits()
|
mergedEdits := edits.NewContainerEdits()
|
||||||
|
|
||||||
for _, d := range deviceSpecs {
|
for _, d := range deviceSpecs {
|
||||||
d := d
|
d := d
|
||||||
edit := cdi.ContainerEdits{
|
edit := cdi.ContainerEdits{
|
||||||
@ -118,9 +117,6 @@ func mergeDeviceSpecs(deviceSpecs []specs.Device, mergedDeviceName string) (*spe
|
|||||||
mergedEdits.Append(&edit)
|
mergedEdits.Append(&edit)
|
||||||
}
|
}
|
||||||
|
|
||||||
merged := specs.Device{
|
merged := edits.NewResource(mergedDeviceName, mergedEdits)
|
||||||
Name: mergedDeviceName,
|
|
||||||
ContainerEdits: *mergedEdits.ContainerEdits,
|
|
||||||
}
|
|
||||||
return &merged, nil
|
return &merged, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user