CreateActorTemplate accepts 1 KiB memoryBytes (OOM-killing shared worker pod on wake), get resources -a <missing-atespace> exits 0, and CreateActor/DeleteAtespace hide missing resource details

Aperta
#3,290 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
28/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
go, kubernetes
Ambito
api, backend, cli, databases

Direzione di ricerca

Start with the listed validation and resource paths in api/ateapi/v1/ateapi.proto, cmd/ateapi/internal/apivalidation/actor_template.go, and internal/ocispec/ocispec.go, then trace the list, create, and delete paths cited under Affected Code Locations. Reproduce the missing-atespace and tiny-memory cases with the provided kubectl ate commands. Done means the four behaviors have clear, separately verified expected outcomes; the worker OOM containment cause is explicitly unestablished in the issue.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

area/api area/api-machinery area/cli area/node kind/bug
Summary & Root Cause
  • Part 1: API validation enforces only memory_bytes > 0, and ociResources passes the value through unclamped as the container's memory limit. CPU, by contrast, is raised to a kernel floor. Nothing stops a value far below what gVisor needs to start a sandbox, so the failure moves from admission (400) to the first wake. There, runsc create is killed and the actor goes to CRASHED. The kernel OOM kill (Memory cgroup out of memory: Killed process (runsc)) also took down the shared worker container (OOMKilled, restart). One tenant template can therefore restart a worker pod that other actors may be using. The code examined here does not establish why the OOM escapes the sandbox's limit and reaches the worker container. That part is observed behavior only.
  • Part 2: List RPCs filter rows by atespace without checking that the atespace exists, so a typo returns an empty page with RC 0.
  • Part 3: CreateActor resolves the template before anything checks the atespace. A missing atespace and a missing template both become the same errActorTemplateNotFound, and apierror.FromError drops the ObjectRef context that resolveActorTemplate added.
  • Part 4: The store collapses every foreign-key violation into one sentinel. The handler cannot say which child resource type (actors, templates, tags) blocks the delete, and the CLI help mentions only actors.
Affected Code Locations
  • Part 1 (tiny memoryBytes accepted and OOM-kills worker on wake):
    • api/ateapi/v1/ateapi.proto:1235-1248: Resources.memory_bytes only has +k8s:minimum=0. There is no lower bound.
    • cmd/ateapi/internal/apivalidation/zz_generated.validation.go:7541-7570: the generated check for memory_bytes is validate.Minimum(..., 0) only.
    • cmd/ateapi/internal/apivalidation/actor_template.go:86-97: ValidateCustom_ActorTemplate_Resources rejects only == 0 (Required).
    • cmd/ateapi/internal/apivalidation/zz_generated.validation.go:7526-7531: for CPU, -100 hits Minimum(0) ("must be greater than or equal to 0"), while 0 hits the custom Required at actor_template.go:90-91. That is why the two errors differ.
    • internal/ocispec/ocispec.go:63-78: ociResources copies memory_bytes unchanged into the OCI linux.resources.memory.limit (lines 69-71). CPU gets a kernel-minimum floor (cpuQuotaMinUS, lines 53-57 and 73). Memory has no floor at all.
    • internal/ocispec/ocispec.go:161-164: the container spec's Linux.Resources is set from ociResources.
  • Part 2 (get / List* in a missing atespace exits 0):
    • cmd/ateapi/internal/controlapi/actor.go:225-242: ListActors passes the atespace straight to the store with no existence check. The same applies to actor_template.go:103-118 (ListActorTemplates) and tag.go:87-101 (ListTags, which carries // TODO: implement this at line 99).
    • Contrast: cmd/kubectl-ate/internal/cmd/access_policy.go:170-185 (requireScope) calls GetAtespace and prints atespace %q not found.
  • Part 3 (CreateActor hides whether atespace or template is missing and drops ObjectRef):
    • cmd/ateapi/internal/controlapi/actor.go:86: CreateActor calls resolveActorTemplate first.
    • cmd/ateapi/internal/controlapi/actor_template.go:152-171: errActorTemplateNotFound = apierror.FailedPrecondition("actor template not found") (line 156), wrapped as "%w; ObjectRef: %s " (line 165).
    • internal/apierror/apierror.go:128-139: FromError returns apiErr.Error(), the apierror's own message without the wrapping context, so the ObjectRef suffix never reaches the client.
    • cmd/ateapi/internal/controlapi/actor.go:161-163: the Atespace %s not found branch exists but is unreachable for a missing atespace, because template lookup fails first.
  • Part 4 (DeleteAtespace returns generic "not empty" message vs. --help):
    • cmd/ateapi/internal/store/atepg/atespace.go:136-138: a foreign-key violation maps to bare store.ErrFailedPrecondition.
    • cmd/ateapi/internal/controlapi/atespace.go:120-121: the server formats this as Atespace %s is not empty.
    • cmd/kubectl-ate/internal/cmd/atespace.go:102: Short: "Delete an empty atespace (fails if any actors remain)".

Note: Earlier write-ups cited actor.go:86, 161-163 as the location of errActorTemplateNotFound. At 15b58199 the variable and its wrapping live in actor_template.go:152-171, and actor.go:161-163 is the unreachable "Atespace not found" branch.

Detailed Reproduction Guide
  1. Tiny memory limit:
metadata:
  name: s5-mem1k
  atespace: ate-demo-counter
workerSelector:
  matchLabels:
    workload: counter
resources:
  limits:
    cpuMillicores: "250"
    memoryBytes: "1024"
containers:
- name: counter
  image: gcr.io/<PROJECT_ID>/ate-images/counter
  command:
  - /ko-app/counter
kubectl ate create actor-template -f s5-mem1k.yaml
kubectl ate create actor s5-mem1k-actor -a ate-demo-counter --template s5-mem1k
# Wake through atenet-router (mTLS client), for example:
curl -sS -X POST --cert credential-bundle.pem --key credential-bundle.pem --cacert router-ca.pem \
  -H "ate-target-actor: ate-demo-counter/s5-mem1k-actor" https://atenet-router.ate-system.svc/
kubectl ate get actor s5-mem1k-actor -a ate-demo-counter -o yaml
# Check the worker pod that hosted the wake for an OOM restart:
kubectl -n ate-demo-counter get pods -o custom-columns=NAME:.metadata.name,RESTARTS:.status.containerStatuses[0].restartCount,LAST:.status.containerStatuses[0].lastState.terminated.reason
kubectl get events -A --field-selector reason=OOMKilling
  1. CPU 0 vs -100 (same template with cpuMillicores: "0", then "-100"):
kubectl ate create actor-template -f s5-cpu0.yaml
kubectl ate create actor-template -f s5-cpuneg.yaml
  1. Missing atespace on list:
kubectl ate get actors -a does-not-exist
kubectl ate get actor-templates -a does-not-exist
kubectl ate get tags -a does-not-exist
kubectl ate get access-policy -a does-not-exist
  1. Missing atespace vs missing template, and the delete message:
kubectl ate create actor x -a does-not-exist --template counter
kubectl ate create actor x -a ate-demo-counter --template nope
kubectl ate delete atespace ate-demo-counter
kubectl ate delete atespace --help | head -n 1
Expected vs. Actual Behavior
  • Expected:
    • memoryBytes: 1024 is rejected at create time with InvalidArgument and a documented minimum. A sandbox that exceeds its memory limit is contained to that actor and does not affect the worker pod. CPU 0 and -100 produce consistent messages.
    • Listing in a missing atespace returns NotFound: atespace "does-not-exist" not found, as get access-policy does.
    • CreateActor says which of atespace or template is missing, including <atespace>/<template>.
    • DeleteAtespace names the blocking resource types, and --help does not say "actors" only.
  • Actual:
    • The template is admitted (RC=0). The wake returns HTTP 500 and the actor goes ACTOR_STATE_CRASHED (runsc create killed). The shared worker container is also OOM-killed and restarted. CPU 0 gives Required value but -100 gives must be greater than or equal to 0.
    • The three list commands exit 0 with only a header.
    • Both create actor cases return the identical actor template not found.
    • Delete returns Atespace ate-demo-counter is not empty, while help says (fails if any actors remain).
Observed Output & Logs
$ kubectl ate create actor-template -f s5-mem1k.yaml
ATESPACE           NAME       GOLDEN TAG   ERROR   AGE
ate-demo-counter   s5-mem1k                        0s
RC=0
$ ate_curl s5-mem1k-actor
error resuming actor ate-demo-counter/s5-mem1k-actor
HTTP_CODE:500
status:
  assignedNode: gke-worker-node-1
  crash:
    crashTime: "2026-10-27T00:39:30.763493847Z"
    message: 'resume failed: atelet Run: while calling worker RunWorkload: rpc error:
      code = Internal desc = while creating the sandbox: while running `runsc create`:
      signal: killed'
  state: ACTOR_STATE_CRASHED
$ kubectl ate create actor-template -f s5-cpu0.yaml
Error: failed to create actor template: rpc error: code = InvalidArgument desc = actor_template.resources.limits.cpu_millicores: Required value
RC=1
$ kubectl ate create actor-template -f s5-cpuneg.yaml
Error: failed to create actor template: rpc error: code = InvalidArgument desc = actor_template.resources.limits.cpu_millicores: Invalid value: -100: must be greater than or equal to 0
RC=1
$ kubectl ate get actors -a does-not-exist
ATESPACE   NAME   TEMPLATE   STATE   WORKER POD   WORKER IPS   VERSION   AGE
RC=0
$ kubectl ate get actor-templates -a does-not-exist
ATESPACE   NAME   GOLDEN TAG   ERROR   AGE
RC=0
$ kubectl ate get tags -a does-not-exist
ATESPACE   NAME   SCOPE   STATE   SNAPSHOT   FIDELITY   AGE
RC=0
$ kubectl ate get access-policy -a does-not-exist
Error: atespace "does-not-exist" not found
RC=1
$ kubectl ate create actor x -a does-not-exist --template counter
Error: failed to create actor: rpc error: code = FailedPrecondition desc = actor template not found
RC=1
$ kubectl ate create actor x -a ate-demo-counter --template nope
Error: failed to create actor: rpc error: code = FailedPrecondition desc = actor template not found
RC=1
$ kubectl ate delete atespace ate-demo-counter
Error: failed to delete atespace: rpc error: code = FailedPrecondition desc = Atespace ate-demo-counter is not empty
RC=1
$ kubectl ate delete atespace --help | head -n 1
Delete an empty atespace (fails if any actors remain)
$ kubectl ate create actor-template -f r3-mem1k.yaml
ATESPACE           NAME       GOLDEN TAG   ERROR   AGE
ate-demo-counter   r3-mem1k                        0s
RC=0
$ ate_curl ate-demo-counter/r3-mem1k-actor
error resuming actor ate-demo-counter/r3-mem1k-actor
HTTP_CODE:500 TIME_TOTAL:0.521037s
{
  "state": "ACTOR_STATE_CRASHED",
  "crash": {
    "crashTime": "2026-10-27T02:00:27.157823263Z",
    "message": "resume failed: atelet Run: while calling worker RunWorkload: rpc error: code = Internal desc = while creating the sandbox: while running `runsc create`: signal: terminated"
  },
  "uid": "7d894d64-525c-450b-beb9-954cd9581553"
}
Lingua principale
Go
Stelle
5.9k
Fork
637
Merge medio
2g 1h
PR unite (30g)
523

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di agent-substrate/substrate

Tutte le issue di agent-substrate/substrate

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.