Skip to main content

lilo_rm_core/
admin.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use chrono::{DateTime, Utc};
5
6use crate::{Lifecycle, LogAvailability, LostEvidence, VersionInfo};
7
8/// Result of a kill request.
9#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
10#[non_exhaustive]
11#[serde(rename_all = "snake_case")]
12pub enum KillOutcome {
13    /// A signal was delivered to the target process.
14    Signalled,
15    /// The target process had already exited before the signal landed.
16    AlreadyExited,
17}
18
19#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
20pub struct KillByPidRequest {
21    pub pid: u32,
22    pub signal: i32,
23    pub grace_secs: u64,
24}
25
26#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
27pub struct KillByPidResponse {
28    pub pid: u32,
29    pub signal: i32,
30    pub killed_after_grace: bool,
31    pub outcome: KillOutcome,
32}
33
34#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
35pub struct StatusFilter {
36    pub session_id: Option<Uuid>,
37    #[serde(default, skip_serializing_if = "Vec::is_empty")]
38    pub session_ids: Vec<Uuid>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub updated_since: Option<DateTime<Utc>>,
41    pub runtime: Option<String>,
42    pub state: Option<String>,
43}
44
45impl StatusFilter {
46    pub const fn empty() -> Self {
47        Self {
48            session_id: None,
49            session_ids: Vec::new(),
50            updated_since: None,
51            runtime: None,
52            state: None,
53        }
54    }
55
56    pub fn requested_session_ids(&self) -> Vec<Uuid> {
57        let mut ids = self.session_ids.clone();
58        if let Some(session_id) = self.session_id
59            && !ids.contains(&session_id)
60        {
61            ids.push(session_id);
62        }
63        ids
64    }
65}
66
67impl Default for StatusFilter {
68    fn default() -> Self {
69        Self::empty()
70    }
71}
72
73#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
74pub struct StatusResponse {
75    pub lifecycles: Vec<Lifecycle>,
76}
77
78#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79pub struct WatcherCounts {
80    pub process_exit_watchers: usize,
81    pub shim_sockets: usize,
82    pub event_waiters: usize,
83}
84
85#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
86pub struct LifecycleCounts {
87    pub forking: u64,
88    pub running: u64,
89    pub exited: u64,
90    pub lost: u64,
91}
92
93#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
94pub struct MigrationState {
95    pub applied: usize,
96    pub total: usize,
97    pub applied_descriptions: Vec<String>,
98    pub pending_descriptions: Vec<String>,
99}
100
101#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct RecentLostEvent {
103    pub session_id: Uuid,
104    pub evidence: LostEvidence,
105    pub occurred_at: DateTime<Utc>,
106}
107
108#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
109pub struct LauncherStatus {
110    pub runtime: String,
111    pub command: Option<String>,
112    pub error: Option<String>,
113}
114
115#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
116pub struct TmuxStatus {
117    pub available: bool,
118    pub version: Option<String>,
119    pub error: Option<String>,
120}
121
122#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
123pub struct DockerReadiness {
124    pub ready: bool,
125    pub detail: Option<String>,
126    pub error: Option<String>,
127}
128
129impl DockerReadiness {
130    pub fn ready(detail: impl Into<String>) -> Self {
131        Self {
132            ready: true,
133            detail: Some(detail.into()),
134            error: None,
135        }
136    }
137
138    pub fn unavailable(error: impl Into<String>) -> Self {
139        Self {
140            ready: false,
141            detail: None,
142            error: Some(error.into()),
143        }
144    }
145}
146
147#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
148pub struct DockerIsolationStatus {
149    pub supported: bool,
150    pub default_workspace: String,
151    pub experimental: bool,
152}
153
154#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
155pub struct DockerStatus {
156    pub cli: DockerReadiness,
157    pub daemon: DockerReadiness,
158    pub manifest_validation: DockerReadiness,
159    pub isolation: DockerIsolationStatus,
160}
161
162impl DockerStatus {
163    pub fn legacy_missing() -> Self {
164        Self {
165            cli: DockerReadiness::unavailable("not reported by this daemon"),
166            daemon: DockerReadiness::unavailable("not reported by this daemon"),
167            manifest_validation: DockerReadiness::unavailable("not reported by this daemon"),
168            isolation: DockerIsolationStatus {
169                supported: false,
170                default_workspace: String::new(),
171                experimental: false,
172            },
173        }
174    }
175
176    pub fn is_legacy_missing(&self) -> bool {
177        self == &Self::legacy_missing()
178    }
179}
180
181fn legacy_missing_docker_status() -> Box<DockerStatus> {
182    Box::new(DockerStatus::legacy_missing())
183}
184
185fn is_legacy_missing_docker_status(status: &DockerStatus) -> bool {
186    status.is_legacy_missing()
187}
188
189#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
190pub struct LifecycleLogAvailability {
191    pub session_id: Uuid,
192    pub log_availability: LogAvailability,
193}
194
195/// Stable v0.4 daemon diagnostics JSON.
196///
197/// Clients may rely on the field names and JSON value kinds in this response.
198/// The concrete diagnostic values are host and process specific.
199#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
200pub struct DoctorResponse {
201    pub version: VersionInfo,
202    pub socket_path: String,
203    pub uptime_secs: u64,
204    pub sqlite: MigrationState,
205    pub lifecycles: LifecycleCounts,
206    pub watchers: WatcherCounts,
207    pub launchers: Vec<LauncherStatus>,
208    pub tmux: TmuxStatus,
209    #[serde(
210        default = "legacy_missing_docker_status",
211        skip_serializing_if = "is_legacy_missing_docker_status"
212    )]
213    pub docker: Box<DockerStatus>,
214    pub log_availability: Vec<LifecycleLogAvailability>,
215    pub last_probe_sweep: Option<DateTime<Utc>>,
216    pub recent_lost: Vec<RecentLostEvent>,
217}