Skip to content

Commit 2b5b248

Browse files
committed
Update Validator for /usr/bin symlink
Signed-off-by: Arjun <agadiyar@nvidia.com>
1 parent 9aff6f5 commit 2b5b248

12 files changed

Lines changed: 921 additions & 7 deletions

File tree

cmd/nvidia-validator/main.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/NVIDIA/go-nvlib/pkg/nvmdev"
3232
"github.com/NVIDIA/go-nvlib/pkg/nvpci"
3333
devchar "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/create-dev-char-symlinks"
34+
"github.com/moby/sys/symlink"
3435
log "github.com/sirupsen/logrus"
3536
"github.com/stretchr/testify/assert/yaml"
3637
cli "github.com/urfave/cli/v3"
@@ -135,6 +136,7 @@ var (
135136
hostRootFlag string
136137
driverInstallDirFlag string
137138
driverInstallDirCtrPathFlag string
139+
hostRootCtrPath = "/host"
138140
)
139141

140142
// defaultGPUWorkloadConfig is "vm-passthrough" unless
@@ -645,7 +647,7 @@ func validateComponent(ctx context.Context, componentFlag string) error {
645647
}
646648
}
647649

648-
func runCommand(command string, args []string, silent bool) error {
650+
var runCommand = func(command string, args []string, silent bool) error {
649651
cmd := exec.Command(command, args...)
650652
if !silent {
651653
cmd.Stdout = os.Stdout
@@ -744,20 +746,26 @@ func isDriverManagedByOperator(ctx context.Context) (bool, error) {
744746

745747
func validateHostDriver(silent bool) error {
746748
log.Info("Attempting to validate a pre-installed driver on the host")
747-
if fileInfo, err := os.Lstat(filepath.Join("/host", wslNvidiaSMIPath)); err == nil && fileInfo.Size() != 0 {
749+
if fileInfo, err := os.Lstat(filepath.Join(hostRootCtrPath, wslNvidiaSMIPath)); err == nil && fileInfo.Size() != 0 {
748750
log.Infof("WSL2 system detected, assuming driver is pre-installed")
749751
disableDevCharSymlinkCreation = true
750752
return nil
751753
}
752-
fileInfo, err := os.Lstat("/host/usr/bin/nvidia-smi")
754+
755+
nvidiaSMIPath, err := symlink.FollowSymlinkInScope(filepath.Join(hostRootCtrPath, "usr/bin/nvidia-smi"), hostRootCtrPath)
756+
if err != nil {
757+
return fmt.Errorf("failed to resolve 'nvidia-smi' path on the host: %w", err)
758+
}
759+
760+
fileInfo, err := os.Lstat(nvidiaSMIPath)
753761
if err != nil {
754762
return fmt.Errorf("no 'nvidia-smi' file present on the host: %w", err)
755763
}
756764
if fileInfo.Size() == 0 {
757765
return fmt.Errorf("empty 'nvidia-smi' file found on the host")
758766
}
759767
command := "chroot"
760-
args := []string{"/host", "nvidia-smi"}
768+
args := []string{hostRootCtrPath, "nvidia-smi"}
761769

762770
return runCommand(command, args, silent)
763771
}
@@ -1753,9 +1761,12 @@ func (v *VGPUManager) runValidation(silent bool) (hostDriver bool, err error) {
17531761
args := []string{"/run/nvidia/driver", "nvidia-smi"}
17541762

17551763
// check if driver is pre-installed on the host and use host path for validation
1756-
if _, err := os.Lstat("/host/usr/bin/nvidia-smi"); err == nil {
1757-
args = []string{"/host", "nvidia-smi"}
1758-
hostDriver = true
1764+
nvidiaSMIPath, resolveErr := symlink.FollowSymlinkInScope(filepath.Join(hostRootCtrPath, "usr/bin/nvidia-smi"), hostRootCtrPath)
1765+
if resolveErr == nil {
1766+
if _, err := os.Lstat(nvidiaSMIPath); err == nil {
1767+
args = []string{hostRootCtrPath, "nvidia-smi"}
1768+
hostDriver = true
1769+
}
17591770
}
17601771

17611772
if withWaitFlag {

cmd/nvidia-validator/main_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,96 @@ package main
1919
import (
2020
"context"
2121
"os"
22+
"path/filepath"
23+
"reflect"
2224
"testing"
2325
)
2426

27+
func Test_validateHostDriverWithAbsoluteUsrBinSymlink(t *testing.T) {
28+
hostRoot := t.TempDir()
29+
30+
targetDir := filepath.Join(hostRoot, "run/current-system/sw/bin")
31+
if err := os.MkdirAll(targetDir, 0755); err != nil {
32+
t.Fatalf("failed to create target directory: %v", err)
33+
}
34+
if err := os.WriteFile(filepath.Join(targetDir, "nvidia-smi"), []byte("fake nvidia-smi"), 0600); err != nil {
35+
t.Fatalf("failed to create nvidia-smi: %v", err)
36+
}
37+
38+
usrDir := filepath.Join(hostRoot, "usr")
39+
if err := os.MkdirAll(usrDir, 0755); err != nil {
40+
t.Fatalf("failed to create usr directory: %v", err)
41+
}
42+
if err := os.Symlink("/run/current-system/sw/bin", filepath.Join(usrDir, "bin")); err != nil {
43+
t.Fatalf("failed to create usr/bin symlink: %v", err)
44+
}
45+
46+
originalHostRootCtrPath := hostRootCtrPath
47+
hostRootCtrPath = hostRoot
48+
defer func() { hostRootCtrPath = originalHostRootCtrPath }()
49+
50+
originalWithWaitFlag := withWaitFlag
51+
withWaitFlag = false
52+
defer func() { withWaitFlag = originalWithWaitFlag }()
53+
54+
originalRunCommand := runCommand
55+
defer func() { runCommand = originalRunCommand }()
56+
57+
var gotCommand string
58+
var gotArgs []string
59+
var gotSilent bool
60+
runCommandCalled := false
61+
runCommand = func(command string, args []string, silent bool) error {
62+
runCommandCalled = true
63+
gotCommand = command
64+
gotArgs = append([]string(nil), args...)
65+
gotSilent = silent
66+
return nil
67+
}
68+
69+
if err := validateHostDriver(true); err != nil {
70+
t.Fatalf("validateHostDriver() unexpected error: %v", err)
71+
}
72+
if !runCommandCalled {
73+
t.Fatal("validateHostDriver() did not run nvidia-smi validation command")
74+
}
75+
if gotCommand != "chroot" {
76+
t.Fatalf("command = %q, want %q", gotCommand, "chroot")
77+
}
78+
wantArgs := []string{hostRoot, "nvidia-smi"}
79+
if !reflect.DeepEqual(gotArgs, wantArgs) {
80+
t.Fatalf("args = %v, want %v", gotArgs, wantArgs)
81+
}
82+
if !gotSilent {
83+
t.Fatal("silent = false, want true")
84+
}
85+
86+
runCommandCalled = false
87+
gotCommand = ""
88+
gotArgs = nil
89+
gotSilent = false
90+
91+
hostDriver, err := (&VGPUManager{}).runValidation(true)
92+
if err != nil {
93+
t.Fatalf("VGPUManager.runValidation() unexpected error: %v", err)
94+
}
95+
if !hostDriver {
96+
t.Fatal("VGPUManager.runValidation() did not detect host driver")
97+
}
98+
if !runCommandCalled {
99+
t.Fatal("VGPUManager.runValidation() did not run nvidia-smi validation command")
100+
}
101+
if gotCommand != "chroot" {
102+
t.Fatalf("command = %q, want %q", gotCommand, "chroot")
103+
}
104+
if !reflect.DeepEqual(gotArgs, wantArgs) {
105+
t.Fatalf("args = %v, want %v", gotArgs, wantArgs)
106+
}
107+
if !gotSilent {
108+
t.Fatal("silent = false, want true")
109+
}
110+
}
111+
25112
func Test_isValidComponent(t *testing.T) {
26113
tests := []struct {
27114
name string

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
1212
github.com/go-logr/logr v1.4.3
1313
github.com/go-logr/zapr v1.3.0
14+
github.com/moby/sys/symlink v0.3.0
1415
github.com/onsi/ginkgo/v2 v2.28.3
1516
github.com/onsi/gomega v1.40.0
1617
github.com/openshift/api v0.0.0-20260213204242-d34f11c515b3

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
148148
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
149149
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
150150
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
151+
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
152+
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
151153
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
152154
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
153155
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

vendor/github.com/moby/sys/symlink/LICENSE

Lines changed: 202 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)