lightshuttle_runtime/lifecycle/view.rs
1//! Lightweight value types exposed by the control plane.
2//!
3//! Built on demand by [`crate::ManagerHandle`] and surfaced through the
4//! [`crate::LifecycleHandle`] trait. The shape is intentionally stable
5//! across runtime backends so callers (REST API, dashboard, CLI) never
6//! see backend-specific types.
7
8use std::time::SystemTime;
9
10use serde::{Deserialize, Serialize};
11
12use crate::lifecycle::status::NodeStatus;
13use lightshuttle_spec::ImageSource;
14
15/// Dashboard-friendly view of a single managed resource.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct ResourceView {
18 /// Manifest-declared resource name.
19 pub name: String,
20 /// Resource kind discriminant (`postgres`, `redis`, `container`, `dockerfile`).
21 pub kind: String,
22 /// Coarse-grained status suitable for UI rendering.
23 pub status: ResourceStatus,
24 /// Whether the resource passed its healthcheck.
25 pub healthy: bool,
26 /// Container image reference, as resolved at start time.
27 pub image: String,
28 /// Wall-clock time at which the runtime accepted the start request.
29 pub started_at: Option<SystemTime>,
30 /// Last terminal failure reason, when applicable.
31 pub last_error: Option<String>,
32}
33
34/// Coarse-grained resource status, derived from [`NodeStatus`] and
35/// flattened for UI consumption.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37pub enum ResourceStatus {
38 /// Not started yet.
39 Pending,
40 /// Container is booting up.
41 Starting,
42 /// Container is up, with or without a green healthcheck.
43 Running,
44 /// Resource has entered a terminal failure state.
45 Failed,
46 /// Resource has been stopped on request.
47 Stopped,
48}
49
50impl From<&NodeStatus> for ResourceStatus {
51 fn from(status: &NodeStatus) -> Self {
52 match status {
53 NodeStatus::Pending => Self::Pending,
54 NodeStatus::Starting => Self::Starting,
55 NodeStatus::Running | NodeStatus::Healthy => Self::Running,
56 NodeStatus::Failed { .. } => Self::Failed,
57 NodeStatus::Stopped => Self::Stopped,
58 }
59 }
60}
61
62/// Render an [`ImageSource`] as the user-facing image reference: the
63/// pulled image string for `Pull`, the produced tag for `Build`.
64pub(crate) fn image_label(src: &ImageSource) -> String {
65 match src {
66 ImageSource::Pull(s) => s.clone(),
67 ImageSource::Build { tag, .. } => tag.clone(),
68 }
69}
70
71/// Extract the last error message from a [`NodeStatus`], when terminal.
72pub(crate) fn last_error_from(status: &NodeStatus) -> Option<String> {
73 match status {
74 NodeStatus::Failed { reason } => Some(reason.clone()),
75 _ => None,
76 }
77}