Skip to content

Commit 71b3692

Browse files
authored
[LEP-355] feat(package): introduce skipped subcomand (#1194)
[LEP-355](https://nvbugspro.nvidia.com/bug/5720729) <img width="816" height="381" alt="image" src="https://github.com/user-attachments/assets/56d73218-3b13-4fb5-9a02-cfed484af1d4" /> --------- Signed-off-by: cardyok <zuniorone@gmail.com>
1 parent 55bb48a commit 71b3692

7 files changed

Lines changed: 313 additions & 3 deletions

File tree

api/v1/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ const (
177177
InstalledPhase PackagePhase = "Installed"
178178
InstallingPhase PackagePhase = "Installing"
179179
UnknownPhase PackagePhase = "Unknown"
180+
SkippedPhase PackagePhase = "Skipped"
180181
)
181182

182183
type RepairActionType string

pkg/gpud-manager/controllers/package_controller.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func (c *PackageController) reconcileLoop(ctx context.Context) {
6161
if _, ok := c.packageStatus[packageInfo.Name]; !ok {
6262
c.packageStatus[packageInfo.Name] = &packages.PackageStatus{
6363
Name: packageInfo.Name,
64+
Skipped: false,
6465
IsInstalled: false,
6566
Installing: false,
6667
Progress: 0,
@@ -109,6 +110,16 @@ func (c *PackageController) updateRunner(ctx context.Context) {
109110
c.Lock()
110111
c.packageStatus[pkg.Name].CurrentVersion = version
111112
c.Unlock()
113+
114+
var shouldSkipResult string
115+
if err = runCommand(ctx, pkg.ScriptPath, "shouldSkip", &shouldSkipResult); err == nil {
116+
c.Lock()
117+
c.packageStatus[pkg.Name].Skipped = true
118+
c.Unlock()
119+
log.Logger.Debugf("[package controller]: %v shouldSkip returned 0, skipping update", pkg.Name)
120+
continue
121+
}
122+
112123
if version == pkg.TargetVersion {
113124
log.Logger.Debugf("[package controller]: %v version is %v (same as target, no-op)", pkg.Name, version)
114125
continue
@@ -186,6 +197,18 @@ func (c *PackageController) installRunner(ctx context.Context) {
186197
if skipCheck {
187198
continue
188199
}
200+
201+
var shouldSkipResult string
202+
if err := runCommand(ctx, pkg.ScriptPath, "shouldSkip", &shouldSkipResult); err == nil {
203+
c.Lock()
204+
c.packageStatus[pkg.Name].Skipped = true
205+
c.packageStatus[pkg.Name].Progress = 100
206+
c.packageStatus[pkg.Name].IsInstalled = true
207+
c.Unlock()
208+
log.Logger.Debugf("[package controller]: %v shouldSkip returned 0, skipping install", pkg.Name)
209+
continue
210+
}
211+
189212
if pkg.Installing {
190213
log.Logger.Infof("[package controller]: %v installing...", pkg.Name)
191214
continue
@@ -284,6 +307,17 @@ func (c *PackageController) statusRunner(ctx context.Context) {
284307
if !pkg.IsInstalled {
285308
continue
286309
}
310+
311+
var shouldSkipResult string
312+
if err := runCommand(ctx, pkg.ScriptPath, "shouldSkip", &shouldSkipResult); err == nil {
313+
c.Lock()
314+
c.packageStatus[pkg.Name].Skipped = true
315+
c.packageStatus[pkg.Name].Status = true
316+
c.Unlock()
317+
log.Logger.Debugf("[package controller]: %v shouldSkip returned 0, setting status to true", pkg.Name)
318+
continue
319+
}
320+
287321
err := runCommand(ctx, pkg.ScriptPath, "status", nil)
288322
if err == nil {
289323
c.Lock()

pkg/gpud-manager/controllers/package_controller_test.go

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func TestStatus(t *testing.T) {
3030
// Add some test data
3131
controller.packageStatus["pkg1"] = &packages.PackageStatus{
3232
Name: "pkg1",
33+
Skipped: false,
3334
IsInstalled: true,
3435
Installing: false,
3536
Progress: 100,
@@ -39,6 +40,7 @@ func TestStatus(t *testing.T) {
3940
}
4041
controller.packageStatus["pkg2"] = &packages.PackageStatus{
4142
Name: "pkg2",
43+
Skipped: true,
4244
IsInstalled: true,
4345
Installing: false,
4446
Progress: 100,
@@ -53,7 +55,9 @@ func TestStatus(t *testing.T) {
5355

5456
// Verify sorting works (packages should be sorted by name)
5557
assert.Equal(t, "pkg1", status[0].Name)
58+
assert.False(t, status[0].Skipped)
5659
assert.Equal(t, "pkg2", status[1].Name)
60+
assert.True(t, status[1].Skipped)
5761
}
5862

5963
func TestRun(t *testing.T) {
@@ -124,6 +128,8 @@ func TestUpdateRunner(t *testing.T) {
124128
if [ "$1" == "version" ]; then
125129
echo "1.0.0"
126130
exit 0
131+
elif [ "$1" == "shouldSkip" ]; then
132+
exit 1 # Don't skip
127133
elif [ "$1" == "upgrade" ]; then
128134
exit 0
129135
else
@@ -139,6 +145,7 @@ fi
139145
// Set up a package that needs update
140146
controller.packageStatus["test-pkg"] = &packages.PackageStatus{
141147
Name: "test-pkg",
148+
Skipped: false,
142149
IsInstalled: true,
143150
Installing: false,
144151
Progress: 100,
@@ -169,6 +176,69 @@ fi
169176
t.Logf("Package status: installing=%v, progress=%d", status.Installing, status.Progress)
170177
}
171178

179+
func TestUpdateRunnerShouldSkip(t *testing.T) {
180+
// Create a temporary directory for test scripts
181+
tempDir, err := os.MkdirTemp("", "package-controller-test")
182+
require.NoError(t, err)
183+
defer func() {
184+
_ = os.RemoveAll(tempDir)
185+
}()
186+
187+
// Create a test script that returns shouldSkip = 0 (should skip)
188+
scriptPath := filepath.Join(tempDir, "update-skip-test.sh")
189+
scriptContent := `#!/bin/bash
190+
if [ "$1" == "version" ]; then
191+
echo "1.0.0"
192+
exit 0
193+
elif [ "$1" == "shouldSkip" ]; then
194+
exit 0 # Should skip
195+
elif [ "$1" == "upgrade" ]; then
196+
exit 0
197+
else
198+
exit 1
199+
fi
200+
`
201+
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755)
202+
require.NoError(t, err)
203+
204+
watcher := make(chan packages.PackageInfo)
205+
controller := NewPackageController(watcher)
206+
controller.syncPeriod = 100 * time.Millisecond
207+
208+
// Set up a package that would need update but should be skipped
209+
controller.packageStatus["skip-pkg"] = &packages.PackageStatus{
210+
Name: "skip-pkg",
211+
Skipped: false,
212+
IsInstalled: true,
213+
Installing: false,
214+
Progress: 100,
215+
Status: true,
216+
TargetVersion: "2.0.0", // Higher version than current
217+
CurrentVersion: "1.0.0",
218+
ScriptPath: scriptPath,
219+
TotalTime: 5 * time.Second,
220+
}
221+
222+
// Create context with timeout
223+
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
224+
defer cancel()
225+
226+
// Run the update runner
227+
go controller.updateRunner(ctx)
228+
229+
// Allow time for at least one sync cycle
230+
time.Sleep(controller.syncPeriod + 200*time.Millisecond)
231+
232+
// Verify that the package was marked as skipped
233+
controller.RLock()
234+
status := controller.packageStatus["skip-pkg"]
235+
controller.RUnlock()
236+
237+
assert.True(t, status.Skipped, "Package should be marked as skipped")
238+
assert.False(t, status.Installing, "Package should not be installing since it was skipped")
239+
t.Logf("Package status: skipped=%v, installing=%v", status.Skipped, status.Installing)
240+
}
241+
172242
func TestInstallRunner(t *testing.T) {
173243
if os.Getenv("TEST_INSTALL_RUNNER") != "true" {
174244
t.Skip("TEST_INSTALL_RUNNER is not set")
@@ -186,6 +256,8 @@ func TestInstallRunner(t *testing.T) {
186256
scriptContent := `#!/bin/bash
187257
if [ "$1" == "isInstalled" ]; then
188258
exit 1 # Not installed
259+
elif [ "$1" == "shouldSkip" ]; then
260+
exit 1 # Don't skip
189261
elif [ "$1" == "install" ]; then
190262
exit 0 # Installation successful
191263
elif [ "$1" == "start" ]; then
@@ -204,6 +276,7 @@ fi
204276
// Set up a package to be installed
205277
controller.packageStatus["install-pkg"] = &packages.PackageStatus{
206278
Name: "install-pkg",
279+
Skipped: false,
207280
IsInstalled: false,
208281
Installing: false,
209282
Progress: 0,
@@ -234,6 +307,72 @@ fi
234307
status.Installing, status.IsInstalled, status.Progress)
235308
}
236309

310+
func TestInstallRunnerShouldSkip(t *testing.T) {
311+
// Create a temporary directory for test scripts
312+
tempDir, err := os.MkdirTemp("", "package-controller-test")
313+
require.NoError(t, err)
314+
defer func() {
315+
_ = os.RemoveAll(tempDir)
316+
}()
317+
318+
// Create a test script that returns shouldSkip = 0 (should skip)
319+
scriptPath := filepath.Join(tempDir, "install-skip-test.sh")
320+
scriptContent := `#!/bin/bash
321+
if [ "$1" == "isInstalled" ]; then
322+
exit 1 # Not installed
323+
elif [ "$1" == "shouldSkip" ]; then
324+
exit 0 # Should skip
325+
elif [ "$1" == "install" ]; then
326+
exit 0 # Installation successful
327+
elif [ "$1" == "start" ]; then
328+
exit 0 # Start successful
329+
else
330+
exit 1
331+
fi
332+
`
333+
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755)
334+
require.NoError(t, err)
335+
336+
watcher := make(chan packages.PackageInfo)
337+
controller := NewPackageController(watcher)
338+
controller.syncPeriod = 100 * time.Millisecond
339+
340+
// Set up a package to be installed but should be skipped
341+
controller.packageStatus["skip-install-pkg"] = &packages.PackageStatus{
342+
Name: "skip-install-pkg",
343+
Skipped: false,
344+
IsInstalled: false,
345+
Installing: false,
346+
Progress: 0,
347+
Status: false,
348+
TargetVersion: "1.0.0",
349+
CurrentVersion: "",
350+
ScriptPath: scriptPath,
351+
TotalTime: 2 * time.Second,
352+
}
353+
354+
// Create context with timeout
355+
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
356+
defer cancel()
357+
358+
// Run the install runner
359+
go controller.installRunner(ctx)
360+
361+
// Allow time for at least one sync cycle
362+
time.Sleep(controller.syncPeriod + 200*time.Millisecond)
363+
364+
// Verify that the package was marked as skipped
365+
controller.RLock()
366+
status := controller.packageStatus["skip-install-pkg"]
367+
controller.RUnlock()
368+
369+
assert.True(t, status.Skipped, "Package should be marked as skipped")
370+
assert.False(t, status.Installing, "Package should not be installing since it was skipped")
371+
assert.True(t, status.IsInstalled, "Package should not be installed since it was skipped")
372+
t.Logf("Package status: skipped=%v, installing=%v, isInstalled=%v",
373+
status.Skipped, status.Installing, status.IsInstalled)
374+
}
375+
237376
func TestDeleteRunner(t *testing.T) {
238377
// Create a temporary directory for test scripts
239378
tempDir, err := os.MkdirTemp("", "package-controller-test")
@@ -298,6 +437,8 @@ func TestStatusRunner(t *testing.T) {
298437
workingScriptContent := `#!/bin/bash
299438
if [ "$1" == "status" ]; then
300439
exit 0 # Status is OK
440+
elif [ "$1" == "shouldSkip" ]; then
441+
exit 1 # Don't skip
301442
else
302443
exit 1
303444
fi
@@ -312,6 +453,7 @@ fi
312453
// Set up a package with good status
313454
controller.packageStatus["ok-pkg"] = &packages.PackageStatus{
314455
Name: "ok-pkg",
456+
Skipped: false,
315457
IsInstalled: true,
316458
Installing: false,
317459
Progress: 100,
@@ -342,6 +484,69 @@ fi
342484
// and execution environment conditions
343485
}
344486

487+
func TestStatusRunnerShouldSkip(t *testing.T) {
488+
// Create a temporary directory for test scripts
489+
tempDir, err := os.MkdirTemp("", "package-controller-test")
490+
require.NoError(t, err)
491+
defer func() {
492+
_ = os.RemoveAll(tempDir)
493+
}()
494+
495+
// Create a test script that returns shouldSkip = 0 (should skip)
496+
scriptPath := filepath.Join(tempDir, "status-skip-test.sh")
497+
scriptContent := `#!/bin/bash
498+
if [ "$1" == "status" ]; then
499+
exit 1 # Status check would fail
500+
elif [ "$1" == "shouldSkip" ]; then
501+
exit 0 # Should skip
502+
elif [ "$1" == "stop" ]; then
503+
exit 0
504+
elif [ "$1" == "start" ]; then
505+
exit 0
506+
else
507+
exit 1
508+
fi
509+
`
510+
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755)
511+
require.NoError(t, err)
512+
513+
watcher := make(chan packages.PackageInfo)
514+
controller := NewPackageController(watcher)
515+
controller.syncPeriod = 100 * time.Millisecond
516+
517+
// Set up an installed package that should be skipped
518+
controller.packageStatus["skip-status-pkg"] = &packages.PackageStatus{
519+
Name: "skip-status-pkg",
520+
Skipped: false,
521+
IsInstalled: true,
522+
Installing: false,
523+
Progress: 100,
524+
Status: false, // Will be set to true due to shouldSkip
525+
TargetVersion: "1.0.0",
526+
CurrentVersion: "1.0.0",
527+
ScriptPath: scriptPath,
528+
}
529+
530+
// Create context with timeout
531+
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
532+
defer cancel()
533+
534+
// Run the status runner
535+
go controller.statusRunner(ctx)
536+
537+
// Allow time for at least one sync cycle
538+
time.Sleep(controller.syncPeriod + 200*time.Millisecond)
539+
540+
// Verify that the package was marked as skipped and status set to true
541+
controller.RLock()
542+
status := controller.packageStatus["skip-status-pkg"]
543+
controller.RUnlock()
544+
545+
assert.True(t, status.Skipped, "Package should be marked as skipped")
546+
assert.True(t, status.Status, "Package status should be true when skipped")
547+
t.Logf("Package status: skipped=%v, status=%v", status.Skipped, status.Status)
548+
}
549+
345550
func TestRunCommand(t *testing.T) {
346551
// Create a temporary directory for test scripts
347552
tempDir, err := os.MkdirTemp("", "package-controller-test")
@@ -360,6 +565,8 @@ elif [ "$1" == "isInstalled" ]; then
360565
exit 0
361566
elif [ "$1" == "status" ]; then
362567
exit 0
568+
elif [ "$1" == "shouldSkip" ]; then
569+
exit 0
363570
else
364571
exit 1
365572
fi
@@ -377,6 +584,11 @@ fi
377584
err = runCommand(context.Background(), scriptPath, "isInstalled", nil)
378585
assert.NoError(t, err)
379586

587+
// Test runCommand with shouldSkip query (capturing output to avoid log file)
588+
var shouldSkipResult string
589+
err = runCommand(context.Background(), scriptPath, "shouldSkip", &shouldSkipResult)
590+
assert.NoError(t, err)
591+
380592
// Test runCommand with failing command
381593
err = runCommand(context.Background(), scriptPath, "invalid", nil)
382594
assert.Error(t, err)

pkg/gpud-manager/packages/packages.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type PackageInfo struct {
2020

2121
type PackageStatus struct {
2222
Name string `json:"name"`
23+
Skipped bool `json:"skipped"`
2324
IsInstalled bool `json:"is_installed"`
2425
Installing bool `json:"installing"`
2526
Progress int `json:"progress"`
@@ -51,7 +52,9 @@ func (ps PackageStatuses) RenderTable(wr io.Writer) {
5152
for _, status := range ps {
5253
// Determine status text
5354
statusText := "Not Installed"
54-
if status.IsInstalled {
55+
if status.Skipped {
56+
statusText = "Skipped"
57+
} else if status.IsInstalled {
5558
statusText = "✅"
5659
} else if status.Installing {
5760
statusText = "Installing"

0 commit comments

Comments
 (0)