liminal_server/services/site/lifecycle_records.rs
1//! Service shapes migrate absent construction fields on read.
2
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6/// Where a service stands.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum ServiceState {
10 /// Registered, not running: created, or exited without being asked.
11 Asleep,
12 /// Contracts are approved; construction has not completed.
13 Building,
14 /// Its worker registered its subjects.
15 Live,
16 /// Stopped by a holder; woken by one.
17 Held,
18 /// Stopped for good; kept with its history.
19 Retired,
20}
21
22/// One change of state, with who made it and when.
23#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct Transition {
25 /// The state before.
26 pub from: ServiceState,
27 /// The state after.
28 pub to: ServiceState,
29 /// The Lys holder that asked, or `process` for the service's own exit.
30 pub holder: String,
31 /// Milliseconds since the epoch.
32 pub at_ms: u64,
33 /// What happened beside the state, such as an exit status.
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub note: Option<String>,
36}
37
38/// One service, as kept.
39#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct ServiceRecord {
41 /// Its id, which is also its `liminal.service` id in Lys.
42 pub id: String,
43 /// The name it was created with, one segment.
44 pub name: String,
45 /// The template it runs.
46 #[serde(default)]
47 pub template: Option<String>,
48 /// Exact serving subjects. Configuration creates each root with the name;
49 /// construction keeps its promised subject identifiers unchanged.
50 pub subjects: Vec<String>,
51 /// Approved purpose, absent on records migrated from configuration services.
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub purpose: Option<String>,
54 /// Exact input/output promises, empty on pre-construction records.
55 #[serde(default)]
56 pub contracts: BTreeMap<String, super::site::Contract>,
57 /// The sole builder, kept after completion for attribution.
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub builder: Option<String>,
60 /// Grant ids retained even after revocation for the audit history.
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub builder_grants: Option<super::site::BuilderGrants>,
63 /// Whether the binding still admits construction writes.
64 #[serde(default)]
65 pub builder_bound: bool,
66 /// Immutable actual site approvers from the approval connector, or `lys` for an act Lys
67 /// answered outright.
68 #[serde(default)]
69 pub approved_by: Vec<String>,
70 /// The person answerable for the site: the responsible person of the pass that created it.
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub responsible: Option<String>,
73 /// Retained plan and its complete write history.
74 #[serde(default)]
75 pub plan: super::plan::Plan,
76 /// Approval receipts retained alongside this service's transitions.
77 #[serde(default)]
78 pub approvals: Vec<super::site::Approval>,
79 /// Where it stands.
80 pub state: ServiceState,
81 /// The holder that created it.
82 pub created_by: String,
83 /// Every transition, oldest first.
84 pub transitions: Vec<Transition>,
85}
86
87impl std::fmt::Debug for ServiceRecord {
88 fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 out.debug_struct("ServiceRecord")
90 .field("state", &self.state)
91 .field("transitions", &self.transitions.len())
92 .finish_non_exhaustive()
93 }
94}