ic_backup/workflow/ic_mutation/
mod.rs1use crate::ports::IcProviderError;
4use crate::{
5 model::{
6 attempt_journal::AttemptAuthorityRecord,
7 ic_mutation::{IcMutationAcknowledgement, IcMutationRequest, IcMutationRequestError},
8 ic_request::IcManagementRequestRecord,
9 },
10 ops::persistence::{
11 AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
12 ExecutionStageGuard, ExecutionWorkflowPersistenceError,
13 },
14 ports::ic_mutation::IcMutationProvider,
15};
16
17pub(super) async fn submit_with<E, T>(
20 stage: &ExecutionStageGuard<'_>,
21 authority: &AttemptAuthorityRecord,
22 payload: &IcManagementRequestRecord,
23 provider: &mut impl IcMutationProvider,
24 admit: impl AsyncFnOnce(&IcMutationRequest<'_>) -> Result<(), E>,
25 complete: impl AsyncFnOnce(
26 &mut AttemptJournalGuard<'_>,
27 &IcMutationRequest<'_>,
28 &IcMutationAcknowledgement,
29 ) -> T,
30) -> Result<(IcMutationAcknowledgement, T), E>
31where
32 E: From<IcMutationRequestError> + From<ExecutionReservationError> + From<IcProviderError>,
33{
34 payload
35 .validate_mutation_binding(authority.binding())
36 .map_err(IcMutationRequestError::from)
37 .map_err(E::from)?;
38 execute_reserved(
39 stage,
40 authority,
41 |_| Ok(()),
42 |journal| {
43 IcMutationRequest::new(
44 stage.plan(),
45 authority.binding().operation_sequence(),
46 journal,
47 payload,
48 )
49 .map_err(E::from)
50 },
51 admit,
52 async |request, journal| {
53 provider
54 .submit_mutation(request, journal)
55 .await
56 .map_err(E::from)
57 },
58 complete,
59 )
60 .await
61}
62
63pub(super) async fn execute_reserved<Q, R, T, E>(
67 stage: &ExecutionStageGuard<'_>,
68 authority: &AttemptAuthorityRecord,
69 validate: impl FnOnce(&crate::model::attempt_journal::AttemptJournalRecord) -> Result<(), E>,
70 bind: impl FnOnce(&crate::model::attempt_journal::AttemptJournalRecord) -> Result<Q, E>,
71 admit: impl AsyncFnOnce(&Q) -> Result<(), E>,
72 submit: impl AsyncFnOnce(&Q, &crate::model::attempt_journal::AttemptJournalRecord) -> Result<R, E>,
73 complete: impl AsyncFnOnce(&mut AttemptJournalGuard<'_>, &Q, &R) -> T,
74) -> Result<(R, T), E>
75where
76 E: From<ExecutionReservationError>,
77{
78 dispatch_reserved(
79 stage,
80 authority,
81 &mut (),
82 |journal| {
83 validate(
84 journal
85 .record()
86 .map_err(ExecutionReservationError::from)
87 .map_err(E::from)?,
88 )?;
89 journal
90 .reserve_planned_mutation(&stage.plan().digest())
91 .map_err(ExecutionReservationError::from)
92 .map_err(E::from)?;
93 Ok(())
94 },
95 bind,
96 async |(), request, journal| {
97 admit(request).await?;
98 submit(request, journal).await
99 },
100 async |(), journal, request, reply| complete(journal, request, reply).await,
101 )
102 .await
103}
104
105pub(super) async fn dispatch_reserved<Q, R, T, E, C>(
108 stage: &ExecutionStageGuard<'_>,
109 authority: &AttemptAuthorityRecord,
110 context: &mut C,
111 reserve: impl FnOnce(&mut AttemptJournalGuard<'_>) -> Result<(), E>,
112 bind: impl FnOnce(&crate::model::attempt_journal::AttemptJournalRecord) -> Result<Q, E>,
113 dispatch: impl AsyncFnOnce(
114 &mut C,
115 &Q,
116 &crate::model::attempt_journal::AttemptJournalRecord,
117 ) -> Result<R, E>,
118 complete: impl AsyncFnOnce(&mut C, &mut AttemptJournalGuard<'_>, &Q, &R) -> T,
119) -> Result<(R, T), E>
120where
121 E: From<ExecutionReservationError>,
122{
123 let mut journal = AttemptJournalGuard::open(
124 stage
125 .layout()
126 .map_err(ExecutionReservationError::from)
127 .map_err(E::from)?,
128 authority,
129 )
130 .map_err(ExecutionReservationError::from)
131 .map_err(E::from)?;
132 reserve(&mut journal)?;
133 let request = bind(
134 journal
135 .record()
136 .map_err(ExecutionReservationError::from)
137 .map_err(E::from)?,
138 )?;
139 let reply = dispatch(
140 context,
141 &request,
142 journal
143 .record()
144 .map_err(ExecutionReservationError::from)
145 .map_err(E::from)?,
146 )
147 .await?;
148 let result = complete(context, &mut journal, &request, &reply).await;
149 Ok((reply, result))
150}
151
152#[derive(Debug, thiserror::Error)]
155pub enum ExecutionReservationError {
156 #[error(transparent)]
158 Stage(#[from] ExecutionWorkflowPersistenceError),
159 #[error(transparent)]
161 Journal(#[from] AttemptJournalError),
162 #[error(transparent)]
164 Progress(#[from] ExecutionProgressPersistenceError),
165}
166
167#[cfg(all(test, unix))]
168mod tests;