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 ControlAuthorityPreflightRequest {
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 pub requires_root_controller: bool,
365 pub targets: Vec<ControlAuthorityPreflightTarget>,
366}
367
368#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
376#[serde(deny_unknown_fields)]
377pub struct ControlAuthorityPreflightTarget {
378 pub canister_id: String,
379 #[serde(deserialize_with = "crate::serialization::required_option")]
380 pub role: Option<String>,
381 #[serde(deserialize_with = "crate::serialization::required_option")]
382 pub parent_canister_id: Option<String>,
383 pub declared_authority: ControlAuthority,
384}
385
386impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
387 fn from(target: &BackupTarget) -> Self {
388 Self {
389 canister_id: target.canister_id.clone(),
390 role: target.role.clone(),
391 parent_canister_id: target.parent_canister_id.clone(),
392 declared_authority: target.control_authority.clone(),
393 }
394 }
395}
396
397#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
405#[serde(deny_unknown_fields)]
406pub struct SnapshotReadAuthorityPreflightRequest {
407 pub plan_id: String,
408 pub run_id: String,
409 pub fleet: String,
410 pub environment: String,
411 pub root_canister_id: String,
412 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
413}
414
415#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
423#[serde(deny_unknown_fields)]
424pub struct SnapshotReadAuthorityPreflightTarget {
425 pub canister_id: String,
426 #[serde(deserialize_with = "crate::serialization::required_option")]
427 pub role: Option<String>,
428 #[serde(deserialize_with = "crate::serialization::required_option")]
429 pub parent_canister_id: Option<String>,
430 pub declared_authority: SnapshotReadAuthority,
431}
432
433impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
434 fn from(target: &BackupTarget) -> Self {
435 Self {
436 canister_id: target.canister_id.clone(),
437 role: target.role.clone(),
438 parent_canister_id: target.parent_canister_id.clone(),
439 declared_authority: target.snapshot_read_authority.clone(),
440 }
441 }
442}
443
444#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
452#[serde(deny_unknown_fields)]
453pub struct TopologyPreflightRequest {
454 pub plan_id: String,
455 pub run_id: String,
456 pub fleet: String,
457 pub environment: String,
458 pub root_canister_id: String,
459 #[serde(deserialize_with = "crate::serialization::required_option")]
460 pub selected_subtree_root: Option<String>,
461 pub selected_scope_kind: BackupScopeKind,
462 pub topology_hash_before_quiesce: String,
463 pub targets: Vec<TopologyPreflightTarget>,
464}
465
466#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
474#[serde(deny_unknown_fields)]
475pub struct TopologyPreflightTarget {
476 pub canister_id: String,
477 #[serde(deserialize_with = "crate::serialization::required_option")]
478 pub parent_canister_id: Option<String>,
479 pub depth: u32,
480}
481
482impl From<&BackupTarget> for TopologyPreflightTarget {
483 fn from(target: &BackupTarget) -> Self {
484 Self {
485 canister_id: target.canister_id.clone(),
486 parent_canister_id: target.parent_canister_id.clone(),
487 depth: target.depth,
488 }
489 }
490}
491
492#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
500#[serde(deny_unknown_fields)]
501pub struct TopologyPreflightReceipt {
502 pub plan_id: String,
503 pub preflight_id: String,
504 pub topology_hash_before_quiesce: String,
505 pub topology_hash_at_preflight: String,
506 pub targets: Vec<TopologyPreflightTarget>,
507 pub validated_at: String,
508 pub expires_at: String,
509 #[serde(deserialize_with = "crate::serialization::required_option")]
510 pub message: Option<String>,
511}
512
513#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
521#[serde(deny_unknown_fields)]
522pub struct QuiescencePreflightRequest {
523 pub plan_id: String,
524 pub run_id: String,
525 pub fleet: String,
526 pub environment: String,
527 pub root_canister_id: String,
528 #[serde(deserialize_with = "crate::serialization::required_option")]
529 pub selected_subtree_root: Option<String>,
530 pub quiescence_policy: QuiescencePolicy,
531 pub targets: Vec<QuiescencePreflightTarget>,
532}
533
534#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
542#[serde(deny_unknown_fields)]
543pub struct QuiescencePreflightTarget {
544 pub canister_id: String,
545 #[serde(deserialize_with = "crate::serialization::required_option")]
546 pub role: Option<String>,
547 #[serde(deserialize_with = "crate::serialization::required_option")]
548 pub parent_canister_id: Option<String>,
549}
550
551impl From<&BackupTarget> for QuiescencePreflightTarget {
552 fn from(target: &BackupTarget) -> Self {
553 Self {
554 canister_id: target.canister_id.clone(),
555 role: target.role.clone(),
556 parent_canister_id: target.parent_canister_id.clone(),
557 }
558 }
559}
560
561#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
569#[serde(deny_unknown_fields)]
570pub struct QuiescencePreflightReceipt {
571 pub plan_id: String,
572 pub preflight_id: String,
573 pub quiescence_policy: QuiescencePolicy,
574 pub accepted: bool,
575 pub targets: Vec<QuiescencePreflightTarget>,
576 pub validated_at: String,
577 pub expires_at: String,
578 #[serde(deserialize_with = "crate::serialization::required_option")]
579 pub message: Option<String>,
580}