radicle/sigrefs/write: Treat error to verify head
To prevent from inability to write sigrefs in case of a verification error, swallow verification errors about the head commit, log, and carry on writing.
This commit is contained in:
parent
8bc3ffc0b0
commit
7f19044a72
|
|
@ -129,6 +129,12 @@ pub mod error {
|
||||||
Git(#[from] radicle::git::raw::Error),
|
Git(#[from] radicle::git::raw::Error),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Refs(#[from] storage::refs::Error),
|
Refs(Box<storage::refs::Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<storage::refs::Error> for Policy {
|
||||||
|
fn from(err: storage::refs::Error) -> Self {
|
||||||
|
Self::Refs(Box::new(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub enum Fetch {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Repository(#[from] radicle::storage::RepositoryError),
|
Repository(#[from] radicle::storage::RepositoryError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
RefsDb(#[from] radicle::node::refs::Error),
|
RefsDb(Box<radicle::node::refs::Error>),
|
||||||
#[error("validation of the storage repository failed: the delegates {delegates:?} failed to validate to meet a threshold of {threshold}")]
|
#[error("validation of the storage repository failed: the delegates {delegates:?} failed to validate to meet a threshold of {threshold}")]
|
||||||
Validation {
|
Validation {
|
||||||
threshold: usize,
|
threshold: usize,
|
||||||
|
|
@ -28,6 +28,12 @@ pub enum Fetch {
|
||||||
Cache(#[from] Cache),
|
Cache(#[from] Cache),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<radicle::node::refs::Error> for Fetch {
|
||||||
|
fn from(err: radicle::node::refs::Error) -> Self {
|
||||||
|
Self::RefsDb(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Cache {
|
pub enum Cache {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ impl SetHead {
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum RepositoryError {
|
pub enum RepositoryError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Storage(#[from] Error),
|
Storage(Box<Error>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Store(#[from] cob::store::Error),
|
Store(#[from] cob::store::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
@ -119,7 +119,7 @@ pub enum RepositoryError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Quorum(#[from] canonical::error::QuorumError),
|
Quorum(#[from] canonical::error::QuorumError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Refs(#[from] refs::Error),
|
Refs(Box<refs::Error>),
|
||||||
#[error("missing canonical reference rule for default branch")]
|
#[error("missing canonical reference rule for default branch")]
|
||||||
MissingBranchRule,
|
MissingBranchRule,
|
||||||
#[error("could not get the default branch rule: {0}")]
|
#[error("could not get the default branch rule: {0}")]
|
||||||
|
|
@ -130,6 +130,18 @@ pub enum RepositoryError {
|
||||||
FindObjects(#[from] canonical::effects::FindObjectsError),
|
FindObjects(#[from] canonical::effects::FindObjectsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Error> for RepositoryError {
|
||||||
|
fn from(err: Error) -> Self {
|
||||||
|
Self::Storage(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<refs::Error> for RepositoryError {
|
||||||
|
fn from(err: refs::Error) -> Self {
|
||||||
|
Self::Refs(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RepositoryError {
|
impl RepositoryError {
|
||||||
pub fn is_not_found(&self) -> bool {
|
pub fn is_not_found(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ impl WriteStorage for Storage {
|
||||||
// N.b. we remove the repository if the `local` peer has no
|
// N.b. we remove the repository if the `local` peer has no
|
||||||
// `rad/sigrefs`. There's no risk of them corrupting data.
|
// `rad/sigrefs`. There's no risk of them corrupting data.
|
||||||
let has_sigrefs = SignedRefsAt::load(self.info.key, &repo)
|
let has_sigrefs = SignedRefsAt::load(self.info.key, &repo)
|
||||||
.map_err(|err| RepositoryError::Storage(Error::Refs(refs::Error::Read(err))))?
|
.map_err(|err| RepositoryError::from(refs::Error::Read(err)))?
|
||||||
.is_some();
|
.is_some();
|
||||||
if has_sigrefs {
|
if has_sigrefs {
|
||||||
repo.clean(&self.info.key)
|
repo.clean(&self.info.key)
|
||||||
|
|
@ -259,7 +259,7 @@ impl Storage {
|
||||||
let (_, head) = repo.head()?;
|
let (_, head) = repo.head()?;
|
||||||
|
|
||||||
let refs = refs::SignedRefsAt::load(self.info.key, &repo)
|
let refs = refs::SignedRefsAt::load(self.info.key, &repo)
|
||||||
.map_err(|err| RepositoryError::Refs(refs::Error::Read(err)))?;
|
.map_err(|err| RepositoryError::from(refs::Error::Read(err)))?;
|
||||||
let synced_at = refs
|
let synced_at = refs
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r| SyncedAt::new(r.at, &repo))
|
.map(|r| SyncedAt::new(r.at, &repo))
|
||||||
|
|
|
||||||
|
|
@ -135,12 +135,13 @@ where
|
||||||
} = self;
|
} = self;
|
||||||
let reference = SIGREFS_BRANCH.with_namespace(git::fmt::Component::from(&namespace));
|
let reference = SIGREFS_BRANCH.with_namespace(git::fmt::Component::from(&namespace));
|
||||||
|
|
||||||
let head = HeadReader::new(&reference, repository, rid, self.signer)
|
let head = HeadReader::new(&reference, repository, rid, self.signer).read();
|
||||||
.read()
|
|
||||||
.map_err(error::Write::Head)?;
|
|
||||||
let commit_writer = match head {
|
let commit_writer = match head {
|
||||||
Some(head) if head.is_unchanged(&refs) => return Ok(Update::unchanged(head.verified)),
|
Ok(Some(head)) if head.is_unchanged(&refs) => {
|
||||||
Some(head) => CommitWriter::with_parent(
|
return Ok(Update::unchanged(head.verified))
|
||||||
|
}
|
||||||
|
Ok(Some(head)) => CommitWriter::with_parent(
|
||||||
refs,
|
refs,
|
||||||
*head.verified.commit().oid(),
|
*head.verified.commit().oid(),
|
||||||
author,
|
author,
|
||||||
|
|
@ -148,7 +149,12 @@ where
|
||||||
repository,
|
repository,
|
||||||
signer,
|
signer,
|
||||||
),
|
),
|
||||||
None => CommitWriter::root(refs, author, message, repository, signer),
|
Ok(None) => CommitWriter::root(refs, author, message, repository, signer),
|
||||||
|
Err(error::Head::Verify { commit, source }) => {
|
||||||
|
log::warn!("Verification of head of signed references failed: {source}");
|
||||||
|
CommitWriter::with_parent(refs, commit, author, message, repository, signer)
|
||||||
|
}
|
||||||
|
Err(err) => return Err(error::Write::Head(err)),
|
||||||
};
|
};
|
||||||
let commit = commit_writer.write().map_err(error::Write::Commit)?;
|
let commit = commit_writer.write().map_err(error::Write::Commit)?;
|
||||||
repository
|
repository
|
||||||
|
|
@ -383,7 +389,10 @@ where
|
||||||
.read()
|
.read()
|
||||||
.map_err(error::Head::Commit)?
|
.map_err(error::Head::Commit)?
|
||||||
.verify(self.rid, self.verifier)
|
.verify(self.rid, self.verifier)
|
||||||
.map_err(error::Head::Verify)?;
|
.map_err(|err| error::Head::Verify {
|
||||||
|
commit: oid,
|
||||||
|
source: err,
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Some(Head { verified }))
|
Ok(Some(Head { verified }))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use radicle_oid::Oid;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::storage::refs::sigrefs::git::{object, reference};
|
use crate::storage::refs::sigrefs::git::{object, reference};
|
||||||
|
|
@ -43,6 +44,9 @@ pub enum Head {
|
||||||
Reference(reference::error::FindReference),
|
Reference(reference::error::FindReference),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Commit(super::read::error::Commit),
|
Commit(super::read::error::Commit),
|
||||||
#[error(transparent)]
|
#[error("failed to verify commit {commit}: {source}")]
|
||||||
Verify(super::read::error::Verify),
|
Verify {
|
||||||
|
commit: Oid,
|
||||||
|
source: super::read::error::Verify,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ impl ReadStorage for MockStorage {
|
||||||
self.repos
|
self.repos
|
||||||
.get(&rid)
|
.get(&rid)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
RepositoryError::Storage(Error::Io(io::Error::from(io::ErrorKind::NotFound)))
|
RepositoryError::from(Error::Io(io::Error::from(io::ErrorKind::NotFound)))
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +115,7 @@ impl WriteStorage for MockStorage {
|
||||||
fn repository_mut(&self, rid: RepoId) -> Result<Self::RepositoryMut, RepositoryError> {
|
fn repository_mut(&self, rid: RepoId) -> Result<Self::RepositoryMut, RepositoryError> {
|
||||||
self.repos
|
self.repos
|
||||||
.get(&rid)
|
.get(&rid)
|
||||||
.ok_or(RepositoryError::Storage(Error::Io(io::Error::from(
|
.ok_or(RepositoryError::from(Error::Io(io::Error::from(
|
||||||
io::ErrorKind::NotFound,
|
io::ErrorKind::NotFound,
|
||||||
))))
|
))))
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue