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 targets::{backup_target, selected_subtree_root, snapshot_targets, target_depths};
19
20pub(in crate::plan) use phases::build_backup_phases;
21
22///
23/// BackupPlanBuildInput
24///
25/// Input bundle required to build a backup plan from registry entries.
26/// Owned by backup plan construction and supplied by higher-level workflows.
27///
28
29pub struct BackupPlanBuildInput<'a> {
30    pub plan_id: String,
31    pub run_id: String,
32    pub fleet: String,
33    pub environment: String,
34    pub root_canister_id: String,
35    pub selected_canister_id: Option<String>,
36    pub selected_scope_kind: BackupScopeKind,
37    pub include_descendants: bool,
38    pub topology_hash_before_quiesce: String,
39    pub registry: &'a [RegistryEntry],
40    pub control_authority: ControlAuthority,
41    pub snapshot_read_authority: SnapshotReadAuthority,
42    pub quiescence_policy: QuiescencePolicy,
43    pub identity_mode: IdentityMode,
44}
45
46/// Build a validated backup plan from the live root registry projection.
47pub fn build_backup_plan(input: BackupPlanBuildInput<'_>) -> Result<BackupPlan, BackupPlanError> {
48    let snapshot_read_authority = input.snapshot_read_authority.clone();
49    let quiescence_policy = input.quiescence_policy.clone();
50    let root_included = input.selected_scope_kind == BackupScopeKind::MaintenanceRoot;
51    let selected_subtree_root = selected_subtree_root(&input)?;
52    let snapshot_targets = snapshot_targets(&input)?;
53    let target_depths = target_depths(input.registry);
54    let targets = snapshot_targets
55        .into_iter()
56        .map(|target| {
57            backup_target(
58                target,
59                &target_depths,
60                input.control_authority.clone(),
61                input.snapshot_read_authority.clone(),
62                input.identity_mode.clone(),
63            )
64        })
65        .collect::<Vec<_>>();
66    let phases = build_backup_phases(&targets);
67
68    let plan = BackupPlan {
69        plan_version: 1,
70        plan_id: input.plan_id,
71        run_id: input.run_id,
72        fleet: input.fleet,
73        environment: input.environment,
74        root_canister_id: input.root_canister_id,
75        selected_subtree_root,
76        selected_scope_kind: input.selected_scope_kind,
77        include_descendants: input.include_descendants,
78        root_included,
79        requires_root_controller: input.control_authority.source
80            == ControlAuthoritySource::RootController,
81        snapshot_read_authority,
82        quiescence_policy,
83        topology_hash_before_quiesce: input.topology_hash_before_quiesce,
84        targets,
85        phases,
86    };
87    plan.validate()?;
88    Ok(plan)
89}