eugenegujing opened a new pull request, #7944:
URL: https://github.com/apache/texera/pull/7944

   ### What changes were proposed in this PR?
   
   When a computing unit becomes unhealthy(the pod is OOM-killed into a crash 
loop, evicted for disk pressure, stuck on an image pull, or its node becomes 
unreachable), the UI used to show **"(Connecting)"** with a "starting up" 
tooltip forever, because `ComputingUnitState` only had `Running` and `Pending` 
and status resolution only looked at `pod.status.phase`.
   
   This PR follows the decisions settled in #7670: mirror Kubernetes for the 
status vocabulary; owner-only, actionable failure reasons; no auto-recovery — 
users delete and recreate (the existing terminate flow already handles dead 
pods).
   
   #### Screenshots
   
   Two representative states as examples, and the complete set of states and 
reasons is the mapping table in the next section, and every display combination 
(status × reason × owner) was verified manually.
   
   An `Unknown` unit (node unreachable): red badge, disabled "Unit Unavailable" 
run button, and the owner-facing reason on the row tooltip:
   
   <img width="614" height="135" alt="Screenshot 2026-08-23 at 12 21 44 AM" 
src="https://github.com/user-attachments/assets/dc3f05f4-a723-46d3-8306-29f827fd530b";
 />
   
   A recovered OOM-killed unit: stays green and runnable, but the tooltip 
carries the out-of-memory warning with the restart count:
   
   <img width="550" height="241" alt="Screenshot 2026-08-23 at 1 05 13 AM" 
src="https://github.com/user-attachments/assets/555d440c-411a-4b6f-a970-bef3a7ac2555";
 />
   
   #### Status vocabulary and reasons
   
   `ComputingUnitState` gains `Failed`, `Unknown`, `Terminating`. The full 
mapping from observed pod state to status and owner-facing `statusReason`:
   
   | Observed pod state | Status | Owner-facing `statusReason` |
   |---|---|---|
   | `deletionTimestamp` set | `Terminating` | — |
   | phase `Failed` + reason `Evicted`, message mentions ephemeral/disk | 
`Failed` | "The computing unit was evicted because it ran out of local disk 
storage. Consider storing less data on the unit's local file system, or 
recreate it with more storage." |
   | phase `Failed` + reason `Evicted`, other | `Failed` | "The computing unit 
was evicted by the cluster (\<first sentence of the cluster message, capped at 
120 chars\>). Consider recreating it." |
   | container waiting `ImagePullBackOff` / `ErrImagePull` / `InvalidImageName` 
| `Failed` | "The computing unit's image could not be pulled. Please recreate 
the unit or contact an administrator." |
   | container waiting `CrashLoopBackOff`, last termination `OOMKilled` | 
`Failed` | "The computing unit keeps crashing because it runs out of memory. 
Please terminate it and recreate it with a higher memory limit." |
   | container waiting `CrashLoopBackOff`, other | `Failed` | "The computing 
unit is repeatedly crashing (restarted N times). Please terminate and recreate 
it, or contact an administrator." |
   | phase `Failed`, not evicted | `Failed` | "The computing unit stopped 
unexpectedly. Please terminate and recreate it, or contact an administrator." |
   | phase `Unknown` | `Unknown` | "The state of the computing unit cannot be 
determined (its node may be unreachable)." |
   | phase `Pending` + `PodScheduled=False/Unschedulable` condition | `Pending` 
| "The computing unit is waiting for cluster resources to become available." |
   | phase `Running` + a container's last termination `OOMKilled` | `Running` | 
"The last run was terminated because the computing unit ran out of memory 
(restarted N times). Consider recreating the unit with a higher memory limit 
before running the same workload." |
   | phase `Running`, healthy | `Running` | — |
   | anything else / pod absent / `local` unit | `Pending` / `Running` 
(unchanged) | — |
   
   Precedence is top-to-bottom; in particular a `CrashLoopBackOff` failure wins 
over the recovered-OOM warning, and `Terminating` wins over everything.
   
   `statusReason` is **owner-only**: the backend sends `null` to shared users 
at every DTO construction site, and the frontend then falls back to a generic 
text. Frontend fallbacks when `statusReason` is null: `Running` → "Ready to 
use", `Pending` → "Computing unit is starting up", `Failed`/`Unknown` → "This 
computing unit is unavailable.", `Terminating` → "Computing unit is shutting 
down", other states → the status word itself.
   
   #### Backend
   
   - The existing single namespace-level pod listing now yields a 
`PodStatusSnapshot` per pod (phase, deletionTimestamp, pod reason/message, 
`Unschedulable` condition, per-container waiting reason / last termination 
reason / restart count) via a pure, unit-testable transform — **no additional 
Kubernetes round trips**.
   - The restartPolicy subtlety that shaped the design: with `restartPolicy: 
Always`, an OOM-killed container restarts in place and the pod phase never 
leaves `Running`, so OOM kills are only visible through 
`containerStatuses[].lastState.terminated.reason`. A unit that recovered stays 
`Running` with a warning; a unit that cannot come back up lands in 
`CrashLoopBackOff` and is reported `Failed`.
   - Vanished-pod reconciliation (#6853/#6854) is untouched: it stays keyed 
purely on pod presence, and a present-but-`Failed` pod is **not** treated as 
vanished (covered by tests).
   - `local` units are unchanged (always `Running`) — local liveness is out of 
scope here, per the discussion.
   
   #### Frontend
   
   - `status` union widened to the five states; `statusReason?` added.
   - `Failed`/`Unknown`: red badge in the dropdown (the previously dead red 
branch, now reachable).
   - No text suffixes next to the unit name for any state: a "(Unavailable)" 
label truncated at real dropdown widths, and the pre-existing Pending 
"(Connecting)" suffix was equally redundant (gold badge + tooltip + the run 
button's own "Connecting" spinner) — status is uniformly conveyed by the badge 
color and explained by the row tooltip.
   - Each dropdown row has exactly **one tooltip surface — the row itself**: 
hovering the row body shows the status/reason, plus "Cannot select." when the 
unit is not selectable. The nested badge and name tooltips were removed because 
they stacked a second bubble on top of the row's (the name's tooltip was the 
raw pod URI, which stays available in the details modal; the action icons keep 
their own tooltips, as on main). This also makes the recovered-OOM warning on a 
`Running` unit reachable by hovering the row rather than a few-pixel dot.
   - The run button shows a disabled **"Unit Unavailable"** state instead of 
the endless "Connecting" spinner when the selected unit is `Failed`/`Unknown` 
(the spinner branch never consulted unit status).
   - The ". Cannot select." concatenation now trims a trailing period off the 
status tooltip before appending, since a `statusReason` is a full sentence — 
previously this produced a doubled dot.
   
   ### Any related issues, documentation, discussions?
   
   Closes #7669. The design questions (status vocabulary, how much detail 
reaches which users, auto-recovery) were discussed and settled in #7670.
   
   ### How was this PR tested?
   
   - TDD: one backend test per decision-table row using `PodBuilder`-built pods 
through the pure snapshot + mapping functions, including both eviction 
wordings, all three image-pull reasons, crash-loop with and without OOM 
history, the crash-loop-beats-recovered-OOM precedence, multi-container pods, 
owner gating, and the absent-pod path used by creation polling. fabric8 
null-guard paths (`status`, `conditions`, `containerStatuses`, `state`, 
`lastState`, `terminated`) have dedicated tests.
   - Mutation-checked: precedence swap, Evicted→Pending, wording-branch 
removal, restart-count hardcoding, owner-gate removal, absent-pod→Unknown, and 
run-button branch removal each make at least one test fail.
   - Edge-case sweep on top of the row-by-row tests: Terminating precedence 
over eviction (and over a status-less pod), eviction-message 
truncation/whitespace/case-insensitivity, mixed multi-container pods (the 
crash-looping container's own history decides the wording), stale/malformed 
`Unschedulable` conditions, image-pull-beats-crash-loop precedence, 
empty-string statusReason not shadowing the fallback, and a pinning test 
documenting that phase `Succeeded` maps to `Pending`.
   - `ComputingUnitManagingService` util specs 90/90, resource specs 60/60, 
scalafmt clean; frontend `tsc --noEmit` clean, prettier clean, all changed spec 
files pass under `ng test`.
   - Manually verified every frontend display state (all status × reason × 
owner combinations) against a live dev stack.
   
   Notes for more information: `Option[String] → null` serialization relies on 
`DefaultScalaModule`, registered on the service's Dropwizard `ObjectMapper`; 
there is no end-to-end JSON test for the new field. A bare pod in phase 
`Succeeded` still maps to `Pending` (pinned by a test) — with `restartPolicy: 
Always` this phase is practically unreachable, so it is left as a known 
limitation. Follow-ups deliberately out of scope (per #7670): frontend timeout 
for the "pod Running but engine unreachable" zombie case, UX for the silent 
vanish-reconcile, and `local` CU liveness.
   
   ### Was this PR authored or co-authored using generative AI tooling?
   
   Co-authored by: Claude Code (Claude Fable 5)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to