ic_backup/ops/persistence/execution_settlement/
mod.rs1use super::json::check_json_size;
4use super::{
5 AttemptJournalError, BackupLayoutGuard, JournalLock, JournalLockError,
6 OperationPlanPersistenceError, PersistenceError, create_json_durable, read_json,
7 read_operation_plan,
8};
9use crate::{
10 model::{
11 artifacts::ArtifactChecksumRecord,
12 attempt_journal::AttemptJournalRecord,
13 execution_settlement::{
14 ExecutionSettlementError, ExecutionSettlementJournalRecord, ExecutionSettlementRecord,
15 MAX_EXECUTION_SETTLEMENT_BYTES,
16 },
17 operation_plan::OperationPlanRecord,
18 },
19 policy::execution_settlement::{ExecutionSettlementPolicyError, validate},
20};
21use std::path::Path;
22use thiserror::Error;
23
24pub fn checkpoint_execution_settlement(
34 layout: &BackupLayoutGuard,
35 expected_plan: &ArtifactChecksumRecord,
36) -> Result<ExecutionSettlementRecord, ExecutionSettlementCheckpointError> {
37 let (plan, journals) = read_retained_journals(layout, expected_plan)?;
38 let rows = journals
39 .iter()
40 .map(ExecutionSettlementJournalRecord::from_journal)
41 .collect();
42 let record = ExecutionSettlementRecord::new(plan.digest(), rows)?;
43 create_execution_settlement(layout, &record)?;
44 Ok(record)
45}
46
47pub fn create_execution_settlement(
57 layout: &BackupLayoutGuard,
58 record: &ExecutionSettlementRecord,
59) -> Result<(), ExecutionSettlementPersistenceError> {
60 create_with(layout, record, create_json_durable)
61}
62fn create_with(
63 layout: &BackupLayoutGuard,
64 record: &ExecutionSettlementRecord,
65 writer: impl FnOnce(&Path, &ExecutionSettlementRecord) -> Result<(), PersistenceError>,
66) -> Result<(), ExecutionSettlementPersistenceError> {
67 layout.check_root()?;
68 let path = layout.root().join("execution-settlement.json");
69 let _lock = JournalLock::acquire(&path)?;
70 check_json_size(record, MAX_EXECUTION_SETTLEMENT_BYTES)?;
71 validate_retained(layout, record)?;
72 writer(&path, record)?;
73 Ok(())
74}
75pub fn read_execution_settlement(
83 layout: &BackupLayoutGuard,
84 expected_plan: &ArtifactChecksumRecord,
85 expected: &ArtifactChecksumRecord,
86) -> Result<ExecutionSettlementRecord, ExecutionSettlementPersistenceError> {
87 layout.check_root()?;
88 let path = layout.root().join("execution-settlement.json");
89 let _lock = JournalLock::acquire(&path)?;
90 let record: ExecutionSettlementRecord = read_json(&path, MAX_EXECUTION_SETTLEMENT_BYTES)?;
91 check_json_size(&record, MAX_EXECUTION_SETTLEMENT_BYTES)?;
92 if record.plan_intent() != expected_plan || &record.digest() != expected {
93 return Err(ExecutionSettlementPersistenceError::DigestMismatch);
94 }
95 validate_retained(layout, &record)?;
96 Ok(record)
97}
98fn validate_retained(
99 layout: &BackupLayoutGuard,
100 record: &ExecutionSettlementRecord,
101) -> Result<(), ExecutionSettlementPersistenceError> {
102 let (plan, journals) = read_retained_journals(layout, record.plan_intent())?;
103 let references: Vec<_> = journals.iter().collect();
104 validate(&plan, &references, record)?;
105 layout.check_root()?;
106 Ok(())
107}
108fn read_retained_journals(
109 layout: &BackupLayoutGuard,
110 expected_plan: &ArtifactChecksumRecord,
111) -> Result<(OperationPlanRecord, Vec<AttemptJournalRecord>), ExecutionSettlementPersistenceError> {
112 let plan = read_operation_plan(layout, expected_plan)?;
113 let authorities = plan.attempt_authorities()?;
114 let journals = super::attempt_journal::read_original_journals(layout, &authorities, None)?;
115 Ok((plan, journals))
116}
117#[derive(Debug, Error)]
119pub enum ExecutionSettlementCheckpointError {
120 #[error(transparent)]
122 Persistence(#[from] ExecutionSettlementPersistenceError),
123 #[error(transparent)]
125 Record(#[from] ExecutionSettlementError),
126}
127#[derive(Debug, Error)]
129pub enum ExecutionSettlementPersistenceError {
130 #[error("execution settlement digest mismatch")]
132 DigestMismatch,
133 #[error(transparent)]
135 Plan(#[from] OperationPlanPersistenceError),
136 #[error(transparent)]
138 Authority(#[from] crate::model::operation_plan::OperationPlanError),
139 #[error(transparent)]
141 Journal(#[from] AttemptJournalError),
142 #[error(transparent)]
144 Policy(#[from] ExecutionSettlementPolicyError),
145 #[error(transparent)]
147 Lock(#[from] JournalLockError),
148 #[error(transparent)]
150 Persistence(#[from] PersistenceError),
151}
152#[cfg(all(test, unix))]
153mod tests;