1use crate::manifest::IdentityMode;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19#[serde(deny_unknown_fields)]
20pub struct BackupPlan {
21 pub plan_version: u16,
22 pub plan_id: String,
23 pub run_id: String,
24 pub fleet: String,
25 pub environment: String,
26 pub root_canister_id: String,
27 #[serde(deserialize_with = "crate::serialization::required_option")]
28 pub selected_subtree_root: Option<String>,
29 pub selected_scope_kind: BackupScopeKind,
30 pub include_descendants: bool,
31 pub root_included: bool,
32 pub requires_root_controller: bool,
33 pub snapshot_read_authority: SnapshotReadAuthority,
34 pub quiescence_policy: QuiescencePolicy,
35 pub topology_hash_before_quiesce: String,
36 pub targets: Vec<BackupTarget>,
37 pub phases: Vec<BackupOperation>,
38}
39
40#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
48#[serde(rename_all = "kebab-case")]
49pub enum BackupScopeKind {
50 Member,
51 Subtree,
52 NonRootDeployment,
53 MaintenanceRoot,
54}
55
56#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
64#[serde(deny_unknown_fields)]
65pub struct BackupTarget {
66 pub canister_id: String,
67 #[serde(deserialize_with = "crate::serialization::required_option")]
68 pub role: Option<String>,
69 #[serde(deserialize_with = "crate::serialization::required_option")]
70 pub parent_canister_id: Option<String>,
71 pub depth: u32,
72 pub control_authority: ControlAuthority,
73 pub snapshot_read_authority: SnapshotReadAuthority,
74 pub identity_mode: IdentityMode,
75 #[serde(deserialize_with = "crate::serialization::required_option")]
76 pub expected_module_hash: Option<String>,
77}
78
79#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
87#[serde(rename_all = "kebab-case")]
88pub enum AuthorityEvidence {
89 Proven,
90 Declared,
91 Unknown,
92}
93
94#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102#[serde(deny_unknown_fields)]
103pub struct ControlAuthority {
104 pub source: ControlAuthoritySource,
105 pub evidence: AuthorityEvidence,
106}
107
108impl ControlAuthority {
109 #[must_use]
110 pub const fn unknown() -> Self {
111 Self {
112 source: ControlAuthoritySource::Unknown,
113 evidence: AuthorityEvidence::Unknown,
114 }
115 }
116
117 #[must_use]
118 pub const fn root_controller(evidence: AuthorityEvidence) -> Self {
119 Self {
120 source: ControlAuthoritySource::RootController,
121 evidence,
122 }
123 }
124
125 #[must_use]
126 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
127 Self {
128 source: ControlAuthoritySource::OperatorController,
129 evidence,
130 }
131 }
132
133 #[must_use]
134 pub fn is_proven(&self) -> bool {
135 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
136 }
137
138 #[must_use]
139 pub fn is_proven_root_controller(&self) -> bool {
140 self.evidence == AuthorityEvidence::Proven
141 && self.source == ControlAuthoritySource::RootController
142 }
143}
144
145#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
153#[serde(rename_all = "kebab-case", tag = "kind")]
154pub enum ControlAuthoritySource {
155 Unknown,
156 RootController,
157 OperatorController,
158}
159
160#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
168#[serde(deny_unknown_fields)]
169pub struct SnapshotReadAuthority {
170 pub source: SnapshotReadAuthoritySource,
171 pub evidence: AuthorityEvidence,
172}
173
174impl SnapshotReadAuthority {
175 #[must_use]
176 pub const fn unknown() -> Self {
177 Self {
178 source: SnapshotReadAuthoritySource::Unknown,
179 evidence: AuthorityEvidence::Unknown,
180 }
181 }
182
183 #[must_use]
184 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
185 Self {
186 source: SnapshotReadAuthoritySource::OperatorController,
187 evidence,
188 }
189 }
190
191 #[must_use]
192 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
193 Self {
194 source: SnapshotReadAuthoritySource::RootConfiguredRead,
195 evidence,
196 }
197 }
198
199 #[must_use]
200 pub fn is_proven(&self) -> bool {
201 self.evidence == AuthorityEvidence::Proven
202 && self.source != SnapshotReadAuthoritySource::Unknown
203 }
204}
205
206#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
214#[serde(rename_all = "kebab-case")]
215pub enum SnapshotReadAuthoritySource {
216 Unknown,
217 OperatorController,
218 RootConfiguredRead,
219}
220
221#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
229#[serde(rename_all = "kebab-case")]
230pub enum QuiescencePolicy {
231 CrashConsistent,
232 RootCoordinated,
233}
234
235#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
243#[serde(deny_unknown_fields)]
244pub struct BackupOperation {
245 pub operation_id: String,
246 pub order: u32,
247 pub kind: BackupOperationKind,
248 #[serde(deserialize_with = "crate::serialization::required_option")]
249 pub target_canister_id: Option<String>,
250}
251
252#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
260#[serde(rename_all = "kebab-case")]
261pub enum BackupOperationKind {
262 ValidateTopology,
263 ValidateControlAuthority,
264 ValidateSnapshotReadAuthority,
265 ValidateQuiescencePolicy,
266 Stop,
267 CreateSnapshot,
268 Start,
269 DownloadSnapshot,
270 VerifyArtifact,
271 FinalizeManifest,
272}
273
274#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
282#[serde(deny_unknown_fields)]
283pub struct BackupExecutionPreflightReceipts {
284 pub plan_id: String,
285 pub preflight_id: String,
286 pub validated_at: String,
287 pub expires_at: String,
288 pub topology: TopologyPreflightReceipt,
289 pub control_authority: Vec<ControlAuthorityReceipt>,
290 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
291 pub quiescence: QuiescencePreflightReceipt,
292}
293
294#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
302#[serde(deny_unknown_fields)]
303pub struct ControlAuthorityReceipt {
304 pub plan_id: String,
305 pub preflight_id: String,
306 pub target_canister_id: String,
307 pub authority: ControlAuthority,
308 pub proof_source: AuthorityProofSource,
309 pub validated_at: String,
310 pub expires_at: String,
311 #[serde(deserialize_with = "crate::serialization::required_option")]
312 pub message: Option<String>,
313}
314
315#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
323#[serde(deny_unknown_fields)]
324pub struct SnapshotReadAuthorityReceipt {
325 pub plan_id: String,
326 pub preflight_id: String,
327 pub target_canister_id: String,
328 pub authority: SnapshotReadAuthority,
329 pub proof_source: AuthorityProofSource,
330 pub validated_at: String,
331 pub expires_at: String,
332 #[serde(deserialize_with = "crate::serialization::required_option")]
333 pub message: Option<String>,
334}
335
336#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
344#[serde(rename_all = "kebab-case")]
345pub enum AuthorityProofSource {
346 ManagementStatus,
347}
348
349#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
357#[serde(deny_unknown_fields)]
358pub struct TopologyPreflightRequest {
359 pub plan_id: String,
360 pub run_id: String,
361 pub fleet: String,
362 pub environment: String,
363 pub root_canister_id: String,
364 #[serde(deserialize_with = "crate::serialization::required_option")]
365 pub selected_subtree_root: Option<String>,
366 pub selected_scope_kind: BackupScopeKind,
367 pub topology_hash_before_quiesce: String,
368 pub targets: Vec<TopologyPreflightTarget>,
369}
370
371#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
379#[serde(deny_unknown_fields)]
380pub struct TopologyPreflightTarget {
381 pub canister_id: String,
382 #[serde(deserialize_with = "crate::serialization::required_option")]
383 pub parent_canister_id: Option<String>,
384 pub depth: u32,
385}
386
387impl From<&BackupTarget> for TopologyPreflightTarget {
388 fn from(target: &BackupTarget) -> Self {
389 Self {
390 canister_id: target.canister_id.clone(),
391 parent_canister_id: target.parent_canister_id.clone(),
392 depth: target.depth,
393 }
394 }
395}
396
397#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
405#[serde(deny_unknown_fields)]
406pub struct TopologyPreflightReceipt {
407 pub plan_id: String,
408 pub preflight_id: String,
409 pub topology_hash_before_quiesce: String,
410 pub topology_hash_at_preflight: String,
411 pub targets: Vec<TopologyPreflightTarget>,
412 pub validated_at: String,
413 pub expires_at: String,
414 #[serde(deserialize_with = "crate::serialization::required_option")]
415 pub message: Option<String>,
416}
417
418#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
426#[serde(deny_unknown_fields)]
427pub struct QuiescencePreflightRequest {
428 pub plan_id: String,
429 pub run_id: String,
430 pub fleet: String,
431 pub environment: String,
432 pub root_canister_id: String,
433 #[serde(deserialize_with = "crate::serialization::required_option")]
434 pub selected_subtree_root: Option<String>,
435 pub quiescence_policy: QuiescencePolicy,
436 pub targets: Vec<QuiescencePreflightTarget>,
437}
438
439#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
447#[serde(deny_unknown_fields)]
448pub struct QuiescencePreflightTarget {
449 pub canister_id: String,
450 #[serde(deserialize_with = "crate::serialization::required_option")]
451 pub role: Option<String>,
452 #[serde(deserialize_with = "crate::serialization::required_option")]
453 pub parent_canister_id: Option<String>,
454}
455
456impl From<&BackupTarget> for QuiescencePreflightTarget {
457 fn from(target: &BackupTarget) -> Self {
458 Self {
459 canister_id: target.canister_id.clone(),
460 role: target.role.clone(),
461 parent_canister_id: target.parent_canister_id.clone(),
462 }
463 }
464}
465
466#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
474#[serde(deny_unknown_fields)]
475pub struct QuiescencePreflightReceipt {
476 pub plan_id: String,
477 pub preflight_id: String,
478 pub quiescence_policy: QuiescencePolicy,
479 pub accepted: bool,
480 pub targets: Vec<QuiescencePreflightTarget>,
481 pub validated_at: String,
482 pub expires_at: String,
483 #[serde(deserialize_with = "crate::serialization::required_option")]
484 pub message: Option<String>,
485}