1use crate::ids::{
4 GenerationId, Hash, IncidentId, JobId, Millis, PolicyVersion, PrincipalId, SnapshotId, StorePath, Timestamp, TxnId,
5};
6use crate::state::UnitStatus;
7use crate::transition::{
8 ApproverClass, ConfigDiff, DerivedFacts, DriverRef, Expectation, RecoveryStrategy, RejectReason, Transition,
9};
10use crate::unit::{Actor, DesiredStatus, UnitId};
11use serde::{Deserialize, Serialize};
12use vstd::prelude::*;
13
14verus! {
16
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
18#[verifier::external_derive]
19pub enum TxnState {
20 Prepared,
21 Staged,
22 Built,
23 Activating,
24 Checking,
25 Published,
27 Publishing,
30 AwaitingBoot,
33 Promoted,
35 Committed,
36 Reverting,
37 Reverted,
38 RecoveryFailed,
39 Rejected(RejectReason),
41 Cancelled(Actor),
43}
44
45impl TxnState {
46 pub open spec fn terminal(self) -> bool {
48 match self {
49 TxnState::Committed | TxnState::Reverted | TxnState::RecoveryFailed | TxnState::Rejected(_)
50 | TxnState::Cancelled(_) => true,
51 _ => false,
52 }
53 }
54
55 pub open spec fn pre_mutation(self) -> bool {
57 match self {
58 TxnState::Prepared | TxnState::Staged | TxnState::Built => true,
59 _ => false,
60 }
61 }
62
63 pub open spec fn exclusive(self) -> bool {
66 match self {
67 TxnState::Activating | TxnState::Checking | TxnState::Publishing | TxnState::Published
68 | TxnState::Promoted | TxnState::Reverting => true,
69 _ => false,
70 }
71 }
72
73 pub fn is_terminal(&self) -> (r: bool)
74 ensures
75 r == self.terminal(),
76 {
77 match self {
78 TxnState::Committed | TxnState::Reverted | TxnState::RecoveryFailed | TxnState::Rejected(_)
79 | TxnState::Cancelled(_) => true,
80 _ => false,
81 }
82 }
83
84 pub fn is_pre_mutation(&self) -> (r: bool)
85 ensures
86 r == self.pre_mutation(),
87 {
88 match self {
89 TxnState::Prepared | TxnState::Staged | TxnState::Built => true,
90 _ => false,
91 }
92 }
93
94 pub fn is_exclusive(&self) -> (r: bool)
95 ensures
96 r == self.exclusive(),
97 {
98 match self {
99 TxnState::Activating | TxnState::Checking | TxnState::Publishing | TxnState::Published
100 | TxnState::Promoted | TxnState::Reverting => true,
101 _ => false,
102 }
103 }
104}
105
106#[verifier::external_type_specification]
108#[verifier::external_body]
109#[allow(dead_code)]
110pub struct ExCheckContext(CheckContext);
111
112#[verifier::external_type_specification]
113#[verifier::external_body]
114#[allow(dead_code)]
115pub struct ExApprovalToken(ApprovalToken);
116
117#[verifier::external_type_specification]
118#[verifier::external_body]
119#[allow(dead_code)]
120pub struct ExIncident(Incident);
121
122#[verifier::external_type_specification]
123#[verifier::external_body]
124#[allow(dead_code)]
125pub struct ExExpectation(Expectation);
126
127#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
128#[verifier::external_derive]
129pub enum OwnerId {
130 Daemon { pid: u32, boot_id: Hash },
131 Watchdog { pid: u32, boot_id: Hash },
132}
133
134#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
137#[verifier::external_derive]
138pub struct Lease {
139 pub txn: TxnId,
140 pub owner: OwnerId,
141 pub epoch: u64,
142 pub worker_pid: Option<u32>,
143 pub renewed: Timestamp,
144 pub jobs: Vec<JobId>,
146}
147
148#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
149#[verifier::external_derive]
150pub struct Transaction {
151 pub id: TxnId,
152 pub state: TxnState,
153 pub lease: Lease,
154 pub base_source: Option<Hash>,
156 pub staged_source: Option<Hash>,
158 #[serde(default)]
162 pub candidate_nar: Option<Hash>,
163 pub base_override: Option<Hash>,
164 pub candidate_override: Option<Hash>,
165 pub gc_root: Option<String>,
167 pub recovery: RecoveryStrategy,
168 pub transition: Transition,
169 pub facts: DerivedFacts,
170 pub base: GenerationId,
171 pub candidate: Option<(GenerationId, StorePath)>,
172 pub policy: PolicyVersion,
173 pub approval: Option<ApprovalToken>,
174 pub snapshot: Option<SnapshotId>,
175 pub expected: Expectation,
176 pub started: Timestamp,
177 pub deadline: Timestamp,
179 pub revert_to: GenerationId,
181 pub prior_driver: Option<DriverRef>,
184 pub check: CheckContext,
186 #[serde(default)]
189 pub prepared_again_from: Option<TxnId>,
190}
191
192#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
193#[verifier::external_derive]
194pub enum Outcome {
195 Succeeded,
196 Failed(String),
197 Partial(String),
198 Unknown(String),
199}
200
201} impl OwnerId {
204 pub fn pid(&self) -> u32 {
205 match self {
206 OwnerId::Daemon { pid, .. } | OwnerId::Watchdog { pid, .. } => *pid,
207 }
208 }
209
210 pub fn is_watchdog(&self) -> bool {
211 matches!(self, OwnerId::Watchdog { .. })
212 }
213
214 pub fn boot_id(&self) -> Hash {
215 match self {
216 OwnerId::Daemon { boot_id, .. } | OwnerId::Watchdog { boot_id, .. } => *boot_id,
217 }
218 }
219}
220
221#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
223pub struct Manifest {
224 pub units: crate::unit::UnitDefs,
225 pub user_units: Vec<crate::unit::UnitDef>,
227}
228
229#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
232pub struct CheckContext {
233 pub candidate: Option<Manifest>,
234 pub base: Manifest,
235 pub pre: std::collections::BTreeMap<UnitId, UnitStatus>,
236 #[serde(default)]
239 pub pre_passing: std::collections::BTreeMap<UnitId, Vec<crate::ids::ProbeId>>,
240}
241
242impl Outcome {
243 pub fn is_success(&self) -> bool {
245 matches!(self, Outcome::Succeeded)
246 }
247
248 pub fn fenced() -> Outcome {
249 Outcome::Failed("fenced".into())
250 }
251
252 pub fn is_fenced(&self) -> bool {
253 matches!(self, Outcome::Failed(m) if m == "fenced")
254 }
255}
256
257#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
258pub struct ApprovalToken {
259 pub bound_to: Hash,
263 pub approver: PrincipalId,
265 pub class: ApproverClass,
266 pub issued: Timestamp,
267 pub expires: Timestamp,
268 pub nonce: [u8; 16],
269 pub used: bool,
270}
271
272pub const TOKEN_LIFETIME: Millis = Millis(24 * 3600 * 1000);
273
274pub const PARKING_LIMIT: Millis = Millis(24 * 3600 * 1000);
277
278#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
279pub struct Session {
280 pub applied: Vec<TxnId>,
281 pub cumulative: ConfigDiff,
282 pub policy: Option<PolicyVersion>,
283}
284
285#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
288pub enum IncidentState {
289 Open,
290 Cooldown {
291 until: Timestamp,
292 },
293 Escalated {
296 until: Timestamp,
297 reason: EscalationReason,
298 },
299 Closed,
300 Regressed {
301 count: u8,
302 },
303}
304
305#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
306pub enum EscalationReason {
307 Regressed,
308 BudgetExhausted,
309 DiagnoserExhausted,
310 RevertBlockedByFloor,
312 RecoveryFailed,
313}
314
315#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
316pub enum IncidentKind {
317 Repair,
319 Diagnostic,
322}
323
324#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
325pub struct Incident {
326 pub id: IncidentId,
327 pub kind: IncidentKind,
328 pub target: UnitId,
329 pub divergence: (DesiredStatus, UnitStatus),
330 pub opened: Timestamp,
331 pub attempts: Vec<(TxnId, Outcome)>,
332 pub budget_left: u8,
334 pub state: IncidentState,
335 pub backoff: Millis,
336 pub next_attempt: Timestamp,
339 pub regressions: u8,
341}