Skip to content

Commit a693bf6

Browse files
committed
fix(validator): accept renamed nvidia-smi banner fields
Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
1 parent 1608bc0 commit a693bf6

2 files changed

Lines changed: 137 additions & 10 deletions

File tree

validators/deployment/nvidia_smi.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,24 +169,40 @@ func getLogSnippet(logs string, maxLines int) string {
169169
}
170170

171171
func verifyNvidiaSMILogs(podLogs string, pod *v1.Pod) error {
172-
requiredStrings := []string{
173-
"NVIDIA-SMI",
174-
"Driver Version:",
175-
"CUDA Version:",
176-
gpuCheckSuccessMsg,
172+
requiredMarkerGroups := [][]string{
173+
{"NVIDIA-SMI"},
174+
{"Driver Version:", "KMD Version:"},
175+
{"CUDA Version:", "CUDA UMD Version:"},
176+
{gpuCheckSuccessMsg},
177177
}
178178

179+
// Match case-insensitively: the renamed banner fields are only documented
180+
// via `nvidia-smi --version` deprecation text, which spells them lowercase
181+
// ("KMD version"), and no captured plain-`nvidia-smi` banner pins the
182+
// exact casing of the table header (issue #1667). Case-insensitivity
183+
// accepts either spelling — and future casing tweaks — without false
184+
// positives: the verified log is only nvidia-smi output plus the success
185+
// echo. The diagnostic below keeps the canonical casing for readability.
186+
logsLower := strings.ToLower(podLogs)
187+
179188
var missing []string
180-
for _, required := range requiredStrings {
181-
if !strings.Contains(podLogs, required) {
182-
missing = append(missing, required)
189+
for _, markerGroup := range requiredMarkerGroups {
190+
found := false
191+
for _, marker := range markerGroup {
192+
if strings.Contains(logsLower, strings.ToLower(marker)) {
193+
found = true
194+
break
195+
}
196+
}
197+
if !found {
198+
missing = append(missing, strings.Join(markerGroup, " or "))
183199
}
184200
}
185201

186202
if len(missing) > 0 {
187203
return errors.New(errors.ErrCodeInternal,
188-
fmt.Sprintf("log verification failed for pod %s/%s: missing %v",
189-
pod.Namespace, pod.Name, missing))
204+
fmt.Sprintf("log verification failed for pod %s/%s: missing [%s]",
205+
pod.Namespace, pod.Name, strings.Join(missing, "; ")))
190206
}
191207

192208
return nil
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"testing"
19+
20+
v1 "k8s.io/api/core/v1"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
)
23+
24+
func TestVerifyNvidiaSMILogs(t *testing.T) {
25+
t.Parallel()
26+
27+
tests := []struct {
28+
name string
29+
logs string
30+
wantErr string
31+
}{
32+
{
33+
name: "accepts legacy banner fields",
34+
logs: "NVIDIA-SMI\nDriver Version: 570.86.15\nCUDA Version: 12.8\n" + gpuCheckSuccessMsg,
35+
},
36+
{
37+
name: "accepts renamed banner fields",
38+
logs: "NVIDIA-SMI\nKMD Version: 580.65.06\nCUDA UMD Version: 13.0\n" + gpuCheckSuccessMsg,
39+
},
40+
{
41+
// Representative table-banner layout of a renamed-field driver
42+
// branch (see issue #1667): single header row, pipe-delimited,
43+
// fields separated by padding rather than newlines.
44+
name: "accepts renamed banner in table layout",
45+
logs: "| NVIDIA-SMI 610.43.02 KMD Version: 610.43.02 CUDA UMD Version: 13.3 |\n" +
46+
gpuCheckSuccessMsg,
47+
},
48+
{
49+
name: "accepts mixed legacy and renamed banner fields",
50+
logs: "NVIDIA-SMI\nDriver Version: 570.86.15\nCUDA UMD Version: 13.0\n" + gpuCheckSuccessMsg,
51+
},
52+
{
53+
// The renamed fields are documented only via `nvidia-smi
54+
// --version` deprecation text, which spells them lowercase
55+
// ("KMD version"); no fixture pins the table banner's casing
56+
// (issue #1667), so matching is case-insensitive.
57+
name: "accepts lowercase renamed banner fields",
58+
logs: "NVIDIA-SMI\nKMD version: 580.65.06\nCUDA UMD version: 13.0\n" + gpuCheckSuccessMsg,
59+
},
60+
{
61+
name: "accepts uppercase legacy banner fields",
62+
logs: "NVIDIA-SMI\nDRIVER VERSION: 570.86.15\nCUDA VERSION: 12.8\n" + gpuCheckSuccessMsg,
63+
},
64+
{
65+
name: "rejects logs missing both driver banner alternatives",
66+
logs: "NVIDIA-SMI\nCUDA UMD Version: 13.0\n" + gpuCheckSuccessMsg,
67+
wantErr: "[INTERNAL] log verification failed for pod aicr-validation/nvidia-smi-verify-test: missing [Driver Version: or KMD Version:]",
68+
},
69+
{
70+
name: "rejects logs missing both CUDA banner alternatives",
71+
logs: "NVIDIA-SMI\nKMD Version: 580.65.06\n" + gpuCheckSuccessMsg,
72+
wantErr: "[INTERNAL] log verification failed for pod aicr-validation/nvidia-smi-verify-test: missing [CUDA Version: or CUDA UMD Version:]",
73+
},
74+
{
75+
name: "separates multiple missing marker groups",
76+
logs: "NVIDIA-SMI\n" + gpuCheckSuccessMsg,
77+
wantErr: "[INTERNAL] log verification failed for pod aicr-validation/nvidia-smi-verify-test: missing [Driver Version: or KMD Version:; CUDA Version: or CUDA UMD Version:]",
78+
},
79+
{
80+
name: "rejects logs missing NVIDIA-SMI marker",
81+
logs: "Driver Version: 570.86.15\nCUDA Version: 12.8\n" + gpuCheckSuccessMsg,
82+
wantErr: "[INTERNAL] log verification failed for pod aicr-validation/nvidia-smi-verify-test: missing [NVIDIA-SMI]",
83+
},
84+
{
85+
name: "rejects logs missing success marker",
86+
logs: "NVIDIA-SMI\nDriver Version: 570.86.15\nCUDA Version: 12.8\n",
87+
wantErr: "[INTERNAL] log verification failed for pod aicr-validation/nvidia-smi-verify-test: missing [" + gpuCheckSuccessMsg + "]",
88+
},
89+
}
90+
91+
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "aicr-validation", Name: "nvidia-smi-verify-test"}}
92+
for _, tt := range tests {
93+
t.Run(tt.name, func(t *testing.T) {
94+
t.Parallel()
95+
96+
err := verifyNvidiaSMILogs(tt.logs, pod)
97+
if tt.wantErr == "" {
98+
if err != nil {
99+
t.Fatalf("verifyNvidiaSMILogs() error = %v, want nil", err)
100+
}
101+
return
102+
}
103+
if err == nil {
104+
t.Fatalf("verifyNvidiaSMILogs() error = nil, want %q", tt.wantErr)
105+
}
106+
if err.Error() != tt.wantErr {
107+
t.Fatalf("verifyNvidiaSMILogs() error = %q, want %q", err, tt.wantErr)
108+
}
109+
})
110+
}
111+
}

0 commit comments

Comments
 (0)