Skip to content

Commit 8ff1fe7

Browse files
committed
fix: added test for radis store, build sandox object and fix what reviewers ask volcano-sh#360
- CodeInterpreter now explicitly sets DefaultSandboxTTL (8h) when MaxSessionDuration is nil, in both the direct sandbox path and the warm-pool placeholder path, preserving the agreed-upon behavior while AgentRuntime remains unlimited by default. - Add TestRedisStore_StoreSandbox_ZeroExpiresAt: verifies that a sandbox with zero ExpiresAt is stored and retrievable, absent from the expiry index (not hard-deleted), and still tracked in the last-activity index (idle-timeout GC still works). - Add TestBuildSandboxObject_NoTTL: verifies that ttl==0 produces a Sandbox with nil ShutdownTime (AgentRuntime indefinite lifetime path). - Replace DefaultSandboxTTL with time.Hour in workload_builder_test.go since DefaultSandboxTTL is no longer the implied default for every buildSandboxObject caller. - Update example/agent-runtime/agent-runtime.yaml: remove hard-coded maxSessionDuration: 8h and replace with a commented-out example that documents the field is optional. Signed-off-by: HarshitPal25 <harshit13082006@gmail.com>
1 parent ca81bed commit 8ff1fe7

4 files changed

Lines changed: 99 additions & 9 deletions

File tree

example/agent-runtime/agent-runtime.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ spec:
1919
image: busybox
2020
command: ["/bin/sh", "-c", "sleep 36000"]
2121
sessionTimeout: "15m"
22-
maxSessionDuration: "8h"
22+
# maxSessionDuration is optional. When omitted, the sandbox runs indefinitely
23+
# and is only cleaned up by the idle timeout (sessionTimeout) above.
24+
# Uncomment the line below to set a hard upper limit on session lifetime.
25+
# maxSessionDuration: "24h"

pkg/store/store_redis_test.go

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,13 +343,70 @@ func TestUpdateSandboxLastActivity(t *testing.T) {
343343
if int64(score) != newLastActivity.Unix() {
344344
t.Fatalf("unexpected lastActivity score after update: got %v, want %v", score, newLastActivity.Unix())
345345
}
346+
}
347+
348+
// TestRedisStore_StoreSandbox_ZeroExpiresAt verifies the behavior for AgentRuntime
349+
// sandboxes that have no MaxSessionDuration (ExpiresAt == zero time):
350+
// - The session is still stored and retrievable by session ID.
351+
// - The session is NOT added to the expiry sorted-set (no hard cap).
352+
// - The session IS tracked in the last-activity index after UpdateSessionLastActivity,
353+
// so idle-timeout GC still works correctly.
354+
func TestRedisStore_StoreSandbox_ZeroExpiresAt(t *testing.T) {
355+
ctx := context.Background()
356+
c, mr := newTestRedisClient(t)
357+
358+
// Zero ExpiresAt — simulates an AgentRuntime sandbox with no MaxSessionDuration.
359+
sb := &types.SandboxInfo{
360+
SandboxID: "sb-no-expiry",
361+
Name: "test-sandbox-no-expiry",
362+
SessionID: "sess-no-expiry",
363+
CreatedAt: time.Now().UTC(),
364+
ExpiresAt: time.Time{}, // zero
365+
Status: "running",
366+
}
367+
368+
// StoreSandbox must succeed even with zero ExpiresAt.
369+
if err := c.StoreSandbox(ctx, sb); err != nil {
370+
t.Fatalf("StoreSandbox with zero ExpiresAt failed: %v", err)
371+
}
372+
373+
// The session must be retrievable.
374+
got, err := c.GetSandboxBySessionID(ctx, "sess-no-expiry")
375+
if err != nil {
376+
t.Fatalf("GetSandboxBySessionID failed: %v", err)
377+
}
378+
if got.SandboxID != "sb-no-expiry" {
379+
t.Errorf("expected SandboxID sb-no-expiry, got %q", got.SandboxID)
380+
}
346381

347-
// session not exists
348-
err = c.UpdateSessionLastActivity(ctx, "sess-1-not-exist", newLastActivity)
382+
// Must NOT be in the expiry sorted-set (zero ExpiresAt = no hard cap).
383+
_, err = mr.ZScore(c.expiryIndexKey, "sess-no-expiry")
349384
if err == nil {
350-
t.Fatalf("expected error for non-existent session")
385+
t.Error("expected sess-no-expiry to be absent from expiry index, but ZScore succeeded")
351386
}
352-
if !errors.Is(err, ErrNotFound) {
353-
t.Fatalf("expected ErrNotFound, got %v", err)
387+
388+
// Must NOT appear in ListExpiredSandboxes even at a very late cutoff.
389+
farFuture := time.Now().Add(100 * 365 * 24 * time.Hour)
390+
expired, err := c.ListExpiredSandboxes(ctx, farFuture, 100)
391+
if err != nil {
392+
t.Fatalf("ListExpiredSandboxes error: %v", err)
393+
}
394+
for _, s := range expired {
395+
if s.SessionID == "sess-no-expiry" {
396+
t.Error("sess-no-expiry must not appear in ListExpiredSandboxes")
397+
}
398+
}
399+
400+
// After UpdateSessionLastActivity the session MUST be tracked in the activity index.
401+
now := time.Now().UTC().Truncate(time.Second)
402+
if err := c.UpdateSessionLastActivity(ctx, "sess-no-expiry", now); err != nil {
403+
t.Fatalf("UpdateSessionLastActivity failed: %v", err)
404+
}
405+
score, err := mr.ZScore(c.lastActivityIndexKey, "sess-no-expiry")
406+
if err != nil {
407+
t.Fatalf("expected sess-no-expiry in last-activity index: %v", err)
408+
}
409+
if int64(score) != now.Unix() {
410+
t.Errorf("unexpected last-activity score: got %v, want %v", int64(score), now.Unix())
354411
}
355412
}

pkg/workloadmanager/workload_builder.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,15 @@ func buildSandboxByCodeInterpreter(namespace string, codeInterpreterName string,
401401
},
402402
},
403403
}
404+
// CodeInterpreter always has a hard cap: use explicit MaxSessionDuration or
405+
// fall back to DefaultSandboxTTL (8h). This differs from AgentRuntime where
406+
// omitting MaxSessionDuration means no hard expiry.
407+
ttl := DefaultSandboxTTL
404408
if codeInterpreterObj.Spec.MaxSessionDuration != nil {
405-
shutdownTime := metav1.NewTime(time.Now().Add(codeInterpreterObj.Spec.MaxSessionDuration.Duration))
406-
simpleSandbox.Spec.Lifecycle.ShutdownTime = &shutdownTime
409+
ttl = codeInterpreterObj.Spec.MaxSessionDuration.Duration
407410
}
411+
shutdownTime := metav1.NewTime(time.Now().Add(ttl))
412+
simpleSandbox.Spec.Lifecycle.ShutdownTime = &shutdownTime
408413
sandboxEntry.Kind = types.SandboxClaimsKind
409414
return simpleSandbox, sandboxClaim, sandboxEntry, nil
410415
}
@@ -444,8 +449,13 @@ func buildSandboxByCodeInterpreter(namespace string, codeInterpreterName string,
444449
idleTimeout: idleTimeout,
445450
}
446451

452+
// CodeInterpreter always has a hard cap: use explicit MaxSessionDuration or
453+
// fall back to DefaultSandboxTTL (8h). This differs from AgentRuntime where
454+
// omitting MaxSessionDuration means no hard expiry.
447455
if codeInterpreterObj.Spec.MaxSessionDuration != nil {
448456
buildParams.ttl = codeInterpreterObj.Spec.MaxSessionDuration.Duration
457+
} else {
458+
buildParams.ttl = DefaultSandboxTTL
449459
}
450460
sandbox := buildSandboxObject(buildParams)
451461
return sandbox, nil, sandboxEntry, nil

pkg/workloadmanager/workload_builder_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestBuildSandboxObject_DoesNotMutateCallerLabels(t *testing.T) {
5151
namespace: "default",
5252
sandboxName: "sandbox-abc",
5353
sessionID: "session-123",
54-
ttl: DefaultSandboxTTL,
54+
ttl: time.Hour,
5555
idleTimeout: DefaultSandboxIdleTimeout,
5656
podLabels: original,
5757
}
@@ -115,6 +115,26 @@ func TestBuildSandboxObject_NilLabels(t *testing.T) {
115115
}
116116
}
117117

118+
// TestBuildSandboxObject_NoTTL verifies that when ttl==0 (AgentRuntime with no
119+
// MaxSessionDuration configured), ShutdownTime is nil — the sandbox runs
120+
// indefinitely and is only cleaned up by idle timeout.
121+
func TestBuildSandboxObject_NoTTL(t *testing.T) {
122+
params := &buildSandboxParams{
123+
namespace: "default",
124+
sandboxName: "sandbox-no-ttl",
125+
sessionID: "session-no-ttl",
126+
ttl: 0, // explicitly no TTL — AgentRuntime without MaxSessionDuration
127+
idleTimeout: 15 * time.Minute,
128+
}
129+
130+
sandbox := buildSandboxObject(params)
131+
132+
if sandbox.Spec.Lifecycle.ShutdownTime != nil {
133+
t.Errorf("expected ShutdownTime to be nil for zero-ttl sandbox, got %v",
134+
sandbox.Spec.Lifecycle.ShutdownTime)
135+
}
136+
}
137+
118138
// TestBuildSandboxObject_WorkloadNameLabel verifies that the WorkloadNameLabelKey
119139
// label is correctly set on the Sandbox object metadata from the workloadName param.
120140
// This covers the bug where CodeInterpreter sandboxes were missing this label.

0 commit comments

Comments
 (0)