Skip to content

fix(api): keep CRD-only fields through edits, cap inline JSON sources, restart 409, safe owner label - #96

Merged
jbouder merged 1 commit into
mainfrom
fix/api-model-fidelity
Oct 19, 2026
Merged

jbouder merged 1 commit into
mainfrom
fix/api-model-fidelity

Conversation

@jbouder

@jbouder jbouder commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Closes #57.

What changed

  • CRD-only fields survive edits:
    • EnvVar gains valueFrom.
    • runtime gains keepAlive.
    • Resources takes any resource name (ephemeral-storage, nvidia.com/gpu, …, plus claims through extra="allow") instead of only cpu and memory.
  • PATCH merges runtime and access field by field (based on Pydantic's model_fields_set):
    • A field the request sends replaces the stored one, and null or empty removes it.
    • Fields it leaves out keep their value, so PATCH {"runtime": {"replicas": 3}} no longer wipes env, resources or keepAlive.
    • source is still replaced whole, since switching source type needs that.
  • UI edit form: it carries over resource names it doesn't edit, and sends resources: null to clear (an omitted field is now kept). It also shows a valueFrom variable read-only ("from secret site/token") instead of an empty box that would wipe it on save.
  • Inline cap on JSON: POST and PATCH with source.inline.files over MAX_UPLOAD_BYTES now return 413, the same cap uploads already had, counted the same way (content bytes). launch_app surfaces it.
  • restart on a stopped app (replicas: 0) returns 409, as the API reference already promised, instead of rolling nothing and returning 200.
  • Owner label: apps.nebari.dev/owner is written only when the username is a valid label value. spec.owner was already the owner of record and the label only a fallback. Rewriting a@b.c into a label could collide with another user's name and hand them the fallback ownership, so such usernames simply get no label.
  • Docs: API reference (PATCH merge semantics, 413 scope); CRD reference (owner/label).

Event matching is already fixed. Matching events by exact child names landed in #82: app_events uses field selectors on app-<name>, the pods and their ReplicaSets, so app-foo no longer matches app-foobar.

Testing

  • api: 144 pass. New tests/test_model_fidelity.py covers:
    • valueFrom, keepAlive and extra resource names survive PATCH {displayName}, PATCH {runtime: {replicas}} and a UI-style GET→PATCH round trip.
    • env: [] and resources: null clear their field.
    • 413 on POST and PATCH.
    • restart returns 409 when stopped.
    • an a@b.c user creates and edits their app.
  • mcp: 36 pass (new: launch_app over the cap → 413, nothing created).
  • UI: Biome, build and 51 tests pass. ruff and mypy are clean.
  • nebari-local, with this branch's API image, run in-process in the API pod against the real Kubernetes store (identity stubbed to a@b.c, owner checks on):
Step Result
create as a@b.c 201; spec.owner: a@b.c, only the subdomain label
kubectl patch: keepAlive, env[].valueFrom (fieldRef), ephemeral-storage request —
PATCH displayName, PATCH runtime.replicas=2, GET→PATCH round trip all 200; stored runtime still has keepAlive, valueFrom, ephemeral-storage, and replicas: 2
app pod printenv POD → the pod name (valueFrom reached the workload)
stop, then restart 409
PATCH inline source of 921601 bytes 413 … over the 921600-byte cap

Afterwards the cluster was restored: the test App deleted, original image, sync back on.

🤖 Generated with Claude Code

https://claude.ai/code/session_016pvMUQJoLkBDk6B94TTm45

…, restart 409, safe owner label

- Models carry what the CRD allows: env[].valueFrom, runtime.keepAlive and
  any resource name (ephemeral-storage, nvidia.com/gpu, claims).
- PATCH merges `runtime` and `access` field by field: fields the request
  leaves out keep their stored value (null/empty removes one), so a UI or
  MCP edit no longer drops what kubectl or GitOps set. The UI form carries
  resource names it does not edit, sends null to clear, and shows a
  valueFrom variable read-only.
- POST and PATCH (and so launch_app) return 413 when inline files exceed
  MAX_UPLOAD_BYTES, the cap uploads already had.
- restart returns 409 for a stopped app (replicas 0), as documented.
- The owner label is written only when the username is a valid label
  value; spec.owner stays the owner, so `a@b.c` can create and edit apps.

Event matching by exact child names already landed in #82.

Closes #57

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016pvMUQJoLkBDk6B94TTm45
@github-actions

Copy link
Copy Markdown

📄 Docs preview for fix/api-model-fidelity:
https://fix-api-model-fidelity.apps-pack.pages.dev

@jbouder
jbouder merged commit b04de81 into main Oct 19, 2026
23 checks passed
@jbouder
jbouder deleted the fix/api-model-fidelity branch October 19, 2026 16:21
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.

API model fidelity: preserve CRD-only fields on edit, cap inline sources on POST, fix restart/events/owner-label edge cases

1 participant