Skip to content

sandbox-apiserver: derive the claim TTL from the submitted Sandbox - #5

Merged
CMGS merged 5 commits into
masterfrom
fix/create-ttl-passthrough
Aug 17, 2026
Merged

sandbox-apiserver: derive the claim TTL from the submitted Sandbox#5
CMGS merged 5 commits into
masterfrom
fix/create-ttl-passthrough

Conversation

@doge-rgb

@doge-rgb doge-rgb commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Problem

The aggregated Create path claimed every sandbox with a hardcoded TTL of 0 (storage.go: r.store.Claim(ctx, namespace, name, pool, 0)), so sandboxd applied its default lease and every k8s-created Sandbox lived exactly 5 minutes — spec.shutdownTime and any requested lifetime were silently ignored. Nothing is stored on this path, so the claim body is the only moment the node can hear a lifetime.

Change

  • Create derives the lease from the submitted object: spec.shutdownTime (the API's own lifecycle field, rounded up to whole seconds) wins; the sandbox.cocoonstack.io/ttl-seconds annotation covers clients that cannot set the field (the same key the vk-sandbox provider reads on pods).
  • Unusable lifetimes 400 before a microVM is spent: a shutdownTime already in the past or a malformed/negative annotation is rejected up front instead of silently claiming with the default lease.
  • The node-granted deadline is reported back: ClaimResult.Deadline is now a time.Time and rides Assignment.Deadline through claim / gateway / fork / snapshot-claim; Create stamps it as the sandbox.cocoonstack.io/deadline annotation (RFC3339) beside the claim id and address, leaving the submitted spec untouched. The node clamps the ask to its own default and maximum, so the grant — not an echo of the request — is what callers see. InventoryEntry gains an optional deadline so the synthesized Get/List path stamps the identical key once nodes publish it (vk-sandbox follow-up), and a refreshed deadline surfaces as a Modified entry.

Testing

  • go test ./... green; make lint (linux + darwin) 0 issues.
  • New unit coverage: fixed-clock derivation table (rounding, precedence, reject matrix), Create-level wiring (TTL reaches the store, 400 spends no claim), granted-deadline annotation on Create and on the synthesized read path.
  • Follow-up in vk-sandbox after this merges: provider/create.go consumes ClaimResult.Deadline as time.Time, and inventory/publisher.go fills InventoryEntry.Deadline.
  • End-to-end on a two-node test cluster running the real sandboxd stack:
    • kubectl create with spec.shutdownTime now+90s → response carried the granted deadline (verified before the switch to the annotation; unit-tested since); node log reaped expired sandbox … 5 s after the deadline, VM process gone.
    • annotation ttl-seconds: "600" → deadline exactly create+600 s.
    • no lifetime → sandboxd default 300 s deadline echoed (behavior unchanged).
    • ttl-seconds: "banana" → 400, no claim spent.

doge-rgb and others added 5 commits August 17, 2026 15:17
Create claimed every sandbox with a hardcoded TTL of 0, so the node
applied its default lease and every k8s-created sandbox lived exactly
five minutes whatever the caller asked for. Nothing is stored on this
path - the claim is the node's only chance to hear a lifetime.

Create now derives ttl_seconds from spec.shutdownTime (rounded up to
whole seconds), or from the sandbox.cocoonstack.io/ttl-seconds
annotation for clients that cannot set the field - the same key the
vk-cocoon-sandbox provider reads on pods, now defined once in package
scale. Unusable lifetimes (a shutdownTime already past, a malformed
annotation) are rejected with a 400 before a warm microVM is spent.

The node's granted deadline now rides ClaimResult/Assignment back and
is reported as spec.shutdownTime on the returned object: the node
clamps the ask to its own default and maximum, so the grant - not the
echo of the request - is the truth. Verified end-to-end on a two-node
test cluster: a 90 s shutdownTime came back verbatim and the node
reaped the sandbox five seconds after its deadline.
Test funcs before helpers so the layout gate passes; drop the
internal/lifecycle import (TimeLeft with two nil args is t.Sub(now));
math.Ceil for the whole-second round-up; one-arm ShutdownTime echo;
comments cut to the budget and the sibling module name corrected.
Only the aggregated apiserver reads the key; the scale definition had no
second consumer, so the alias hop bought nothing.
Create no longer rewrites the submitted spec.shutdownTime; the node's
granted expiry rides sandbox.cocoonstack.io/deadline beside the claim id
and address. InventoryEntry gains an optional deadline so the synthesized
Get/List path stamps the identical key once nodes publish it, and a
refreshed deadline surfaces as a Modified entry.
@CMGS
CMGS merged commit b86d04f into master Aug 17, 2026
2 checks passed
@CMGS
CMGS deleted the fix/create-ttl-passthrough branch August 17, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants