ic_backup/ops/persistence/fence_obligation/
mod.rs1use super::{
4 BackupLayoutGuard, ConsistencyPersistenceError, JournalLock, JournalLockError,
5 PersistenceError, RestoreSafetyPersistenceError, create_json_durable,
6 read_consistency_requirement, read_json, read_restore_safety_requirement,
7};
8use crate::model::{
9 artifacts::ArtifactChecksumRecord,
10 consistency::{ApplicationFenceBinding, ConsistencyRequirementRecord},
11 fence_obligation::{FenceObligationError, FenceObligationRecord, MAX_FENCE_OBLIGATION_BYTES},
12 operation_plan::OperationPlanRecord,
13 restore_safety::RestoreSafetyRequirementRecord,
14};
15use thiserror::Error;
16
17#[derive(Clone, Copy, Debug)]
21pub enum FenceObligationRequirement<'a> {
22 Capture {
24 requirement: &'a ConsistencyRequirementRecord,
26 fence: &'a ApplicationFenceBinding,
28 },
29 Restore {
31 source_layout: &'a BackupLayoutGuard,
33 source: &'a OperationPlanRecord,
35 requirement: &'a RestoreSafetyRequirementRecord,
37 },
38}
39impl FenceObligationRequirement<'_> {
40 fn validate(
41 self,
42 layout: &BackupLayoutGuard,
43 plan: &OperationPlanRecord,
44 record: &FenceObligationRecord,
45 ) -> Result<(), FenceObligationPersistenceError> {
46 match self {
47 Self::Capture { requirement, fence } => {
48 record.validate_capture(plan, requirement, fence)?;
49 read_consistency_requirement(layout, plan, &requirement.digest())?;
50 }
51 Self::Restore {
52 source_layout,
53 source,
54 requirement,
55 } => {
56 record.validate_restore(plan, source, requirement)?;
57 read_restore_safety_requirement(
58 layout,
59 source_layout,
60 plan,
61 source,
62 &requirement.digest(),
63 )?;
64 }
65 }
66 Ok(())
67 }
68}
69pub fn create_fence_obligation(
77 layout: &BackupLayoutGuard,
78 plan: &OperationPlanRecord,
79 requirement: FenceObligationRequirement<'_>,
80 record: &FenceObligationRecord,
81) -> Result<(), FenceObligationPersistenceError> {
82 requirement.validate(layout, plan, record)?;
83 let path = layout.root().join("fence-obligation.json");
84 let _lock = JournalLock::acquire(&path)?;
85 check_size(record)?;
86 create_json_durable(&path, record)?;
87 Ok(())
88}
89pub fn read_fence_obligation(
97 layout: &BackupLayoutGuard,
98 plan: &OperationPlanRecord,
99 requirement: FenceObligationRequirement<'_>,
100 expected: &ArtifactChecksumRecord,
101) -> Result<FenceObligationRecord, FenceObligationPersistenceError> {
102 super::read_operation_plan(layout, &plan.digest())?;
104 let path = layout.root().join("fence-obligation.json");
105 let _lock = JournalLock::acquire(&path)?;
106 let record: FenceObligationRecord = read_json(&path, MAX_FENCE_OBLIGATION_BYTES)?;
107 check_size(&record)?;
108 requirement.validate(layout, plan, &record)?;
109 if &record.digest() != expected {
110 return Err(FenceObligationPersistenceError::DigestMismatch);
111 }
112 Ok(record)
113}
114fn check_size(record: &FenceObligationRecord) -> Result<(), PersistenceError> {
115 if serde_json::to_vec_pretty(record)?.len() as u64 > MAX_FENCE_OBLIGATION_BYTES {
116 return Err(PersistenceError::RecordTooLarge {
117 limit: MAX_FENCE_OBLIGATION_BYTES,
118 });
119 }
120 Ok(())
121}
122#[derive(Debug, Error)]
124pub enum FenceObligationPersistenceError {
125 #[error("fence obligation digest mismatch")]
127 DigestMismatch,
128 #[error(transparent)]
130 Obligation(#[from] FenceObligationError),
131 #[error(transparent)]
133 Plan(#[from] super::OperationPlanPersistenceError),
134 #[error(transparent)]
136 Consistency(#[from] ConsistencyPersistenceError),
137 #[error(transparent)]
139 Restore(#[from] RestoreSafetyPersistenceError),
140 #[error(transparent)]
142 Lock(#[from] JournalLockError),
143 #[error(transparent)]
145 Persistence(#[from] PersistenceError),
146}
147
148#[cfg(all(test, unix))]
149mod tests;