Live Service Status in Service Detail Page #350

Merged
rixx merged 4 commits from feat/341 into main 2026-01-22 15:02:55 +00:00
Owner

Implements #341

Implements #341
Display pod health status with polling:
- Status badge in header row with hover popover showing pod summary
- Pods table in Technical Details showing name, phase, ready state
- Instance events table showing last 10 events from instance namespace
- 10-second polling for status, events poll only when accordion visible

Uses "worst wins" logic for aggregating multiple pod statuses.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
add polling for connection credentials
All checks were successful
Tests / test (push) Successful in 33s
fc96965a1e
Credentials now poll every 10 seconds and automatically appear
when they become available, without requiring a page refresh.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
tobru requested review from rixx 2026-01-16 15:17:32 +00:00
Author
Owner

@rixx I've implemented this with the help of Claude Code, including the obra/superpowers skills. Before submitting the PR, I tested it locally in various variations and reviewed the code manually. To me, it looks good, and I think it integrates well. OK to merge?

Additionally, this PR adds polling for the connection credentials so that they appear once they're available.

One concern with polling I have is that when many detail pages are open and all of them are polling every 10s, without backoff, etc., it could DDoS the Django application or the Kubernetes API. For now, I think this is negligible, but after a certain growth of Servala this should certainly be changed.

@rixx I've implemented this with the help of Claude Code, including the [obra/superpowers](https://github.com/obra/superpowers) skills. Before submitting the PR, I tested it locally in various variations and reviewed the code manually. To me, it looks good, and I think it integrates well. OK to merge? Additionally, this PR adds polling for the connection credentials so that they appear once they're available. One concern with polling I have is that when many detail pages are open and all of them are polling every 10s, without backoff, etc., it could DDoS the Django application or the Kubernetes API. For now, I think this is negligible, but after a certain growth of Servala this should certainly be changed.
@ -0,0 +1,191 @@
# Service Instance Live Status Design
Member

Are we going to commit design documents like this in the future, too?

Are we going to commit design documents like this in the future, too?
Author
Owner

This is an outcome of using obra/superpowers and it helps to have a context for Claude Code. In this case it probably doesn't add any additional value, and I'll remove it. It might add value in bigger changes, though. What's your take on this?

This is an outcome of using obra/superpowers and it helps to have a context for Claude Code. In this case it probably doesn't add any additional value, and I'll remove it. It might add value in bigger changes, though. What's your take on this?
Member

I generally feel that plans are more oriented towards action, so keeping them around does not add much value. Would probably better to prompt AI to add design documents (e.g. on db design like the very rough sketch on relations I put in CLAUDE.md) if there are missing docs.

I generally feel that plans are more oriented towards action, so keeping them around does not add much value. Would probably better to prompt AI to add design documents (e.g. on db design like the very rough sketch on relations I put in CLAUDE.md) if there are missing docs.
rixx marked this conversation as resolved
@ -1378,0 +1383,4 @@
"""Get the namespace where the service pods are deployed."""
if not self.kubernetes_object:
return None
status = self.kubernetes_object.get("status", {})
Member

I prefer status = self.kubernetes_object.get("status") or {}, as if status happens to be null, this still falls back to an empty object. Otherwise, the next line will throw an exception.

I prefer `status = self.kubernetes_object.get("status") or {}`, as if `status` happens to be `null`, this still falls back to an empty object. Otherwise, the next line will throw an exception.
rixx marked this conversation as resolved
@ -1378,0 +1399,4 @@
"status": "unknown",
"status_label": _("Unknown"),
"badge_class": "secondary",
"icon": "question-circle",
Member

This is basically a matter of taste: I tend to leave icon handling to templates, which can decide based on the status key which icon and badge classes to use. Deciding this kind of thing on the database model level feels overly verbose and like it breaks the layers of separation. Same goes for popover_html. Putting a status, pods and a summary there seems sufficient to me, and carries the required information, leaving the display of the information up to the template.

Looking at how this code is used, I'd probably go further and just add a pods property to this class, and leave the entire handling and interpretation of the pod status itself up to the processing view (or at most a helper utils function). That way, the already extensive models file doesn't get cluttered with presentation code.

This is basically a matter of taste: I tend to leave icon handling to templates, which can decide based on the `status` key which icon and badge classes to use. Deciding this kind of thing on the database model level feels overly verbose and like it breaks the layers of separation. Same goes for `popover_html`. Putting a status, pods and a summary there seems sufficient to me, and carries the required information, leaving the display of the information up to the template. Looking at how this code is used, I'd probably go further and just add a `pods` property to this class, and leave the entire handling and interpretation of the pod status itself up to the processing view (or at most a helper utils function). That way, the already extensive models file doesn't get cluttered with presentation code.
@ -1378,0 +1402,4 @@
"icon": "question-circle",
"pods": [],
"summary": _("No instance namespace available"),
"popover_html": str(_("Status cannot be determined")),
Member

There shouldn’t be a need to cast the translated strings to string here explicitly.

There shouldn’t be a need to cast the translated strings to string here explicitly.
@ -356,0 +358,4 @@
def get_object(self, **kwargs):
instance = super().get_object(**kwargs)
# Clear cached k8s data on HTMX polling requests to get fresh status
if self.is_htmx and self._get_fragment():
Member

Where would this cache pollution come from? These are cached properties, but they are cached on the object, which is newly instantiated on the request, so the cache can only be as old as the request/response cycle. Under which circumstances are these properties outdated (or even pre-computed) and need deleting? We may want to drop the use of cached_property for these if this is a concern…

Where would this cache pollution come from? These are cached properties, but they are cached on the object, which is newly instantiated on the request, so the cache can only be as old as the request/response cycle. Under which circumstances are these properties outdated (or even pre-computed) and need deleting? We may want to drop the use of `cached_property` for these if this is a concern…
rixx force-pushed feat/341 from fc96965a1e
All checks were successful
Tests / test (push) Successful in 33s
to 06d869e0c4
All checks were successful
Tests / test (push) Successful in 35s
2026-01-22 13:14:08 +00:00
Compare
Remove unused plan file, make property access safer
All checks were successful
Tests / test (push) Successful in 40s
5b6c073fc0
rixx merged commit d996c9370d into main 2026-01-22 15:02:55 +00:00
rixx deleted branch feat/341 2026-01-22 15:02:55 +00:00
Sign in to join this conversation.
No description provided.