ic_backup/ops/persistence/fence_obligation/
mod.rs1use super::json::check_json_size;
4use super::{
5 BackupLayoutGuard, ConsistencyPersistenceError, JournalLock, JournalLockError,
6 PersistenceError, RestoreSafetyPersistenceError, create_json_durable,
7 read_consistency_requirement, read_json, read_restore_safety_requirement,
8};
9use crate::model::{
10 artifacts::ArtifactChecksumRecord,
11 consistency::{ApplicationFenceBinding, ConsistencyRequirementRecord},
12 fence_obligation::{FenceObligationError, FenceObligationRecord, MAX_FENCE_OBLIGATION_BYTES},
13 operation_plan::OperationPlanRecord,
14 restore_safety::RestoreSafetyRequirementRecord,
15};
16use thiserror::Error;
17
18#[derive(Clone, Copy, Debug)]
22pub enum FenceObligationRequirement<'a> {
23 Capture {
25 requirement: &'a ConsistencyRequirementRecord,
27 fence: &'a ApplicationFenceBinding,
29 },
30 Restore {
32 source_layout: &'a BackupLayoutGuard,
34 source: &'a OperationPlanRecord,
36 requirement: &'a RestoreSafetyRequirementRecord,
38 },
39}
40impl FenceObligationRequirement<'_> {
41 fn validate(
42 self,
43 layout: &BackupLayoutGuard,
44 plan: &OperationPlanRecord,
45 record: &FenceObligationRecord,
46 ) -> Result<(), FenceObligationPersistenceError> {
47 match self {
48 Self::Capture { requirement, fence } => {
49 record.validate_capture(plan, requirement, fence)?;
50 read_consistency_requirement(layout, plan, &requirement.digest())?;
51 }
52 Self::Restore {
53 source_layout,
54 source,
55 requirement,
56 } => {
57 record.validate_restore(plan, source, requirement)?;
58 read_restore_safety_requirement(
59 layout,
60 source_layout,
61 plan,
62 source,
63 &requirement.digest(),
64 )?;
65 }
66 }
67 Ok(())
68 }
69}
70pub fn create_fence_obligation(
78 layout: &BackupLayoutGuard,
79 plan: &OperationPlanRecord,
80 requirement: FenceObligationRequirement<'_>,
81 record: &FenceObligationRecord,
82) -> Result<(), FenceObligationPersistenceError> {
83 requirement.validate(layout, plan, record)?;
84 let path = layout.root().join("fence-obligation.json");
85 let _lock = JournalLock::acquire(&path)?;
86 check_json_size(record, MAX_FENCE_OBLIGATION_BYTES)?;
87 create_json_durable(&path, record)?;
88 Ok(())
89}
90pub fn read_fence_obligation(
98 layout: &BackupLayoutGuard,
99 plan: &OperationPlanRecord,
100 requirement: FenceObligationRequirement<'_>,
101 expected: &ArtifactChecksumRecord,
102) -> Result<FenceObligationRecord, FenceObligationPersistenceError> {
103 super::read_operation_plan(layout, &plan.digest())?;
105 let path = layout.root().join("fence-obligation.json");
106 let _lock = JournalLock::acquire(&path)?;
107 let record: FenceObligationRecord = read_json(&path, MAX_FENCE_OBLIGATION_BYTES)?;
108 check_json_size(&record, MAX_FENCE_OBLIGATION_BYTES)?;
109 requirement.validate(layout, plan, &record)?;
110 if &record.digest() != expected {
111 return Err(FenceObligationPersistenceError::DigestMismatch);
112 }
113 Ok(record)
114}
115#[derive(Debug, Error)]
117pub enum FenceObligationPersistenceError {
118 #[error("fence obligation digest mismatch")]
120 DigestMismatch,
121 #[error(transparent)]
123 Obligation(#[from] FenceObligationError),
124 #[error(transparent)]
126 Plan(#[from] super::OperationPlanPersistenceError),
127 #[error(transparent)]
129 Consistency(#[from] ConsistencyPersistenceError),
130 #[error(transparent)]
132 Restore(#[from] RestoreSafetyPersistenceError),
133 #[error(transparent)]
135 Lock(#[from] JournalLockError),
136 #[error(transparent)]
138 Persistence(#[from] PersistenceError),
139}
140
141#[cfg(all(test, unix))]
142mod tests;