node: Return only remotes that were fetched from
Instead of returning the namespaces that we fetched with, we return the remotes that were actually fetched from. This can differ if some trusted peers were not available on the remote node. Previously, if that was the case, it would cause errors since the remotes were looked up and that lookup failed.
This commit is contained in:
parent
bb07e25571
commit
94bef61944
|
|
@ -110,7 +110,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
|
|
||||||
pub fn fetch(rid: Id, node: &mut Node) -> Result<FetchResults, node::Error> {
|
pub fn fetch(rid: Id, node: &mut Node) -> Result<FetchResults, node::Error> {
|
||||||
// Get seeds. This consults the local routing table only.
|
// Get seeds. This consults the local routing table only.
|
||||||
let mut seeds = node.seeds(rid)?;
|
let seeds = node.seeds(rid)?;
|
||||||
let mut results = FetchResults::default();
|
let mut results = FetchResults::default();
|
||||||
|
|
||||||
if seeds.has_connections() {
|
if seeds.has_connections() {
|
||||||
|
|
|
||||||
|
|
@ -512,7 +512,7 @@ where
|
||||||
resp.send(untracked).ok();
|
resp.send(untracked).ok();
|
||||||
}
|
}
|
||||||
Command::AnnounceRefs(id) => {
|
Command::AnnounceRefs(id) => {
|
||||||
if let Err(err) = self.announce_refs(id, &Namespaces::from_iter([self.node_id()])) {
|
if let Err(err) = self.announce_refs(id, [self.node_id()]) {
|
||||||
error!("Error announcing refs: {}", err);
|
error!("Error announcing refs: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -582,12 +582,11 @@ where
|
||||||
pub fn fetched(
|
pub fn fetched(
|
||||||
&mut self,
|
&mut self,
|
||||||
rid: Id,
|
rid: Id,
|
||||||
namespaces: Namespaces,
|
|
||||||
remote: NodeId,
|
remote: NodeId,
|
||||||
result: Result<Vec<RefUpdate>, FetchError>,
|
result: Result<(Vec<RefUpdate>, HashSet<NodeId>), FetchError>,
|
||||||
) {
|
) {
|
||||||
let result = match result {
|
let result = match result {
|
||||||
Ok(updated) => {
|
Ok((updated, namespaces)) => {
|
||||||
log::debug!(target: "service", "Fetched {rid} from {remote} successfully");
|
log::debug!(target: "service", "Fetched {rid} from {remote} successfully");
|
||||||
|
|
||||||
for update in &updated {
|
for update in &updated {
|
||||||
|
|
@ -599,7 +598,10 @@ where
|
||||||
updated: updated.clone(),
|
updated: updated.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
FetchResult::Success { updated }
|
FetchResult::Success {
|
||||||
|
updated,
|
||||||
|
namespaces,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let reason = err.to_string();
|
let reason = err.to_string();
|
||||||
|
|
@ -630,8 +632,11 @@ where
|
||||||
// because the user might want to announce his fork, once he has created one,
|
// because the user might want to announce his fork, once he has created one,
|
||||||
// or may choose to not announce anything.
|
// or may choose to not announce anything.
|
||||||
match result {
|
match result {
|
||||||
FetchResult::Success { updated } if !updated.is_empty() => {
|
FetchResult::Success {
|
||||||
if let Err(e) = self.announce_refs(rid, &namespaces) {
|
updated,
|
||||||
|
namespaces,
|
||||||
|
} if !updated.is_empty() => {
|
||||||
|
if let Err(e) = self.announce_refs(rid, namespaces) {
|
||||||
error!(target: "service", "Failed to announce new refs: {e}");
|
error!(target: "service", "Failed to announce new refs: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1190,29 +1195,19 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Announce local refs for given id.
|
/// Announce local refs for given id.
|
||||||
fn announce_refs(&mut self, rid: Id, namespaces: &Namespaces) -> Result<(), storage::Error> {
|
fn announce_refs(
|
||||||
|
&mut self,
|
||||||
|
rid: Id,
|
||||||
|
remotes: impl IntoIterator<Item = NodeId>,
|
||||||
|
) -> Result<(), storage::Error> {
|
||||||
let repo = self.storage.repository(rid)?;
|
let repo = self.storage.repository(rid)?;
|
||||||
let peers = self.sessions.connected().map(|(_, p)| p);
|
let peers = self.sessions.connected().map(|(_, p)| p);
|
||||||
let timestamp = self.time();
|
let timestamp = self.time();
|
||||||
let mut refs = BoundedVec::<_, REF_REMOTE_LIMIT>::new();
|
let mut refs = BoundedVec::<_, REF_REMOTE_LIMIT>::new();
|
||||||
|
|
||||||
match namespaces {
|
for remote_id in remotes.into_iter() {
|
||||||
Namespaces::All => {
|
|
||||||
for (remote_id, remote) in repo.remotes()?.into_iter() {
|
|
||||||
if refs.push((remote_id, remote.refs.unverified())).is_err() {
|
|
||||||
warn!(
|
|
||||||
target: "service",
|
|
||||||
"refs announcement limit ({}) exceeded, peers will see only some of your repository references",
|
|
||||||
REF_REMOTE_LIMIT,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Namespaces::Trusted(trusted) => {
|
|
||||||
for remote_id in trusted.iter() {
|
|
||||||
if refs
|
if refs
|
||||||
.push((*remote_id, repo.remote(remote_id)?.refs.unverified()))
|
.push((remote_id, repo.remote(&remote_id)?.refs.unverified()))
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
|
|
@ -1223,8 +1218,6 @@ where
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = AnnouncementMessage::from(RefsAnnouncement {
|
let msg = AnnouncementMessage::from(RefsAnnouncement {
|
||||||
rid,
|
rid,
|
||||||
|
|
@ -1428,7 +1421,7 @@ where
|
||||||
|
|
||||||
for rid in missing {
|
for rid in missing {
|
||||||
match self.seeds(&rid) {
|
match self.seeds(&rid) {
|
||||||
Ok(mut seeds) => {
|
Ok(seeds) => {
|
||||||
if seeds.has_connections() {
|
if seeds.has_connections() {
|
||||||
for seed in seeds.connected() {
|
for seed in seeds.connected() {
|
||||||
self.fetch(rid, seed);
|
self.fetch(rid, seed);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use crate::node::{FetchResult, Seeds};
|
||||||
use crate::runtime::HandleError;
|
use crate::runtime::HandleError;
|
||||||
use crate::service::NodeId;
|
use crate::service::NodeId;
|
||||||
use crate::service::{self, tracking};
|
use crate::service::{self, tracking};
|
||||||
use crate::storage::RefUpdate;
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
|
|
@ -32,7 +31,10 @@ impl radicle::node::Handle for Handle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, _id: Id, _from: NodeId) -> Result<FetchResult, Self::Error> {
|
fn fetch(&mut self, _id: Id, _from: NodeId) -> Result<FetchResult, Self::Error> {
|
||||||
Ok(FetchResult::from(Ok::<Vec<RefUpdate>, Self::Error>(vec![])))
|
Ok(FetchResult::Success {
|
||||||
|
updated: vec![],
|
||||||
|
namespaces: HashSet::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn track_repo(&mut self, id: Id, _scope: tracking::Scope) -> Result<bool, Self::Error> {
|
fn track_repo(&mut self, id: Id, _scope: tracking::Scope) -> Result<bool, Self::Error> {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
//! A simple P2P network simulator. Acts as the _reactor_, but without doing any I/O.
|
//! A simple P2P network simulator. Acts as the _reactor_, but without doing any I/O.
|
||||||
#![allow(clippy::collapsible_if)]
|
#![allow(clippy::collapsible_if)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, DerefMut, Range};
|
use std::ops::{Deref, DerefMut, Range};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
@ -65,9 +66,8 @@ pub enum Input {
|
||||||
/// Fetch completed for a node.
|
/// Fetch completed for a node.
|
||||||
Fetched(
|
Fetched(
|
||||||
Id,
|
Id,
|
||||||
Namespaces,
|
|
||||||
NodeId,
|
NodeId,
|
||||||
Rc<Result<Vec<RefUpdate>, FetchError>>,
|
Rc<Result<(Vec<RefUpdate>, HashSet<NodeId>), FetchError>>,
|
||||||
),
|
),
|
||||||
/// Used to advance the state machine after some wall time has passed.
|
/// Used to advance the state machine after some wall time has passed.
|
||||||
Wake,
|
Wake,
|
||||||
|
|
@ -110,7 +110,7 @@ impl fmt::Display for Scheduled {
|
||||||
Input::Wake => {
|
Input::Wake => {
|
||||||
write!(f, "{}: Tock", self.node)
|
write!(f, "{}: Tock", self.node)
|
||||||
}
|
}
|
||||||
Input::Fetched(rid, _, nid, _) => {
|
Input::Fetched(rid, nid, _) => {
|
||||||
write!(f, "{} <<~ {} ({}): Fetched", self.node, nid, rid)
|
write!(f, "{} <<~ {} ({}): Fetched", self.node, nid, rid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -409,15 +409,21 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
p.received_message(id, msg);
|
p.received_message(id, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Input::Fetched(rid, ns, nid, result) => {
|
Input::Fetched(rid, nid, result) => {
|
||||||
let result = Rc::try_unwrap(result).unwrap();
|
let result = Rc::try_unwrap(result).unwrap();
|
||||||
let mut repo = match p.storage().repository_mut(rid) {
|
let mut repo = match p.storage().repository_mut(rid) {
|
||||||
Ok(repo) => repo,
|
Ok(repo) => repo,
|
||||||
Err(e) if e.is_not_found() => p.storage().create(rid).unwrap(),
|
Err(e) if e.is_not_found() => p.storage().create(rid).unwrap(),
|
||||||
Err(e) => panic!("Failed to open repository: {e}"),
|
Err(e) => panic!("Failed to open repository: {e}"),
|
||||||
};
|
};
|
||||||
fetch(&mut repo, &nid, ns.clone()).unwrap();
|
match &result {
|
||||||
p.fetched(rid, ns, nid, result);
|
Ok((_, remotes)) => {
|
||||||
|
fetch(&mut repo, &nid, Namespaces::Trusted(remotes.clone()))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Err(err) => panic!("Error fetching: {err}"),
|
||||||
|
}
|
||||||
|
p.fetched(rid, nid, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while let Some(o) = p.next() {
|
while let Some(o) = p.next() {
|
||||||
|
|
@ -621,7 +627,6 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
remote,
|
remote,
|
||||||
input: Input::Fetched(
|
input: Input::Fetched(
|
||||||
rid,
|
rid,
|
||||||
namespaces,
|
|
||||||
remote,
|
remote,
|
||||||
Rc::new(Err(FetchError::Io(io::ErrorKind::Other.into()))),
|
Rc::new(Err(FetchError::Io(io::ErrorKind::Other.into()))),
|
||||||
),
|
),
|
||||||
|
|
@ -633,7 +638,17 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
Scheduled {
|
Scheduled {
|
||||||
node,
|
node,
|
||||||
remote,
|
remote,
|
||||||
input: Input::Fetched(rid, namespaces, remote, Rc::new(Ok(vec![]))),
|
input: Input::Fetched(
|
||||||
|
rid,
|
||||||
|
remote,
|
||||||
|
Rc::new(Ok((
|
||||||
|
vec![],
|
||||||
|
match namespaces {
|
||||||
|
Namespaces::Trusted(hs) => hs,
|
||||||
|
Namespaces::All => HashSet::new(),
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ use crate::service::ServiceState as _;
|
||||||
use crate::service::*;
|
use crate::service::*;
|
||||||
use crate::storage::git::transport::{local, remote};
|
use crate::storage::git::transport::{local, remote};
|
||||||
use crate::storage::git::Storage;
|
use crate::storage::git::Storage;
|
||||||
use crate::storage::Namespaces;
|
|
||||||
use crate::storage::ReadStorage;
|
use crate::storage::ReadStorage;
|
||||||
use crate::test::arbitrary;
|
use crate::test::arbitrary;
|
||||||
use crate::test::assert_matches;
|
use crate::test::assert_matches;
|
||||||
|
|
@ -1121,14 +1120,14 @@ fn test_queued_fetch() {
|
||||||
alice.elapse(KEEP_ALIVE_DELTA);
|
alice.elapse(KEEP_ALIVE_DELTA);
|
||||||
|
|
||||||
// Finish the 1st fetch.
|
// Finish the 1st fetch.
|
||||||
alice.fetched(rid1, Namespaces::All, bob.id, Ok(vec![]));
|
alice.fetched(rid1, bob.id, Ok((vec![], Default::default())));
|
||||||
// Now the 1st fetch is done, the 2nd fetch is dequeued.
|
// Now the 1st fetch is done, the 2nd fetch is dequeued.
|
||||||
assert_matches!(alice.fetches().next(), Some((rid, _, _)) if rid == rid2);
|
assert_matches!(alice.fetches().next(), Some((rid, _, _)) if rid == rid2);
|
||||||
// ... but not the third.
|
// ... but not the third.
|
||||||
assert_matches!(alice.fetches().next(), None);
|
assert_matches!(alice.fetches().next(), None);
|
||||||
|
|
||||||
// Finish the 2nd fetch.
|
// Finish the 2nd fetch.
|
||||||
alice.fetched(rid2, Namespaces::All, bob.id, Ok(vec![]));
|
alice.fetched(rid2, bob.id, Ok((vec![], Default::default())));
|
||||||
// Now the 2nd fetch is done, the 3rd fetch is dequeued.
|
// Now the 2nd fetch is done, the 3rd fetch is dequeued.
|
||||||
assert_matches!(alice.fetches().next(), Some((rid, _, _)) if rid == rid3);
|
assert_matches!(alice.fetches().next(), Some((rid, _, _)) if rid == rid3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ fn test_replication() {
|
||||||
assert!(result.is_success());
|
assert!(result.is_success());
|
||||||
|
|
||||||
let updated = match result {
|
let updated = match result {
|
||||||
FetchResult::Success { updated } => updated,
|
FetchResult::Success { updated, .. } => updated,
|
||||||
FetchResult::Failed { reason } => {
|
FetchResult::Failed { reason } => {
|
||||||
panic!("Fetch failed from {}: {reason}", bob.id);
|
panic!("Fetch failed from {}: {reason}", bob.id);
|
||||||
}
|
}
|
||||||
|
|
@ -446,7 +446,7 @@ fn test_fetch_preserve_owned_refs() {
|
||||||
|
|
||||||
// Fetch shouldn't prune any of our own refs.
|
// Fetch shouldn't prune any of our own refs.
|
||||||
let result = alice.handle.fetch(acme, bob.id).unwrap();
|
let result = alice.handle.fetch(acme, bob.id).unwrap();
|
||||||
let updated = result.success().unwrap();
|
let (updated, _) = result.success().unwrap();
|
||||||
assert_eq!(updated, vec![]);
|
assert_eq!(updated, vec![]);
|
||||||
|
|
||||||
let after = alice
|
let after = alice
|
||||||
|
|
@ -540,7 +540,10 @@ fn test_fetch_up_to_date() {
|
||||||
|
|
||||||
// Fetch again! This time, everything's up to date.
|
// Fetch again! This time, everything's up to date.
|
||||||
let result = alice.handle.fetch(acme, bob.id).unwrap();
|
let result = alice.handle.fetch(acme, bob.id).unwrap();
|
||||||
assert_eq!(result.success(), Some(vec![]));
|
assert_eq!(
|
||||||
|
result.success(),
|
||||||
|
Some((vec![], HashSet::from_iter([bob.id])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -365,12 +365,8 @@ where
|
||||||
|
|
||||||
// Only call into the service if we initiated this fetch.
|
// Only call into the service if we initiated this fetch.
|
||||||
match task.result {
|
match task.result {
|
||||||
FetchResult::Initiator {
|
FetchResult::Initiator { rid, result } => {
|
||||||
rid,
|
self.service.fetched(rid, remote, result);
|
||||||
namespaces,
|
|
||||||
result,
|
|
||||||
} => {
|
|
||||||
self.service.fetched(rid, namespaces, remote, result);
|
|
||||||
}
|
}
|
||||||
FetchResult::Responder { .. } => {
|
FetchResult::Responder { .. } => {
|
||||||
// We don't do anything with upload results for now.
|
// We don't do anything with upload results for now.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ mod channels;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod tunnel;
|
mod tunnel;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::io::{prelude::*, BufReader};
|
use std::io::{prelude::*, BufReader};
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
|
|
@ -112,10 +113,8 @@ pub enum FetchResult {
|
||||||
Initiator {
|
Initiator {
|
||||||
/// Repo fetched.
|
/// Repo fetched.
|
||||||
rid: Id,
|
rid: Id,
|
||||||
/// Namespaces fetched.
|
/// Fetch result, including remotes fetched.
|
||||||
namespaces: Namespaces,
|
result: Result<(Vec<RefUpdate>, HashSet<NodeId>), FetchError>,
|
||||||
/// Fetch result.
|
|
||||||
result: Result<Vec<RefUpdate>, FetchError>,
|
|
||||||
},
|
},
|
||||||
Responder {
|
Responder {
|
||||||
/// Upload result.
|
/// Upload result.
|
||||||
|
|
@ -200,11 +199,7 @@ impl Worker {
|
||||||
log::debug!(target: "worker", "Worker processing outgoing fetch for {}", rid);
|
log::debug!(target: "worker", "Worker processing outgoing fetch for {}", rid);
|
||||||
let result = self.fetch(rid, remote, stream, &namespaces, channels);
|
let result = self.fetch(rid, remote, stream, &namespaces, channels);
|
||||||
|
|
||||||
FetchResult::Initiator {
|
FetchResult::Initiator { rid, result }
|
||||||
rid,
|
|
||||||
namespaces,
|
|
||||||
result,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FetchRequest::Responder { remote } => {
|
FetchRequest::Responder { remote } => {
|
||||||
log::debug!(target: "worker", "Worker processing incoming fetch..");
|
log::debug!(target: "worker", "Worker processing incoming fetch..");
|
||||||
|
|
@ -233,7 +228,7 @@ impl Worker {
|
||||||
stream: StreamId,
|
stream: StreamId,
|
||||||
namespaces: &Namespaces,
|
namespaces: &Namespaces,
|
||||||
mut channels: Channels,
|
mut channels: Channels,
|
||||||
) -> Result<Vec<RefUpdate>, FetchError> {
|
) -> Result<(Vec<RefUpdate>, HashSet<NodeId>), FetchError> {
|
||||||
let staging = fetch::StagingPhaseInitial::new(&self.storage, rid, namespaces.clone())?;
|
let staging = fetch::StagingPhaseInitial::new(&self.storage, rid, namespaces.clone())?;
|
||||||
match self._fetch(
|
match self._fetch(
|
||||||
&staging.repo,
|
&staging.repo,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use radicle::crypto::{PublicKey, Unverified, Verified};
|
use radicle::crypto::{PublicKey, Unverified, Verified};
|
||||||
use radicle::git::url;
|
use radicle::git::url;
|
||||||
use radicle::prelude::{Doc, Id};
|
use radicle::prelude::{Doc, Id, NodeId};
|
||||||
use radicle::storage::git::Repository;
|
use radicle::storage::git::Repository;
|
||||||
use radicle::storage::refs::{SignedRefs, IDENTITY_BRANCH};
|
use radicle::storage::refs::{SignedRefs, IDENTITY_BRANCH};
|
||||||
use radicle::storage::{Namespaces, RefUpdate, Remote, RemoteId};
|
use radicle::storage::{Namespaces, RefUpdate, Remote, RemoteId};
|
||||||
|
|
@ -242,7 +242,7 @@ impl<'a> StagingPhaseFinal<'a> {
|
||||||
///
|
///
|
||||||
/// All references that were updated are returned as a
|
/// All references that were updated are returned as a
|
||||||
/// [`RefUpdate`].
|
/// [`RefUpdate`].
|
||||||
pub fn transfer(self) -> Result<Vec<RefUpdate>, error::Transfer> {
|
pub fn transfer(self) -> Result<(Vec<RefUpdate>, HashSet<NodeId>), error::Transfer> {
|
||||||
let verifications = self.verify();
|
let verifications = self.verify();
|
||||||
let production = match &self.repo {
|
let production = match &self.repo {
|
||||||
StagedRepository::Cloning(repo) => self.production.create(repo.id)?,
|
StagedRepository::Cloning(repo) => self.production.create(repo.id)?,
|
||||||
|
|
@ -253,7 +253,7 @@ impl<'a> StagingPhaseFinal<'a> {
|
||||||
let mut updates = Vec::new();
|
let mut updates = Vec::new();
|
||||||
|
|
||||||
let callbacks = ref_updates(&mut updates);
|
let callbacks = ref_updates(&mut updates);
|
||||||
{
|
let remotes = {
|
||||||
let specs = verifications
|
let specs = verifications
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|(remote, verified)| match verified {
|
.flat_map(|(remote, verified)| match verified {
|
||||||
|
|
@ -339,14 +339,16 @@ impl<'a> StagingPhaseFinal<'a> {
|
||||||
opts.prune(git::raw::FetchPrune::Off);
|
opts.prune(git::raw::FetchPrune::Off);
|
||||||
|
|
||||||
remote.fetch(&specs, Some(&mut opts), None)?;
|
remote.fetch(&specs, Some(&mut opts), None)?;
|
||||||
}
|
|
||||||
|
fetching
|
||||||
|
};
|
||||||
let head = production.set_head()?;
|
let head = production.set_head()?;
|
||||||
log::debug!(target: "worker", "Head for {} set to {head}", production.id);
|
log::debug!(target: "worker", "Head for {} set to {head}", production.id);
|
||||||
|
|
||||||
let head = production.set_identity_head()?;
|
let head = production.set_identity_head()?;
|
||||||
log::debug!(target: "worker", "'refs/rad/id' for {} set to {head}", production.id);
|
log::debug!(target: "worker", "'refs/rad/id' for {} set to {head}", production.id);
|
||||||
|
|
||||||
Ok(updates)
|
Ok((updates, remotes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remotes(&self) -> impl Iterator<Item = Remote> + '_ {
|
fn remotes(&self) -> impl Iterator<Item = Remote> + '_ {
|
||||||
|
|
@ -364,23 +366,21 @@ impl<'a> StagingPhaseFinal<'a> {
|
||||||
fn verify(&self) -> BTreeMap<RemoteId, VerifiedRemote> {
|
fn verify(&self) -> BTreeMap<RemoteId, VerifiedRemote> {
|
||||||
self.trusted
|
self.trusted
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter_map(|remote| self.repo.remote(remote).ok())
|
||||||
.map(|remote| {
|
.map(|remote| {
|
||||||
let verification =
|
let remote_id = remote.id;
|
||||||
match (self.repo.identity_doc_of(remote), self.repo.remote(remote)) {
|
let verification = match self.repo.identity_doc_of(&remote_id) {
|
||||||
(Ok(doc), Ok(remote)) => match self.repo.validate_remote(&remote) {
|
Ok(doc) => match self.repo.validate_remote(&remote) {
|
||||||
Ok(()) => VerifiedRemote::Success { _doc: doc, remote },
|
Ok(()) => VerifiedRemote::Success { _doc: doc, remote },
|
||||||
Err(e) => VerifiedRemote::Failed {
|
Err(e) => VerifiedRemote::Failed {
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
(Err(e), _) => VerifiedRemote::Failed {
|
Err(e) => VerifiedRemote::Failed {
|
||||||
reason: e.to_string(),
|
|
||||||
},
|
|
||||||
(_, Err(e)) => VerifiedRemote::Failed {
|
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
(*remote, verification)
|
(remote_id, verification)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ mod features;
|
||||||
pub mod routing;
|
pub mod routing;
|
||||||
pub mod tracking;
|
pub mod tracking;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::{BTreeSet, HashSet};
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
@ -195,7 +195,6 @@ impl Command {
|
||||||
#[serde(tag = "state", content = "id")]
|
#[serde(tag = "state", content = "id")]
|
||||||
pub enum Seed {
|
pub enum Seed {
|
||||||
Disconnected(NodeId),
|
Disconnected(NodeId),
|
||||||
Fetching(NodeId),
|
|
||||||
Connected(NodeId),
|
Connected(NodeId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,31 +206,24 @@ impl Seeds {
|
||||||
self.0.insert(seed);
|
self.0.insert(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connected(&mut self) -> impl Iterator<Item = &NodeId> {
|
pub fn connected(&self) -> impl Iterator<Item = &NodeId> {
|
||||||
self.0.iter().filter_map(|s| match s {
|
self.0.iter().filter_map(|s| match s {
|
||||||
Seed::Connected(node) => Some(node),
|
Seed::Connected(node) => Some(node),
|
||||||
Seed::Fetching(_) | Seed::Disconnected(_) => None,
|
Seed::Disconnected(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disconnected(&mut self) -> impl Iterator<Item = &NodeId> {
|
pub fn disconnected(&self) -> impl Iterator<Item = &NodeId> {
|
||||||
self.0.iter().filter_map(|s| match s {
|
self.0.iter().filter_map(|s| match s {
|
||||||
Seed::Disconnected(node) => Some(node),
|
Seed::Disconnected(node) => Some(node),
|
||||||
Seed::Fetching(_) | Seed::Connected(_) => None,
|
Seed::Connected(_) => None,
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fetching(&mut self) -> impl Iterator<Item = &NodeId> {
|
|
||||||
self.0.iter().filter_map(|s| match s {
|
|
||||||
Seed::Fetching(node) => Some(node),
|
|
||||||
Seed::Connected(_) | Seed::Disconnected(_) => None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_connections(&self) -> bool {
|
pub fn has_connections(&self) -> bool {
|
||||||
self.0.iter().any(|s| match s {
|
self.0.iter().any(|s| match s {
|
||||||
Seed::Connected(_) => true,
|
Seed::Connected(_) => true,
|
||||||
Seed::Disconnected(_) | Seed::Fetching(_) => false,
|
Seed::Disconnected(_) => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,18 +234,19 @@ impl Seeds {
|
||||||
pub fn is_disconnected(&self, node: &NodeId) -> bool {
|
pub fn is_disconnected(&self, node: &NodeId) -> bool {
|
||||||
self.0.contains(&Seed::Disconnected(*node))
|
self.0.contains(&Seed::Disconnected(*node))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fetching(&self, node: &NodeId) -> bool {
|
|
||||||
self.0.contains(&Seed::Fetching(*node))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(tag = "status", rename_all = "kebab-case")]
|
#[serde(tag = "status", rename_all = "kebab-case")]
|
||||||
pub enum FetchResult {
|
pub enum FetchResult {
|
||||||
Success { updated: Vec<RefUpdate> },
|
Success {
|
||||||
|
updated: Vec<RefUpdate>,
|
||||||
|
namespaces: HashSet<NodeId>,
|
||||||
|
},
|
||||||
// TODO: Create enum for reason.
|
// TODO: Create enum for reason.
|
||||||
Failed { reason: String },
|
Failed {
|
||||||
|
reason: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchResult {
|
impl FetchResult {
|
||||||
|
|
@ -261,18 +254,24 @@ impl FetchResult {
|
||||||
matches!(self, FetchResult::Success { .. })
|
matches!(self, FetchResult::Success { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn success(self) -> Option<Vec<RefUpdate>> {
|
pub fn success(self) -> Option<(Vec<RefUpdate>, HashSet<NodeId>)> {
|
||||||
match self {
|
match self {
|
||||||
Self::Success { updated } => Some(updated),
|
Self::Success {
|
||||||
|
updated,
|
||||||
|
namespaces,
|
||||||
|
} => Some((updated, namespaces)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ToString> From<Result<Vec<RefUpdate>, S>> for FetchResult {
|
impl<S: ToString> From<Result<(Vec<RefUpdate>, HashSet<NodeId>), S>> for FetchResult {
|
||||||
fn from(value: Result<Vec<RefUpdate>, S>) -> Self {
|
fn from(value: Result<(Vec<RefUpdate>, HashSet<NodeId>), S>) -> Self {
|
||||||
match value {
|
match value {
|
||||||
Ok(updated) => Self::Success { updated },
|
Ok((updated, namespaces)) => Self::Success {
|
||||||
|
updated,
|
||||||
|
namespaces,
|
||||||
|
},
|
||||||
Err(err) => Self::Failed {
|
Err(err) => Self::Failed {
|
||||||
reason: err.to_string(),
|
reason: err.to_string(),
|
||||||
},
|
},
|
||||||
|
|
@ -296,10 +295,14 @@ impl FetchResults {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over successful fetches.
|
/// Iterate over successful fetches.
|
||||||
pub fn success(&self) -> impl Iterator<Item = (&NodeId, &[RefUpdate])> {
|
pub fn success(&self) -> impl Iterator<Item = (&NodeId, &[RefUpdate], HashSet<NodeId>)> {
|
||||||
self.0.iter().filter_map(|(nid, r)| {
|
self.0.iter().filter_map(|(nid, r)| {
|
||||||
if let FetchResult::Success { updated } = r {
|
if let FetchResult::Success {
|
||||||
Some((nid, updated.as_slice()))
|
updated,
|
||||||
|
namespaces,
|
||||||
|
} = r
|
||||||
|
{
|
||||||
|
Some((nid, updated.as_slice(), namespaces.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue