use crate::{
model::{
ic_mutation::{IcMutationAcknowledgement, IcMutationRequest, IcMutationRequestError},
ic_request::{IcManagementMethodRecord, IcManagementRequestRecord},
operation_plan::OperationPlanError,
},
ops::persistence::{
AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
ExecutionStageGuard, ExecutionWorkflowPersistenceError,
},
policy::ic_mutation::{IcMutationAssociationError, validate_acknowledgement},
ports::ic_mutation::{IcMutationProvider, IcMutationProviderError},
};
use thiserror::Error;
pub async fn capture_snapshot<E: std::error::Error + 'static>(
stage: &ExecutionStageGuard<'_>,
operation_sequence: u64,
payload: &IcManagementRequestRecord,
provider: &mut impl IcMutationProvider,
admit: impl AsyncFnOnce(&IcMutationRequest<'_>) -> Result<(), E>,
) -> Result<IcMutationAcknowledgement, IcSnapshotCaptureExecutionError<E>> {
if payload.method() != IcManagementMethodRecord::TakeCanisterSnapshot {
return Err(IcSnapshotCaptureExecutionError::CaptureRequired);
}
let plan = stage.plan();
let authority = plan.attempt_authority(operation_sequence)?;
payload
.validate_mutation_binding(authority.binding())
.map_err(IcMutationRequestError::from)?;
let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
journal.reserve_planned_mutation(&plan.digest())?;
let request = IcMutationRequest::new(plan, operation_sequence, journal.record()?, payload)?;
admit(&request)
.await
.map_err(IcSnapshotCaptureExecutionError::Admission)?;
stage.layout()?;
let acknowledgement = provider
.submit_mutation(&request, journal.record()?)
.await?;
let admit_reply = || -> Result<(), IcSnapshotCaptureReplyError> {
validate_acknowledgement(&request, journal.record()?, &acknowledgement)?;
stage.layout()?;
Ok(())
};
match admit_reply() {
Ok(()) => Ok(acknowledgement),
Err(source) => Err(IcSnapshotCaptureExecutionError::AfterReply {
source,
acknowledgement: Box::new(acknowledgement),
}),
}
}
#[derive(Debug, Error)]
pub enum IcSnapshotCaptureExecutionError<E: std::error::Error + 'static> {
#[error("snapshot capture requires take_canister_snapshot")]
CaptureRequired,
#[error(transparent)]
Plan(#[from] OperationPlanError),
#[error(transparent)]
Stage(#[from] ExecutionWorkflowPersistenceError),
#[error(transparent)]
Journal(#[from] AttemptJournalError),
#[error(transparent)]
Progress(#[from] ExecutionProgressPersistenceError),
#[error(transparent)]
Request(#[from] IcMutationRequestError),
#[error("fresh snapshot capture admission failed: {0}")]
Admission(#[source] E),
#[error(transparent)]
Provider(#[from] IcMutationProviderError),
#[error("snapshot capture reply rejected: {source}")]
AfterReply {
source: IcSnapshotCaptureReplyError,
acknowledgement: Box<IcMutationAcknowledgement>,
},
}
#[derive(Debug, Error)]
pub enum IcSnapshotCaptureReplyError {
#[error(transparent)]
Journal(#[from] AttemptJournalError),
#[error(transparent)]
Association(#[from] IcMutationAssociationError),
#[error(transparent)]
Stage(#[from] ExecutionWorkflowPersistenceError),
}
#[cfg(all(test, unix))]
mod tests;