ic_backup/model/ic_snapshot_upload/attempt/
mod.rs1use super::IcSnapshotUploadRequest;
4use crate::model::{
5 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
6 operation_plan::{OperationPlanError, OperationPlanRecord},
7};
8use thiserror::Error;
9
10#[derive(Debug)]
12pub struct IcSnapshotUploadAttempt<'request, 'source> {
13 plan: &'request OperationPlanRecord,
14 payload: &'request IcSnapshotUploadRequest<'source>,
15 authority: AttemptAuthorityRecord,
16 mutation_attempt: u32,
17}
18
19impl<'request, 'source> IcSnapshotUploadAttempt<'request, 'source> {
20 pub fn new(
29 plan: &'request OperationPlanRecord,
30 operation_sequence: u64,
31 journal: &AttemptJournalRecord,
32 payload: &'request IcSnapshotUploadRequest<'source>,
33 ) -> Result<Self, IcSnapshotUploadAttemptError> {
34 let authority = plan.attempt_authority(operation_sequence)?;
35 if journal.authority() != &authority {
36 return Err(IcSnapshotUploadAttemptError::AuthorityMismatch);
37 }
38 if payload.target() != authority.binding().target()
39 || payload.binding_digest().hash() != authority.binding().request()
40 {
41 return Err(IcSnapshotUploadAttemptError::PayloadMismatch);
42 }
43 if plan.context().network() != payload.source_plan().context().network()
44 || plan.context().release() != payload.source_plan().context().release()
45 {
46 return Err(IcSnapshotUploadAttemptError::SourceContextMismatch);
47 }
48 let mutation_attempt = journal
49 .view()
50 .pending_mutation
51 .ok_or(IcSnapshotUploadAttemptError::NoPendingMutation)?;
52 let attempt = Self {
53 plan,
54 payload,
55 authority,
56 mutation_attempt,
57 };
58 attempt.validate_journal(journal)?;
59 Ok(attempt)
60 }
61 #[must_use]
63 pub const fn plan(&self) -> &'request OperationPlanRecord {
64 self.plan
65 }
66 #[must_use]
68 pub const fn payload(&self) -> &'request IcSnapshotUploadRequest<'source> {
69 self.payload
70 }
71 #[must_use]
73 pub const fn authority(&self) -> &AttemptAuthorityRecord {
74 &self.authority
75 }
76 #[must_use]
78 pub const fn mutation_attempt(&self) -> u32 {
79 self.mutation_attempt
80 }
81 pub fn validate_journal(
85 &self,
86 journal: &AttemptJournalRecord,
87 ) -> Result<(), IcSnapshotUploadAttemptError> {
88 if journal.authority() != &self.authority {
89 return Err(IcSnapshotUploadAttemptError::AuthorityMismatch);
90 }
91 let view = journal.view();
92 if view.pending_mutation != Some(self.mutation_attempt) {
93 return Err(IcSnapshotUploadAttemptError::MutationMismatch);
94 }
95 if view.pending_observation.is_some() {
96 return Err(IcSnapshotUploadAttemptError::ObservationPending);
97 }
98 Ok(())
99 }
100}
101
102#[derive(Debug, Error)]
104pub enum IcSnapshotUploadAttemptError {
105 #[error("snapshot upload source network or release differs")]
107 SourceContextMismatch,
108 #[error("snapshot upload original authority mismatch")]
110 AuthorityMismatch,
111 #[error("snapshot upload original payload mismatch")]
113 PayloadMismatch,
114 #[error("snapshot upload requires an original pending mutation")]
116 NoPendingMutation,
117 #[error("snapshot upload original mutation differs")]
119 MutationMismatch,
120 #[error("snapshot upload recovery observation is pending")]
122 ObservationPending,
123 #[error(transparent)]
125 Plan(#[from] OperationPlanError),
126}