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 super::json::check_json_size(record, MAX_FENCE_OBLIGATION_BYTES)
116}
117#[derive(Debug, Error)]
119pub enum FenceObligationPersistenceError {
120 #[error("fence obligation digest mismatch")]
122 DigestMismatch,
123 #[error(transparent)]
125 Obligation(#[from] FenceObligationError),
126 #[error(transparent)]
128 Plan(#[from] super::OperationPlanPersistenceError),
129 #[error(transparent)]
131 Consistency(#[from] ConsistencyPersistenceError),
132 #[error(transparent)]
134 Restore(#[from] RestoreSafetyPersistenceError),
135 #[error(transparent)]
137 Lock(#[from] JournalLockError),
138 #[error(transparent)]
140 Persistence(#[from] PersistenceError),
141}
142
143#[cfg(all(test, unix))]
144mod tests;