ic_backup/workflow/fence_reconciliation/
mod.rs1use crate::{
4 model::{
5 artifacts::ArtifactChecksumRecord,
6 fence_acquisition::{FenceAcquisitionError, FenceAcquisitionPayload, original_authority},
7 fence_obligation::FenceObligationRecord,
8 fence_reconciliation::{
9 FenceReconciliationIntent, FenceReconciliationObservation, FenceReconciliationRequest,
10 FenceReconciliationRequestError,
11 },
12 operation_plan::OperationPlanError,
13 },
14 ops::persistence::{
15 AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
16 ExecutionStageGuard, ExecutionWorkflowPersistenceError, FenceObligationPersistenceError,
17 FenceObligationRequirement, read_fence_obligation,
18 },
19 policy::fence_reconciliation::{FenceReconciliationError, validate},
20 ports::fence_reconciliation::{FenceReconciliationProvider, FenceReconciliationProviderError},
21};
22use thiserror::Error;
23
24pub async fn observe_fence_acquisition<E: std::error::Error + 'static>(
50 stage: &ExecutionStageGuard<'_>,
51 requirement: FenceObligationRequirement<'_>,
52 obligation: &FenceObligationRecord,
53 payload: &FenceAcquisitionPayload,
54 challenge: ArtifactChecksumRecord,
55 provider: &mut impl FenceReconciliationProvider,
56 admit: impl AsyncFnOnce(&FenceReconciliationRequest<'_, '_>) -> Result<(), E>,
57) -> Result<FenceReconciliationObservation, FenceReconciliationExecutionError<E>> {
58 let plan = stage.plan();
59 let layout = stage.layout()?;
60 read_fence_obligation(layout, plan, requirement, &obligation.digest())?;
61 let authority = plan.attempt_authority(obligation.acquisition_operation())?;
62 let mut journal = AttemptJournalGuard::open(layout, &authority)?;
63 original_authority(plan, obligation, journal.record()?, payload)?;
64 let intent = FenceReconciliationIntent::new(plan, obligation, journal.record()?, challenge)?;
65 journal.reserve_planned_observation(
66 &plan.digest(),
67 intent.mutation_attempt(),
68 intent.digest().hash(),
69 )?;
70 let request = FenceReconciliationRequest::new(&intent, journal.record()?)?;
71 admit(&request)
72 .await
73 .map_err(FenceReconciliationExecutionError::Admission)?;
74 read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
75 let observation = provider
76 .observe_fence_acquisition(&request, journal.record()?)
77 .await?;
78 let association = match journal.record() {
79 Ok(record) => record,
80 Err(source) => {
81 return Err(FenceReconciliationExecutionError::AfterReplyJournal {
82 source,
83 observation: Box::new(observation),
84 });
85 }
86 };
87 if let Err(source) = validate(&request, association, &observation) {
88 return Err(FenceReconciliationExecutionError::Association {
89 source,
90 observation: Box::new(observation),
91 });
92 }
93 let layout = match stage.layout() {
94 Ok(layout) => layout,
95 Err(source) => {
96 return Err(FenceReconciliationExecutionError::AfterReplyStage {
97 source,
98 observation: Box::new(observation),
99 });
100 }
101 };
102 if let Err(source) = read_fence_obligation(layout, plan, requirement, &obligation.digest()) {
103 return Err(FenceReconciliationExecutionError::AfterReplyObligation {
104 source,
105 observation: Box::new(observation),
106 });
107 }
108 Ok(observation)
109}
110
111#[derive(Debug, Error)]
113pub enum FenceReconciliationExecutionError<E: std::error::Error + 'static> {
114 #[error(transparent)]
116 Plan(#[from] OperationPlanError),
117 #[error(transparent)]
119 Stage(#[from] ExecutionWorkflowPersistenceError),
120 #[error(transparent)]
122 Obligation(#[from] FenceObligationPersistenceError),
123 #[error(transparent)]
125 Journal(#[from] AttemptJournalError),
126 #[error(transparent)]
128 Payload(#[from] FenceAcquisitionError),
129 #[error(transparent)]
131 Progress(#[from] ExecutionProgressPersistenceError),
132 #[error(transparent)]
134 Request(#[from] FenceReconciliationRequestError),
135 #[error("fresh fence reconciliation admission failed: {0}")]
137 Admission(#[source] E),
138 #[error(transparent)]
140 Provider(#[from] FenceReconciliationProviderError),
141 #[error("fence reconciliation association failed: {source}")]
143 Association {
144 source: FenceReconciliationError,
146 observation: Box<FenceReconciliationObservation>,
148 },
149 #[error("fence reconciliation journal changed after reply: {source}")]
151 AfterReplyJournal {
152 source: AttemptJournalError,
154 observation: Box<FenceReconciliationObservation>,
156 },
157 #[error("fence reconciliation stage changed after reply: {source}")]
159 AfterReplyStage {
160 source: ExecutionWorkflowPersistenceError,
162 observation: Box<FenceReconciliationObservation>,
164 },
165 #[error("fence reconciliation obligation changed after reply: {source}")]
167 AfterReplyObligation {
168 source: FenceObligationPersistenceError,
170 observation: Box<FenceReconciliationObservation>,
172 },
173}