Skip to main content

canic_backup/plan/build/
mod.rs

1//! Module: plan::build
2//!
3//! Responsibility: construct backup plans from registry-backed selections.
4//! Does not own: registry querying, preflight execution, or journal state.
5//! Boundary: maps discovered targets into validated plan operations.
6
7mod phases;
8mod selector;
9mod targets;
10
11pub use selector::resolve_backup_selector;
12
13use crate::plan::{
14    BackupPlan, BackupPlanError, BackupScopeKind, ControlAuthority, ControlAuthoritySource,
15    QuiescencePolicy, SnapshotReadAuthority,
16};
17use crate::{manifest::IdentityMode, registry::RegistryEntry};
18use phases::build_backup_phases;
19use targets::{backup_target, selected_subtree_root, snapshot_targets, target_depths};
20
21///
22/// BackupPlanBuildInput
23///
24/// Input bundle required to build a backup plan from registry entries.
25/// Owned by backup plan construction and supplied by higher-level workflows.
26///
27
28pub struct BackupPlanBuildInput<'a> {
29    pub plan_id: String,
30    pub run_id: String,
31    pub fleet: String,
32    pub network: String,
33    pub root_canister_id: String,
34    pub selected_canister_id: Option<String>,
35    pub selected_scope_kind: BackupScopeKind,
36    pub include_descendants: bool,
37    pub topology_hash_before_quiesce: String,
38    pub registry: &'a [RegistryEntry],
39    pub control_authority: ControlAuthority,
40    pub snapshot_read_authority: SnapshotReadAuthority,
41    pub quiescence_policy: QuiescencePolicy,
42    pub identity_mode: IdentityMode,
43}
44
45/// Build a validated backup plan from the live root registry projection.
46pub fn build_backup_plan(input: BackupPlanBuildInput<'_>) -> Result<BackupPlan, BackupPlanError> {
47    let snapshot_read_authority = input.snapshot_read_authority.clone();
48    let quiescence_policy = input.quiescence_policy.clone();
49    let root_included = input.selected_scope_kind == BackupScopeKind::MaintenanceRoot;
50    let selected_subtree_root = selected_subtree_root(&input)?;
51    let snapshot_targets = snapshot_targets(&input)?;
52    let target_depths = target_depths(input.registry);
53    let targets = snapshot_targets
54        .into_iter()
55        .map(|target| {
56            backup_target(
57                target,
58                &target_depths,
59                input.control_authority.clone(),
60                input.snapshot_read_authority.clone(),
61                input.identity_mode.clone(),
62            )
63        })
64        .collect::<Vec<_>>();
65    let phases = build_backup_phases(&targets);
66
67    let plan = BackupPlan {
68        plan_id: input.plan_id,
69        run_id: input.run_id,
70        fleet: input.fleet,
71        network: input.network,
72        root_canister_id: input.root_canister_id,
73        selected_subtree_root,
74        selected_scope_kind: input.selected_scope_kind,
75        include_descendants: input.include_descendants,
76        root_included,
77        requires_root_controller: input.control_authority.source
78            == ControlAuthoritySource::RootController,
79        snapshot_read_authority,
80        quiescence_policy,
81        topology_hash_before_quiesce: input.topology_hash_before_quiesce,
82        targets,
83        phases,
84    };
85    plan.validate()?;
86    Ok(plan)
87}