Compare commits

..

3 Commits

Author SHA1 Message Date
Evan Lezar
be25223e7a [no-relnote] Refactor driver library discovery
This change aligns the driver file discovery with device discovery
and allows other sources such as nvsandboxutils to be added.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2025-02-20 23:14:20 +02:00
Evan Lezar
de230a7e60 [no-relnote] Refactor CDI version extraction
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2025-02-20 23:14:13 +02:00
Evan Lezar
6746a412af [no-relnote] Move nvcdi wrapper to separate file
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2025-02-20 23:12:29 +02:00
126 changed files with 644 additions and 7060 deletions

View File

@@ -115,7 +115,7 @@ mod-verify:
check-vendor: vendor
git diff --exit-code HEAD -- go.mod go.sum vendor
git diff --quiet HEAD -- go.mod go.sum vendor
licenses:
go-licenses csv $(MODULE)/...

View File

@@ -21,7 +21,6 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/cudacompat"
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/update-ldcache"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
)
@@ -33,6 +32,5 @@ func New(logger logger.Interface) []*cli.Command {
ldcache.NewCommand(logger),
symlinks.NewCommand(logger),
chmod.NewCommand(logger),
cudacompat.NewCommand(logger),
}
}

View File

@@ -1,76 +0,0 @@
/**
# 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 cudacompat
import (
"os"
"path/filepath"
"github.com/moby/sys/symlink"
)
// A containerRoot represents the root filesystem of a container.
type containerRoot string
// hasPath checks whether the specified path exists in the root.
func (r containerRoot) hasPath(path string) bool {
resolved, err := r.resolve(path)
if err != nil {
return false
}
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
return false
}
return true
}
// globFiles matches the specified pattern in the root.
// The files that match must be regular files.
func (r containerRoot) globFiles(pattern string) ([]string, error) {
patternPath, err := r.resolve(pattern)
if err != nil {
return nil, err
}
matches, err := filepath.Glob(patternPath)
if err != nil {
return nil, err
}
var files []string
for _, match := range matches {
info, err := os.Lstat(match)
if err != nil {
return nil, err
}
// Ignore symlinks.
if info.Mode()&os.ModeSymlink != 0 {
continue
}
// Ignore directories.
if info.IsDir() {
continue
}
files = append(files, match)
}
return files, nil
}
// resolve returns the absolute path including root path.
// Symlinks are resolved, but are guaranteed to resolve in the root.
func (r containerRoot) resolve(path string) (string, error) {
absolute := filepath.Clean(filepath.Join(string(r), path))
return symlink.FollowSymlinkInScope(absolute, string(r))
}

View File

@@ -1,221 +0,0 @@
/**
# 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 cudacompat
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/urfave/cli/v2"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
const (
cudaCompatPath = "/usr/local/cuda/compat"
// cudaCompatLdsoconfdFilenamePattern specifies the pattern for the filename
// in ld.so.conf.d that includes a reference to the CUDA compat path.
// The 00-compat prefix is chosen to ensure that these libraries have a
// higher precedence than other libraries on the system.
cudaCompatLdsoconfdFilenamePattern = "00-compat-*.conf"
)
type command struct {
logger logger.Interface
}
type options struct {
hostDriverVersion string
containerSpec string
}
// NewCommand constructs a cuda-compat command with the specified logger
func NewCommand(logger logger.Interface) *cli.Command {
c := command{
logger: logger,
}
return c.build()
}
// build the enable-cuda-compat command
func (m command) build() *cli.Command {
cfg := options{}
// Create the 'enable-cuda-compat' command
c := cli.Command{
Name: "enable-cuda-compat",
Usage: "This hook ensures that the folder containing the CUDA compat libraries is added to the ldconfig search path if required.",
Before: func(c *cli.Context) error {
return m.validateFlags(c, &cfg)
},
Action: func(c *cli.Context) error {
return m.run(c, &cfg)
},
}
c.Flags = []cli.Flag{
&cli.StringFlag{
Name: "host-driver-version",
Usage: "Specify the host driver version. If the CUDA compat libraries detected in the container do not have a higher MAJOR version, the hook is a no-op.",
Destination: &cfg.hostDriverVersion,
},
&cli.StringFlag{
Name: "container-spec",
Hidden: true,
Category: "testing-only",
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN",
Destination: &cfg.containerSpec,
},
}
return &c
}
func (m command) validateFlags(_ *cli.Context, cfg *options) error {
return nil
}
func (m command) run(_ *cli.Context, cfg *options) error {
if cfg.hostDriverVersion == "" {
return nil
}
s, err := oci.LoadContainerState(cfg.containerSpec)
if err != nil {
return fmt.Errorf("failed to load container state: %w", err)
}
containerRootDir, err := s.GetContainerRoot()
if err != nil {
return fmt.Errorf("failed to determined container root: %w", err)
}
containerForwardCompatDir, err := m.getContainerForwardCompatDir(containerRoot(containerRootDir), cfg.hostDriverVersion)
if err != nil {
return fmt.Errorf("failed to get container forward compat directory: %w", err)
}
if containerForwardCompatDir == "" {
return nil
}
return m.createLdsoconfdFile(containerRoot(containerRootDir), cudaCompatLdsoconfdFilenamePattern, containerForwardCompatDir)
}
func (m command) getContainerForwardCompatDir(containerRoot containerRoot, hostDriverVersion string) (string, error) {
if hostDriverVersion == "" {
m.logger.Debugf("Host driver version not specified")
return "", nil
}
if !containerRoot.hasPath(cudaCompatPath) {
m.logger.Debugf("No CUDA forward compatibility libraries directory in container")
return "", nil
}
if !containerRoot.hasPath("/etc/ld.so.cache") {
m.logger.Debugf("The container does not have an LDCache")
return "", nil
}
libs, err := containerRoot.globFiles(filepath.Join(cudaCompatPath, "libcuda.so.*.*"))
if err != nil {
m.logger.Warningf("Failed to find CUDA compat library: %w", err)
return "", nil
}
if len(libs) == 0 {
m.logger.Debugf("No CUDA forward compatibility libraries container")
return "", nil
}
if len(libs) != 1 {
m.logger.Warningf("Unexpected number of CUDA compat libraries in container: %v", libs)
return "", nil
}
compatDriverVersion := strings.TrimPrefix(filepath.Base(libs[0]), "libcuda.so.")
compatMajor, err := extractMajorVersion(compatDriverVersion)
if err != nil {
return "", fmt.Errorf("failed to extract major version from %q: %v", compatDriverVersion, err)
}
driverMajor, err := extractMajorVersion(hostDriverVersion)
if err != nil {
return "", fmt.Errorf("failed to extract major version from %q: %v", hostDriverVersion, err)
}
if driverMajor >= compatMajor {
m.logger.Debugf("Compat major version is not greater than the host driver major version (%v >= %v)", hostDriverVersion, compatDriverVersion)
return "", nil
}
resolvedCompatDir := strings.TrimPrefix(filepath.Dir(libs[0]), string(containerRoot))
return resolvedCompatDir, nil
}
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/ in the specified root.
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
// contains the specified directories on each line.
func (m command) createLdsoconfdFile(in containerRoot, pattern string, dirs ...string) error {
if len(dirs) == 0 {
m.logger.Debugf("No directories to add to /etc/ld.so.conf")
return nil
}
ldsoconfdDir, err := in.resolve("/etc/ld.so.conf.d")
if err != nil {
return err
}
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
}
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
if err != nil {
return fmt.Errorf("failed to create config file: %w", err)
}
defer configFile.Close()
m.logger.Debugf("Adding directories %v to %v", dirs, configFile.Name())
added := make(map[string]bool)
for _, dir := range dirs {
if added[dir] {
continue
}
_, err = configFile.WriteString(fmt.Sprintf("%s\n", dir))
if err != nil {
return fmt.Errorf("failed to update config file: %w", err)
}
added[dir] = true
}
// The created file needs to be world readable for the cases where the container is run as a non-root user.
if err := configFile.Chmod(0644); err != nil {
return fmt.Errorf("failed to chmod config file: %w", err)
}
return nil
}
// extractMajorVersion parses a version string and returns the major version as an int.
func extractMajorVersion(version string) (int, error) {
majorString := strings.SplitN(version, ".", 2)[0]
return strconv.Atoi(majorString)
}

View File

@@ -1,182 +0,0 @@
/*
# 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 cudacompat
import (
"os"
"path/filepath"
"strings"
"testing"
testlog "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/require"
)
func TestCompatLibs(t *testing.T) {
logger, _ := testlog.NewNullLogger()
testCases := []struct {
description string
contents map[string]string
hostDriverVersion string
expectedContainerForwardCompatDir string
}{
{
description: "empty root",
hostDriverVersion: "222.55.66",
},
{
description: "compat lib is newer; no ldcache",
contents: map[string]string{
"/usr/local/cuda/compat/libcuda.so.333.88.99": "",
},
hostDriverVersion: "222.55.66",
},
{
description: "compat lib is newer; ldcache",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/usr/local/cuda/compat/libcuda.so.333.88.99": "",
},
hostDriverVersion: "222.55.66",
expectedContainerForwardCompatDir: "/usr/local/cuda/compat",
},
{
description: "compat lib is older; ldcache",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/usr/local/cuda/compat/libcuda.so.111.88.99": "",
},
hostDriverVersion: "222.55.66",
expectedContainerForwardCompatDir: "",
},
{
description: "compat lib has same major version; ldcache",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
},
hostDriverVersion: "222.55.66",
expectedContainerForwardCompatDir: "",
},
{
description: "numeric comparison is used; ldcache",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
},
hostDriverVersion: "99.55.66",
expectedContainerForwardCompatDir: "/usr/local/cuda/compat",
},
{
description: "driver version empty; ldcache",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
},
hostDriverVersion: "",
},
{
description: "symlinks are followed",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/etc/alternatives/cuda/compat/libcuda.so.333.88.99": "",
"/usr/local/cuda": "symlink=/etc/alternatives/cuda",
},
hostDriverVersion: "222.55.66",
expectedContainerForwardCompatDir: "/etc/alternatives/cuda/compat",
},
{
description: "symlinks stay in container",
contents: map[string]string{
"/etc/ld.so.cache": "",
"/compat/libcuda.so.333.88.99": "",
"/usr/local/cuda": "symlink=../../../../../../",
},
hostDriverVersion: "222.55.66",
expectedContainerForwardCompatDir: "/compat",
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
containerRootDir := t.TempDir()
for name, contents := range tc.contents {
target := filepath.Join(containerRootDir, name)
require.NoError(t, os.MkdirAll(filepath.Dir(target), 0755))
if strings.HasPrefix(contents, "symlink=") {
require.NoError(t, os.Symlink(strings.TrimPrefix(contents, "symlink="), target))
continue
}
require.NoError(t, os.WriteFile(target, []byte(contents), 0600))
}
c := command{
logger: logger,
}
containerForwardCompatDir, err := c.getContainerForwardCompatDir(containerRoot(containerRootDir), tc.hostDriverVersion)
require.NoError(t, err)
require.EqualValues(t, tc.expectedContainerForwardCompatDir, containerForwardCompatDir)
})
}
}
func TestUpdateLdconfig(t *testing.T) {
logger, _ := testlog.NewNullLogger()
testCases := []struct {
description string
folders []string
expectedContents string
}{
{
description: "no folders; have no contents",
},
{
description: "single folder is added",
folders: []string{"/usr/local/cuda/compat"},
expectedContents: "/usr/local/cuda/compat\n",
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
containerRootDir := t.TempDir()
c := command{
logger: logger,
}
err := c.createLdsoconfdFile(containerRoot(containerRootDir), cudaCompatLdsoconfdFilenamePattern, tc.folders...)
require.NoError(t, err)
matches, err := filepath.Glob(filepath.Join(containerRootDir, "/etc/ld.so.conf.d/00-compat-*.conf"))
require.NoError(t, err)
if tc.expectedContents == "" {
require.Empty(t, matches)
return
}
require.Len(t, matches, 1)
contents, err := os.ReadFile(matches[0])
require.NoError(t, err)
require.EqualValues(t, tc.expectedContents, string(contents))
})
}
}

View File

@@ -1,46 +0,0 @@
/**
# 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 ldcache
import (
"os"
"path/filepath"
"github.com/moby/sys/symlink"
)
// A containerRoot represents the root filesystem of a container.
type containerRoot string
// hasPath checks whether the specified path exists in the root.
func (r containerRoot) hasPath(path string) bool {
resolved, err := r.resolve(path)
if err != nil {
return false
}
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
return false
}
return true
}
// resolve returns the absolute path including root path.
// Symlinks are resolved, but are guaranteed to resolve in the root.
func (r containerRoot) resolve(path string) (string, error) {
absolute := filepath.Clean(filepath.Join(string(r), path))
return symlink.FollowSymlinkInScope(absolute, string(r))
}

View File

@@ -1,57 +0,0 @@
/**
# 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 ldcache
import (
"fmt"
"os"
"strconv"
"syscall"
"github.com/opencontainers/runc/libcontainer/dmz"
)
// SafeExec attempts to clone the specified binary (as an memfd, for example) before executing it.
func (m command) SafeExec(path string, args []string, envv []string) error {
safeExe, err := cloneBinary(path)
if err != nil {
m.logger.Warningf("Failed to clone binary %q: %v; falling back to Exec", path, err)
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
return syscall.Exec(path, args, envv)
}
defer safeExe.Close()
exePath := "/proc/self/fd/" + strconv.Itoa(int(safeExe.Fd()))
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
return syscall.Exec(exePath, args, envv)
}
func cloneBinary(path string) (*os.File, error) {
exe, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("opening current binary: %w", err)
}
defer exe.Close()
stat, err := exe.Stat()
if err != nil {
return nil, fmt.Errorf("checking %v size: %w", path, err)
}
size := stat.Size()
return dmz.CloneBinary(exe, size, path, os.TempDir())
}

View File

@@ -22,6 +22,7 @@ import (
"os"
"path/filepath"
"strings"
"syscall"
"github.com/urfave/cli/v2"
@@ -30,15 +31,6 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
const (
// ldsoconfdFilenamePattern specifies the pattern for the filename
// in ld.so.conf.d that includes references to the specified directories.
// The 00-nvcr prefix is chosen to ensure that these libraries have a
// higher precedence than other libraries on the system, but lower than
// the 00-cuda-compat that is included in some containers.
ldsoconfdFilenamePattern = "00-nvcr-*.conf"
)
type command struct {
logger logger.Interface
}
@@ -108,20 +100,18 @@ func (m command) run(c *cli.Context, cfg *options) error {
return fmt.Errorf("failed to load container state: %v", err)
}
containerRootDir, err := s.GetContainerRoot()
containerRoot, err := s.GetContainerRoot()
if err != nil {
return fmt.Errorf("failed to determined container root: %v", err)
}
ldconfigPath := m.resolveLDConfigPath(cfg.ldconfigPath)
args := []string{filepath.Base(ldconfigPath)}
if containerRootDir != "" {
args = append(args, "-r", containerRootDir)
if containerRoot != "" {
args = append(args, "-r", containerRoot)
}
containerRoot := containerRoot(containerRootDir)
if containerRoot.hasPath("/etc/ld.so.cache") {
if root(containerRoot).hasPath("/etc/ld.so.cache") {
args = append(args, "-C", "/etc/ld.so.cache")
} else {
m.logger.Debugf("No ld.so.cache found, skipping update")
@@ -129,8 +119,8 @@ func (m command) run(c *cli.Context, cfg *options) error {
}
folders := cfg.folders.Value()
if containerRoot.hasPath("/etc/ld.so.conf.d") {
err := m.createLdsoconfdFile(containerRoot, ldsoconfdFilenamePattern, folders...)
if root(containerRoot).hasPath("/etc/ld.so.conf.d") {
err := m.createConfig(containerRoot, folders)
if err != nil {
return fmt.Errorf("failed to update ld.so.conf.d: %v", err)
}
@@ -142,7 +132,18 @@ func (m command) run(c *cli.Context, cfg *options) error {
// be configured to use a different config file by default.
args = append(args, "-f", "/etc/ld.so.conf")
return m.SafeExec(ldconfigPath, args, nil)
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
return syscall.Exec(ldconfigPath, args, nil)
}
type root string
func (r root) hasPath(path string) bool {
_, err := os.Stat(filepath.Join(string(r), path))
if err != nil && os.IsNotExist(err) {
return false
}
return true
}
// resolveLDConfigPath determines the LDConfig path to use for the system.
@@ -152,46 +153,44 @@ func (m command) resolveLDConfigPath(path string) string {
return strings.TrimPrefix(config.NormalizeLDConfigPath("@"+path), "@")
}
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/ in the specified root.
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
// contains the specified directories on each line.
func (m command) createLdsoconfdFile(in containerRoot, pattern string, dirs ...string) error {
if len(dirs) == 0 {
m.logger.Debugf("No directories to add to /etc/ld.so.conf")
// createConfig creates (or updates) /etc/ld.so.conf.d/00-nvcr-<RANDOM_STRING>.conf in the container
// to include the required paths.
// Note that the 00-nvcr prefix is chosen to ensure that these libraries have
// a higher precedence than other libraries on the system but are applied AFTER
// 00-cuda-compat.conf.
func (m command) createConfig(root string, folders []string) error {
if len(folders) == 0 {
m.logger.Debugf("No folders to add to /etc/ld.so.conf")
return nil
}
ldsoconfdDir, err := in.resolve("/etc/ld.so.conf.d")
if err != nil {
return err
}
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
if err := os.MkdirAll(filepath.Join(root, "/etc/ld.so.conf.d"), 0755); err != nil {
return fmt.Errorf("failed to create ld.so.conf.d: %v", err)
}
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
configFile, err := os.CreateTemp(filepath.Join(root, "/etc/ld.so.conf.d"), "00-nvcr-*.conf")
if err != nil {
return fmt.Errorf("failed to create config file: %w", err)
return fmt.Errorf("failed to create config file: %v", err)
}
defer configFile.Close()
m.logger.Debugf("Adding directories %v to %v", dirs, configFile.Name())
m.logger.Debugf("Adding folders %v to %v", folders, configFile.Name())
added := make(map[string]bool)
for _, dir := range dirs {
if added[dir] {
configured := make(map[string]bool)
for _, folder := range folders {
if configured[folder] {
continue
}
_, err = configFile.WriteString(fmt.Sprintf("%s\n", dir))
_, err = configFile.WriteString(fmt.Sprintf("%s\n", folder))
if err != nil {
return fmt.Errorf("failed to update config file: %w", err)
return fmt.Errorf("failed to update ld.so.conf.d: %v", err)
}
added[dir] = true
configured[folder] = true
}
// The created file needs to be world readable for the cases where the container is run as a non-root user.
if err := configFile.Chmod(0644); err != nil {
return fmt.Errorf("failed to chmod config file: %w", err)
if err := os.Chmod(configFile.Name(), 0644); err != nil {
return fmt.Errorf("failed to chmod config file: %v", err)
}
return nil

View File

@@ -198,10 +198,6 @@ func getMigDevices(image image.CUDA, envvar string) *string {
}
func (hookConfig *hookConfig) getImexChannels(image image.CUDA, privileged bool) []string {
if hookConfig.Features.IgnoreImexChannelRequests.IsEnabled() {
return nil
}
// If enabled, try and get the device list from volume mounts first
if hookConfig.AcceptDeviceListAsVolumeMounts {
devices := image.ImexChannelsFromMounts()

View File

@@ -141,9 +141,6 @@ 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
@@ -205,9 +202,6 @@ 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
@@ -272,9 +266,6 @@ 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
@@ -336,9 +327,6 @@ 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
@@ -422,9 +410,6 @@ 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

View File

@@ -25,8 +25,6 @@ import (
"github.com/urfave/cli/v2"
cdi "tags.cncf.io/container-device-interface/pkg/parser"
"github.com/NVIDIA/go-nvml/pkg/nvml"
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
@@ -62,9 +60,6 @@ type options struct {
files cli.StringSlice
ignorePatterns cli.StringSlice
}
// the following are used for dependency injection during spec generation.
nvmllib nvml.Interface
}
// NewCommand constructs a generate-cdi command with the specified logger
@@ -274,8 +269,6 @@ func (m command) generateSpec(opts *options) (spec.Interface, error) {
nvcdi.WithLibrarySearchPaths(opts.librarySearchPaths.Value()),
nvcdi.WithCSVFiles(opts.csv.files.Value()),
nvcdi.WithCSVIgnorePatterns(opts.csv.ignorePatterns.Value()),
// We set the following to allow for dependency injection:
nvcdi.WithNvmlLib(opts.nvmllib),
)
if err != nil {
return nil, fmt.Errorf("failed to create CDI library: %v", err)

View File

@@ -1,157 +0,0 @@
/**
# 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 generate
import (
"bytes"
"path/filepath"
"strings"
"testing"
"github.com/NVIDIA/go-nvml/pkg/nvml"
"github.com/NVIDIA/go-nvml/pkg/nvml/mock/dgxa100"
testlog "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/require"
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
)
func TestGenerateSpec(t *testing.T) {
t.Setenv("__NVCT_TESTING_DEVICES_ARE_FILES", "true")
moduleRoot, err := test.GetModuleRoot()
require.NoError(t, err)
driverRoot := filepath.Join(moduleRoot, "testdata", "lookup", "rootfs-1")
logger, _ := testlog.NewNullLogger()
testCases := []struct {
description string
options options
expectedValidateError error
expectedOptions options
expectedError error
expectedSpec string
}{
{
description: "default",
options: options{
format: "yaml",
mode: "nvml",
vendor: "example.com",
class: "device",
driverRoot: driverRoot,
},
expectedOptions: options{
format: "yaml",
mode: "nvml",
vendor: "example.com",
class: "device",
nvidiaCDIHookPath: "/usr/bin/nvidia-cdi-hook",
driverRoot: driverRoot,
},
expectedSpec: `---
cdiVersion: 0.5.0
containerEdits:
deviceNodes:
- hostPath: {{ .driverRoot }}/dev/nvidiactl
path: /dev/nvidiactl
env:
- NVIDIA_VISIBLE_DEVICES=void
hooks:
- args:
- nvidia-cdi-hook
- create-symlinks
- --link
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
- args:
- nvidia-cdi-hook
- enable-cuda-compat
- --host-driver-version=999.88.77
hookName: createContainer
path: /usr/bin/nvidia-cdi-hook
- 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
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
`,
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
c := command{
logger: logger,
}
err := c.validateFlags(nil, &tc.options)
require.ErrorIs(t, err, tc.expectedValidateError)
require.EqualValues(t, tc.expectedOptions, tc.options)
// Set up a mock server, reusing the DGX A100 mock.
server := dgxa100.New()
// Override the driver version to match the version in our mock filesystem.
server.SystemGetDriverVersionFunc = func() (string, nvml.Return) {
return "999.88.77", nvml.SUCCESS
}
// Set the device count to 1 explicitly since we only have a single device node.
server.DeviceGetCountFunc = func() (int, nvml.Return) {
return 1, nvml.SUCCESS
}
for _, d := range server.Devices {
// TODO: This is not implemented in the mock.
(d.(*dgxa100.Device)).GetMaxMigDeviceCountFunc = func() (int, nvml.Return) {
return 0, nvml.SUCCESS
}
}
tc.options.nvmllib = server
spec, err := c.generateSpec(&tc.options)
require.ErrorIs(t, err, tc.expectedError)
var buf bytes.Buffer
_, err = spec.WriteTo(&buf)
require.NoError(t, err)
require.Equal(t, strings.ReplaceAll(tc.expectedSpec, "{{ .driverRoot }}", driverRoot), buf.String())
})
}
}

View File

@@ -5,14 +5,14 @@ go 1.24
toolchain go1.24.0
require (
github.com/golangci/golangci-lint v1.64.6
github.com/golangci/golangci-lint v1.64.5
github.com/matryer/moq v0.5.3
)
require (
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
4d63.com/gochecknoglobals v0.2.2 // indirect
github.com/4meepo/tagalign v1.4.2 // indirect
github.com/4meepo/tagalign v1.4.1 // indirect
github.com/Abirdcfly/dupword v0.1.3 // indirect
github.com/Antonboom/errname v1.0.0 // indirect
github.com/Antonboom/nilnil v1.0.1 // indirect
@@ -20,7 +20,7 @@ require (
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
github.com/Crocmagnon/fatcontext v0.7.1 // indirect
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
@@ -38,7 +38,7 @@ require (
github.com/breml/errchkjson v0.4.0 // indirect
github.com/butuzov/ireturn v0.3.1 // indirect
github.com/butuzov/mirror v1.3.0 // indirect
github.com/catenacyber/perfsprint v0.8.2 // indirect
github.com/catenacyber/perfsprint v0.8.1 // indirect
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
@@ -75,10 +75,10 @@ require (
github.com/golangci/plugin-module-register v0.1.1 // indirect
github.com/golangci/revgrep v0.8.0 // indirect
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gordonklaus/ineffassign v0.1.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.5.0 // indirect
github.com/gostaticanalysis/comment v1.4.2 // indirect
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
@@ -92,12 +92,12 @@ require (
github.com/jjti/go-spancheck v0.6.4 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/kisielk/errcheck v1.8.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.5 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.10 // indirect
github.com/lasiar/canonicalheader v1.1.2 // indirect
github.com/ldez/exptostd v0.4.2 // indirect
github.com/ldez/exptostd v0.4.1 // indirect
github.com/ldez/gomoddirectives v0.6.1 // indirect
github.com/ldez/grignotin v0.9.0 // indirect
github.com/ldez/tagliatelle v0.7.1 // indirect
@@ -112,14 +112,14 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgechev/revive v1.7.0 // indirect
github.com/mgechev/revive v1.6.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
github.com/nunnatsa/ginkgolinter v0.19.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
@@ -136,7 +136,7 @@ require (
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/raeperd/recvcheck v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/ryancurrah/gomodguard v1.3.5 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
@@ -151,7 +151,7 @@ require (
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.12.0 // indirect
@@ -160,8 +160,8 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tdakkota/asciicheck v0.4.1 // indirect
github.com/tetafro/godot v1.5.0 // indirect
github.com/tdakkota/asciicheck v0.4.0 // indirect
github.com/tetafro/godot v1.4.20 // indirect
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect
github.com/timonwong/loggercheck v0.10.1 // indirect
github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect

View File

@@ -1,5 +1,5 @@
4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A=
4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@@ -35,8 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
github.com/4meepo/tagalign v1.4.1 h1:GYTu2FaPGOGb/xJalcqHeD4il5BiCywyEYZOA55P6J4=
github.com/4meepo/tagalign v1.4.1/go.mod h1:2H9Yu6sZ67hmuraFgfZkNcg5Py9Ch/Om9l2K/2W1qS4=
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
@@ -53,8 +53,8 @@ github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVU
github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k=
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
@@ -102,8 +102,8 @@ github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY
github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M=
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
github.com/catenacyber/perfsprint v0.8.2 h1:+o9zVmCSVa7M4MvabsWvESEhpsMkhfE7k0sHNGL95yw=
github.com/catenacyber/perfsprint v0.8.2/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
github.com/catenacyber/perfsprint v0.8.1 h1:bGOHuzHe0IkoGeY831RW4aSlt1lPRd3WRAScSWOaV7E=
github.com/catenacyber/perfsprint v0.8.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50=
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -122,7 +122,7 @@ github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd
github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
@@ -235,8 +235,8 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP
github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
github.com/golangci/golangci-lint v1.64.6 h1:jOLaQN41IV7bMzXuNC4UnQGll7N1xY6eFDXkXEPGKAs=
github.com/golangci/golangci-lint v1.64.6/go.mod h1:Wz9q+6EVuqGQ94GQ96RB2mjpcZYTOGhBhbt4O7REPu4=
github.com/golangci/golangci-lint v1.64.5 h1:5omC86XFBKXZgCrVdUWU+WNHKd+CWCxNx717KXnzKZY=
github.com/golangci/golangci-lint v1.64.5/go.mod h1:WZnwq8TF0z61h3jLQ7Sk5trcP7b3kUFxLD6l1ivtdvU=
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
@@ -259,8 +259,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -281,9 +281,8 @@ github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8=
github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc=
github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk=
github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY=
github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
@@ -328,11 +327,11 @@ github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI=
github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmTJxVCY0lhxBtlfOypMM=
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg=
github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg=
github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -349,8 +348,8 @@ github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCT
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs=
github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
github.com/ldez/exptostd v0.4.1 h1:DIollgQ3LWZMp3HJbSXsdE2giJxMfjyHj3eX4oiD6JU=
github.com/ldez/exptostd v0.4.1/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
@@ -384,8 +383,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=
github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=
github.com/mgechev/revive v1.6.1 h1:ncK0ZCMWtb8GXwVAmk+IeWF2ULIDsvRxSRfg5sTwQ2w=
github.com/mgechev/revive v1.6.1/go.mod h1:/2tfHWVO8UQi/hqJsIYNEKELi+DJy/e+PQpLgTB1v88=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -405,8 +404,8 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
github.com/nunnatsa/ginkgolinter v0.19.0 h1:CnHRFAeBS3LdLI9h+Jidbcc5KH71GKOmaBZQk8Srnto=
github.com/nunnatsa/ginkgolinter v0.19.0/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
@@ -472,8 +471,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE=
@@ -508,8 +507,8 @@ github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -539,14 +538,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
github.com/tdakkota/asciicheck v0.4.0 h1:VZ13Itw4k1i7d+dpDSNS8Op645XgGHpkCEh/WHicgWw=
github.com/tdakkota/asciicheck v0.4.0/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw=
github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
github.com/tetafro/godot v1.4.20 h1:z/p8Ek55UdNvzt4TFn2zx2KscpW4rWqcnUrdmvWJj7E=
github.com/tetafro/godot v1.4.20/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 h1:y4mJRFlM6fUyPhoXuFg/Yu02fg/nIPFMOY8tOqppoFg=
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg=

15
go.mod
View File

@@ -1,32 +1,31 @@
module github.com/NVIDIA/nvidia-container-toolkit
go 1.23.0
go 1.22.0
require (
github.com/NVIDIA/go-nvlib v0.7.1
github.com/NVIDIA/go-nvml v0.12.4-1
github.com/moby/sys/symlink v0.3.0
github.com/opencontainers/runc v1.2.5
github.com/opencontainers/runtime-spec v1.2.1
github.com/opencontainers/runtime-spec v1.2.0
github.com/pelletier/go-toml v1.9.5
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
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
github.com/urfave/cli/v2 v2.27.5
golang.org/x/mod v0.23.0
golang.org/x/sys v0.30.0
tags.cncf.io/container-device-interface v0.8.0
tags.cncf.io/container-device-interface/specs-go v0.8.0
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect

24
go.sum
View File

@@ -7,8 +7,6 @@ github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -33,11 +31,9 @@ github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/opencontainers/runc v1.2.5 h1:8KAkq3Wrem8bApgOHyhRI/8IeLXIfmZ6Qaw6DNSLnA4=
github.com/opencontainers/runc v1.2.5/go.mod h1:dOQeFo29xZKBNeRBI0B19mJtfHv68YgCTh1X+YphA+4=
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
@@ -64,8 +60,8 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -75,13 +71,13 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -92,7 +88,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 v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
tags.cncf.io/container-device-interface v0.8.0/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=

View File

@@ -121,9 +121,6 @@ func GetDefault() (*Config, error) {
AnnotationPrefixes: []string{cdi.AnnotationPrefix},
SpecDirs: cdi.DefaultSpecDirs,
},
JitCDI: jitCDIModeConfig{
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
},
},
},
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{

View File

@@ -74,9 +74,6 @@ 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{
@@ -105,7 +102,6 @@ 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\"",
},
@@ -138,9 +134,6 @@ func TestGetConfig(t *testing.T) {
"/not/var/run/cdi",
},
},
JitCDI: jitCDIModeConfig{
LoadKernelModules: []string{"foo"},
},
},
},
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
@@ -185,9 +178,6 @@ func TestGetConfig(t *testing.T) {
"/var/run/cdi",
},
},
JitCDI: jitCDIModeConfig{
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
},
},
},
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
@@ -223,8 +213,6 @@ 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]",
@@ -259,9 +247,6 @@ func TestGetConfig(t *testing.T) {
"/not/var/run/cdi",
},
},
JitCDI: jitCDIModeConfig{
LoadKernelModules: []string{"foo"},
},
},
},
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
@@ -298,9 +283,6 @@ 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{
@@ -340,9 +322,6 @@ 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{

View File

@@ -25,23 +25,9 @@ type features struct {
// If this feature flag is not set to 'true' only host-rooted config paths
// (i.e. paths starting with an '@' are considered valid)
AllowLDConfigFromContainer *feature `toml:"allow-ldconfig-from-container,omitempty"`
// DisableCUDACompatLibHook, when enabled skips the injection of a specific
// hook to process CUDA compatibility libraries.
//
// Note: Since this mechanism replaces the logic in the `nvidia-container-cli`,
// toggling this feature has no effect if `allow-cuda-compat-libs-from-container` is enabled.
DisableCUDACompatLibHook *feature `toml:"disable-cuda-compat-lib-hook,omitempty"`
// DisableImexChannelCreation ensures that the implicit creation of
// requested IMEX channels is skipped when invoking the nvidia-container-cli.
DisableImexChannelCreation *feature `toml:"disable-imex-channel-creation,omitempty"`
// IgnoreImexChannelRequests configures the NVIDIA Container Toolkit to
// ignore IMEX channel requests through the NVIDIA_IMEX_CHANNELS envvar or
// volume mounts.
// This ensures that the NVIDIA Container Toolkit cannot be used to provide
// access to an IMEX channel by simply specifying an environment variable,
// possibly bypassing other checks by an orchestration system such as
// kubernetes.
IgnoreImexChannelRequests *feature `toml:"ignore-imex-channel-requests,omitempty"`
}
type feature bool

View File

@@ -29,9 +29,8 @@ type RuntimeConfig struct {
// modesConfig defines (optional) per-mode configs
type modesConfig struct {
CSV csvModeConfig `toml:"csv"`
CDI cdiModeConfig `toml:"cdi"`
JitCDI jitCDIModeConfig `toml:"jit-cdi"`
CSV csvModeConfig `toml:"csv"`
CDI cdiModeConfig `toml:"cdi"`
}
type cdiModeConfig struct {
@@ -46,11 +45,3 @@ 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"`
}

View File

@@ -74,9 +74,6 @@ 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

View File

@@ -1,24 +0,0 @@
package discover
import (
"strings"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
)
// NewCUDACompatHookDiscoverer creates a discoverer for a enable-cuda-compat hook.
// This hook is responsible for setting up CUDA compatibility in the container and depends on the host driver version.
func NewCUDACompatHookDiscoverer(logger logger.Interface, nvidiaCDIHookPath string, driver *root.Driver) Discover {
_, cudaVersionPattern := getCUDALibRootAndVersionPattern(logger, driver)
var args []string
if !strings.Contains(cudaVersionPattern, "*") {
args = append(args, "--host-driver-version="+cudaVersionPattern)
}
return CreateNvidiaCDIHook(
nvidiaCDIHookPath,
"enable-cuda-compat",
args...,
)
}

View File

@@ -17,15 +17,12 @@
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.
@@ -128,20 +125,3 @@ 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
}

View File

@@ -25,7 +25,6 @@ import (
"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/pkg/nvcdi"
@@ -35,7 +34,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, driver *root.Driver, ociSpec oci.Spec) (oci.SpecModifier, error) {
func NewCDIModifier(logger logger.Interface, cfg *config.Config, 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)
@@ -51,7 +50,7 @@ func NewCDIModifier(logger logger.Interface, cfg *config.Config, driver *root.Dr
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, driver, automaticDevices)
automaticModifier, err := newAutomaticCDISpecModifier(logger, cfg, automaticDevices)
if err == nil {
return automaticModifier, nil
}
@@ -164,9 +163,9 @@ func filterAutomaticDevices(devices []string) []string {
return automatic
}
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (oci.SpecModifier, error) {
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
spec, err := generateAutomaticCDISpec(logger, cfg, driver, devices)
spec, err := generateAutomaticCDISpec(logger, cfg, devices)
if err != nil {
return nil, fmt.Errorf("failed to generate CDI spec: %w", err)
}
@@ -181,7 +180,7 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, dr
return cdiModifier, nil
}
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (spec.Interface, error) {
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devices []string) (spec.Interface, error) {
cdilib, err := nvcdi.New(
nvcdi.WithLogger(logger),
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
@@ -193,11 +192,6 @@ func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, drive
return nil, fmt.Errorf("failed to construct CDI library: %w", err)
}
// 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)
}
identifiers := []string{}
for _, device := range devices {
_, _, id := parser.ParseDevice(device)

View File

@@ -23,7 +23,6 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
@@ -36,7 +35,7 @@ import (
// NVIDIA_GDRCOPY=enabled
//
// If not devices are selected, no changes are made.
func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image image.CUDA, driver *root.Driver) (oci.SpecModifier, error) {
func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
if devices := image.VisibleDevicesFromEnvVar(); len(devices) == 0 {
logger.Infof("No modification required; no devices requested")
return nil, nil
@@ -79,24 +78,5 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
discoverers = append(discoverers, d)
}
if !cfg.Features.AllowCUDACompatLibsFromContainer.IsEnabled() && !cfg.Features.DisableCUDACompatLibHook.IsEnabled() {
compatLibHookDiscoverer := discover.NewCUDACompatHookDiscoverer(logger, cfg.NVIDIACTKConfig.Path, driver)
discoverers = append(discoverers, compatLibHookDiscoverer)
// For legacy mode, we also need to inject a hook to update the LDCache
// after we have modifed the configuration.
if cfg.NVIDIAContainerRuntimeConfig.Mode == "legacy" {
ldcacheUpdateHookDiscoverer, err := discover.NewLDCacheUpdateHook(
logger,
discover.None{},
cfg.NVIDIACTKConfig.Path,
"",
)
if err != nil {
return nil, fmt.Errorf("failed to construct ldcache update discoverer: %w", err)
}
discoverers = append(discoverers, ldcacheUpdateHookDiscoverer)
}
}
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...))
}

View File

@@ -49,6 +49,7 @@ TRANSLATOR:
const:
- {action: accept, from: "^NVSANDBOXUTILS_"}
- {action: accept, from: "^nvSandboxUtils"}
- {action: replace, from: "^NVSANDBOXUTILS_255_MASK_", to: "MASK255_" }
- {action: replace, from: "^NVSANDBOXUTILS_"}
- {action: replace, from: "^nvSandboxUtils"}
- {action: accept, from: "^NV"}

View File

@@ -14,7 +14,7 @@
# limitations under the License.
**/
package nvcdi
package dgpu
import (
"fmt"
@@ -22,7 +22,6 @@ import (
"path/filepath"
"strings"
"github.com/NVIDIA/go-nvml/pkg/nvml"
"golang.org/x/sys/unix"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
@@ -32,47 +31,22 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
)
// NewDriverDiscoverer creates a discoverer for the libraries and binaries associated with a driver installation.
// The supplied NVML Library is used to query the expected driver version.
func (l *nvmllib) NewDriverDiscoverer() (discover.Discover, error) {
if r := l.nvmllib.Init(); r != nvml.SUCCESS {
return nil, fmt.Errorf("failed to initialize NVML: %v", r)
}
defer func() {
if r := l.nvmllib.Shutdown(); r != nvml.SUCCESS {
l.logger.Warningf("failed to shutdown NVML: %v", r)
}
}()
version, r := l.nvmllib.SystemGetDriverVersion()
if r != nvml.SUCCESS {
return nil, fmt.Errorf("failed to determine driver version: %v", r)
}
return (*nvcdilib)(l).newDriverVersionDiscoverer(version)
}
func (l *nvcdilib) newDriverVersionDiscoverer(version string) (discover.Discover, error) {
libraries, err := l.NewDriverLibraryDiscoverer(version)
// newNvmlDriverDiscoverer constructs a discoverer from the specified NVML library.
func (o *options) newNvmlDriverDiscoverer() (discover.Discover, error) {
libraries, err := o.newNvmlDriverLibraryDiscoverer()
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for driver libraries: %v", err)
}
ipcs, err := discover.NewIPCDiscoverer(l.logger, l.driver.Root)
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for IPC sockets: %v", err)
}
firmwares, err := NewDriverFirmwareDiscoverer(l.logger, l.driver.Root, version)
firmwares, err := o.newNvmlDriverFirmwareDiscoverer()
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for GSP firmware: %v", err)
}
binaries := NewDriverBinariesDiscoverer(l.logger, l.driver.Root)
binaries := o.newNvmlDriverBinariesDiscoverer()
d := discover.Merge(
libraries,
ipcs,
firmwares,
binaries,
)
@@ -80,42 +54,28 @@ func (l *nvcdilib) newDriverVersionDiscoverer(version string) (discover.Discover
return d, nil
}
// NewDriverLibraryDiscoverer creates a discoverer for the libraries associated with the specified driver version.
func (l *nvcdilib) NewDriverLibraryDiscoverer(version string) (discover.Discover, error) {
libraryPaths, err := getVersionLibs(l.logger, l.driver, version)
// newNvmlDriverLibraryDiscoverer creates a discoverer for the libraries associated with the specified driver version.
func (o *options) newNvmlDriverLibraryDiscoverer() (discover.Discover, error) {
libraryPaths, err := getVersionLibs(o.logger, o.driver, o.version)
if err != nil {
return nil, fmt.Errorf("failed to get libraries for driver version: %v", err)
}
libraries := discover.NewMounts(
l.logger,
o.logger,
lookup.NewFileLocator(
lookup.WithLogger(l.logger),
lookup.WithRoot(l.driver.Root),
lookup.WithLogger(o.logger),
lookup.WithRoot(o.driver.Root),
),
l.driver.Root,
o.driver.Root,
libraryPaths,
)
var discoverers []discover.Discover
driverDotSoSymlinksDiscoverer := discover.WithDriverDotSoSymlinks(
d := discover.WithDriverDotSoSymlinks(
libraries,
version,
l.nvidiaCDIHookPath,
o.version,
o.nvidiaCDIHookPath,
)
discoverers = append(discoverers, driverDotSoSymlinksDiscoverer)
if l.HookIsSupported(HookEnableCudaCompat) {
// TODO: The following should use the version directly.
cudaCompatLibHookDiscoverer := discover.NewCUDACompatHookDiscoverer(l.logger, l.nvidiaCDIHookPath, l.driver)
discoverers = append(discoverers, cudaCompatLibHookDiscoverer)
}
updateLDCache, _ := discover.NewLDCacheUpdateHook(l.logger, libraries, l.nvidiaCDIHookPath, l.ldconfigPath)
discoverers = append(discoverers, updateLDCache)
d := discover.Merge(discoverers...)
return d, nil
}
@@ -162,31 +122,31 @@ func getCustomFirmwareClassPath(logger logger.Interface) string {
return strings.TrimSpace(string(customFirmwareClassPath))
}
// NewDriverFirmwareDiscoverer creates a discoverer for GSP firmware associated with the specified driver version.
func NewDriverFirmwareDiscoverer(logger logger.Interface, driverRoot string, version string) (discover.Discover, error) {
gspFirmwareSearchPaths, err := getFirmwareSearchPaths(logger)
// newNvmlDriverFirmwareDiscoverer creates a discoverer for GSP firmware associated with the specified driver version.
func (o *options) newNvmlDriverFirmwareDiscoverer() (discover.Discover, error) {
gspFirmwareSearchPaths, err := getFirmwareSearchPaths(o.logger)
if err != nil {
return nil, fmt.Errorf("failed to get firmware search paths: %v", err)
}
gspFirmwarePaths := filepath.Join("nvidia", version, "gsp*.bin")
gspFirmwarePaths := filepath.Join("nvidia", o.version, "gsp*.bin")
return discover.NewMounts(
logger,
o.logger,
lookup.NewFileLocator(
lookup.WithLogger(logger),
lookup.WithRoot(driverRoot),
lookup.WithLogger(o.logger),
lookup.WithRoot(o.driver.Root),
lookup.WithSearchPaths(gspFirmwareSearchPaths...),
),
driverRoot,
o.driver.Root,
[]string{gspFirmwarePaths},
), nil
}
// NewDriverBinariesDiscoverer creates a discoverer for GSP firmware associated with the GPU driver.
func NewDriverBinariesDiscoverer(logger logger.Interface, driverRoot string) discover.Discover {
// newNvmlDriverBinariesDiscoverer creates a discoverer for binaries associated with the specified driver version.
func (o *options) newNvmlDriverBinariesDiscoverer() discover.Discover {
return discover.NewMounts(
logger,
lookup.NewExecutableLocator(logger, driverRoot),
driverRoot,
o.logger,
lookup.NewExecutableLocator(o.logger, o.driver.Root),
o.driver.Root,
[]string{
"nvidia-smi", /* System management interface */
"nvidia-debugdump", /* GPU coredump utility */

View File

@@ -1,8 +1,5 @@
//go:build !linux
// +build !linux
/**
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 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.
@@ -17,13 +14,18 @@
# limitations under the License.
**/
package ldcache
package dgpu
import "syscall"
import (
"fmt"
// SafeExec is not implemented on non-linux systems and forwards directly to the
// Exec syscall.
func (m *command) SafeExec(path string, args []string, envv []string) error {
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
return syscall.Exec(path, args, envv)
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
)
// newNvsandboxutilsDriverDiscoverer constructs a discoverer from the specified nvsandboxutils library.
func (o *options) newNvsandboxutilsDriverDiscoverer() (discover.Discover, error) {
if o.nvsandboxutilslib == nil {
return nil, nil
}
return nil, fmt.Errorf("nvsandboxutils driver discovery is not implemented")
}

View File

@@ -0,0 +1,74 @@
/**
# Copyright (c) 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 dgpu
import (
"errors"
"fmt"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
)
// NewDriverDiscoverer creates a discoverer for the libraries and binaries associated with a driver installation.
func NewDriverDiscoverer(opts ...Option) (discover.Discover, error) {
o := new(opts...)
if o.version == "" {
return nil, fmt.Errorf("a version must be specified")
}
var discoverers []discover.Discover
var errs error
nvsandboxutilsDiscoverer, err := o.newNvsandboxutilsDriverDiscoverer()
if err != nil {
// TODO: Log a warning
errs = errors.Join(errs, err)
} else if nvsandboxutilsDiscoverer != nil {
discoverers = append(discoverers, nvsandboxutilsDiscoverer)
}
nvmlDiscoverer, err := o.newNvmlDriverDiscoverer()
if err != nil {
// TODO: Log a warning
errs = errors.Join(errs, err)
} else if nvmlDiscoverer != nil {
discoverers = append(discoverers, nvmlDiscoverer)
}
if len(discoverers) == 0 {
return nil, errs
}
cached := discover.WithCache(
discover.FirstValid(
discoverers...,
),
)
updateLDCache, _ := discover.NewLDCacheUpdateHook(o.logger, cached, o.nvidiaCDIHookPath, o.ldconfigPath)
ipcs, err := discover.NewIPCDiscoverer(o.logger, o.driver.Root)
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for IPC sockets: %v", err)
}
return discover.Merge(
cached,
updateLDCache,
ipcs,
), nil
}

View File

@@ -18,13 +18,16 @@ package dgpu
import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
)
type options struct {
logger logger.Interface
driver *root.Driver
devRoot string
ldconfigPath string
nvidiaCDIHookPath string
isMigDevice bool
@@ -33,6 +36,9 @@ type options struct {
migCaps nvcaps.MigCaps
migCapsError error
// version stores the driver version.
version string
nvsandboxutilslib nvsandboxutils.Interface
}
@@ -45,6 +51,19 @@ func WithDevRoot(root string) Option {
}
}
func WithDriver(driver *root.Driver) Option {
return func(l *options) {
l.driver = driver
}
}
// WithLdconfigPath sets the path to the ldconfig program
func WithLdconfigPath(path string) Option {
return func(l *options) {
l.ldconfigPath = path
}
}
// WithLogger sets the logger for the library
func WithLogger(logger logger.Interface) Option {
return func(l *options) {
@@ -72,3 +91,9 @@ func WithNvsandboxuitilsLib(nvsandboxutilslib nvsandboxutils.Interface) Option {
l.nvsandboxutilslib = nvsandboxutilslib
}
}
func WithVersion(version string) Option {
return func(l *options) {
l.version = version
}
}

View File

@@ -75,9 +75,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, driver, ociSpec, image)
modeModifier, err := newModeModifier(logger, mode, cfg, ociSpec, image)
if err != nil {
return nil, err
}
@@ -96,7 +94,7 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
}
modifiers = append(modifiers, graphicsModifier)
case "feature-gated":
featureGatedModifier, err := modifier.NewFeatureGatedModifier(logger, cfg, image, driver)
featureGatedModifier, err := modifier.NewFeatureGatedModifier(logger, cfg, image)
if err != nil {
return nil, err
}
@@ -107,14 +105,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, driver *root.Driver, ociSpec oci.Spec, image image.CUDA) (oci.SpecModifier, error) {
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, 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, driver, ociSpec)
return modifier.NewCDIModifier(logger, cfg, ociSpec)
}
return nil, fmt.Errorf("invalid runtime mode: %v", cfg.NVIDIAContainerRuntimeConfig.Mode)
@@ -128,8 +126,8 @@ func supportedModifierTypes(mode string) []string {
return []string{"nvidia-hook-remover", "mode"}
case "csv":
// For CSV mode we support mode and feature-gated modification.
return []string{"nvidia-hook-remover", "feature-gated", "mode"}
return []string{"nvidia-hook-remover", "mode", "feature-gated"}
default:
return []string{"feature-gated", "graphics", "mode"}
return []string{"mode", "graphics", "feature-gated"}
}
}

View File

@@ -35,13 +35,3 @@ type Interface interface {
GetMIGDeviceSpecs(int, device.Device, int, device.MigDevice) ([]specs.Device, error)
GetDeviceSpecsByID(...string) ([]specs.Device, error)
}
// A HookName refers to one of the predefined set of CDI hooks that may be
// included in the generated CDI specification.
type HookName string
const (
// HookEnableCudaCompat refers to the hook used to enable CUDA Forward Compatibility.
// This was added with v1.17.5 of the NVIDIA Container Toolkit.
HookEnableCudaCompat = HookName("enable-cuda-compat")
)

View File

@@ -20,11 +20,12 @@ import (
"fmt"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/dgpu"
)
// newCommonNVMLDiscoverer returns a discoverer for entities that are not associated with a specific CDI device.
// This includes driver libraries and meta devices, for example.
func (l *nvmllib) newCommonNVMLDiscoverer() (discover.Discover, error) {
func (l *nvmllib) newCommonNVMLDiscoverer(version string) (discover.Discover, error) {
metaDevices := discover.NewCharDeviceDiscoverer(
l.logger,
l.devRoot,
@@ -41,7 +42,15 @@ func (l *nvmllib) newCommonNVMLDiscoverer() (discover.Discover, error) {
l.logger.Warningf("failed to create discoverer for graphics mounts: %v", err)
}
driverFiles, err := l.NewDriverDiscoverer()
driverFiles, err := dgpu.NewDriverDiscoverer(
dgpu.WithDevRoot(l.devRoot),
dgpu.WithDriver(l.driver),
dgpu.WithLdconfigPath(l.ldconfigPath),
dgpu.WithLogger(l.logger),
dgpu.WithNVIDIACDIHookPath(l.nvidiaCDIHookPath),
dgpu.WithNvsandboxuitilsLib(l.nvsandboxutilslib),
dgpu.WithVersion(version),
)
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for driver files: %v", err)
}

View File

@@ -1,30 +0,0 @@
/**
# 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 nvcdi
// disabledHooks allows individual hooks to be disabled.
type disabledHooks map[HookName]bool
// HookIsSupported checks whether a hook of the specified name is supported.
// Hooks must be explicitly disabled, meaning that if no disabled hooks are
// all hooks are supported.
func (l *nvcdilib) HookIsSupported(h HookName) bool {
if len(l.disabledHooks) == 0 {
return true
}
return !l.disabledHooks[h]
}

View File

@@ -83,7 +83,25 @@ func (l *nvmllib) GetAllDeviceSpecs() ([]specs.Device, error) {
// GetCommonEdits generates a CDI specification that can be used for ANY devices
func (l *nvmllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
common, err := l.newCommonNVMLDiscoverer()
if l.nvsandboxutilslib != nil {
if r := l.nvsandboxutilslib.Init(l.driverRoot); r != nvsandboxutils.SUCCESS {
l.logger.Warningf("Failed to init nvsandboxutils: %v; ignoring", r)
l.nvsandboxutilslib = nil
}
defer func() {
if l.nvsandboxutilslib == nil {
return
}
_ = l.nvsandboxutilslib.Shutdown()
}()
}
version, err := (*nvcdilib)(l).getDriverVersion()
if err != nil {
return nil, fmt.Errorf("failed to get driver version: %v", err)
}
common, err := l.newCommonNVMLDiscoverer(version)
if err != nil {
return nil, fmt.Errorf("failed to create discoverer for common entities: %v", err)
}

View File

@@ -18,12 +18,15 @@ package nvcdi
import (
"fmt"
"path/filepath"
"strings"
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
"github.com/NVIDIA/go-nvml/pkg/nvml"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/cuda"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
@@ -54,15 +57,11 @@ type nvcdilib struct {
infolib info.Interface
mergedDeviceOptions []transform.MergedDeviceOption
disabledHooks disabledHooks
}
// New creates a new nvcdi library
func New(opts ...Option) (Interface, error) {
l := &nvcdilib{
disabledHooks: make(disabledHooks),
}
l := &nvcdilib{}
for _, opt := range opts {
opt(l)
}
@@ -103,24 +102,19 @@ func New(opts ...Option) (Interface, error) {
}
l.nvmllib = nvml.New(nvmlOpts...)
}
// TODO: Repeated calls to nvsandboxutils.Init and Shutdown are causing
// segmentation violations. Here we disabled nvsandbox utils unless explicitly
// specified.
// This will be reenabled as soon as we have more visibility into why this is
// happening and a mechanism to detect and disable this if required.
// if l.nvsandboxutilslib == nil {
// var nvsandboxutilsOpts []nvsandboxutils.LibraryOption
// // Set the library path for libnvidia-sandboxutils
// candidates, err := l.driver.Libraries().Locate("libnvidia-sandboxutils.so.1")
// if err != nil {
// l.logger.Warningf("Ignoring error in locating libnvidia-sandboxutils.so.1: %v", err)
// } else {
// libNvidiaSandboxutilsPath := candidates[0]
// l.logger.Infof("Using %v", libNvidiaSandboxutilsPath)
// nvsandboxutilsOpts = append(nvsandboxutilsOpts, nvsandboxutils.WithLibraryPath(libNvidiaSandboxutilsPath))
// }
// l.nvsandboxutilslib = nvsandboxutils.New(nvsandboxutilsOpts...)
// }
if l.nvsandboxutilslib == nil {
var nvsandboxutilsOpts []nvsandboxutils.LibraryOption
// Set the library path for libnvidia-sandboxutils
candidates, err := l.driver.Libraries().Locate("libnvidia-sandboxutils.so.1")
if err != nil {
l.logger.Warningf("Ignoring error in locating libnvidia-sandboxutils.so.1: %v", err)
} else {
libNvidiaSandboxutilsPath := candidates[0]
l.logger.Infof("Using %v", libNvidiaSandboxutilsPath)
nvsandboxutilsOpts = append(nvsandboxutilsOpts, nvsandboxutils.WithLibraryPath(libNvidiaSandboxutilsPath))
}
l.nvsandboxutilslib = nvsandboxutils.New(nvsandboxutilsOpts...)
}
if l.devicelib == nil {
l.devicelib = device.New(l.nvmllib)
}
@@ -144,8 +138,6 @@ func New(opts ...Option) (Interface, error) {
if l.vendor == "" {
l.vendor = "management.nvidia.com"
}
// Management containers in general do not require CUDA Forward compatibility.
l.disabledHooks[HookEnableCudaCompat] = true
lib = (*managementlib)(l)
case ModeNvml:
lib = (*nvmllib)(l)
@@ -179,18 +171,36 @@ func New(opts ...Option) (Interface, error) {
return &w, nil
}
// getCudaVersion returns the CUDA version of the current system.
func (l *nvcdilib) getCudaVersion() (string, error) {
version, err := l.getCudaVersionNvsandboxutils()
if err == nil {
// getDriverVersion returns the driver version of the current system.
func (l *nvcdilib) getDriverVersion() (string, error) {
if version, err := l.getDriverVersionNvsandboxutils(); err == nil && version != "" {
return version, err
}
// Fallback to NVML
return l.getCudaVersionNvml()
if version, err := l.getDriverVersionNvml(); err == nil && version != "" {
return version, err
}
// Fallback to getting the version from the libcuda.so suffix.
return l.getDriverVersionLibcudaSo()
}
func (l *nvcdilib) getCudaVersionNvml() (string, error) {
func (l *nvcdilib) getDriverVersionLibcudaSo() (string, error) {
libCudaPaths, err := cuda.New(
l.driver.Libraries(),
).Locate(".*.*")
if err != nil {
return "", fmt.Errorf("failed to locate libcuda.so: %v", err)
}
libCudaPath := libCudaPaths[0]
version := strings.TrimPrefix(filepath.Base(libCudaPath), "libcuda.so.")
return version, nil
}
func (l *nvcdilib) getDriverVersionNvml() (string, error) {
if hasNVML, reason := l.infolib.HasNvml(); !hasNVML {
return "", fmt.Errorf("nvml not detected: %v", reason)
}
@@ -214,7 +224,7 @@ func (l *nvcdilib) getCudaVersionNvml() (string, error) {
return version, nil
}
func (l *nvcdilib) getCudaVersionNvsandboxutils() (string, error) {
func (l *nvcdilib) getDriverVersionNvsandboxutils() (string, error) {
if l.nvsandboxutilslib == nil {
return "", fmt.Errorf("libnvsandboxutils is not available")
}

View File

@@ -27,8 +27,8 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/cuda"
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/dgpu"
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
)
@@ -75,12 +75,20 @@ func (m *managementlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
}()
}
version, err := m.getCudaVersion()
version, err := (*nvcdilib)(m).getDriverVersion()
if err != nil {
return nil, fmt.Errorf("failed to get CUDA version: %v", err)
return nil, fmt.Errorf("failed to get driver version: %v", err)
}
driver, err := (*nvcdilib)(m).newDriverVersionDiscoverer(version)
driver, err := dgpu.NewDriverDiscoverer(
dgpu.WithDevRoot(m.devRoot),
dgpu.WithDriver(m.driver),
dgpu.WithLdconfigPath(m.ldconfigPath),
dgpu.WithLogger(m.logger),
dgpu.WithNVIDIACDIHookPath(m.nvidiaCDIHookPath),
dgpu.WithNvsandboxuitilsLib(m.nvsandboxutilslib),
dgpu.WithVersion(version),
)
if err != nil {
return nil, fmt.Errorf("failed to create driver library discoverer: %v", err)
}
@@ -93,27 +101,6 @@ func (m *managementlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
return edits, nil
}
// getCudaVersion returns the CUDA version for use in managementlib containers.
func (m *managementlib) getCudaVersion() (string, error) {
version, err := (*nvcdilib)(m).getCudaVersion()
if err == nil {
return version, nil
}
libCudaPaths, err := cuda.New(
m.driver.Libraries(),
).Locate(".*.*")
if err != nil {
return "", fmt.Errorf("failed to locate libcuda.so: %v", err)
}
libCudaPath := libCudaPaths[0]
version = strings.TrimPrefix(filepath.Base(libCudaPath), "libcuda.so.")
return version, nil
}
type managementDiscoverer struct {
discover.Discover
}

View File

@@ -155,14 +155,3 @@ func WithLibrarySearchPaths(paths []string) Option {
o.librarySearchPaths = paths
}
}
// WithDisabledHook allows specific hooks to the disabled.
// This option can be specified multiple times for each hook.
func WithDisabledHook(hook HookName) Option {
return func(o *nvcdilib) {
if o.disabledHooks == nil {
o.disabledHooks = make(map[HookName]bool)
}
o.disabledHooks[hook] = true
}
}

View File

@@ -1,5 +1,5 @@
/**
# Copyright 2025 NVIDIA CORPORATION
# 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.

View File

@@ -18,15 +18,13 @@ package e2e
import (
"context"
"path/filepath"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// Integration tests for Docker runtime
var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
var _ = Describe("docker", Ordered, func() {
var r Runner
// Install the NVIDIA Container Toolkit
@@ -168,51 +166,4 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
Expect(referenceOutput).To(Equal(out4))
})
})
Describe("CUDA Forward compatibility", Ordered, func() {
BeforeAll(func(ctx context.Context) {
_, _, err := r.Run("docker pull nvcr.io/nvidia/cuda:12.8.0-base-ubi8")
Expect(err).ToNot(HaveOccurred())
})
BeforeAll(func(ctx context.Context) {
compatOutput, _, err := r.Run("docker run --rm -i -e NVIDIA_VISIBLE_DEVICES=void nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ls /usr/local/cuda/compat/libcuda.*.*\"")
Expect(err).ToNot(HaveOccurred())
Expect(compatOutput).ToNot(BeEmpty())
compatDriverVersion := strings.TrimPrefix(filepath.Base(compatOutput), "libcuda.so.")
compatMajor := strings.SplitN(compatDriverVersion, ".", 2)[0]
driverOutput, _, err := r.Run("nvidia-smi -q | grep \"Driver Version\"")
Expect(err).ToNot(HaveOccurred())
parts := strings.SplitN(driverOutput, ":", 2)
Expect(parts).To(HaveLen(2))
hostDriverVersion := strings.TrimSpace(parts[1])
Expect(hostDriverVersion).ToNot(BeEmpty())
driverMajor := strings.SplitN(hostDriverVersion, ".", 2)[0]
if driverMajor >= compatMajor {
GinkgoLogr.Info("CUDA Forward Compatibility tests require an older driver version", "hostDriverVersion", hostDriverVersion, "compatDriverVersion", compatDriverVersion)
Skip("CUDA Forward Compatibility tests require an older driver version")
}
})
It("should work with the nvidia runtime in legacy mode", func(ctx context.Context) {
ldconfigOut, _, err := r.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia --gpus all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
Expect(err).ToNot(HaveOccurred())
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda/compat"))
})
It("should work with the nvidia runtime in CDI mode", func(ctx context.Context) {
ldconfigOut, _, err := r.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
Expect(err).ToNot(HaveOccurred())
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda/compat"))
})
It("should NOT work with nvidia-container-runtime-hook", func(ctx context.Context) {
ldconfigOut, _, err := r.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=runc --gpus all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
Expect(err).ToNot(HaveOccurred())
Expect(ldconfigOut).To(ContainSubstring("/usr/lib64"))
})
})
})

View File

@@ -3,9 +3,9 @@ module github.com/NVIDIA/nvidia-container-toolkit/tests
go 1.23.2
require (
github.com/onsi/ginkgo/v2 v2.23.0
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
golang.org/x/crypto v0.36.0
golang.org/x/crypto v0.33.0
)
require (
@@ -13,9 +13,9 @@ require (
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -8,26 +8,26 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

View File

@@ -1,26 +1,3 @@
## 2.23.0
Ginkgo 2.23.0 adds a handful of methods to `GinkgoT()` to make it compatible with the `testing.TB` interface in Go 1.24. `GinkgoT().Context()`, in particular, is a useful shorthand for generating a new context that will clean itself up in a `DeferCleanup()`. This has subtle behavior differences from the golang implementation but should make sense in a Ginkgo... um... context.
### Features
- bump to go 1.24.0 - support new testing.TB methods and add a test to cover testing.TB regressions [37a511b]
### Fixes
- fix edge case where build -o is pointing at an explicit file, not a directory [7556a86]
- Fix binary paths when precompiling multiple suites. [4df06c6]
### Maintenance
- Fix: Correct Markdown list rendering in MIGRATING_TO_V2.md [cbcf39a]
- docs: fix test workflow badge (#1512) [9b261ff]
- Bump golang.org/x/net in /integration/_fixtures/version_mismatch_fixture (#1516) [00f19c8]
- Bump golang.org/x/tools from 0.28.0 to 0.30.0 (#1515) [e98a4df]
- Bump activesupport from 6.0.6.1 to 6.1.7.5 in /docs (#1504) [60cc4e2]
- Bump github-pages from 231 to 232 in /docs (#1447) [fea6f2d]
- Bump rexml from 3.2.8 to 3.3.9 in /docs (#1497) [31d7813]
- Bump webrick from 1.8.1 to 1.9.1 in /docs (#1501) [fc3bbd6]
- Code linting (#1500) [aee0d56]
- change interface{} to any (#1502) [809a710]
## 2.22.2
### Maintenance
@@ -653,7 +630,7 @@ Ginkgo also uses this progress reporting infrastructure under the hood when hand
### Features
- `BeforeSuite`, `AfterSuite`, `SynchronizedBeforeSuite`, `SynchronizedAfterSuite`, and `ReportAfterSuite` now support (the relevant subset of) decorators. These can be passed in _after_ the callback functions that are usually passed into these nodes.
As a result the **signature of these methods has changed** and now includes a trailing `args ...any`. For most users simply using the DSL, this change is transparent. However if you were assigning one of these functions to a custom variable (or passing it around) then your code may need to change to reflect the new signature.
As a result the **signature of these methods has changed** and now includes a trailing `args ...interface{}`. For most users simply using the DSL, this change is transparent. However if you were assigning one of these functions to a custom variable (or passing it around) then your code may need to change to reflect the new signature.
### Maintenance
- Modernize the invocation of Ginkgo in github actions [0ffde58]

View File

@@ -1,6 +1,6 @@
![Ginkgo](https://onsi.github.io/ginkgo/images/ginkgo.png)
[![test](https://github.com/onsi/ginkgo/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster) | [Ginkgo Docs](https://onsi.github.io/ginkgo/)
[![test](https://github.com/onsi/ginkgo/workflows/test/badge.svg?branch=master)](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster) | [Ginkgo Docs](https://onsi.github.io/ginkgo/)
---

View File

@@ -83,9 +83,9 @@ func exitIfErrors(errors []error) {
type GinkgoWriterInterface interface {
io.Writer
Print(a ...any)
Printf(format string, a ...any)
Println(a ...any)
Print(a ...interface{})
Printf(format string, a ...interface{})
Println(a ...interface{})
TeeTo(writer io.Writer)
ClearTeeWriters()
@@ -243,7 +243,7 @@ for more on how specs are parallelized in Ginkgo.
You can also pass suite-level Label() decorators to RunSpecs. The passed-in labels will apply to all specs in the suite.
*/
func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
func RunSpecs(t GinkgoTestingT, description string, args ...interface{}) bool {
if suiteDidRun {
exitIfErr(types.GinkgoErrors.RerunningSuite())
}
@@ -316,7 +316,7 @@ func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
return passed
}
func extractSuiteConfiguration(args []any) Labels {
func extractSuiteConfiguration(args []interface{}) Labels {
suiteLabels := Labels{}
configErrors := []error{}
for _, arg := range args {
@@ -491,14 +491,14 @@ to Describe the behavior of an object or function and, within that Describe, out
You can learn more at https://onsi.github.io/ginkgo/#organizing-specs-with-container-nodes
In addition, container nodes can be decorated with a variety of decorators. You can learn more here: https://onsi.github.io/ginkgo/#decorator-reference
*/
func Describe(text string, args ...any) bool {
func Describe(text string, args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...))
}
/*
FDescribe focuses specs within the Describe block.
*/
func FDescribe(text string, args ...any) bool {
func FDescribe(text string, args ...interface{}) bool {
args = append(args, internal.Focus)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...))
}
@@ -506,7 +506,7 @@ func FDescribe(text string, args ...any) bool {
/*
PDescribe marks specs within the Describe block as pending.
*/
func PDescribe(text string, args ...any) bool {
func PDescribe(text string, args ...interface{}) bool {
args = append(args, internal.Pending)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...))
}
@@ -522,18 +522,18 @@ var XDescribe = PDescribe
var Context, FContext, PContext, XContext = Describe, FDescribe, PDescribe, XDescribe
/* When is an alias for Describe - it generates the exact same kind of Container node */
func When(text string, args ...any) bool {
func When(text string, args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...))
}
/* When is an alias for Describe - it generates the exact same kind of Container node */
func FWhen(text string, args ...any) bool {
func FWhen(text string, args ...interface{}) bool {
args = append(args, internal.Focus)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...))
}
/* When is an alias for Describe - it generates the exact same kind of Container node */
func PWhen(text string, args ...any) bool {
func PWhen(text string, args ...interface{}) bool {
args = append(args, internal.Pending)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...))
}
@@ -550,14 +550,14 @@ You can pass It nodes bare functions (func() {}) or functions that receive a Spe
You can learn more at https://onsi.github.io/ginkgo/#spec-subjects-it
In addition, subject nodes can be decorated with a variety of decorators. You can learn more here: https://onsi.github.io/ginkgo/#decorator-reference
*/
func It(text string, args ...any) bool {
func It(text string, args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...))
}
/*
FIt allows you to focus an individual It.
*/
func FIt(text string, args ...any) bool {
func FIt(text string, args ...interface{}) bool {
args = append(args, internal.Focus)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...))
}
@@ -565,7 +565,7 @@ func FIt(text string, args ...any) bool {
/*
PIt allows you to mark an individual It as pending.
*/
func PIt(text string, args ...any) bool {
func PIt(text string, args ...interface{}) bool {
args = append(args, internal.Pending)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...))
}
@@ -611,8 +611,8 @@ BeforeSuite can take a func() body, or an interruptible func(SpecContext)/func(c
You cannot nest any other Ginkgo nodes within a BeforeSuite node's closure.
You can learn more here: https://onsi.github.io/ginkgo/#suite-setup-and-cleanup-beforesuite-and-aftersuite
*/
func BeforeSuite(body any, args ...any) bool {
combinedArgs := []any{body}
func BeforeSuite(body interface{}, args ...interface{}) bool {
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeSuite, "", combinedArgs...))
}
@@ -630,8 +630,8 @@ AfterSuite can take a func() body, or an interruptible func(SpecContext)/func(co
You cannot nest any other Ginkgo nodes within an AfterSuite node's closure.
You can learn more here: https://onsi.github.io/ginkgo/#suite-setup-and-cleanup-beforesuite-and-aftersuite
*/
func AfterSuite(body any, args ...any) bool {
combinedArgs := []any{body}
func AfterSuite(body interface{}, args ...interface{}) bool {
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterSuite, "", combinedArgs...))
}
@@ -667,8 +667,8 @@ If either function receives a context.Context/SpecContext it is considered inter
You cannot nest any other Ginkgo nodes within an SynchronizedBeforeSuite node's closure.
You can learn more, and see some examples, here: https://onsi.github.io/ginkgo/#parallel-suite-setup-and-cleanup-synchronizedbeforesuite-and-synchronizedaftersuite
*/
func SynchronizedBeforeSuite(process1Body any, allProcessBody any, args ...any) bool {
combinedArgs := []any{process1Body, allProcessBody}
func SynchronizedBeforeSuite(process1Body interface{}, allProcessBody interface{}, args ...interface{}) bool {
combinedArgs := []interface{}{process1Body, allProcessBody}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeSynchronizedBeforeSuite, "", combinedArgs...))
@@ -687,8 +687,8 @@ Note that you can also use DeferCleanup() in SynchronizedBeforeSuite to accompli
You cannot nest any other Ginkgo nodes within an SynchronizedAfterSuite node's closure.
You can learn more, and see some examples, here: https://onsi.github.io/ginkgo/#parallel-suite-setup-and-cleanup-synchronizedbeforesuite-and-synchronizedaftersuite
*/
func SynchronizedAfterSuite(allProcessBody any, process1Body any, args ...any) bool {
combinedArgs := []any{allProcessBody, process1Body}
func SynchronizedAfterSuite(allProcessBody interface{}, process1Body interface{}, args ...interface{}) bool {
combinedArgs := []interface{}{allProcessBody, process1Body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeSynchronizedAfterSuite, "", combinedArgs...))
@@ -703,7 +703,7 @@ BeforeEach can take a func() body, or an interruptible func(SpecContext)/func(co
You cannot nest any other Ginkgo nodes within a BeforeEach node's closure.
You can learn more here: https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach
*/
func BeforeEach(args ...any) bool {
func BeforeEach(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeEach, "", args...))
}
@@ -716,7 +716,7 @@ JustBeforeEach can take a func() body, or an interruptible func(SpecContext)/fun
You cannot nest any other Ginkgo nodes within a JustBeforeEach node's closure.
You can learn more and see some examples here: https://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach
*/
func JustBeforeEach(args ...any) bool {
func JustBeforeEach(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeJustBeforeEach, "", args...))
}
@@ -731,7 +731,7 @@ AfterEach can take a func() body, or an interruptible func(SpecContext)/func(con
You cannot nest any other Ginkgo nodes within an AfterEach node's closure.
You can learn more here: https://onsi.github.io/ginkgo/#spec-cleanup-aftereach-and-defercleanup
*/
func AfterEach(args ...any) bool {
func AfterEach(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterEach, "", args...))
}
@@ -743,7 +743,7 @@ JustAfterEach can take a func() body, or an interruptible func(SpecContext)/func
You cannot nest any other Ginkgo nodes within a JustAfterEach node's closure.
You can learn more and see some examples here: https://onsi.github.io/ginkgo/#separating-diagnostics-collection-and-teardown-justaftereach
*/
func JustAfterEach(args ...any) bool {
func JustAfterEach(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeJustAfterEach, "", args...))
}
@@ -758,7 +758,7 @@ You cannot nest any other Ginkgo nodes within a BeforeAll node's closure.
You can learn more about Ordered Containers at: https://onsi.github.io/ginkgo/#ordered-containers
And you can learn more about BeforeAll at: https://onsi.github.io/ginkgo/#setup-in-ordered-containers-beforeall-and-afterall
*/
func BeforeAll(args ...any) bool {
func BeforeAll(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeAll, "", args...))
}
@@ -775,7 +775,7 @@ You cannot nest any other Ginkgo nodes within an AfterAll node's closure.
You can learn more about Ordered Containers at: https://onsi.github.io/ginkgo/#ordered-containers
And you can learn more about AfterAll at: https://onsi.github.io/ginkgo/#setup-in-ordered-containers-beforeall-and-afterall
*/
func AfterAll(args ...any) bool {
func AfterAll(args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterAll, "", args...))
}
@@ -818,7 +818,7 @@ When DeferCleanup is called in BeforeSuite, SynchronizedBeforeSuite, AfterSuite,
Note that DeferCleanup does not represent a node but rather dynamically generates the appropriate type of cleanup node based on the context in which it is called. As such you must call DeferCleanup within a Setup or Subject node, and not within a Container node.
You can learn more about DeferCleanup here: https://onsi.github.io/ginkgo/#cleaning-up-our-cleanup-code-defercleanup
*/
func DeferCleanup(args ...any) {
func DeferCleanup(args ...interface{}) {
fail := func(message string, cl types.CodeLocation) {
global.Failer.Fail(message, cl)
}

View File

@@ -118,9 +118,9 @@ Use Gomega's gmeasure package instead.
You can learn more here: https://onsi.github.io/ginkgo/#benchmarking-code
*/
type Benchmarker interface {
Time(name string, body func(), info ...any) (elapsedTime time.Duration)
RecordValue(name string, value float64, info ...any)
RecordValueWithPrecision(name string, value float64, units string, precision int, info ...any)
Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration)
RecordValue(name string, value float64, info ...interface{})
RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{})
}
/*
@@ -129,7 +129,7 @@ Deprecated: Measure() has been removed from Ginkgo 2.0
Use Gomega's gmeasure package instead.
You can learn more here: https://onsi.github.io/ginkgo/#benchmarking-code
*/
func Measure(_ ...any) bool {
func Measure(_ ...interface{}) bool {
deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), types.NewCodeLocation(1))
return true
}

View File

@@ -24,15 +24,15 @@ const (
var SingletonFormatter = New(ColorModeTerminal)
func F(format string, args ...any) string {
func F(format string, args ...interface{}) string {
return SingletonFormatter.F(format, args...)
}
func Fi(indentation uint, format string, args ...any) string {
func Fi(indentation uint, format string, args ...interface{}) string {
return SingletonFormatter.Fi(indentation, format, args...)
}
func Fiw(indentation uint, maxWidth uint, format string, args ...any) string {
func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
return SingletonFormatter.Fiw(indentation, maxWidth, format, args...)
}
@@ -115,15 +115,15 @@ func New(colorMode ColorMode) Formatter {
return f
}
func (f Formatter) F(format string, args ...any) string {
func (f Formatter) F(format string, args ...interface{}) string {
return f.Fi(0, format, args...)
}
func (f Formatter) Fi(indentation uint, format string, args ...any) string {
func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string {
return f.Fiw(indentation, 0, format, args...)
}
func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...any) string {
func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
out := f.style(format)
if len(args) > 0 {
out = fmt.Sprintf(out, args...)

View File

@@ -55,22 +55,18 @@ func buildSpecs(args []string, cliConfig types.CLIConfig, goFlagsConfig types.Go
if suite.State.Is(internal.TestSuiteStateFailedToCompile) {
fmt.Println(suite.CompilationError.Error())
} else {
var testBinPath string
if len(goFlagsConfig.O) != 0 {
if len(goFlagsConfig.O) == 0 {
goFlagsConfig.O = path.Join(suite.Path, suite.PackageName+".test")
} else {
stat, err := os.Stat(goFlagsConfig.O)
if err != nil {
panic(err)
}
if stat.IsDir() {
testBinPath = goFlagsConfig.O + "/" + suite.PackageName + ".test"
} else {
testBinPath = goFlagsConfig.O
goFlagsConfig.O += "/" + suite.PackageName + ".test"
}
}
if len(testBinPath) == 0 {
testBinPath = path.Join(suite.Path, suite.PackageName+".test")
}
fmt.Printf("Compiled %s\n", testBinPath)
fmt.Printf("Compiled %s\n", goFlagsConfig.O)
}
}

View File

@@ -12,7 +12,7 @@ func Abort(details AbortDetails) {
panic(details)
}
func AbortGracefullyWith(format string, args ...any) {
func AbortGracefullyWith(format string, args ...interface{}) {
Abort(AbortDetails{
ExitCode: 0,
Error: fmt.Errorf(format, args...),
@@ -20,7 +20,7 @@ func AbortGracefullyWith(format string, args ...any) {
})
}
func AbortWith(format string, args ...any) {
func AbortWith(format string, args ...interface{}) {
Abort(AbortDetails{
ExitCode: 1,
Error: fmt.Errorf(format, args...),
@@ -28,7 +28,7 @@ func AbortWith(format string, args ...any) {
})
}
func AbortWithUsage(format string, args ...any) {
func AbortWithUsage(format string, args ...interface{}) {
Abort(AbortDetails{
ExitCode: 1,
Error: fmt.Errorf(format, args...),

View File

@@ -68,6 +68,7 @@ func (p Program) RunAndExit(osArgs []string) {
fmt.Fprintln(p.ErrWriter, deprecationTracker.DeprecationsReport())
}
p.Exiter(exitCode)
return
}()
args, additionalArgs := []string{}, []string{}
@@ -156,6 +157,7 @@ func (p Program) handleHelpRequestsAndExit(writer io.Writer, args []string) {
p.EmitUsage(writer)
Abort(AbortDetails{ExitCode: 1})
}
return
}
func (p Program) EmitUsage(writer io.Writer) {

View File

@@ -89,7 +89,7 @@ func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int)
}
i := 0
if !sortFunc(i) {
if sortFunc(i) != true {
i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
}

View File

@@ -142,7 +142,7 @@ OUTER_LOOP:
}
if !endTime.IsZero() {
r.suiteConfig.Timeout = time.Until(endTime)
r.suiteConfig.Timeout = endTime.Sub(time.Now())
if r.suiteConfig.Timeout <= 0 {
suites[suiteIdx].State = internal.TestSuiteStateFailedDueToTimeout
opc.StopAndDrain()

View File

@@ -1,7 +1,6 @@
package ginkgo
import (
"context"
"testing"
"github.com/onsi/ginkgo/v2/internal/testingtproxy"
@@ -49,8 +48,6 @@ The portion of the interface returned by GinkgoT() that maps onto methods in the
*/
type GinkgoTInterface interface {
Cleanup(func())
Chdir(dir string)
Context() context.Context
Setenv(kev, value string)
Error(args ...any)
Errorf(format string, args ...any)

View File

@@ -32,7 +32,7 @@ func (f *Failer) GetFailure() types.Failure {
return f.failure
}
func (f *Failer) Panic(location types.CodeLocation, forwardedPanic any) {
func (f *Failer) Panic(location types.CodeLocation, forwardedPanic interface{}) {
f.lock.Lock()
defer f.lock.Unlock()

View File

@@ -40,7 +40,7 @@ func (ic InterruptCause) String() string {
}
type InterruptStatus struct {
Channel chan any
Channel chan interface{}
Level InterruptLevel
Cause InterruptCause
}
@@ -62,14 +62,14 @@ type InterruptHandlerInterface interface {
}
type InterruptHandler struct {
c chan any
c chan interface{}
lock *sync.Mutex
level InterruptLevel
cause InterruptCause
client parallel_support.Client
stop chan any
stop chan interface{}
signals []os.Signal
requestAbortCheck chan any
requestAbortCheck chan interface{}
}
func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *InterruptHandler {
@@ -77,10 +77,10 @@ func NewInterruptHandler(client parallel_support.Client, signals ...os.Signal) *
signals = []os.Signal{os.Interrupt, syscall.SIGTERM}
}
handler := &InterruptHandler{
c: make(chan any),
c: make(chan interface{}),
lock: &sync.Mutex{},
stop: make(chan any),
requestAbortCheck: make(chan any),
stop: make(chan interface{}),
requestAbortCheck: make(chan interface{}),
client: client,
signals: signals,
}
@@ -98,9 +98,9 @@ func (handler *InterruptHandler) registerForInterrupts() {
signal.Notify(signalChannel, handler.signals...)
// cross-process abort handling
var abortChannel chan any
var abortChannel chan interface{}
if handler.client != nil {
abortChannel = make(chan any)
abortChannel = make(chan interface{})
go func() {
pollTicker := time.NewTicker(ABORT_POLLING_INTERVAL)
for {
@@ -125,7 +125,7 @@ func (handler *InterruptHandler) registerForInterrupts() {
}()
}
go func(abortChannel chan any) {
go func(abortChannel chan interface{}) {
var interruptCause InterruptCause
for {
select {
@@ -151,7 +151,7 @@ func (handler *InterruptHandler) registerForInterrupts() {
}
if handler.level != oldLevel {
close(handler.c)
handler.c = make(chan any)
handler.c = make(chan interface{})
}
handler.lock.Unlock()
}

View File

@@ -84,7 +84,7 @@ const SuppressProgressReporting = suppressProgressReporting(true)
type FlakeAttempts uint
type MustPassRepeatedly uint
type Offset uint
type Done chan<- any // Deprecated Done Channel for asynchronous testing
type Done chan<- interface{} // Deprecated Done Channel for asynchronous testing
type Labels []string
type PollProgressInterval time.Duration
type PollProgressAfter time.Duration
@@ -110,9 +110,9 @@ func UnionOfLabels(labels ...Labels) Labels {
return out
}
func PartitionDecorations(args ...any) ([]any, []any) {
decorations := []any{}
remainingArgs := []any{}
func PartitionDecorations(args ...interface{}) ([]interface{}, []interface{}) {
decorations := []interface{}{}
remainingArgs := []interface{}{}
for _, arg := range args {
if isDecoration(arg) {
decorations = append(decorations, arg)
@@ -123,7 +123,7 @@ func PartitionDecorations(args ...any) ([]any, []any) {
return decorations, remainingArgs
}
func isDecoration(arg any) bool {
func isDecoration(arg interface{}) bool {
switch t := reflect.TypeOf(arg); {
case t == nil:
return false
@@ -168,7 +168,7 @@ func isDecoration(arg any) bool {
}
}
func isSliceOfDecorations(slice any) bool {
func isSliceOfDecorations(slice interface{}) bool {
vSlice := reflect.ValueOf(slice)
if vSlice.Len() == 0 {
return false
@@ -184,7 +184,7 @@ func isSliceOfDecorations(slice any) bool {
var contextType = reflect.TypeOf(new(context.Context)).Elem()
var specContextType = reflect.TypeOf(new(SpecContext)).Elem()
func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeType, text string, args ...any) (Node, []error) {
func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeType, text string, args ...interface{}) (Node, []error) {
baseOffset := 2
node := Node{
ID: UniqueNodeID(),
@@ -207,7 +207,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
args = unrollInterfaceSlice(args)
remainingArgs := []any{}
remainingArgs := []interface{}{}
// First get the CodeLocation up-to-date
for _, arg := range args {
switch v := arg.(type) {
@@ -223,7 +223,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
labelsSeen := map[string]bool{}
trackedFunctionError := false
args = remainingArgs
remainingArgs = []any{}
remainingArgs = []interface{}{}
// now process the rest of the args
for _, arg := range args {
switch t := reflect.TypeOf(arg); {
@@ -451,7 +451,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
var doneType = reflect.TypeOf(make(Done))
func extractBodyFunction(deprecationTracker *types.DeprecationTracker, cl types.CodeLocation, arg any) (func(SpecContext), bool) {
func extractBodyFunction(deprecationTracker *types.DeprecationTracker, cl types.CodeLocation, arg interface{}) (func(SpecContext), bool) {
t := reflect.TypeOf(arg)
if t.NumOut() > 0 || t.NumIn() > 1 {
return nil, false
@@ -477,7 +477,7 @@ func extractBodyFunction(deprecationTracker *types.DeprecationTracker, cl types.
var byteType = reflect.TypeOf([]byte{})
func extractSynchronizedBeforeSuiteProc1Body(arg any) (func(SpecContext) []byte, bool) {
func extractSynchronizedBeforeSuiteProc1Body(arg interface{}) (func(SpecContext) []byte, bool) {
t := reflect.TypeOf(arg)
v := reflect.ValueOf(arg)
@@ -505,7 +505,7 @@ func extractSynchronizedBeforeSuiteProc1Body(arg any) (func(SpecContext) []byte,
}, hasContext
}
func extractSynchronizedBeforeSuiteAllProcsBody(arg any) (func(SpecContext, []byte), bool) {
func extractSynchronizedBeforeSuiteAllProcsBody(arg interface{}) (func(SpecContext, []byte), bool) {
t := reflect.TypeOf(arg)
v := reflect.ValueOf(arg)
hasContext, hasByte := false, false
@@ -536,11 +536,11 @@ func extractSynchronizedBeforeSuiteAllProcsBody(arg any) (func(SpecContext, []by
var errInterface = reflect.TypeOf((*error)(nil)).Elem()
func NewCleanupNode(deprecationTracker *types.DeprecationTracker, fail func(string, types.CodeLocation), args ...any) (Node, []error) {
func NewCleanupNode(deprecationTracker *types.DeprecationTracker, fail func(string, types.CodeLocation), args ...interface{}) (Node, []error) {
decorations, remainingArgs := PartitionDecorations(args...)
baseOffset := 2
cl := types.NewCodeLocation(baseOffset)
finalArgs := []any{}
finalArgs := []interface{}{}
for _, arg := range decorations {
switch t := reflect.TypeOf(arg); {
case t == reflect.TypeOf(Offset(0)):
@@ -920,12 +920,12 @@ func (n Nodes) GetMaxMustPassRepeatedly() int {
return maxMustPassRepeatedly
}
func unrollInterfaceSlice(args any) []any {
func unrollInterfaceSlice(args interface{}) []interface{} {
v := reflect.ValueOf(args)
if v.Kind() != reflect.Slice {
return []any{args}
return []interface{}{args}
}
out := []any{}
out := []interface{}{}
for i := 0; i < v.Len(); i++ {
el := reflect.ValueOf(v.Index(i).Interface())
if el.Kind() == reflect.Slice && el.Type() != reflect.TypeOf(Labels{}) {

View File

@@ -69,7 +69,7 @@ type pipePair struct {
writer *os.File
}
func startPipeFactory(pipeChannel chan pipePair, shutdown chan any) {
func startPipeFactory(pipeChannel chan pipePair, shutdown chan interface{}) {
for {
//make the next pipe...
pair := pipePair{}
@@ -101,8 +101,8 @@ type genericOutputInterceptor struct {
stderrClone *os.File
pipe pipePair
shutdown chan any
emergencyBailout chan any
shutdown chan interface{}
emergencyBailout chan interface{}
pipeChannel chan pipePair
interceptedContent chan string
@@ -139,7 +139,7 @@ func (interceptor *genericOutputInterceptor) ResumeIntercepting() {
interceptor.intercepting = true
if interceptor.stdoutClone == nil {
interceptor.stdoutClone, interceptor.stderrClone = interceptor.implementation.CreateStdoutStderrClones()
interceptor.shutdown = make(chan any)
interceptor.shutdown = make(chan interface{})
go startPipeFactory(interceptor.pipeChannel, interceptor.shutdown)
}
@@ -147,13 +147,13 @@ func (interceptor *genericOutputInterceptor) ResumeIntercepting() {
// we get the pipe from our pipe factory. it runs in the background so we can request the next pipe while the spec being intercepted is running
interceptor.pipe = <-interceptor.pipeChannel
interceptor.emergencyBailout = make(chan any)
interceptor.emergencyBailout = make(chan interface{})
//Spin up a goroutine to copy data from the pipe into a buffer, this is how we capture any output the user is emitting
go func() {
buffer := &bytes.Buffer{}
destination := io.MultiWriter(buffer, interceptor.forwardTo)
copyFinished := make(chan any)
copyFinished := make(chan interface{})
reader := interceptor.pipe.reader
go func() {
io.Copy(destination, reader)
@@ -224,7 +224,7 @@ func NewOSGlobalReassigningOutputInterceptor() OutputInterceptor {
return &genericOutputInterceptor{
interceptedContent: make(chan string),
pipeChannel: make(chan pipePair),
shutdown: make(chan any),
shutdown: make(chan interface{}),
implementation: &osGlobalReassigningOutputInterceptorImpl{},
}
}

View File

@@ -13,7 +13,7 @@ func NewOutputInterceptor() OutputInterceptor {
return &genericOutputInterceptor{
interceptedContent: make(chan string),
pipeChannel: make(chan pipePair),
shutdown: make(chan any),
shutdown: make(chan interface{}),
implementation: &dupSyscallOutputInterceptorImpl{},
}
}

View File

@@ -30,7 +30,7 @@ type Server interface {
Close()
Address() string
RegisterAlive(node int, alive func() bool)
GetSuiteDone() chan any
GetSuiteDone() chan interface{}
GetOutputDestination() io.Writer
SetOutputDestination(io.Writer)
}

View File

@@ -34,7 +34,7 @@ func (client *httpClient) Close() error {
return nil
}
func (client *httpClient) post(path string, data any) error {
func (client *httpClient) post(path string, data interface{}) error {
var body io.Reader
if data != nil {
encoded, err := json.Marshal(data)
@@ -54,7 +54,7 @@ func (client *httpClient) post(path string, data any) error {
return nil
}
func (client *httpClient) poll(path string, data any) error {
func (client *httpClient) poll(path string, data interface{}) error {
for {
resp, err := http.Get(client.serverHost + path)
if err != nil {
@@ -153,7 +153,10 @@ func (client *httpClient) PostAbort() error {
func (client *httpClient) ShouldAbort() bool {
err := client.poll("/abort", nil)
return err == ErrorGone
if err == ErrorGone {
return true
}
return false
}
func (client *httpClient) Write(p []byte) (int, error) {

View File

@@ -75,7 +75,7 @@ func (server *httpServer) Address() string {
return "http://" + server.listener.Addr().String()
}
func (server *httpServer) GetSuiteDone() chan any {
func (server *httpServer) GetSuiteDone() chan interface{} {
return server.handler.done
}
@@ -96,7 +96,7 @@ func (server *httpServer) RegisterAlive(node int, alive func() bool) {
//
// The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object any) bool {
func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool {
defer request.Body.Close()
if json.NewDecoder(request.Body).Decode(object) != nil {
writer.WriteHeader(http.StatusBadRequest)

View File

@@ -35,7 +35,7 @@ func (client *rpcClient) Close() error {
return client.client.Close()
}
func (client *rpcClient) poll(method string, data any) error {
func (client *rpcClient) poll(method string, data interface{}) error {
for {
err := client.client.Call(method, voidSender, data)
if err == nil {

View File

@@ -25,7 +25,7 @@ type RPCServer struct {
handler *ServerHandler
}
// Create a new server, automatically selecting a port
//Create a new server, automatically selecting a port
func newRPCServer(parallelTotal int, reporter reporters.Reporter) (*RPCServer, error) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -37,7 +37,7 @@ func newRPCServer(parallelTotal int, reporter reporters.Reporter) (*RPCServer, e
}, nil
}
// Start the server. You don't need to `go s.Start()`, just `s.Start()`
//Start the server. You don't need to `go s.Start()`, just `s.Start()`
func (server *RPCServer) Start() {
rpcServer := rpc.NewServer()
rpcServer.RegisterName("Server", server.handler) //register the handler's methods as the server
@@ -48,17 +48,17 @@ func (server *RPCServer) Start() {
go httpServer.Serve(server.listener)
}
// Stop the server
//Stop the server
func (server *RPCServer) Close() {
server.listener.Close()
}
// The address the server can be reached it. Pass this into the `ForwardingReporter`.
//The address the server can be reached it. Pass this into the `ForwardingReporter`.
func (server *RPCServer) Address() string {
return server.listener.Addr().String()
}
func (server *RPCServer) GetSuiteDone() chan any {
func (server *RPCServer) GetSuiteDone() chan interface{} {
return server.handler.done
}

View File

@@ -18,7 +18,7 @@ var voidSender Void
// It handles all the business logic to avoid duplication between the two servers
type ServerHandler struct {
done chan any
done chan interface{}
outputDestination io.Writer
reporter reporters.Reporter
alives []func() bool
@@ -46,7 +46,7 @@ func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHan
parallelTotal: parallelTotal,
outputDestination: os.Stdout,
done: make(chan any),
done: make(chan interface{}),
}
}

View File

@@ -8,7 +8,7 @@ import (
type ReportEntry = types.ReportEntry
func NewReportEntry(name string, cl types.CodeLocation, args ...any) (ReportEntry, error) {
func NewReportEntry(name string, cl types.CodeLocation, args ...interface{}) (ReportEntry, error) {
out := ReportEntry{
Visibility: types.ReportEntryVisibilityAlways,
Name: name,

View File

@@ -1,7 +1,6 @@
package testingtproxy
import (
"context"
"fmt"
"io"
"os"
@@ -20,9 +19,9 @@ type addReportEntryFunc func(names string, args ...any)
type ginkgoWriterInterface interface {
io.Writer
Print(a ...any)
Printf(format string, a ...any)
Println(a ...any)
Print(a ...interface{})
Printf(format string, a ...interface{})
Println(a ...interface{})
}
type ginkgoRecoverFunc func()
type attachProgressReporterFunc func(func() string) func()
@@ -81,31 +80,11 @@ func (t *ginkgoTestingTProxy) Setenv(key, value string) {
}
}
func (t *ginkgoTestingTProxy) Chdir(dir string) {
currentDir, err := os.Getwd()
if err != nil {
t.fail(fmt.Sprintf("Failed to get current directory: %v", err), 1)
}
t.cleanup(os.Chdir, currentDir, internal.Offset(1))
err = os.Chdir(dir)
if err != nil {
t.fail(fmt.Sprintf("Failed to change directory: %v", err), 1)
}
}
func (t *ginkgoTestingTProxy) Context() context.Context {
ctx, cancel := context.WithCancel(context.Background())
t.cleanup(cancel, internal.Offset(1))
return ctx
}
func (t *ginkgoTestingTProxy) Error(args ...any) {
func (t *ginkgoTestingTProxy) Error(args ...interface{}) {
t.fail(fmt.Sprintln(args...), t.offset)
}
func (t *ginkgoTestingTProxy) Errorf(format string, args ...any) {
func (t *ginkgoTestingTProxy) Errorf(format string, args ...interface{}) {
t.fail(fmt.Sprintf(format, args...), t.offset)
}
@@ -121,11 +100,11 @@ func (t *ginkgoTestingTProxy) Failed() bool {
return t.report().Failed()
}
func (t *ginkgoTestingTProxy) Fatal(args ...any) {
func (t *ginkgoTestingTProxy) Fatal(args ...interface{}) {
t.fail(fmt.Sprintln(args...), t.offset)
}
func (t *ginkgoTestingTProxy) Fatalf(format string, args ...any) {
func (t *ginkgoTestingTProxy) Fatalf(format string, args ...interface{}) {
t.fail(fmt.Sprintf(format, args...), t.offset)
}
@@ -133,11 +112,11 @@ func (t *ginkgoTestingTProxy) Helper() {
types.MarkAsHelper(1)
}
func (t *ginkgoTestingTProxy) Log(args ...any) {
func (t *ginkgoTestingTProxy) Log(args ...interface{}) {
fmt.Fprintln(t.writer, args...)
}
func (t *ginkgoTestingTProxy) Logf(format string, args ...any) {
func (t *ginkgoTestingTProxy) Logf(format string, args ...interface{}) {
t.Log(fmt.Sprintf(format, args...))
}
@@ -149,7 +128,7 @@ func (t *ginkgoTestingTProxy) Parallel() {
// No-op
}
func (t *ginkgoTestingTProxy) Skip(args ...any) {
func (t *ginkgoTestingTProxy) Skip(args ...interface{}) {
t.skip(fmt.Sprintln(args...), t.offset)
}
@@ -157,7 +136,7 @@ func (t *ginkgoTestingTProxy) SkipNow() {
t.skip("skip", t.offset)
}
func (t *ginkgoTestingTProxy) Skipf(format string, args ...any) {
func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) {
t.skip(fmt.Sprintf(format, args...), t.offset)
}

View File

@@ -121,15 +121,15 @@ func (w *Writer) ClearTeeWriters() {
w.teeWriters = []io.Writer{}
}
func (w *Writer) Print(a ...any) {
func (w *Writer) Print(a ...interface{}) {
fmt.Fprint(w, a...)
}
func (w *Writer) Printf(format string, a ...any) {
func (w *Writer) Printf(format string, a ...interface{}) {
fmt.Fprintf(w, format, a...)
}
func (w *Writer) Println(a ...any) {
func (w *Writer) Println(a ...interface{}) {
fmt.Fprintln(w, a...)
}

View File

@@ -685,11 +685,11 @@ func (r *DefaultReporter) _emit(s string, block bool, isDelimiter bool) {
}
/* Rendering text */
func (r *DefaultReporter) f(format string, args ...any) string {
func (r *DefaultReporter) f(format string, args ...interface{}) string {
return r.formatter.F(format, args...)
}
func (r *DefaultReporter) fi(indentation uint, format string, args ...any) string {
func (r *DefaultReporter) fi(indentation uint, format string, args ...interface{}) string {
return r.formatter.Fi(indentation, format, args...)
}

View File

@@ -60,7 +60,7 @@ AddReportEntry() must be called within a Subject or Setup node - not in a Contai
You can learn more about Report Entries here: https://onsi.github.io/ginkgo/#attaching-data-to-reports
*/
func AddReportEntry(name string, args ...any) {
func AddReportEntry(name string, args ...interface{}) {
cl := types.NewCodeLocation(1)
reportEntry, err := internal.NewReportEntry(name, cl, args...)
if err != nil {
@@ -89,7 +89,7 @@ You can learn more about ReportBeforeEach here: https://onsi.github.io/ginkgo/#g
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
*/
func ReportBeforeEach(body any, args ...any) bool {
combinedArgs := []any{body}
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeEach, "", combinedArgs...))
@@ -113,7 +113,7 @@ You can learn more about ReportAfterEach here: https://onsi.github.io/ginkgo/#ge
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
*/
func ReportAfterEach(body any, args ...any) bool {
combinedArgs := []any{body}
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...))
@@ -143,7 +143,7 @@ You can learn more about Ginkgo's reporting infrastructure, including generating
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
*/
func ReportBeforeSuite(body any, args ...any) bool {
combinedArgs := []any{body}
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeSuite, "", combinedArgs...))
}
@@ -174,8 +174,8 @@ You can learn more about Ginkgo's reporting infrastructure, including generating
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
*/
func ReportAfterSuite(text string, body any, args ...any) bool {
combinedArgs := []any{body}
func ReportAfterSuite(text string, body any, args ...interface{}) bool {
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterSuite, text, combinedArgs...))
}

View File

@@ -23,7 +23,7 @@ You can learn more about generating EntryDescriptions here: https://onsi.github.
*/
type EntryDescription string
func (ed EntryDescription) render(args ...any) string {
func (ed EntryDescription) render(args ...interface{}) string {
return fmt.Sprintf(string(ed), args...)
}
@@ -44,7 +44,7 @@ For example:
You can learn more about DescribeTable here: https://onsi.github.io/ginkgo/#table-specs
And can explore some Table patterns here: https://onsi.github.io/ginkgo/#table-specs-patterns
*/
func DescribeTable(description string, args ...any) bool {
func DescribeTable(description string, args ...interface{}) bool {
GinkgoHelper()
generateTable(description, false, args...)
return true
@@ -53,7 +53,7 @@ func DescribeTable(description string, args ...any) bool {
/*
You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`.
*/
func FDescribeTable(description string, args ...any) bool {
func FDescribeTable(description string, args ...interface{}) bool {
GinkgoHelper()
args = append(args, internal.Focus)
generateTable(description, false, args...)
@@ -63,7 +63,7 @@ func FDescribeTable(description string, args ...any) bool {
/*
You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`.
*/
func PDescribeTable(description string, args ...any) bool {
func PDescribeTable(description string, args ...interface{}) bool {
GinkgoHelper()
args = append(args, internal.Pending)
generateTable(description, false, args...)
@@ -109,7 +109,7 @@ Note that you **must** place define an It inside the body function.
You can learn more about DescribeTableSubtree here: https://onsi.github.io/ginkgo/#table-specs
And can explore some Table patterns here: https://onsi.github.io/ginkgo/#table-specs-patterns
*/
func DescribeTableSubtree(description string, args ...any) bool {
func DescribeTableSubtree(description string, args ...interface{}) bool {
GinkgoHelper()
generateTable(description, true, args...)
return true
@@ -118,7 +118,7 @@ func DescribeTableSubtree(description string, args ...any) bool {
/*
You can focus a table with `FDescribeTableSubtree`. This is equivalent to `FDescribe`.
*/
func FDescribeTableSubtree(description string, args ...any) bool {
func FDescribeTableSubtree(description string, args ...interface{}) bool {
GinkgoHelper()
args = append(args, internal.Focus)
generateTable(description, true, args...)
@@ -128,7 +128,7 @@ func FDescribeTableSubtree(description string, args ...any) bool {
/*
You can mark a table as pending with `PDescribeTableSubtree`. This is equivalent to `PDescribe`.
*/
func PDescribeTableSubtree(description string, args ...any) bool {
func PDescribeTableSubtree(description string, args ...interface{}) bool {
GinkgoHelper()
args = append(args, internal.Pending)
generateTable(description, true, args...)
@@ -144,9 +144,9 @@ var XDescribeTableSubtree = PDescribeTableSubtree
TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
*/
type TableEntry struct {
description any
decorations []any
parameters []any
description interface{}
decorations []interface{}
parameters []interface{}
codeLocation types.CodeLocation
}
@@ -162,7 +162,7 @@ If you want to generate interruptible specs simply write a Table function that a
You can learn more about Entry here: https://onsi.github.io/ginkgo/#table-specs
*/
func Entry(description any, args ...any) TableEntry {
func Entry(description interface{}, args ...interface{}) TableEntry {
GinkgoHelper()
decorations, parameters := internal.PartitionDecorations(args...)
return TableEntry{description: description, decorations: decorations, parameters: parameters, codeLocation: types.NewCodeLocation(0)}
@@ -171,7 +171,7 @@ func Entry(description any, args ...any) TableEntry {
/*
You can focus a particular entry with FEntry. This is equivalent to FIt.
*/
func FEntry(description any, args ...any) TableEntry {
func FEntry(description interface{}, args ...interface{}) TableEntry {
GinkgoHelper()
decorations, parameters := internal.PartitionDecorations(args...)
decorations = append(decorations, internal.Focus)
@@ -181,7 +181,7 @@ func FEntry(description any, args ...any) TableEntry {
/*
You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
*/
func PEntry(description any, args ...any) TableEntry {
func PEntry(description interface{}, args ...interface{}) TableEntry {
GinkgoHelper()
decorations, parameters := internal.PartitionDecorations(args...)
decorations = append(decorations, internal.Pending)
@@ -196,17 +196,17 @@ var XEntry = PEntry
var contextType = reflect.TypeOf(new(context.Context)).Elem()
var specContextType = reflect.TypeOf(new(SpecContext)).Elem()
func generateTable(description string, isSubtree bool, args ...any) {
func generateTable(description string, isSubtree bool, args ...interface{}) {
GinkgoHelper()
cl := types.NewCodeLocation(0)
containerNodeArgs := []any{cl}
containerNodeArgs := []interface{}{cl}
entries := []TableEntry{}
var internalBody any
var internalBody interface{}
var internalBodyType reflect.Type
var tableLevelEntryDescription any
tableLevelEntryDescription = func(args ...any) string {
var tableLevelEntryDescription interface{}
tableLevelEntryDescription = func(args ...interface{}) string {
out := []string{}
for _, arg := range args {
out = append(out, fmt.Sprint(arg))
@@ -265,7 +265,7 @@ func generateTable(description string, isSubtree bool, args ...any) {
err = types.GinkgoErrors.InvalidEntryDescription(entry.codeLocation)
}
internalNodeArgs := []any{entry.codeLocation}
internalNodeArgs := []interface{}{entry.codeLocation}
internalNodeArgs = append(internalNodeArgs, entry.decorations...)
hasContext := false
@@ -290,7 +290,7 @@ func generateTable(description string, isSubtree bool, args ...any) {
if err != nil {
panic(err)
}
invokeFunction(internalBody, append([]any{c}, entry.parameters...))
invokeFunction(internalBody, append([]interface{}{c}, entry.parameters...))
})
if isSubtree {
exitIfErr(types.GinkgoErrors.ContextsCannotBeUsedInSubtreeTables(cl))
@@ -316,7 +316,7 @@ func generateTable(description string, isSubtree bool, args ...any) {
pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, description, containerNodeArgs...))
}
func invokeFunction(function any, parameters []any) []reflect.Value {
func invokeFunction(function interface{}, parameters []interface{}) []reflect.Value {
inValues := make([]reflect.Value, len(parameters))
funcType := reflect.TypeOf(function)
@@ -339,7 +339,7 @@ func invokeFunction(function any, parameters []any) []reflect.Value {
return reflect.ValueOf(function).Call(inValues)
}
func validateParameters(function any, parameters []any, kind string, cl types.CodeLocation, hasContext bool) error {
func validateParameters(function interface{}, parameters []interface{}, kind string, cl types.CodeLocation, hasContext bool) error {
funcType := reflect.TypeOf(function)
limit := funcType.NumIn()
offset := 0
@@ -377,7 +377,7 @@ func validateParameters(function any, parameters []any, kind string, cl types.Co
return nil
}
func computeValue(parameter any, t reflect.Type) reflect.Value {
func computeValue(parameter interface{}, t reflect.Type) reflect.Value {
if parameter == nil {
return reflect.Zero(t)
} else {

View File

@@ -365,7 +365,7 @@ var ReporterConfigFlags = GinkgoFlags{
func BuildTestSuiteFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig) (GinkgoFlagSet, error) {
flags := SuiteConfigFlags.CopyAppend(ParallelConfigFlags...).CopyAppend(ReporterConfigFlags...)
flags = flags.WithPrefix("ginkgo")
bindings := map[string]any{
bindings := map[string]interface{}{
"S": suiteConfig,
"R": reporterConfig,
"D": &deprecatedConfig{},
@@ -646,7 +646,7 @@ func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, packageToBuild strin
args := []string{"test", "-c", packageToBuild}
goArgs, err := GenerateFlagArgs(
GoBuildFlags,
map[string]any{
map[string]interface{}{
"Go": &goFlagsConfig,
},
)
@@ -665,7 +665,7 @@ func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterC
flags = flags.CopyAppend(ParallelConfigFlags.WithPrefix("ginkgo")...)
flags = flags.CopyAppend(ReporterConfigFlags.WithPrefix("ginkgo")...)
flags = flags.CopyAppend(GoRunFlags.WithPrefix("test")...)
bindings := map[string]any{
bindings := map[string]interface{}{
"S": &suiteConfig,
"R": &reporterConfig,
"Go": &goFlagsConfig,
@@ -677,7 +677,7 @@ func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterC
// GenerateGoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled non-Ginkgo test binary
func GenerateGoTestRunArgs(goFlagsConfig GoFlagsConfig) ([]string, error) {
flags := GoRunFlags.WithPrefix("test")
bindings := map[string]any{
bindings := map[string]interface{}{
"Go": &goFlagsConfig,
}
@@ -699,7 +699,7 @@ func BuildRunCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterCo
flags = flags.CopyAppend(GoBuildFlags...)
flags = flags.CopyAppend(GoRunFlags...)
bindings := map[string]any{
bindings := map[string]interface{}{
"S": suiteConfig,
"R": reporterConfig,
"C": cliConfig,
@@ -720,7 +720,7 @@ func BuildWatchCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *Reporter
flags = flags.CopyAppend(GoBuildFlags...)
flags = flags.CopyAppend(GoRunFlags...)
bindings := map[string]any{
bindings := map[string]interface{}{
"S": suiteConfig,
"R": reporterConfig,
"C": cliConfig,
@@ -736,7 +736,7 @@ func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig
flags := GinkgoCLISharedFlags
flags = flags.CopyAppend(GoBuildFlags...)
bindings := map[string]any{
bindings := map[string]interface{}{
"C": cliConfig,
"Go": goFlagsConfig,
"D": &deprecatedConfig{},
@@ -760,7 +760,7 @@ func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig
func BuildLabelsCommandFlagSet(cliConfig *CLIConfig) (GinkgoFlagSet, error) {
flags := GinkgoCLISharedFlags.SubsetWithNames("r", "skip-package")
bindings := map[string]any{
bindings := map[string]interface{}{
"C": cliConfig,
}

View File

@@ -113,7 +113,7 @@ type DeprecatedSpecFailure struct {
type DeprecatedSpecMeasurement struct {
Name string
Info any
Info interface{}
Order int
Results []float64

View File

@@ -88,7 +88,7 @@ body of a {{bold}}Describe{{/}}, {{bold}}Context{{/}}, or {{bold}}When{{/}}.`, n
}
}
func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic any, cl CodeLocation) error {
func (g ginkgoErrors) CaughtPanicDuringABuildPhase(caughtPanic interface{}, cl CodeLocation) error {
return GinkgoError{
Heading: "Assertion or Panic detected during tree construction",
Message: formatter.F(
@@ -189,7 +189,7 @@ func (g ginkgoErrors) InvalidDeclarationOfFlakeAttemptsAndMustPassRepeatedly(cl
}
}
func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator any) error {
func (g ginkgoErrors) UnknownDecorator(cl CodeLocation, nodeType NodeType, decorator interface{}) error {
return GinkgoError{
Heading: "Unknown Decorator",
Message: formatter.F(`[%s] node was passed an unknown decorator: '%#v'`, nodeType, decorator),
@@ -345,7 +345,7 @@ func (g ginkgoErrors) PushingCleanupInCleanupNode(cl CodeLocation) error {
}
/* ReportEntry errors */
func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg any) error {
func (g ginkgoErrors) TooManyReportEntryValues(cl CodeLocation, arg interface{}) error {
return GinkgoError{
Heading: "Too Many ReportEntry Values",
Message: formatter.F(`{{bold}}AddGinkgoReport{{/}} can only be given one value. Got unexpected value: %#v`, arg),
@@ -539,7 +539,7 @@ func (g ginkgoErrors) SynchronizedBeforeSuiteDisappearedOnProc1() error {
/* Configuration errors */
func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value any) error {
func (g ginkgoErrors) UnknownTypePassedToRunSpecs(value interface{}) error {
return GinkgoError{
Heading: "Unknown Type passed to RunSpecs",
Message: fmt.Sprintf("RunSpecs() accepts labels, and configuration of type types.SuiteConfig and/or types.ReporterConfig.\n You passed in: %v", value),

View File

@@ -92,7 +92,7 @@ func (gfs GinkgoFlagSections) Lookup(key string) (GinkgoFlagSection, bool) {
type GinkgoFlagSet struct {
flags GinkgoFlags
bindings any
bindings interface{}
sections GinkgoFlagSections
extraGoFlagsSection GinkgoFlagSection
@@ -101,7 +101,7 @@ type GinkgoFlagSet struct {
}
// Call NewGinkgoFlagSet to create GinkgoFlagSet that creates and binds to it's own *flag.FlagSet
func NewGinkgoFlagSet(flags GinkgoFlags, bindings any, sections GinkgoFlagSections) (GinkgoFlagSet, error) {
func NewGinkgoFlagSet(flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections) (GinkgoFlagSet, error) {
return bindFlagSet(GinkgoFlagSet{
flags: flags,
bindings: bindings,
@@ -110,7 +110,7 @@ func NewGinkgoFlagSet(flags GinkgoFlags, bindings any, sections GinkgoFlagSectio
}
// Call NewGinkgoFlagSet to create GinkgoFlagSet that extends an existing *flag.FlagSet
func NewAttachedGinkgoFlagSet(flagSet *flag.FlagSet, flags GinkgoFlags, bindings any, sections GinkgoFlagSections, extraGoFlagsSection GinkgoFlagSection) (GinkgoFlagSet, error) {
func NewAttachedGinkgoFlagSet(flagSet *flag.FlagSet, flags GinkgoFlags, bindings interface{}, sections GinkgoFlagSections, extraGoFlagsSection GinkgoFlagSection) (GinkgoFlagSet, error) {
return bindFlagSet(GinkgoFlagSet{
flags: flags,
bindings: bindings,
@@ -335,7 +335,7 @@ func (f GinkgoFlagSet) substituteUsage() {
fmt.Fprintln(f.flagSet.Output(), f.Usage())
}
func valueAtKeyPath(root any, keyPath string) (reflect.Value, bool) {
func valueAtKeyPath(root interface{}, keyPath string) (reflect.Value, bool) {
if len(keyPath) == 0 {
return reflect.Value{}, false
}
@@ -433,7 +433,7 @@ func (ssv stringSliceVar) Set(s string) error {
}
// given a set of GinkgoFlags and bindings, generate flag arguments suitable to be passed to an application with that set of flags configured.
func GenerateFlagArgs(flags GinkgoFlags, bindings any) ([]string, error) {
func GenerateFlagArgs(flags GinkgoFlags, bindings interface{}) ([]string, error) {
result := []string{}
for _, flag := range flags {
name := flag.ExportAs

View File

@@ -343,7 +343,7 @@ func tokenize(input string) func() (*treeNode, error) {
consumeUntil := func(cutset string) (string, int) {
j := i
for ; j < len(runes); j++ {
if strings.ContainsRune(cutset, runes[j]) {
if strings.IndexRune(cutset, runes[j]) >= 0 {
break
}
}

View File

@@ -9,18 +9,18 @@ import (
// ReportEntryValue wraps a report entry's value ensuring it can be encoded and decoded safely into reports
// and across the network connection when running in parallel
type ReportEntryValue struct {
raw any //unexported to prevent gob from freaking out about unregistered structs
raw interface{} //unexported to prevent gob from freaking out about unregistered structs
AsJSON string
Representation string
}
func WrapEntryValue(value any) ReportEntryValue {
func WrapEntryValue(value interface{}) ReportEntryValue {
return ReportEntryValue{
raw: value,
}
}
func (rev ReportEntryValue) GetRawValue() any {
func (rev ReportEntryValue) GetRawValue() interface{} {
return rev.raw
}
@@ -118,7 +118,7 @@ func (entry ReportEntry) StringRepresentation() string {
// If used from a rehydrated JSON file _or_ in a ReportAfterSuite when running in parallel this will be
// a JSON-decoded {}interface. If you want to reconstitute your original object you can decode the entry.Value.AsJSON
// field yourself.
func (entry ReportEntry) GetRawValue() any {
func (entry ReportEntry) GetRawValue() interface{} {
return entry.Value.GetRawValue()
}

View File

@@ -1,3 +1,3 @@
package types
const VERSION = "2.23.0"
const VERSION = "2.22.2"

View File

@@ -25,11 +25,6 @@ const debugHandshake = false
// quickly.
const chanSize = 16
// maxPendingPackets sets the maximum number of packets to queue while waiting
// for KEX to complete. This limits the total pending data to maxPendingPackets
// * maxPacket bytes, which is ~16.8MB.
const maxPendingPackets = 64
// keyingTransport is a packet based transport that supports key
// changes. It need not be thread-safe. It should pass through
// msgNewKeys in both directions.
@@ -78,19 +73,11 @@ type handshakeTransport struct {
incoming chan []byte
readError error
mu sync.Mutex
// Condition for the above mutex. It is used to notify a completed key
// exchange or a write failure. Writes can wait for this condition while a
// key exchange is in progress.
writeCond *sync.Cond
writeError error
sentInitPacket []byte
sentInitMsg *kexInitMsg
// Used to queue writes when a key exchange is in progress. The length is
// limited by pendingPacketsSize. Once full, writes will block until the key
// exchange is completed or an error occurs. If not empty, it is emptied
// all at once when the key exchange is completed in kexLoop.
pendingPackets [][]byte
mu sync.Mutex
writeError error
sentInitPacket []byte
sentInitMsg *kexInitMsg
pendingPackets [][]byte // Used when a key exchange is in progress.
writePacketsLeft uint32
writeBytesLeft int64
userAuthComplete bool // whether the user authentication phase is complete
@@ -147,7 +134,6 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
config: config,
}
t.writeCond = sync.NewCond(&t.mu)
t.resetReadThresholds()
t.resetWriteThresholds()
@@ -274,7 +260,6 @@ func (t *handshakeTransport) recordWriteError(err error) {
defer t.mu.Unlock()
if t.writeError == nil && err != nil {
t.writeError = err
t.writeCond.Broadcast()
}
}
@@ -378,8 +363,6 @@ write:
}
}
t.pendingPackets = t.pendingPackets[:0]
// Unblock writePacket if waiting for KEX.
t.writeCond.Broadcast()
t.mu.Unlock()
}
@@ -594,20 +577,11 @@ func (t *handshakeTransport) writePacket(p []byte) error {
}
if t.sentInitMsg != nil {
if len(t.pendingPackets) < maxPendingPackets {
// Copy the packet so the writer can reuse the buffer.
cp := make([]byte, len(p))
copy(cp, p)
t.pendingPackets = append(t.pendingPackets, cp)
return nil
}
for t.sentInitMsg != nil {
// Block and wait for KEX to complete or an error.
t.writeCond.Wait()
if t.writeError != nil {
return t.writeError
}
}
// Copy the packet so the writer can reuse the buffer.
cp := make([]byte, len(p))
copy(cp, p)
t.pendingPackets = append(t.pendingPackets, cp)
return nil
}
if t.writeBytesLeft > 0 {
@@ -624,7 +598,6 @@ func (t *handshakeTransport) writePacket(p []byte) error {
if err := t.pushPacket(p); err != nil {
t.writeError = err
t.writeCond.Broadcast()
}
return nil

View File

@@ -818,8 +818,6 @@ func decode(packet []byte) (interface{}, error) {
return new(userAuthSuccessMsg), nil
case msgUserAuthFailure:
msg = new(userAuthFailureMsg)
case msgUserAuthBanner:
msg = new(userAuthBannerMsg)
case msgUserAuthPubKeyOk:
msg = new(userAuthPubKeyOkMsg)
case msgGlobalRequest:

View File

@@ -459,7 +459,7 @@ func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel
return nil, err
}
go DiscardRequests(in)
return ch, nil
return ch, err
}
type tcpChan struct {

View File

@@ -59,7 +59,7 @@ func (c CanonType) Parse(s string) (t Tag, err error) {
if changed {
tt.RemakeString()
}
return makeTag(tt), nil
return makeTag(tt), err
}
// Compose creates a Tag from individual parts, which may be of type Tag, Base,

View File

@@ -36,9 +36,6 @@ package inspector
import (
"go/ast"
_ "unsafe"
"golang.org/x/tools/internal/astutil/edge"
)
// An Inspector provides methods for inspecting
@@ -47,24 +44,6 @@ type Inspector struct {
events []event
}
//go:linkname events
func events(in *Inspector) []event { return in.events }
func packEdgeKindAndIndex(ek edge.Kind, index int) int32 {
return int32(uint32(index+1)<<7 | uint32(ek))
}
// unpackEdgeKindAndIndex unpacks the edge kind and edge index (within
// an []ast.Node slice) from the parent field of a pop event.
//
//go:linkname unpackEdgeKindAndIndex
func unpackEdgeKindAndIndex(x int32) (edge.Kind, int) {
// The "parent" field of a pop node holds the
// edge Kind in the lower 7 bits and the index+1
// in the upper 25.
return edge.Kind(x & 0x7f), int(x>>7) - 1
}
// New returns an Inspector for the specified syntax trees.
func New(files []*ast.File) *Inspector {
return &Inspector{traverse(files)}
@@ -73,10 +52,9 @@ func New(files []*ast.File) *Inspector {
// An event represents a push or a pop
// of an ast.Node during a traversal.
type event struct {
node ast.Node
typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events
index int32 // index of corresponding push or pop event
parent int32 // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only)
node ast.Node
typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events
index int // index of corresponding push or pop event
}
// TODO: Experiment with storing only the second word of event.node (unsafe.Pointer).
@@ -105,7 +83,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// })
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
@@ -135,7 +113,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// matches an element of the types slice.
func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
@@ -169,7 +147,7 @@ func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proc
func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
mask := maskOf(types)
var stack []ast.Node
for i := int32(0); i < int32(len(in.events)); {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
@@ -211,74 +189,43 @@ func traverse(files []*ast.File) []event {
extent += int(f.End() - f.Pos())
}
// This estimate is based on the net/http package.
capacity := min(extent*33/100, 1e6) // impose some reasonable maximum (1M)
v := &visitor{
events: make([]event, 0, capacity),
stack: []item{{index: -1}}, // include an extra event so file nodes have a parent
capacity := extent * 33 / 100
if capacity > 1e6 {
capacity = 1e6 // impose some reasonable maximum
}
for _, file := range files {
walk(v, edge.Invalid, -1, file)
}
return v.events
}
events := make([]event, 0, capacity)
type visitor struct {
events []event
stack []item
}
var stack []event
stack = append(stack, event{}) // include an extra event so file nodes have a parent
for _, f := range files {
ast.Inspect(f, func(n ast.Node) bool {
if n != nil {
// push
ev := event{
node: n,
typ: 0, // temporarily used to accumulate type bits of subtree
index: len(events), // push event temporarily holds own index
}
stack = append(stack, ev)
events = append(events, ev)
} else {
// pop
top := len(stack) - 1
ev := stack[top]
typ := typeOf(ev.node)
push := ev.index
parent := top - 1
type item struct {
index int32 // index of current node's push event
parentIndex int32 // index of parent node's push event
typAccum uint64 // accumulated type bits of current node's descendents
edgeKindAndIndex int32 // edge.Kind and index, bit packed
}
events[push].typ = typ // set type of push
stack[parent].typ |= typ | ev.typ // parent's typ contains push and pop's typs.
events[push].index = len(events) // make push refer to pop
func (v *visitor) push(ek edge.Kind, eindex int, node ast.Node) {
var (
index = int32(len(v.events))
parentIndex = v.stack[len(v.stack)-1].index
)
v.events = append(v.events, event{
node: node,
parent: parentIndex,
typ: typeOf(node),
index: 0, // (pop index is set later by visitor.pop)
})
v.stack = append(v.stack, item{
index: index,
parentIndex: parentIndex,
edgeKindAndIndex: packEdgeKindAndIndex(ek, eindex),
})
// 2B nodes ought to be enough for anyone!
if int32(len(v.events)) < 0 {
panic("event index exceeded int32")
stack = stack[:top]
events = append(events, ev)
}
return true
})
}
// 32M elements in an []ast.Node ought to be enough for anyone!
if ek2, eindex2 := unpackEdgeKindAndIndex(packEdgeKindAndIndex(ek, eindex)); ek2 != ek || eindex2 != eindex {
panic("Node slice index exceeded uint25")
}
}
func (v *visitor) pop(node ast.Node) {
top := len(v.stack) - 1
current := v.stack[top]
push := &v.events[current.index]
parent := &v.stack[top-1]
push.index = int32(len(v.events)) // make push event refer to pop
parent.typAccum |= current.typAccum | push.typ // accumulate type bits into parent
v.stack = v.stack[:top]
v.events = append(v.events, event{
node: node,
typ: current.typAccum,
index: current.index,
parent: current.edgeKindAndIndex, // see [unpackEdgeKindAndIndex]
})
return events
}

View File

@@ -26,7 +26,7 @@ func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] {
return func(yield func(ast.Node) bool) {
mask := maskOf(types)
for i := int32(0); i < int32(len(in.events)); {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
@@ -63,7 +63,7 @@ func All[N interface {
mask := typeOf((N)(nil))
return func(yield func(N) bool) {
for i := int32(0); i < int32(len(in.events)); {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push

View File

@@ -12,8 +12,6 @@ package inspector
import (
"go/ast"
"math"
_ "unsafe"
)
const (
@@ -217,9 +215,8 @@ func typeOf(n ast.Node) uint64 {
return 0
}
//go:linkname maskOf
func maskOf(nodes []ast.Node) uint64 {
if len(nodes) == 0 {
if nodes == nil {
return math.MaxUint64 // match all node types
}
var mask uint64

View File

@@ -1,341 +0,0 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package inspector
// This file is a fork of ast.Inspect to reduce unnecessary dynamic
// calls and to gather edge information.
//
// Consistency with the original is ensured by TestInspectAllNodes.
import (
"fmt"
"go/ast"
"golang.org/x/tools/internal/astutil/edge"
)
func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) {
for i, node := range list {
walk(v, ek, i, node)
}
}
func walk(v *visitor, ek edge.Kind, index int, node ast.Node) {
v.push(ek, index, node)
// walk children
// (the order of the cases matches the order
// of the corresponding node types in ast.go)
switch n := node.(type) {
// Comments and fields
case *ast.Comment:
// nothing to do
case *ast.CommentGroup:
walkList(v, edge.CommentGroup_List, n.List)
case *ast.Field:
if n.Doc != nil {
walk(v, edge.Field_Doc, -1, n.Doc)
}
walkList(v, edge.Field_Names, n.Names)
if n.Type != nil {
walk(v, edge.Field_Type, -1, n.Type)
}
if n.Tag != nil {
walk(v, edge.Field_Tag, -1, n.Tag)
}
if n.Comment != nil {
walk(v, edge.Field_Comment, -1, n.Comment)
}
case *ast.FieldList:
walkList(v, edge.FieldList_List, n.List)
// Expressions
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
// nothing to do
case *ast.Ellipsis:
if n.Elt != nil {
walk(v, edge.Ellipsis_Elt, -1, n.Elt)
}
case *ast.FuncLit:
walk(v, edge.FuncLit_Type, -1, n.Type)
walk(v, edge.FuncLit_Body, -1, n.Body)
case *ast.CompositeLit:
if n.Type != nil {
walk(v, edge.CompositeLit_Type, -1, n.Type)
}
walkList(v, edge.CompositeLit_Elts, n.Elts)
case *ast.ParenExpr:
walk(v, edge.ParenExpr_X, -1, n.X)
case *ast.SelectorExpr:
walk(v, edge.SelectorExpr_X, -1, n.X)
walk(v, edge.SelectorExpr_Sel, -1, n.Sel)
case *ast.IndexExpr:
walk(v, edge.IndexExpr_X, -1, n.X)
walk(v, edge.IndexExpr_Index, -1, n.Index)
case *ast.IndexListExpr:
walk(v, edge.IndexListExpr_X, -1, n.X)
walkList(v, edge.IndexListExpr_Indices, n.Indices)
case *ast.SliceExpr:
walk(v, edge.SliceExpr_X, -1, n.X)
if n.Low != nil {
walk(v, edge.SliceExpr_Low, -1, n.Low)
}
if n.High != nil {
walk(v, edge.SliceExpr_High, -1, n.High)
}
if n.Max != nil {
walk(v, edge.SliceExpr_Max, -1, n.Max)
}
case *ast.TypeAssertExpr:
walk(v, edge.TypeAssertExpr_X, -1, n.X)
if n.Type != nil {
walk(v, edge.TypeAssertExpr_Type, -1, n.Type)
}
case *ast.CallExpr:
walk(v, edge.CallExpr_Fun, -1, n.Fun)
walkList(v, edge.CallExpr_Args, n.Args)
case *ast.StarExpr:
walk(v, edge.StarExpr_X, -1, n.X)
case *ast.UnaryExpr:
walk(v, edge.UnaryExpr_X, -1, n.X)
case *ast.BinaryExpr:
walk(v, edge.BinaryExpr_X, -1, n.X)
walk(v, edge.BinaryExpr_Y, -1, n.Y)
case *ast.KeyValueExpr:
walk(v, edge.KeyValueExpr_Key, -1, n.Key)
walk(v, edge.KeyValueExpr_Value, -1, n.Value)
// Types
case *ast.ArrayType:
if n.Len != nil {
walk(v, edge.ArrayType_Len, -1, n.Len)
}
walk(v, edge.ArrayType_Elt, -1, n.Elt)
case *ast.StructType:
walk(v, edge.StructType_Fields, -1, n.Fields)
case *ast.FuncType:
if n.TypeParams != nil {
walk(v, edge.FuncType_TypeParams, -1, n.TypeParams)
}
if n.Params != nil {
walk(v, edge.FuncType_Params, -1, n.Params)
}
if n.Results != nil {
walk(v, edge.FuncType_Results, -1, n.Results)
}
case *ast.InterfaceType:
walk(v, edge.InterfaceType_Methods, -1, n.Methods)
case *ast.MapType:
walk(v, edge.MapType_Key, -1, n.Key)
walk(v, edge.MapType_Value, -1, n.Value)
case *ast.ChanType:
walk(v, edge.ChanType_Value, -1, n.Value)
// Statements
case *ast.BadStmt:
// nothing to do
case *ast.DeclStmt:
walk(v, edge.DeclStmt_Decl, -1, n.Decl)
case *ast.EmptyStmt:
// nothing to do
case *ast.LabeledStmt:
walk(v, edge.LabeledStmt_Label, -1, n.Label)
walk(v, edge.LabeledStmt_Stmt, -1, n.Stmt)
case *ast.ExprStmt:
walk(v, edge.ExprStmt_X, -1, n.X)
case *ast.SendStmt:
walk(v, edge.SendStmt_Chan, -1, n.Chan)
walk(v, edge.SendStmt_Value, -1, n.Value)
case *ast.IncDecStmt:
walk(v, edge.IncDecStmt_X, -1, n.X)
case *ast.AssignStmt:
walkList(v, edge.AssignStmt_Lhs, n.Lhs)
walkList(v, edge.AssignStmt_Rhs, n.Rhs)
case *ast.GoStmt:
walk(v, edge.GoStmt_Call, -1, n.Call)
case *ast.DeferStmt:
walk(v, edge.DeferStmt_Call, -1, n.Call)
case *ast.ReturnStmt:
walkList(v, edge.ReturnStmt_Results, n.Results)
case *ast.BranchStmt:
if n.Label != nil {
walk(v, edge.BranchStmt_Label, -1, n.Label)
}
case *ast.BlockStmt:
walkList(v, edge.BlockStmt_List, n.List)
case *ast.IfStmt:
if n.Init != nil {
walk(v, edge.IfStmt_Init, -1, n.Init)
}
walk(v, edge.IfStmt_Cond, -1, n.Cond)
walk(v, edge.IfStmt_Body, -1, n.Body)
if n.Else != nil {
walk(v, edge.IfStmt_Else, -1, n.Else)
}
case *ast.CaseClause:
walkList(v, edge.CaseClause_List, n.List)
walkList(v, edge.CaseClause_Body, n.Body)
case *ast.SwitchStmt:
if n.Init != nil {
walk(v, edge.SwitchStmt_Init, -1, n.Init)
}
if n.Tag != nil {
walk(v, edge.SwitchStmt_Tag, -1, n.Tag)
}
walk(v, edge.SwitchStmt_Body, -1, n.Body)
case *ast.TypeSwitchStmt:
if n.Init != nil {
walk(v, edge.TypeSwitchStmt_Init, -1, n.Init)
}
walk(v, edge.TypeSwitchStmt_Assign, -1, n.Assign)
walk(v, edge.TypeSwitchStmt_Body, -1, n.Body)
case *ast.CommClause:
if n.Comm != nil {
walk(v, edge.CommClause_Comm, -1, n.Comm)
}
walkList(v, edge.CommClause_Body, n.Body)
case *ast.SelectStmt:
walk(v, edge.SelectStmt_Body, -1, n.Body)
case *ast.ForStmt:
if n.Init != nil {
walk(v, edge.ForStmt_Init, -1, n.Init)
}
if n.Cond != nil {
walk(v, edge.ForStmt_Cond, -1, n.Cond)
}
if n.Post != nil {
walk(v, edge.ForStmt_Post, -1, n.Post)
}
walk(v, edge.ForStmt_Body, -1, n.Body)
case *ast.RangeStmt:
if n.Key != nil {
walk(v, edge.RangeStmt_Key, -1, n.Key)
}
if n.Value != nil {
walk(v, edge.RangeStmt_Value, -1, n.Value)
}
walk(v, edge.RangeStmt_X, -1, n.X)
walk(v, edge.RangeStmt_Body, -1, n.Body)
// Declarations
case *ast.ImportSpec:
if n.Doc != nil {
walk(v, edge.ImportSpec_Doc, -1, n.Doc)
}
if n.Name != nil {
walk(v, edge.ImportSpec_Name, -1, n.Name)
}
walk(v, edge.ImportSpec_Path, -1, n.Path)
if n.Comment != nil {
walk(v, edge.ImportSpec_Comment, -1, n.Comment)
}
case *ast.ValueSpec:
if n.Doc != nil {
walk(v, edge.ValueSpec_Doc, -1, n.Doc)
}
walkList(v, edge.ValueSpec_Names, n.Names)
if n.Type != nil {
walk(v, edge.ValueSpec_Type, -1, n.Type)
}
walkList(v, edge.ValueSpec_Values, n.Values)
if n.Comment != nil {
walk(v, edge.ValueSpec_Comment, -1, n.Comment)
}
case *ast.TypeSpec:
if n.Doc != nil {
walk(v, edge.TypeSpec_Doc, -1, n.Doc)
}
walk(v, edge.TypeSpec_Name, -1, n.Name)
if n.TypeParams != nil {
walk(v, edge.TypeSpec_TypeParams, -1, n.TypeParams)
}
walk(v, edge.TypeSpec_Type, -1, n.Type)
if n.Comment != nil {
walk(v, edge.TypeSpec_Comment, -1, n.Comment)
}
case *ast.BadDecl:
// nothing to do
case *ast.GenDecl:
if n.Doc != nil {
walk(v, edge.GenDecl_Doc, -1, n.Doc)
}
walkList(v, edge.GenDecl_Specs, n.Specs)
case *ast.FuncDecl:
if n.Doc != nil {
walk(v, edge.FuncDecl_Doc, -1, n.Doc)
}
if n.Recv != nil {
walk(v, edge.FuncDecl_Recv, -1, n.Recv)
}
walk(v, edge.FuncDecl_Name, -1, n.Name)
walk(v, edge.FuncDecl_Type, -1, n.Type)
if n.Body != nil {
walk(v, edge.FuncDecl_Body, -1, n.Body)
}
case *ast.File:
if n.Doc != nil {
walk(v, edge.File_Doc, -1, n.Doc)
}
walk(v, edge.File_Name, -1, n.Name)
walkList(v, edge.File_Decls, n.Decls)
// don't walk n.Comments - they have been
// visited already through the individual
// nodes
default:
// (includes *ast.Package)
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
}
v.pop(node)
}

View File

@@ -1,295 +0,0 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package edge defines identifiers for each field of an ast.Node
// struct type that refers to another Node.
package edge
import (
"fmt"
"go/ast"
"reflect"
)
// A Kind describes a field of an ast.Node struct.
type Kind uint8
// String returns a description of the edge kind.
func (k Kind) String() string {
if k == Invalid {
return "<invalid>"
}
info := fieldInfos[k]
return fmt.Sprintf("%v.%s", info.nodeType.Elem().Name(), info.name)
}
// NodeType returns the pointer-to-struct type of the ast.Node implementation.
func (k Kind) NodeType() reflect.Type { return fieldInfos[k].nodeType }
// FieldName returns the name of the field.
func (k Kind) FieldName() string { return fieldInfos[k].name }
// FieldType returns the declared type of the field.
func (k Kind) FieldType() reflect.Type { return fieldInfos[k].fieldType }
// Get returns the direct child of n identified by (k, idx).
// n's type must match k.NodeType().
// idx must be a valid slice index, or -1 for a non-slice.
func (k Kind) Get(n ast.Node, idx int) ast.Node {
if k.NodeType() != reflect.TypeOf(n) {
panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n))
}
v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index)
if idx != -1 {
v = v.Index(idx) // asserts valid index
} else {
// (The type assertion below asserts that v is not a slice.)
}
return v.Interface().(ast.Node) // may be nil
}
const (
Invalid Kind = iota // for nodes at the root of the traversal
// Kinds are sorted alphabetically.
// Numbering is not stable.
// Each is named Type_Field, where Type is the
// ast.Node struct type and Field is the name of the field
ArrayType_Elt
ArrayType_Len
AssignStmt_Lhs
AssignStmt_Rhs
BinaryExpr_X
BinaryExpr_Y
BlockStmt_List
BranchStmt_Label
CallExpr_Args
CallExpr_Fun
CaseClause_Body
CaseClause_List
ChanType_Value
CommClause_Body
CommClause_Comm
CommentGroup_List
CompositeLit_Elts
CompositeLit_Type
DeclStmt_Decl
DeferStmt_Call
Ellipsis_Elt
ExprStmt_X
FieldList_List
Field_Comment
Field_Doc
Field_Names
Field_Tag
Field_Type
File_Decls
File_Doc
File_Name
ForStmt_Body
ForStmt_Cond
ForStmt_Init
ForStmt_Post
FuncDecl_Body
FuncDecl_Doc
FuncDecl_Name
FuncDecl_Recv
FuncDecl_Type
FuncLit_Body
FuncLit_Type
FuncType_Params
FuncType_Results
FuncType_TypeParams
GenDecl_Doc
GenDecl_Specs
GoStmt_Call
IfStmt_Body
IfStmt_Cond
IfStmt_Else
IfStmt_Init
ImportSpec_Comment
ImportSpec_Doc
ImportSpec_Name
ImportSpec_Path
IncDecStmt_X
IndexExpr_Index
IndexExpr_X
IndexListExpr_Indices
IndexListExpr_X
InterfaceType_Methods
KeyValueExpr_Key
KeyValueExpr_Value
LabeledStmt_Label
LabeledStmt_Stmt
MapType_Key
MapType_Value
ParenExpr_X
RangeStmt_Body
RangeStmt_Key
RangeStmt_Value
RangeStmt_X
ReturnStmt_Results
SelectStmt_Body
SelectorExpr_Sel
SelectorExpr_X
SendStmt_Chan
SendStmt_Value
SliceExpr_High
SliceExpr_Low
SliceExpr_Max
SliceExpr_X
StarExpr_X
StructType_Fields
SwitchStmt_Body
SwitchStmt_Init
SwitchStmt_Tag
TypeAssertExpr_Type
TypeAssertExpr_X
TypeSpec_Comment
TypeSpec_Doc
TypeSpec_Name
TypeSpec_Type
TypeSpec_TypeParams
TypeSwitchStmt_Assign
TypeSwitchStmt_Body
TypeSwitchStmt_Init
UnaryExpr_X
ValueSpec_Comment
ValueSpec_Doc
ValueSpec_Names
ValueSpec_Type
ValueSpec_Values
maxKind
)
// Assert that the encoding fits in 7 bits,
// as the inspector relies on this.
// (We are currently at 104.)
var _ = [1 << 7]struct{}{}[maxKind]
type fieldInfo struct {
nodeType reflect.Type // pointer-to-struct type of ast.Node implementation
name string
index int
fieldType reflect.Type
}
func info[N ast.Node](fieldName string) fieldInfo {
nodePtrType := reflect.TypeFor[N]()
f, ok := nodePtrType.Elem().FieldByName(fieldName)
if !ok {
panic(fieldName)
}
return fieldInfo{nodePtrType, fieldName, f.Index[0], f.Type}
}
var fieldInfos = [...]fieldInfo{
Invalid: {},
ArrayType_Elt: info[*ast.ArrayType]("Elt"),
ArrayType_Len: info[*ast.ArrayType]("Len"),
AssignStmt_Lhs: info[*ast.AssignStmt]("Lhs"),
AssignStmt_Rhs: info[*ast.AssignStmt]("Rhs"),
BinaryExpr_X: info[*ast.BinaryExpr]("X"),
BinaryExpr_Y: info[*ast.BinaryExpr]("Y"),
BlockStmt_List: info[*ast.BlockStmt]("List"),
BranchStmt_Label: info[*ast.BranchStmt]("Label"),
CallExpr_Args: info[*ast.CallExpr]("Args"),
CallExpr_Fun: info[*ast.CallExpr]("Fun"),
CaseClause_Body: info[*ast.CaseClause]("Body"),
CaseClause_List: info[*ast.CaseClause]("List"),
ChanType_Value: info[*ast.ChanType]("Value"),
CommClause_Body: info[*ast.CommClause]("Body"),
CommClause_Comm: info[*ast.CommClause]("Comm"),
CommentGroup_List: info[*ast.CommentGroup]("List"),
CompositeLit_Elts: info[*ast.CompositeLit]("Elts"),
CompositeLit_Type: info[*ast.CompositeLit]("Type"),
DeclStmt_Decl: info[*ast.DeclStmt]("Decl"),
DeferStmt_Call: info[*ast.DeferStmt]("Call"),
Ellipsis_Elt: info[*ast.Ellipsis]("Elt"),
ExprStmt_X: info[*ast.ExprStmt]("X"),
FieldList_List: info[*ast.FieldList]("List"),
Field_Comment: info[*ast.Field]("Comment"),
Field_Doc: info[*ast.Field]("Doc"),
Field_Names: info[*ast.Field]("Names"),
Field_Tag: info[*ast.Field]("Tag"),
Field_Type: info[*ast.Field]("Type"),
File_Decls: info[*ast.File]("Decls"),
File_Doc: info[*ast.File]("Doc"),
File_Name: info[*ast.File]("Name"),
ForStmt_Body: info[*ast.ForStmt]("Body"),
ForStmt_Cond: info[*ast.ForStmt]("Cond"),
ForStmt_Init: info[*ast.ForStmt]("Init"),
ForStmt_Post: info[*ast.ForStmt]("Post"),
FuncDecl_Body: info[*ast.FuncDecl]("Body"),
FuncDecl_Doc: info[*ast.FuncDecl]("Doc"),
FuncDecl_Name: info[*ast.FuncDecl]("Name"),
FuncDecl_Recv: info[*ast.FuncDecl]("Recv"),
FuncDecl_Type: info[*ast.FuncDecl]("Type"),
FuncLit_Body: info[*ast.FuncLit]("Body"),
FuncLit_Type: info[*ast.FuncLit]("Type"),
FuncType_Params: info[*ast.FuncType]("Params"),
FuncType_Results: info[*ast.FuncType]("Results"),
FuncType_TypeParams: info[*ast.FuncType]("TypeParams"),
GenDecl_Doc: info[*ast.GenDecl]("Doc"),
GenDecl_Specs: info[*ast.GenDecl]("Specs"),
GoStmt_Call: info[*ast.GoStmt]("Call"),
IfStmt_Body: info[*ast.IfStmt]("Body"),
IfStmt_Cond: info[*ast.IfStmt]("Cond"),
IfStmt_Else: info[*ast.IfStmt]("Else"),
IfStmt_Init: info[*ast.IfStmt]("Init"),
ImportSpec_Comment: info[*ast.ImportSpec]("Comment"),
ImportSpec_Doc: info[*ast.ImportSpec]("Doc"),
ImportSpec_Name: info[*ast.ImportSpec]("Name"),
ImportSpec_Path: info[*ast.ImportSpec]("Path"),
IncDecStmt_X: info[*ast.IncDecStmt]("X"),
IndexExpr_Index: info[*ast.IndexExpr]("Index"),
IndexExpr_X: info[*ast.IndexExpr]("X"),
IndexListExpr_Indices: info[*ast.IndexListExpr]("Indices"),
IndexListExpr_X: info[*ast.IndexListExpr]("X"),
InterfaceType_Methods: info[*ast.InterfaceType]("Methods"),
KeyValueExpr_Key: info[*ast.KeyValueExpr]("Key"),
KeyValueExpr_Value: info[*ast.KeyValueExpr]("Value"),
LabeledStmt_Label: info[*ast.LabeledStmt]("Label"),
LabeledStmt_Stmt: info[*ast.LabeledStmt]("Stmt"),
MapType_Key: info[*ast.MapType]("Key"),
MapType_Value: info[*ast.MapType]("Value"),
ParenExpr_X: info[*ast.ParenExpr]("X"),
RangeStmt_Body: info[*ast.RangeStmt]("Body"),
RangeStmt_Key: info[*ast.RangeStmt]("Key"),
RangeStmt_Value: info[*ast.RangeStmt]("Value"),
RangeStmt_X: info[*ast.RangeStmt]("X"),
ReturnStmt_Results: info[*ast.ReturnStmt]("Results"),
SelectStmt_Body: info[*ast.SelectStmt]("Body"),
SelectorExpr_Sel: info[*ast.SelectorExpr]("Sel"),
SelectorExpr_X: info[*ast.SelectorExpr]("X"),
SendStmt_Chan: info[*ast.SendStmt]("Chan"),
SendStmt_Value: info[*ast.SendStmt]("Value"),
SliceExpr_High: info[*ast.SliceExpr]("High"),
SliceExpr_Low: info[*ast.SliceExpr]("Low"),
SliceExpr_Max: info[*ast.SliceExpr]("Max"),
SliceExpr_X: info[*ast.SliceExpr]("X"),
StarExpr_X: info[*ast.StarExpr]("X"),
StructType_Fields: info[*ast.StructType]("Fields"),
SwitchStmt_Body: info[*ast.SwitchStmt]("Body"),
SwitchStmt_Init: info[*ast.SwitchStmt]("Init"),
SwitchStmt_Tag: info[*ast.SwitchStmt]("Tag"),
TypeAssertExpr_Type: info[*ast.TypeAssertExpr]("Type"),
TypeAssertExpr_X: info[*ast.TypeAssertExpr]("X"),
TypeSpec_Comment: info[*ast.TypeSpec]("Comment"),
TypeSpec_Doc: info[*ast.TypeSpec]("Doc"),
TypeSpec_Name: info[*ast.TypeSpec]("Name"),
TypeSpec_Type: info[*ast.TypeSpec]("Type"),
TypeSpec_TypeParams: info[*ast.TypeSpec]("TypeParams"),
TypeSwitchStmt_Assign: info[*ast.TypeSwitchStmt]("Assign"),
TypeSwitchStmt_Body: info[*ast.TypeSwitchStmt]("Body"),
TypeSwitchStmt_Init: info[*ast.TypeSwitchStmt]("Init"),
UnaryExpr_X: info[*ast.UnaryExpr]("X"),
ValueSpec_Comment: info[*ast.ValueSpec]("Comment"),
ValueSpec_Doc: info[*ast.ValueSpec]("Doc"),
ValueSpec_Names: info[*ast.ValueSpec]("Names"),
ValueSpec_Type: info[*ast.ValueSpec]("Type"),
ValueSpec_Values: info[*ast.ValueSpec]("Values"),
}

View File

@@ -15,8 +15,8 @@ github.com/google/go-cmp/cmp/internal/value
# github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad
## explicit; go 1.22
github.com/google/pprof/profile
# github.com/onsi/ginkgo/v2 v2.23.0
## explicit; go 1.23.0
# github.com/onsi/ginkgo/v2 v2.22.2
## explicit; go 1.22.0
github.com/onsi/ginkgo/v2
github.com/onsi/ginkgo/v2/config
github.com/onsi/ginkgo/v2/formatter
@@ -50,8 +50,8 @@ github.com/onsi/gomega/matchers/support/goraph/edge
github.com/onsi/gomega/matchers/support/goraph/node
github.com/onsi/gomega/matchers/support/goraph/util
github.com/onsi/gomega/types
# golang.org/x/crypto v0.36.0
## explicit; go 1.23.0
# golang.org/x/crypto v0.33.0
## explicit; go 1.20
golang.org/x/crypto/blowfish
golang.org/x/crypto/chacha20
golang.org/x/crypto/curve25519
@@ -59,17 +59,17 @@ golang.org/x/crypto/internal/alias
golang.org/x/crypto/internal/poly1305
golang.org/x/crypto/ssh
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
# golang.org/x/net v0.35.0
# golang.org/x/net v0.33.0
## explicit; go 1.18
golang.org/x/net/html
golang.org/x/net/html/atom
golang.org/x/net/html/charset
# golang.org/x/sys v0.31.0
## explicit; go 1.23.0
# golang.org/x/sys v0.30.0
## explicit; go 1.18
golang.org/x/sys/cpu
golang.org/x/sys/unix
# golang.org/x/text v0.23.0
## explicit; go 1.23.0
# golang.org/x/text v0.22.0
## explicit; go 1.18
golang.org/x/text/encoding
golang.org/x/text/encoding/charmap
golang.org/x/text/encoding/htmlindex
@@ -87,11 +87,10 @@ golang.org/x/text/internal/utf8internal
golang.org/x/text/language
golang.org/x/text/runes
golang.org/x/text/transform
# golang.org/x/tools v0.30.0
# golang.org/x/tools v0.28.0
## explicit; go 1.22.0
golang.org/x/tools/cover
golang.org/x/tools/go/ast/inspector
golang.org/x/tools/internal/astutil/edge
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3

View File

@@ -1,380 +0,0 @@
/*
* Copyright (c) 2024, 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 dgxa100
import (
"fmt"
"sync"
"github.com/NVIDIA/go-nvml/pkg/nvml"
"github.com/NVIDIA/go-nvml/pkg/nvml/mock"
"github.com/google/uuid"
)
type Server struct {
mock.Interface
mock.ExtendedInterface
Devices [8]nvml.Device
DriverVersion string
NvmlVersion string
CudaDriverVersion int
}
type Device struct {
mock.Device
sync.RWMutex
UUID string
Name string
Brand nvml.BrandType
Architecture nvml.DeviceArchitecture
PciBusID string
Minor int
Index int
CudaComputeCapability CudaComputeCapability
MigMode int
GpuInstances map[*GpuInstance]struct{}
GpuInstanceCounter uint32
MemoryInfo nvml.Memory
}
type GpuInstance struct {
mock.GpuInstance
sync.RWMutex
Info nvml.GpuInstanceInfo
ComputeInstances map[*ComputeInstance]struct{}
ComputeInstanceCounter uint32
}
type ComputeInstance struct {
mock.ComputeInstance
Info nvml.ComputeInstanceInfo
}
type CudaComputeCapability struct {
Major int
Minor int
}
var _ nvml.Interface = (*Server)(nil)
var _ nvml.Device = (*Device)(nil)
var _ nvml.GpuInstance = (*GpuInstance)(nil)
var _ nvml.ComputeInstance = (*ComputeInstance)(nil)
func New() *Server {
server := &Server{
Devices: [8]nvml.Device{
NewDevice(0),
NewDevice(1),
NewDevice(2),
NewDevice(3),
NewDevice(4),
NewDevice(5),
NewDevice(6),
NewDevice(7),
},
DriverVersion: "550.54.15",
NvmlVersion: "12.550.54.15",
CudaDriverVersion: 12040,
}
server.setMockFuncs()
return server
}
func NewDevice(index int) *Device {
device := &Device{
UUID: "GPU-" + uuid.New().String(),
Name: "Mock NVIDIA A100-SXM4-40GB",
Brand: nvml.BRAND_NVIDIA,
Architecture: nvml.DEVICE_ARCH_AMPERE,
PciBusID: fmt.Sprintf("0000:%02x:00.0", index),
Minor: index,
Index: index,
CudaComputeCapability: CudaComputeCapability{
Major: 8,
Minor: 0,
},
GpuInstances: make(map[*GpuInstance]struct{}),
GpuInstanceCounter: 0,
MemoryInfo: nvml.Memory{42949672960, 0, 0},
}
device.setMockFuncs()
return device
}
func NewGpuInstance(info nvml.GpuInstanceInfo) *GpuInstance {
gi := &GpuInstance{
Info: info,
ComputeInstances: make(map[*ComputeInstance]struct{}),
ComputeInstanceCounter: 0,
}
gi.setMockFuncs()
return gi
}
func NewComputeInstance(info nvml.ComputeInstanceInfo) *ComputeInstance {
ci := &ComputeInstance{
Info: info,
}
ci.setMockFuncs()
return ci
}
func (s *Server) setMockFuncs() {
s.ExtensionsFunc = func() nvml.ExtendedInterface {
return s
}
s.LookupSymbolFunc = func(symbol string) error {
return nil
}
s.InitFunc = func() nvml.Return {
return nvml.SUCCESS
}
s.ShutdownFunc = func() nvml.Return {
return nvml.SUCCESS
}
s.SystemGetDriverVersionFunc = func() (string, nvml.Return) {
return s.DriverVersion, nvml.SUCCESS
}
s.SystemGetNVMLVersionFunc = func() (string, nvml.Return) {
return s.NvmlVersion, nvml.SUCCESS
}
s.SystemGetCudaDriverVersionFunc = func() (int, nvml.Return) {
return s.CudaDriverVersion, nvml.SUCCESS
}
s.DeviceGetCountFunc = func() (int, nvml.Return) {
return len(s.Devices), nvml.SUCCESS
}
s.DeviceGetHandleByIndexFunc = func(index int) (nvml.Device, nvml.Return) {
if index < 0 || index >= len(s.Devices) {
return nil, nvml.ERROR_INVALID_ARGUMENT
}
return s.Devices[index], nvml.SUCCESS
}
s.DeviceGetHandleByUUIDFunc = func(uuid string) (nvml.Device, nvml.Return) {
for _, d := range s.Devices {
if uuid == d.(*Device).UUID {
return d, nvml.SUCCESS
}
}
return nil, nvml.ERROR_INVALID_ARGUMENT
}
s.DeviceGetHandleByPciBusIdFunc = func(busID string) (nvml.Device, nvml.Return) {
for _, d := range s.Devices {
if busID == d.(*Device).PciBusID {
return d, nvml.SUCCESS
}
}
return nil, nvml.ERROR_INVALID_ARGUMENT
}
}
func (d *Device) setMockFuncs() {
d.GetMinorNumberFunc = func() (int, nvml.Return) {
return d.Minor, nvml.SUCCESS
}
d.GetIndexFunc = func() (int, nvml.Return) {
return d.Index, nvml.SUCCESS
}
d.GetCudaComputeCapabilityFunc = func() (int, int, nvml.Return) {
return d.CudaComputeCapability.Major, d.CudaComputeCapability.Minor, nvml.SUCCESS
}
d.GetUUIDFunc = func() (string, nvml.Return) {
return d.UUID, nvml.SUCCESS
}
d.GetNameFunc = func() (string, nvml.Return) {
return d.Name, nvml.SUCCESS
}
d.GetBrandFunc = func() (nvml.BrandType, nvml.Return) {
return d.Brand, nvml.SUCCESS
}
d.GetArchitectureFunc = func() (nvml.DeviceArchitecture, nvml.Return) {
return d.Architecture, nvml.SUCCESS
}
d.GetMemoryInfoFunc = func() (nvml.Memory, nvml.Return) {
return d.MemoryInfo, nvml.SUCCESS
}
d.GetPciInfoFunc = func() (nvml.PciInfo, nvml.Return) {
p := nvml.PciInfo{
PciDeviceId: 0x20B010DE,
}
return p, nvml.SUCCESS
}
d.SetMigModeFunc = func(mode int) (nvml.Return, nvml.Return) {
d.MigMode = mode
return nvml.SUCCESS, nvml.SUCCESS
}
d.GetMigModeFunc = func() (int, int, nvml.Return) {
return d.MigMode, d.MigMode, nvml.SUCCESS
}
d.GetGpuInstanceProfileInfoFunc = func(giProfileId int) (nvml.GpuInstanceProfileInfo, nvml.Return) {
if giProfileId < 0 || giProfileId >= nvml.GPU_INSTANCE_PROFILE_COUNT {
return nvml.GpuInstanceProfileInfo{}, nvml.ERROR_INVALID_ARGUMENT
}
if _, exists := MIGProfiles.GpuInstanceProfiles[giProfileId]; !exists {
return nvml.GpuInstanceProfileInfo{}, nvml.ERROR_NOT_SUPPORTED
}
return MIGProfiles.GpuInstanceProfiles[giProfileId], nvml.SUCCESS
}
d.GetGpuInstancePossiblePlacementsFunc = func(info *nvml.GpuInstanceProfileInfo) ([]nvml.GpuInstancePlacement, nvml.Return) {
return MIGPlacements.GpuInstancePossiblePlacements[int(info.Id)], nvml.SUCCESS
}
d.CreateGpuInstanceFunc = func(info *nvml.GpuInstanceProfileInfo) (nvml.GpuInstance, nvml.Return) {
d.Lock()
defer d.Unlock()
giInfo := nvml.GpuInstanceInfo{
Device: d,
Id: d.GpuInstanceCounter,
ProfileId: info.Id,
}
d.GpuInstanceCounter++
gi := NewGpuInstance(giInfo)
d.GpuInstances[gi] = struct{}{}
return gi, nvml.SUCCESS
}
d.CreateGpuInstanceWithPlacementFunc = func(info *nvml.GpuInstanceProfileInfo, placement *nvml.GpuInstancePlacement) (nvml.GpuInstance, nvml.Return) {
d.Lock()
defer d.Unlock()
giInfo := nvml.GpuInstanceInfo{
Device: d,
Id: d.GpuInstanceCounter,
ProfileId: info.Id,
Placement: *placement,
}
d.GpuInstanceCounter++
gi := NewGpuInstance(giInfo)
d.GpuInstances[gi] = struct{}{}
return gi, nvml.SUCCESS
}
d.GetGpuInstancesFunc = func(info *nvml.GpuInstanceProfileInfo) ([]nvml.GpuInstance, nvml.Return) {
d.RLock()
defer d.RUnlock()
var gis []nvml.GpuInstance
for gi := range d.GpuInstances {
if gi.Info.ProfileId == info.Id {
gis = append(gis, gi)
}
}
return gis, nvml.SUCCESS
}
}
func (gi *GpuInstance) setMockFuncs() {
gi.GetInfoFunc = func() (nvml.GpuInstanceInfo, nvml.Return) {
return gi.Info, nvml.SUCCESS
}
gi.GetComputeInstanceProfileInfoFunc = func(ciProfileId int, ciEngProfileId int) (nvml.ComputeInstanceProfileInfo, nvml.Return) {
if ciProfileId < 0 || ciProfileId >= nvml.COMPUTE_INSTANCE_PROFILE_COUNT {
return nvml.ComputeInstanceProfileInfo{}, nvml.ERROR_INVALID_ARGUMENT
}
if ciEngProfileId != nvml.COMPUTE_INSTANCE_ENGINE_PROFILE_SHARED {
return nvml.ComputeInstanceProfileInfo{}, nvml.ERROR_NOT_SUPPORTED
}
giProfileId := int(gi.Info.ProfileId)
if _, exists := MIGProfiles.ComputeInstanceProfiles[giProfileId]; !exists {
return nvml.ComputeInstanceProfileInfo{}, nvml.ERROR_NOT_SUPPORTED
}
if _, exists := MIGProfiles.ComputeInstanceProfiles[giProfileId][ciProfileId]; !exists {
return nvml.ComputeInstanceProfileInfo{}, nvml.ERROR_NOT_SUPPORTED
}
return MIGProfiles.ComputeInstanceProfiles[giProfileId][ciProfileId], nvml.SUCCESS
}
gi.GetComputeInstancePossiblePlacementsFunc = func(info *nvml.ComputeInstanceProfileInfo) ([]nvml.ComputeInstancePlacement, nvml.Return) {
return MIGPlacements.ComputeInstancePossiblePlacements[int(gi.Info.Id)][int(info.Id)], nvml.SUCCESS
}
gi.CreateComputeInstanceFunc = func(info *nvml.ComputeInstanceProfileInfo) (nvml.ComputeInstance, nvml.Return) {
gi.Lock()
defer gi.Unlock()
ciInfo := nvml.ComputeInstanceInfo{
Device: gi.Info.Device,
GpuInstance: gi,
Id: gi.ComputeInstanceCounter,
ProfileId: info.Id,
}
gi.ComputeInstanceCounter++
ci := NewComputeInstance(ciInfo)
gi.ComputeInstances[ci] = struct{}{}
return ci, nvml.SUCCESS
}
gi.GetComputeInstancesFunc = func(info *nvml.ComputeInstanceProfileInfo) ([]nvml.ComputeInstance, nvml.Return) {
gi.RLock()
defer gi.RUnlock()
var cis []nvml.ComputeInstance
for ci := range gi.ComputeInstances {
if ci.Info.ProfileId == info.Id {
cis = append(cis, ci)
}
}
return cis, nvml.SUCCESS
}
gi.DestroyFunc = func() nvml.Return {
d := gi.Info.Device.(*Device)
d.Lock()
defer d.Unlock()
delete(d.GpuInstances, gi)
return nvml.SUCCESS
}
}
func (ci *ComputeInstance) setMockFuncs() {
ci.GetInfoFunc = func() (nvml.ComputeInstanceInfo, nvml.Return) {
return ci.Info, nvml.SUCCESS
}
ci.DestroyFunc = func() nvml.Return {
gi := ci.Info.GpuInstance.(*GpuInstance)
gi.Lock()
defer gi.Unlock()
delete(gi.ComputeInstances, ci)
return nvml.SUCCESS
}
}

View File

@@ -1,471 +0,0 @@
/*
* Copyright (c) 2024, 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 dgxa100
import (
"github.com/NVIDIA/go-nvml/pkg/nvml"
)
// MIGProfiles holds the profile information for GIs and CIs in this mock server.
// We should consider auto-generating this object in the future.
var MIGProfiles = struct {
GpuInstanceProfiles map[int]nvml.GpuInstanceProfileInfo
ComputeInstanceProfiles map[int]map[int]nvml.ComputeInstanceProfileInfo
}{
GpuInstanceProfiles: map[int]nvml.GpuInstanceProfileInfo{
nvml.GPU_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.GPU_INSTANCE_PROFILE_1_SLICE,
IsP2pSupported: 0,
SliceCount: 1,
InstanceCount: 7,
MultiprocessorCount: 14,
CopyEngineCount: 1,
DecoderCount: 0,
EncoderCount: 0,
JpegCount: 0,
OfaCount: 0,
MemorySizeMB: 4864,
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV1: {
Id: nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV1,
IsP2pSupported: 0,
SliceCount: 1,
InstanceCount: 1,
MultiprocessorCount: 14,
CopyEngineCount: 1,
DecoderCount: 1,
EncoderCount: 0,
JpegCount: 1,
OfaCount: 1,
MemorySizeMB: 4864,
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV2: {
Id: nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV2,
IsP2pSupported: 0,
SliceCount: 1,
InstanceCount: 4,
MultiprocessorCount: 14,
CopyEngineCount: 1,
DecoderCount: 1,
EncoderCount: 0,
JpegCount: 0,
OfaCount: 0,
MemorySizeMB: 9856,
},
nvml.GPU_INSTANCE_PROFILE_2_SLICE: {
Id: nvml.GPU_INSTANCE_PROFILE_2_SLICE,
IsP2pSupported: 0,
SliceCount: 2,
InstanceCount: 3,
MultiprocessorCount: 28,
CopyEngineCount: 2,
DecoderCount: 1,
EncoderCount: 0,
JpegCount: 0,
OfaCount: 0,
MemorySizeMB: 9856,
},
nvml.GPU_INSTANCE_PROFILE_3_SLICE: {
Id: nvml.GPU_INSTANCE_PROFILE_3_SLICE,
IsP2pSupported: 0,
SliceCount: 3,
InstanceCount: 2,
MultiprocessorCount: 42,
CopyEngineCount: 3,
DecoderCount: 2,
EncoderCount: 0,
JpegCount: 0,
OfaCount: 0,
MemorySizeMB: 19968,
},
nvml.GPU_INSTANCE_PROFILE_4_SLICE: {
Id: nvml.GPU_INSTANCE_PROFILE_4_SLICE,
IsP2pSupported: 0,
SliceCount: 4,
InstanceCount: 1,
MultiprocessorCount: 56,
CopyEngineCount: 4,
DecoderCount: 2,
EncoderCount: 0,
JpegCount: 0,
OfaCount: 0,
MemorySizeMB: 19968,
},
nvml.GPU_INSTANCE_PROFILE_7_SLICE: {
Id: nvml.GPU_INSTANCE_PROFILE_7_SLICE,
IsP2pSupported: 0,
SliceCount: 7,
InstanceCount: 1,
MultiprocessorCount: 98,
CopyEngineCount: 7,
DecoderCount: 5,
EncoderCount: 0,
JpegCount: 1,
OfaCount: 1,
MemorySizeMB: 40192,
},
},
ComputeInstanceProfiles: map[int]map[int]nvml.ComputeInstanceProfileInfo{
nvml.GPU_INSTANCE_PROFILE_1_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 1,
MultiprocessorCount: 14,
SharedCopyEngineCount: 1,
SharedDecoderCount: 0,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV1: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 1,
MultiprocessorCount: 14,
SharedCopyEngineCount: 1,
SharedDecoderCount: 1,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV2: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 1,
MultiprocessorCount: 14,
SharedCopyEngineCount: 1,
SharedDecoderCount: 1,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
},
nvml.GPU_INSTANCE_PROFILE_2_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 2,
MultiprocessorCount: 14,
SharedCopyEngineCount: 2,
SharedDecoderCount: 1,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE,
SliceCount: 2,
InstanceCount: 1,
MultiprocessorCount: 28,
SharedCopyEngineCount: 2,
SharedDecoderCount: 1,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
},
nvml.GPU_INSTANCE_PROFILE_3_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 3,
MultiprocessorCount: 14,
SharedCopyEngineCount: 3,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE,
SliceCount: 2,
InstanceCount: 1,
MultiprocessorCount: 28,
SharedCopyEngineCount: 3,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE,
SliceCount: 3,
InstanceCount: 1,
MultiprocessorCount: 42,
SharedCopyEngineCount: 3,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
},
nvml.GPU_INSTANCE_PROFILE_4_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 4,
MultiprocessorCount: 14,
SharedCopyEngineCount: 4,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE,
SliceCount: 2,
InstanceCount: 2,
MultiprocessorCount: 28,
SharedCopyEngineCount: 4,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE,
SliceCount: 4,
InstanceCount: 1,
MultiprocessorCount: 56,
SharedCopyEngineCount: 4,
SharedDecoderCount: 2,
SharedEncoderCount: 0,
SharedJpegCount: 0,
SharedOfaCount: 0,
},
},
nvml.GPU_INSTANCE_PROFILE_7_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE,
SliceCount: 1,
InstanceCount: 7,
MultiprocessorCount: 14,
SharedCopyEngineCount: 7,
SharedDecoderCount: 5,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE,
SliceCount: 2,
InstanceCount: 3,
MultiprocessorCount: 28,
SharedCopyEngineCount: 7,
SharedDecoderCount: 5,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE,
SliceCount: 3,
InstanceCount: 2,
MultiprocessorCount: 42,
SharedCopyEngineCount: 7,
SharedDecoderCount: 5,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE,
SliceCount: 4,
InstanceCount: 1,
MultiprocessorCount: 56,
SharedCopyEngineCount: 7,
SharedDecoderCount: 5,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
nvml.COMPUTE_INSTANCE_PROFILE_7_SLICE: {
Id: nvml.COMPUTE_INSTANCE_PROFILE_7_SLICE,
SliceCount: 7,
InstanceCount: 1,
MultiprocessorCount: 98,
SharedCopyEngineCount: 7,
SharedDecoderCount: 5,
SharedEncoderCount: 0,
SharedJpegCount: 1,
SharedOfaCount: 1,
},
},
},
}
// MIGPlacements holds the placement information for GIs and CIs in this mock server.
// We should consider auto-generating this object in the future.
var MIGPlacements = struct {
GpuInstancePossiblePlacements map[int][]nvml.GpuInstancePlacement
ComputeInstancePossiblePlacements map[int]map[int][]nvml.ComputeInstancePlacement
}{
GpuInstancePossiblePlacements: map[int][]nvml.GpuInstancePlacement{
nvml.GPU_INSTANCE_PROFILE_1_SLICE: {
{
Start: 0,
Size: 1,
},
{
Start: 1,
Size: 1,
},
{
Start: 2,
Size: 1,
},
{
Start: 3,
Size: 1,
},
{
Start: 4,
Size: 1,
},
{
Start: 5,
Size: 1,
},
{
Start: 6,
Size: 1,
},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV1: {
{
Start: 0,
Size: 1,
},
{
Start: 1,
Size: 1,
},
{
Start: 2,
Size: 1,
},
{
Start: 3,
Size: 1,
},
{
Start: 4,
Size: 1,
},
{
Start: 5,
Size: 1,
},
{
Start: 6,
Size: 1,
},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV2: {
{
Start: 0,
Size: 2,
},
{
Start: 2,
Size: 2,
},
{
Start: 4,
Size: 2,
},
{
Start: 6,
Size: 2,
},
},
nvml.GPU_INSTANCE_PROFILE_2_SLICE: {
{
Start: 0,
Size: 2,
},
{
Start: 2,
Size: 2,
},
{
Start: 4,
Size: 2,
},
},
nvml.GPU_INSTANCE_PROFILE_3_SLICE: {
{
Start: 0,
Size: 4,
},
{
Start: 4,
Size: 4,
},
},
nvml.GPU_INSTANCE_PROFILE_4_SLICE: {
{
Start: 0,
Size: 4,
},
},
nvml.GPU_INSTANCE_PROFILE_7_SLICE: {
{
Start: 0,
Size: 8,
},
},
},
// TODO: Fill out ComputeInstancePossiblePlacements
ComputeInstancePossiblePlacements: map[int]map[int][]nvml.ComputeInstancePlacement{
nvml.GPU_INSTANCE_PROFILE_1_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV1: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_1_SLICE_REV2: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_2_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_3_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_4_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE: {},
},
nvml.GPU_INSTANCE_PROFILE_7_SLICE: {
nvml.COMPUTE_INSTANCE_PROFILE_1_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_2_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_3_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_4_SLICE: {},
nvml.COMPUTE_INSTANCE_PROFILE_7_SLICE: {},
},
},
}

View File

@@ -1,256 +0,0 @@
# Changelog #
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] ##
## [0.4.1] - 2025-01-28 ##
### Fixed ###
- The restrictions added for `root` paths passed to `SecureJoin` in 0.4.0 was
found to be too strict and caused some regressions when folks tried to
update, so this restriction has been relaxed to only return an error if the
path contains a `..` component. We still recommend users use `filepath.Clean`
(and even `filepath.EvalSymlinks`) on the `root` path they are using, but at
least you will no longer be punished for "trivial" unclean paths.
## [0.4.0] - 2025-01-13 ##
### Breaking ####
- `SecureJoin(VFS)` will now return an error if the provided `root` is not a
`filepath.Clean`'d path.
While it is ultimately the responsibility of the caller to ensure the root is
a safe path to use, passing a path like `/symlink/..` as a root would result
in the `SecureJoin`'d path being placed in `/` even though `/symlink/..`
might be a different directory, and so we should more strongly discourage
such usage.
All major users of `securejoin.SecureJoin` already ensure that the paths they
provide are safe (and this is ultimately a question of user error), but
removing this foot-gun is probably a good idea. Of course, this is
necessarily a breaking API change (though we expect no real users to be
affected by it).
Thanks to [Erik Sjölund](https://github.com/eriksjolund), who initially
reported this issue as a possible security issue.
- `MkdirAll` and `MkdirHandle` now take an `os.FileMode`-style mode argument
instead of a raw `unix.S_*`-style mode argument, which may cause compile-time
type errors depending on how you use `filepath-securejoin`. For most users,
there will be no change in behaviour aside from the type change (as the
bottom `0o777` bits are the same in both formats, and most users are probably
only using those bits).
However, if you were using `unix.S_ISVTX` to set the sticky bit with
`MkdirAll(Handle)` you will need to switch to `os.ModeSticky` otherwise you
will get a runtime error with this update. In addition, the error message you
will get from passing `unix.S_ISUID` and `unix.S_ISGID` will be different as
they are treated as invalid bits now (note that previously passing said bits
was also an error).
## [0.3.6] - 2024-12-17 ##
### Compatibility ###
- The minimum Go version requirement for `filepath-securejoin` is now Go 1.18
(we use generics internally).
For reference, `filepath-securejoin@v0.3.0` somewhat-arbitrarily bumped the
Go version requirement to 1.21.
While we did make some use of Go 1.21 stdlib features (and in principle Go
versions <= 1.21 are no longer even supported by upstream anymore), some
downstreams have complained that the version bump has meant that they have to
do workarounds when backporting fixes that use the new `filepath-securejoin`
API onto old branches. This is not an ideal situation, but since using this
library is probably better for most downstreams than a hand-rolled
workaround, we now have compatibility shims that allow us to build on older
Go versions.
- Lower minimum version requirement for `golang.org/x/sys` to `v0.18.0` (we
need the wrappers for `fsconfig(2)`), which should also make backporting
patches to older branches easier.
## [0.3.5] - 2024-12-06 ##
### Fixed ###
- `MkdirAll` will now no longer return an `EEXIST` error if two racing
processes are creating the same directory. We will still verify that the path
is a directory, but this will avoid spurious errors when multiple threads or
programs are trying to `MkdirAll` the same path. opencontainers/runc#4543
## [0.3.4] - 2024-10-09 ##
### Fixed ###
- Previously, some testing mocks we had resulted in us doing `import "testing"`
in non-`_test.go` code, which made some downstreams like Kubernetes unhappy.
This has been fixed. (#32)
## [0.3.3] - 2024-09-30 ##
### Fixed ###
- The mode and owner verification logic in `MkdirAll` has been removed. This
was originally intended to protect against some theoretical attacks but upon
further consideration these protections don't actually buy us anything and
they were causing spurious errors with more complicated filesystem setups.
- The "is the created directory empty" logic in `MkdirAll` has also been
removed. This was not causing us issues yet, but some pseudofilesystems (such
as `cgroup`) create non-empty directories and so this logic would've been
wrong for such cases.
## [0.3.2] - 2024-09-13 ##
### Changed ###
- Passing the `S_ISUID` or `S_ISGID` modes to `MkdirAllInRoot` will now return
an explicit error saying that those bits are ignored by `mkdirat(2)`. In the
past a different error was returned, but since the silent ignoring behaviour
is codified in the man pages a more explicit error seems apt. While silently
ignoring these bits would be the most compatible option, it could lead to
users thinking their code sets these bits when it doesn't. Programs that need
to deal with compatibility can mask the bits themselves. (#23, #25)
### Fixed ###
- If a directory has `S_ISGID` set, then all child directories will have
`S_ISGID` set when created and a different gid will be used for any inode
created under the directory. Previously, the "expected owner and mode"
validation in `securejoin.MkdirAll` did not correctly handle this. We now
correctly handle this case. (#24, #25)
## [0.3.1] - 2024-07-23 ##
### Changed ###
- By allowing `Open(at)InRoot` to opt-out of the extra work done by `MkdirAll`
to do the necessary "partial lookups", `Open(at)InRoot` now does less work
for both implementations (resulting in a many-fold decrease in the number of
operations for `openat2`, and a modest improvement for non-`openat2`) and is
far more guaranteed to match the correct `openat2(RESOLVE_IN_ROOT)`
behaviour.
- We now use `readlinkat(fd, "")` where possible. For `Open(at)InRoot` this
effectively just means that we no longer risk getting spurious errors during
rename races. However, for our hardened procfs handler, this in theory should
prevent mount attacks from tricking us when doing magic-link readlinks (even
when using the unsafe host `/proc` handle). Unfortunately `Reopen` is still
potentially vulnerable to those kinds of somewhat-esoteric attacks.
Technically this [will only work on post-2.6.39 kernels][linux-readlinkat-emptypath]
but it seems incredibly unlikely anyone is using `filepath-securejoin` on a
pre-2011 kernel.
### Fixed ###
- Several improvements were made to the errors returned by `Open(at)InRoot` and
`MkdirAll` when dealing with invalid paths under the emulated (ie.
non-`openat2`) implementation. Previously, some paths would return the wrong
error (`ENOENT` when the last component was a non-directory), and other paths
would be returned as though they were acceptable (trailing-slash components
after a non-directory would be ignored by `Open(at)InRoot`).
These changes were done to match `openat2`'s behaviour and purely is a
consistency fix (most users are going to be using `openat2` anyway).
[linux-readlinkat-emptypath]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=65cfc6722361570bfe255698d9cd4dccaf47570d
## [0.3.0] - 2024-07-11 ##
### Added ###
- A new set of `*os.File`-based APIs have been added. These are adapted from
[libpathrs][] and we strongly suggest using them if possible (as they provide
far more protection against attacks than `SecureJoin`):
- `Open(at)InRoot` resolves a path inside a rootfs and returns an `*os.File`
handle to the path. Note that the handle returned is an `O_PATH` handle,
which cannot be used for reading or writing (as well as some other
operations -- [see open(2) for more details][open.2])
- `Reopen` takes an `O_PATH` file handle and safely re-opens it to upgrade
it to a regular handle. This can also be used with non-`O_PATH` handles,
but `O_PATH` is the most obvious application.
- `MkdirAll` is an implementation of `os.MkdirAll` that is safe to use to
create a directory tree within a rootfs.
As these are new APIs, they may change in the future. However, they should be
safe to start migrating to as we have extensive tests ensuring they behave
correctly and are safe against various races and other attacks.
[libpathrs]: https://github.com/openSUSE/libpathrs
[open.2]: https://www.man7.org/linux/man-pages/man2/open.2.html
## [0.2.5] - 2024-05-03 ##
### Changed ###
- Some minor changes were made to how lexical components (like `..` and `.`)
are handled during path generation in `SecureJoin`. There is no behaviour
change as a result of this fix (the resulting paths are the same).
### Fixed ###
- The error returned when we hit a symlink loop now references the correct
path. (#10)
## [0.2.4] - 2023-09-06 ##
### Security ###
- This release fixes a potential security issue in filepath-securejoin when
used on Windows ([GHSA-6xv5-86q9-7xr8][], which could be used to generate
paths outside of the provided rootfs in certain cases), as well as improving
the overall behaviour of filepath-securejoin when dealing with Windows paths
that contain volume names. Thanks to Paulo Gomes for discovering and fixing
these issues.
### Fixed ###
- Switch to GitHub Actions for CI so we can test on Windows as well as Linux
and MacOS.
[GHSA-6xv5-86q9-7xr8]: https://github.com/advisories/GHSA-6xv5-86q9-7xr8
## [0.2.3] - 2021-06-04 ##
### Changed ###
- Switch to Go 1.13-style `%w` error wrapping, letting us drop the dependency
on `github.com/pkg/errors`.
## [0.2.2] - 2018-09-05 ##
### Changed ###
- Use `syscall.ELOOP` as the base error for symlink loops, rather than our own
(internal) error. This allows callers to more easily use `errors.Is` to check
for this case.
## [0.2.1] - 2018-09-05 ##
### Fixed ###
- Use our own `IsNotExist` implementation, which lets us handle `ENOTDIR`
properly within `SecureJoin`.
## [0.2.0] - 2017-07-19 ##
We now have 100% test coverage!
### Added ###
- Add a `SecureJoinVFS` API that can be used for mocking (as we do in our new
tests) or for implementing custom handling of lookup operations (such as for
rootless containers, where work is necessary to access directories with weird
modes because we don't have `CAP_DAC_READ_SEARCH` or `CAP_DAC_OVERRIDE`).
## 0.1.0 - 2017-07-19
This is our first release of `github.com/cyphar/filepath-securejoin`,
containing a full implementation with a coverage of 93.5% (the only missing
cases are the error cases, which are hard to mocktest at the moment).
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...HEAD
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...v0.4.0
[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
[0.3.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.2...v0.3.3
[0.3.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.5...v0.3.0
[0.2.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.1.0...v0.2.0

View File

@@ -1,28 +0,0 @@
Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,169 +0,0 @@
## `filepath-securejoin` ##
[![Go Documentation](https://pkg.go.dev/badge/github.com/cyphar/filepath-securejoin.svg)](https://pkg.go.dev/github.com/cyphar/filepath-securejoin)
[![Build Status](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml/badge.svg)](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
### Old API ###
This library was originally just an implementation of `SecureJoin` which was
[intended to be included in the Go standard library][go#20126] as a safer
`filepath.Join` that would restrict the path lookup to be inside a root
directory.
The implementation was based on code that existed in several container
runtimes. Unfortunately, this API is **fundamentally unsafe** against attackers
that can modify path components after `SecureJoin` returns and before the
caller uses the path, allowing for some fairly trivial TOCTOU attacks.
`SecureJoin` (and `SecureJoinVFS`) are still provided by this library to
support legacy users, but new users are strongly suggested to avoid using
`SecureJoin` and instead use the [new api](#new-api) or switch to
[libpathrs][libpathrs].
With the above limitations in mind, this library guarantees the following:
* If no error is set, the resulting string **must** be a child path of
`root` and will not contain any symlink path components (they will all be
expanded).
* When expanding symlinks, all symlink path components **must** be resolved
relative to the provided root. In particular, this can be considered a
userspace implementation of how `chroot(2)` operates on file paths. Note that
these symlinks will **not** be expanded lexically (`filepath.Clean` is not
called on the input before processing).
* Non-existent path components are unaffected by `SecureJoin` (similar to
`filepath.EvalSymlinks`'s semantics).
* The returned path will always be `filepath.Clean`ed and thus not contain any
`..` components.
A (trivial) implementation of this function on GNU/Linux systems could be done
with the following (note that this requires root privileges and is far more
opaque than the implementation in this library, and also requires that
`readlink` is inside the `root` path and is trustworthy):
```go
package securejoin
import (
"os/exec"
"path/filepath"
)
func SecureJoin(root, unsafePath string) (string, error) {
unsafePath = string(filepath.Separator) + unsafePath
cmd := exec.Command("chroot", root,
"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
expanded := string(output)
return filepath.Join(root, expanded), nil
}
```
[libpathrs]: https://github.com/openSUSE/libpathrs
[go#20126]: https://github.com/golang/go/issues/20126
### New API ###
While we recommend users switch to [libpathrs][libpathrs] as soon as it has a
stable release, some methods implemented by libpathrs have been ported to this
library to ease the transition. These APIs are only supported on Linux.
These APIs are implemented such that `filepath-securejoin` will
opportunistically use certain newer kernel APIs that make these operations far
more secure. In particular:
* All of the lookup operations will use [`openat2`][openat2.2] on new enough
kernels (Linux 5.6 or later) to restrict lookups through magic-links and
bind-mounts (for certain operations) and to make use of `RESOLVE_IN_ROOT` to
efficiently resolve symlinks within a rootfs.
* The APIs provide hardening against a malicious `/proc` mount to either detect
or avoid being tricked by a `/proc` that is not legitimate. This is done
using [`openat2`][openat2.2] for all users, and privileged users will also be
further protected by using [`fsopen`][fsopen.2] and [`open_tree`][open_tree.2]
(Linux 5.2 or later).
[openat2.2]: https://www.man7.org/linux/man-pages/man2/openat2.2.html
[fsopen.2]: https://github.com/brauner/man-pages-md/blob/main/fsopen.md
[open_tree.2]: https://github.com/brauner/man-pages-md/blob/main/open_tree.md
#### `OpenInRoot` ####
```go
func OpenInRoot(root, unsafePath string) (*os.File, error)
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error)
func Reopen(handle *os.File, flags int) (*os.File, error)
```
`OpenInRoot` is a much safer version of
```go
path, err := securejoin.SecureJoin(root, unsafePath)
file, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
```
that protects against various race attacks that could lead to serious security
issues, depending on the application. Note that the returned `*os.File` is an
`O_PATH` file descriptor, which is quite restricted. Callers will probably need
to use `Reopen` to get a more usable handle (this split is done to provide
useful features like PTY spawning and to avoid users accidentally opening bad
inodes that could cause a DoS).
Callers need to be careful in how they use the returned `*os.File`. Usually it
is only safe to operate on the handle directly, and it is very easy to create a
security issue. [libpathrs][libpathrs] provides far more helpers to make using
these handles safer -- there is currently no plan to port them to
`filepath-securejoin`.
`OpenatInRoot` is like `OpenInRoot` except that the root is provided using an
`*os.File`. This allows you to ensure that multiple `OpenatInRoot` (or
`MkdirAllHandle`) calls are operating on the same rootfs.
> **NOTE**: Unlike `SecureJoin`, `OpenInRoot` will error out as soon as it hits
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
> which treated non-existent components as though they were real directories,
> and would allow for partial resolution of dangling symlinks. These behaviours
> are at odds with how Linux treats non-existent paths and dangling symlinks,
> and so these are no longer allowed.
#### `MkdirAll` ####
```go
func MkdirAll(root, unsafePath string, mode int) error
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (*os.File, error)
```
`MkdirAll` is a much safer version of
```go
path, err := securejoin.SecureJoin(root, unsafePath)
err = os.MkdirAll(path, mode)
```
that protects against the same kinds of races that `OpenInRoot` protects
against.
`MkdirAllHandle` is like `MkdirAll` except that the root is provided using an
`*os.File` (the reason for this is the same as with `OpenatInRoot`) and an
`*os.File` of the final created directory is returned (this directory is
guaranteed to be effectively identical to the directory created by
`MkdirAllHandle`, which is not possible to ensure by just using `OpenatInRoot`
after `MkdirAll`).
> **NOTE**: Unlike `SecureJoin`, `MkdirAll` will error out as soon as it hits
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
> which treated non-existent components as though they were real directories,
> and would allow for partial resolution of dangling symlinks. These behaviours
> are at odds with how Linux treats non-existent paths and dangling symlinks,
> and so these are no longer allowed. This means that `MkdirAll` will not
> create non-existent directories referenced by a dangling symlink.
### License ###
The license of this project is the same as Go, which is a BSD 3-clause license
available in the `LICENSE` file.

View File

@@ -1 +0,0 @@
0.4.1

View File

@@ -1,39 +0,0 @@
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package securejoin implements a set of helpers to make it easier to write Go
// code that is safe against symlink-related escape attacks. The primary idea
// is to let you resolve a path within a rootfs directory as if the rootfs was
// a chroot.
//
// securejoin has two APIs, a "legacy" API and a "modern" API.
//
// The legacy API is [SecureJoin] and [SecureJoinVFS]. These methods are
// **not** safe against race conditions where an attacker changes the
// filesystem after (or during) the [SecureJoin] operation.
//
// The new API is made up of [OpenInRoot] and [MkdirAll] (and derived
// functions). These are safe against racing attackers and have several other
// protections that are not provided by the legacy API. There are many more
// operations that most programs expect to be able to do safely, but we do not
// provide explicit support for them because we want to encourage users to
// switch to [libpathrs](https://github.com/openSUSE/libpathrs) which is a
// cross-language next-generation library that is entirely designed around
// operating on paths safely.
//
// securejoin has been used by several container runtimes (Docker, runc,
// Kubernetes, etc) for quite a few years as a de-facto standard for operating
// on container filesystem paths "safely". However, most users still use the
// legacy API which is unsafe against various attacks (there is a fairly long
// history of CVEs in dependent as a result). Users should switch to the modern
// API as soon as possible (or even better, switch to libpathrs).
//
// This project was initially intended to be included in the Go standard
// library, but [it was rejected](https://go.dev/issue/20126). There is now a
// [new Go proposal](https://go.dev/issue/67002) for a safe path resolution API
// that shares some of the goals of filepath-securejoin. However, that design
// is intended to work like `openat2(RESOLVE_BENEATH)` which does not fit the
// usecase of container runtimes and most system tools.
package securejoin

View File

@@ -1,18 +0,0 @@
//go:build linux && go1.20
// Copyright (C) 2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import (
"fmt"
)
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
// is only guaranteed to give you baseErr.
func wrapBaseError(baseErr, extraErr error) error {
return fmt.Errorf("%w: %w", extraErr, baseErr)
}

View File

@@ -1,38 +0,0 @@
//go:build linux && !go1.20
// Copyright (C) 2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import (
"fmt"
)
type wrappedError struct {
inner error
isError error
}
func (err wrappedError) Is(target error) bool {
return err.isError == target
}
func (err wrappedError) Unwrap() error {
return err.inner
}
func (err wrappedError) Error() string {
return fmt.Sprintf("%v: %v", err.isError, err.inner)
}
// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
// is only guaranteed to give you baseErr.
func wrapBaseError(baseErr, extraErr error) error {
return wrappedError{
inner: baseErr,
isError: extraErr,
}
}

View File

@@ -1,32 +0,0 @@
//go:build linux && go1.21
// Copyright (C) 2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import (
"slices"
"sync"
)
func slices_DeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
return slices.DeleteFunc(slice, delFn)
}
func slices_Contains[S ~[]E, E comparable](slice S, val E) bool {
return slices.Contains(slice, val)
}
func slices_Clone[S ~[]E, E any](slice S) S {
return slices.Clone(slice)
}
func sync_OnceValue[T any](f func() T) func() T {
return sync.OnceValue(f)
}
func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
return sync.OnceValues(f)
}

View File

@@ -1,124 +0,0 @@
//go:build linux && !go1.21
// Copyright (C) 2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import (
"sync"
)
// These are very minimal implementations of functions that appear in Go 1.21's
// stdlib, included so that we can build on older Go versions. Most are
// borrowed directly from the stdlib, and a few are modified to be "obviously
// correct" without needing to copy too many other helpers.
// clearSlice is equivalent to the builtin clear from Go 1.21.
// Copied from the Go 1.24 stdlib implementation.
func clearSlice[S ~[]E, E any](slice S) {
var zero E
for i := range slice {
slice[i] = zero
}
}
// Copied from the Go 1.24 stdlib implementation.
func slices_IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
for i := range s {
if f(s[i]) {
return i
}
}
return -1
}
// Copied from the Go 1.24 stdlib implementation.
func slices_DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
i := slices_IndexFunc(s, del)
if i == -1 {
return s
}
// Don't start copying elements until we find one to delete.
for j := i + 1; j < len(s); j++ {
if v := s[j]; !del(v) {
s[i] = v
i++
}
}
clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC
return s[:i]
}
// Similar to the stdlib slices.Contains, except that we don't have
// slices.Index so we need to use slices.IndexFunc for this non-Func helper.
func slices_Contains[S ~[]E, E comparable](s S, v E) bool {
return slices_IndexFunc(s, func(e E) bool { return e == v }) >= 0
}
// Copied from the Go 1.24 stdlib implementation.
func slices_Clone[S ~[]E, E any](s S) S {
// Preserve nil in case it matters.
if s == nil {
return nil
}
return append(S([]E{}), s...)
}
// Copied from the Go 1.24 stdlib implementation.
func sync_OnceValue[T any](f func() T) func() T {
var (
once sync.Once
valid bool
p any
result T
)
g := func() {
defer func() {
p = recover()
if !valid {
panic(p)
}
}()
result = f()
f = nil
valid = true
}
return func() T {
once.Do(g)
if !valid {
panic(p)
}
return result
}
}
// Copied from the Go 1.24 stdlib implementation.
func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
var (
once sync.Once
valid bool
p any
r1 T1
r2 T2
)
g := func() {
defer func() {
p = recover()
if !valid {
panic(p)
}
}()
r1, r2 = f()
f = nil
valid = true
}
return func() (T1, T2) {
once.Do(g)
if !valid {
panic(p)
}
return r1, r2
}
}

View File

@@ -1,166 +0,0 @@
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017-2025 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import (
"errors"
"os"
"path/filepath"
"strings"
"syscall"
)
const maxSymlinkLimit = 255
// IsNotExist tells you if err is an error that implies that either the path
// accessed does not exist (or path components don't exist). This is
// effectively a more broad version of [os.IsNotExist].
func IsNotExist(err error) bool {
// Check that it's not actually an ENOTDIR, which in some cases is a more
// convoluted case of ENOENT (usually involving weird paths).
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
}
// errUnsafeRoot is returned if the user provides SecureJoinVFS with a path
// that contains ".." components.
var errUnsafeRoot = errors.New("root path provided to SecureJoin contains '..' components")
// stripVolume just gets rid of the Windows volume included in a path. Based on
// some godbolt tests, the Go compiler is smart enough to make this a no-op on
// Linux.
func stripVolume(path string) string {
return path[len(filepath.VolumeName(path)):]
}
// hasDotDot checks if the path contains ".." components in a platform-agnostic
// way.
func hasDotDot(path string) bool {
// If we are on Windows, strip any volume letters. It turns out that
// C:..\foo may (or may not) be a valid pathname and we need to handle that
// leading "..".
path = stripVolume(path)
// Look for "/../" in the path, but we need to handle leading and trailing
// ".."s by adding separators. Doing this with filepath.Separator is ugly
// so just convert to Unix-style "/" first.
path = filepath.ToSlash(path)
return strings.Contains("/"+path+"/", "/../")
}
// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except
// that the returned path is guaranteed to be scoped inside the provided root
// path (when evaluated). Any symbolic links in the path are evaluated with the
// given root treated as the root of the filesystem, similar to a chroot. The
// filesystem state is evaluated through the given [VFS] interface (if nil, the
// standard [os].* family of functions are used).
//
// Note that the guarantees provided by this function only apply if the path
// components in the returned string are not modified (in other words are not
// replaced with symlinks on the filesystem) after this function has returned.
// Such a symlink race is necessarily out-of-scope of SecureJoinVFS.
//
// NOTE: Due to the above limitation, Linux users are strongly encouraged to
// use [OpenInRoot] instead, which does safely protect against these kinds of
// attacks. There is no way to solve this problem with SecureJoinVFS because
// the API is fundamentally wrong (you cannot return a "safe" path string and
// guarantee it won't be modified afterwards).
//
// Volume names in unsafePath are always discarded, regardless if they are
// provided via direct input or when evaluating symlinks. Therefore:
//
// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
//
// If the provided root is not [filepath.Clean] then an error will be returned,
// as such root paths are bordering on somewhat unsafe and using such paths is
// not best practice. We also strongly suggest that any root path is first
// fully resolved using [filepath.EvalSymlinks] or otherwise constructed to
// avoid containing symlink components. Of course, the root also *must not* be
// attacker-controlled.
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
// The root path must not contain ".." components, otherwise when we join
// the subpath we will end up with a weird path. We could work around this
// in other ways but users shouldn't be giving us non-lexical root paths in
// the first place.
if hasDotDot(root) {
return "", errUnsafeRoot
}
// Use the os.* VFS implementation if none was specified.
if vfs == nil {
vfs = osVFS{}
}
unsafePath = filepath.FromSlash(unsafePath)
var (
currentPath string
remainingPath = unsafePath
linksWalked int
)
for remainingPath != "" {
// On Windows, if we managed to end up at a path referencing a volume,
// drop the volume to make sure we don't end up with broken paths or
// escaping the root volume.
remainingPath = stripVolume(remainingPath)
// Get the next path component.
var part string
if i := strings.IndexRune(remainingPath, filepath.Separator); i == -1 {
part, remainingPath = remainingPath, ""
} else {
part, remainingPath = remainingPath[:i], remainingPath[i+1:]
}
// Apply the component lexically to the path we are building.
// currentPath does not contain any symlinks, and we are lexically
// dealing with a single component, so it's okay to do a filepath.Clean
// here.
nextPath := filepath.Join(string(filepath.Separator), currentPath, part)
if nextPath == string(filepath.Separator) {
currentPath = ""
continue
}
fullPath := root + string(filepath.Separator) + nextPath
// Figure out whether the path is a symlink.
fi, err := vfs.Lstat(fullPath)
if err != nil && !IsNotExist(err) {
return "", err
}
// Treat non-existent path components the same as non-symlinks (we
// can't do any better here).
if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 {
currentPath = nextPath
continue
}
// It's a symlink, so get its contents and expand it by prepending it
// to the yet-unparsed path.
linksWalked++
if linksWalked > maxSymlinkLimit {
return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP}
}
dest, err := vfs.Readlink(fullPath)
if err != nil {
return "", err
}
remainingPath = dest + string(filepath.Separator) + remainingPath
// Absolute symlinks reset any work we've already done.
if filepath.IsAbs(dest) {
currentPath = ""
}
}
// There should be no lexical components like ".." left in the path here,
// but for safety clean up the path before joining it to the root.
finalPath := filepath.Join(string(filepath.Separator), currentPath)
return filepath.Join(root, finalPath), nil
}
// SecureJoin is a wrapper around [SecureJoinVFS] that just uses the [os].* library
// of functions as the [VFS]. If in doubt, use this function over [SecureJoinVFS].
func SecureJoin(root, unsafePath string) (string, error) {
return SecureJoinVFS(root, unsafePath, nil)
}

Some files were not shown because too many files have changed in this diff Show More