use super::{
BackupLayoutGuard, ConsistencyPersistenceError, JournalLock, JournalLockError,
PersistenceError, RestoreSafetyPersistenceError, create_json_durable,
read_consistency_requirement, read_json, read_restore_safety_requirement,
};
use crate::model::{
artifacts::ArtifactChecksumRecord,
consistency::{ApplicationFenceBinding, ConsistencyRequirementRecord},
fence_obligation::{FenceObligationError, FenceObligationRecord, MAX_FENCE_OBLIGATION_BYTES},
operation_plan::OperationPlanRecord,
restore_safety::RestoreSafetyRequirementRecord,
};
use thiserror::Error;
#[derive(Clone, Copy, Debug)]
pub enum FenceObligationRequirement<'a> {
Capture {
requirement: &'a ConsistencyRequirementRecord,
fence: &'a ApplicationFenceBinding,
},
Restore {
source_layout: &'a BackupLayoutGuard,
source: &'a OperationPlanRecord,
requirement: &'a RestoreSafetyRequirementRecord,
},
}
impl FenceObligationRequirement<'_> {
fn validate(
self,
layout: &BackupLayoutGuard,
plan: &OperationPlanRecord,
record: &FenceObligationRecord,
) -> Result<(), FenceObligationPersistenceError> {
match self {
Self::Capture { requirement, fence } => {
record.validate_capture(plan, requirement, fence)?;
read_consistency_requirement(layout, plan, &requirement.digest())?;
}
Self::Restore {
source_layout,
source,
requirement,
} => {
record.validate_restore(plan, source, requirement)?;
read_restore_safety_requirement(
layout,
source_layout,
plan,
source,
&requirement.digest(),
)?;
}
}
Ok(())
}
}
pub fn create_fence_obligation(
layout: &BackupLayoutGuard,
plan: &OperationPlanRecord,
requirement: FenceObligationRequirement<'_>,
record: &FenceObligationRecord,
) -> Result<(), FenceObligationPersistenceError> {
requirement.validate(layout, plan, record)?;
let path = layout.root().join("fence-obligation.json");
let _lock = JournalLock::acquire(&path)?;
check_size(record)?;
create_json_durable(&path, record)?;
Ok(())
}
pub fn read_fence_obligation(
layout: &BackupLayoutGuard,
plan: &OperationPlanRecord,
requirement: FenceObligationRequirement<'_>,
expected: &ArtifactChecksumRecord,
) -> Result<FenceObligationRecord, FenceObligationPersistenceError> {
super::read_operation_plan(layout, &plan.digest())?;
let path = layout.root().join("fence-obligation.json");
let _lock = JournalLock::acquire(&path)?;
let record: FenceObligationRecord = read_json(&path, MAX_FENCE_OBLIGATION_BYTES)?;
check_size(&record)?;
requirement.validate(layout, plan, &record)?;
if &record.digest() != expected {
return Err(FenceObligationPersistenceError::DigestMismatch);
}
Ok(record)
}
fn check_size(record: &FenceObligationRecord) -> Result<(), PersistenceError> {
super::json::check_json_size(record, MAX_FENCE_OBLIGATION_BYTES)
}
#[derive(Debug, Error)]
pub enum FenceObligationPersistenceError {
#[error("fence obligation digest mismatch")]
DigestMismatch,
#[error(transparent)]
Obligation(#[from] FenceObligationError),
#[error(transparent)]
Plan(#[from] super::OperationPlanPersistenceError),
#[error(transparent)]
Consistency(#[from] ConsistencyPersistenceError),
#[error(transparent)]
Restore(#[from] RestoreSafetyPersistenceError),
#[error(transparent)]
Lock(#[from] JournalLockError),
#[error(transparent)]
Persistence(#[from] PersistenceError),
}
#[cfg(all(test, unix))]
mod tests;