ic_backup/ops/persistence/execution_settlement/
mod.rs1use super::json::create_encoded;
4use super::{
5 AttemptJournalError, BackupLayoutGuard, JournalLockError, OperationPlanPersistenceError,
6 PersistenceError,
7};
8use crate::{
9 model::{
10 artifacts::ArtifactChecksumRecord,
11 execution_settlement::{
12 ExecutionSettlementError, ExecutionSettlementJournalRecord, ExecutionSettlementRecord,
13 MAX_EXECUTION_SETTLEMENT_BYTES,
14 },
15 },
16 policy::execution_settlement::{ExecutionSettlementPolicyError, validate_journals},
17};
18use std::{io, path::Path};
19use thiserror::Error;
20
21pub fn replay_execution_settlement(
30 layout: &BackupLayoutGuard,
31 expected_plan: &ArtifactChecksumRecord,
32) -> Result<ExecutionSettlementRecord, ExecutionSettlementCheckpointError> {
33 let derived = derive_execution_settlement(layout, expected_plan)?;
34 Ok(read_execution_settlement(
35 layout,
36 expected_plan,
37 &derived.digest(),
38 )?)
39}
40
41pub fn reconcile_execution_settlement(
53 layout: &BackupLayoutGuard,
54 expected_plan: &ArtifactChecksumRecord,
55) -> Result<ExecutionSettlementRecord, ExecutionSettlementCheckpointError> {
56 let derived = derive_execution_settlement(layout, expected_plan)?;
57 match read_execution_settlement(layout, expected_plan, &derived.digest()) {
58 Ok(existing) => Ok(existing),
59 Err(ExecutionSettlementPersistenceError::Persistence(PersistenceError::Io(error)))
60 if error.kind() == io::ErrorKind::NotFound =>
61 {
62 create_execution_settlement(layout, &derived)?;
63 Ok(read_execution_settlement(
64 layout,
65 expected_plan,
66 &derived.digest(),
67 )?)
68 }
69 Err(error) => Err(error.into()),
70 }
71}
72
73fn derive_execution_settlement(
74 layout: &BackupLayoutGuard,
75 expected_plan: &ArtifactChecksumRecord,
76) -> Result<ExecutionSettlementRecord, ExecutionSettlementCheckpointError> {
77 let (plan, journals) = super::attempt_journal::read_original_plan_journals::<
78 ExecutionSettlementPersistenceError,
79 >(layout, expected_plan, None)?;
80 let rows = journals
81 .iter()
82 .map(ExecutionSettlementJournalRecord::from_journal)
83 .collect();
84 Ok(ExecutionSettlementRecord::new(plan.digest(), rows)?)
85}
86
87fn create_execution_settlement(
97 layout: &BackupLayoutGuard,
98 record: &ExecutionSettlementRecord,
99) -> Result<(), ExecutionSettlementPersistenceError> {
100 create_with(layout, record, create_encoded)
101}
102fn create_with(
103 layout: &BackupLayoutGuard,
104 record: &ExecutionSettlementRecord,
105 writer: impl FnOnce(&Path, &[u8]) -> Result<(), PersistenceError>,
106) -> Result<(), ExecutionSettlementPersistenceError> {
107 super::immutable_record::create_with(
108 layout,
109 "execution-settlement.json",
110 MAX_EXECUTION_SETTLEMENT_BYTES,
111 record,
112 || validate_retained(layout, record),
113 writer,
114 )
115}
116pub fn read_execution_settlement(
124 layout: &BackupLayoutGuard,
125 expected_plan: &ArtifactChecksumRecord,
126 expected: &ArtifactChecksumRecord,
127) -> Result<ExecutionSettlementRecord, ExecutionSettlementPersistenceError> {
128 super::immutable_record::read_with(
129 layout,
130 "execution-settlement.json",
131 MAX_EXECUTION_SETTLEMENT_BYTES,
132 |record: &ExecutionSettlementRecord| {
133 if record.plan_intent() != expected_plan || &record.digest() != expected {
134 return Err(ExecutionSettlementPersistenceError::DigestMismatch);
135 }
136 validate_retained(layout, record)
137 },
138 )
139}
140fn validate_retained(
141 layout: &BackupLayoutGuard,
142 record: &ExecutionSettlementRecord,
143) -> Result<(), ExecutionSettlementPersistenceError> {
144 let (plan, journals) = super::attempt_journal::read_original_plan_journals::<
145 ExecutionSettlementPersistenceError,
146 >(layout, record.plan_intent(), None)?;
147 validate_journals(&plan, journals.iter(), record)?;
148 layout.check_root()?;
149 Ok(())
150}
151#[derive(Debug, Error)]
153pub enum ExecutionSettlementCheckpointError {
154 #[error(transparent)]
156 Persistence(#[from] ExecutionSettlementPersistenceError),
157 #[error(transparent)]
159 Record(#[from] ExecutionSettlementError),
160}
161#[derive(Debug, Error)]
163pub enum ExecutionSettlementPersistenceError {
164 #[error("execution settlement digest mismatch")]
166 DigestMismatch,
167 #[error(transparent)]
169 Plan(#[from] OperationPlanPersistenceError),
170 #[error(transparent)]
172 Authority(#[from] crate::model::operation_plan::OperationPlanError),
173 #[error(transparent)]
175 Journal(#[from] AttemptJournalError),
176 #[error(transparent)]
178 Policy(#[from] ExecutionSettlementPolicyError),
179 #[error(transparent)]
181 Lock(#[from] JournalLockError),
182 #[error(transparent)]
184 Persistence(#[from] PersistenceError),
185}
186#[cfg(all(test, unix))]
187mod tests;