node: Rely on inventory announcement in `rad init`
This commit is contained in:
parent
b461950d59
commit
3e7761740f
|
|
@ -13,7 +13,7 @@ ok Project heartwood created
|
||||||
"description": "Radicle Heartwood Protocol & Stack",
|
"description": "Radicle Heartwood Protocol & Stack",
|
||||||
"defaultBranch": "master"
|
"defaultBranch": "master"
|
||||||
}
|
}
|
||||||
ok Announcing refs..
|
ok Syncing inventory..
|
||||||
|
|
||||||
Your project id is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji. You can show it any time by running:
|
Your project id is rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji. You can show it any time by running:
|
||||||
rad .
|
rad .
|
||||||
|
|
@ -242,8 +242,8 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
self::setup_signing(profile.id(), &repo, interactive)?;
|
self::setup_signing(profile.id(), &repo, interactive)?;
|
||||||
}
|
}
|
||||||
if options.sync {
|
if options.sync {
|
||||||
let spinner = term::spinner("Announcing refs..");
|
let spinner = term::spinner("Syncing inventory..");
|
||||||
if let Err(e) = node.announce_refs(id) {
|
if let Err(e) = node.sync_inventory() {
|
||||||
spinner.error(e);
|
spinner.error(e);
|
||||||
} else {
|
} else {
|
||||||
spinner.finish();
|
spinner.finish();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
use radicle::git;
|
use radicle::git;
|
||||||
use radicle::profile::Home;
|
use radicle::profile::Home;
|
||||||
|
|
@ -226,7 +227,7 @@ fn rad_clone_unknown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_init_announce_refs_and_clone() {
|
fn rad_init_sync_and_clone() {
|
||||||
logger::init(log::Level::Debug);
|
logger::init(log::Level::Debug);
|
||||||
|
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
|
|
@ -241,9 +242,14 @@ fn rad_init_announce_refs_and_clone() {
|
||||||
|
|
||||||
fixtures::repository(working.join("alice"));
|
fixtures::repository(working.join("alice"));
|
||||||
|
|
||||||
|
// Necessary for now, if we don't want the new inventry announcement to be considered stale
|
||||||
|
// for Bob.
|
||||||
|
// TODO: Find a way to advance internal clocks instead.
|
||||||
|
thread::sleep(time::Duration::from_secs(1));
|
||||||
|
|
||||||
// Alice initializes a repo after her node has started, and after bob has connected to it.
|
// Alice initializes a repo after her node has started, and after bob has connected to it.
|
||||||
test(
|
test(
|
||||||
"examples/rad-init-announce-refs.md",
|
"examples/rad-init-sync.md",
|
||||||
&working.join("alice"),
|
&working.join("alice"),
|
||||||
Some(&alice.home),
|
Some(&alice.home),
|
||||||
[],
|
[],
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ pub fn listen<H: Handle<Error = runtime::HandleError, FetchResult = FetchResult>
|
||||||
handle.shutdown().ok();
|
handle.shutdown().ok();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
log::error!(target: "control", "Command returned error: {e}");
|
||||||
|
|
||||||
CommandResult::error(e).to_writer(&mut stream).ok();
|
CommandResult::error(e).to_writer(&mut stream).ok();
|
||||||
|
|
||||||
stream.flush().ok();
|
stream.flush().ok();
|
||||||
|
|
|
||||||
|
|
@ -579,6 +579,15 @@ where
|
||||||
} else {
|
} else {
|
||||||
log::debug!(target: "service", "No fetch requests found for {rid}..");
|
log::debug!(target: "service", "No fetch requests found for {rid}..");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Announce the newly fetched project to the network, if necessary.
|
||||||
|
// Since this fetch could be either a full clone or simply a ref update, we need to
|
||||||
|
// either announce new inventory, or new refs.
|
||||||
|
//
|
||||||
|
// TODO: Announce new refs? Would require the ability to announce other peer refs.
|
||||||
|
if let Err(e) = self.sync_and_announce_inventory() {
|
||||||
|
error!(target: "service", "Failed to announce new inventory: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(session) = self.sessions.get_mut(&remote) {
|
if let Some(session) = self.sessions.get_mut(&remote) {
|
||||||
|
|
@ -760,6 +769,35 @@ where
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for id in message.inventory.as_slice() {
|
||||||
|
if let Some(sess) = self.sessions.get_mut(announcer) {
|
||||||
|
// If we are connected to the announcer of this inventory, update the peer's
|
||||||
|
// subscription filter to include all inventory items. This way, we'll
|
||||||
|
// relay messages relating to the peer's inventory.
|
||||||
|
if let Some(sub) = &mut sess.subscribe {
|
||||||
|
sub.filter.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're tracking and connected to the announcer, and we don't have
|
||||||
|
// the inventory, fetch it from the announcer.
|
||||||
|
if self.tracking.is_repo_tracked(id).expect(
|
||||||
|
"Service::handle_announcement: error accessing tracking configuration",
|
||||||
|
) {
|
||||||
|
// Only if we do not have the repository locally do we fetch here.
|
||||||
|
// If we do have it, only fetch after receiving a ref announcement.
|
||||||
|
if let Ok(true) = self.storage.contains(id) {
|
||||||
|
// Do nothing.
|
||||||
|
} else {
|
||||||
|
// We may hit this branch due to an error returned by storage.
|
||||||
|
// We attempt to fetch in case of error because it's likely
|
||||||
|
// the repository was corrupted, and fetching will fix it.
|
||||||
|
self.fetch(*id, announcer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(relay);
|
return Ok(relay);
|
||||||
}
|
}
|
||||||
// Process a peer inventory update announcement by (maybe) fetching.
|
// Process a peer inventory update announcement by (maybe) fetching.
|
||||||
|
|
@ -1068,11 +1106,6 @@ where
|
||||||
let peers = self.sessions.negotiated().map(|(_, p)| p);
|
let peers = self.sessions.negotiated().map(|(_, p)| p);
|
||||||
let timestamp = self.clock.as_secs();
|
let timestamp = self.clock.as_secs();
|
||||||
|
|
||||||
// Make sure our local routing table is up to date with new repositories.
|
|
||||||
if let Err(e) = self.routing.insert(id, self.node_id(), timestamp) {
|
|
||||||
log::error!(target: "service", "Failed to update routing table with repository {id}: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if remote.refs.len() > Refs::max() {
|
if remote.refs.len() > Refs::max() {
|
||||||
error!(
|
error!(
|
||||||
target: "service",
|
target: "service",
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ where
|
||||||
pub fn inventory_announcement(&self) -> Message {
|
pub fn inventory_announcement(&self) -> Message {
|
||||||
Message::inventory(
|
Message::inventory(
|
||||||
InventoryAnnouncement {
|
InventoryAnnouncement {
|
||||||
inventory: arbitrary::gen(3),
|
inventory: arbitrary::vec(3).try_into().unwrap(),
|
||||||
timestamp: self.timestamp(),
|
timestamp: self.timestamp(),
|
||||||
},
|
},
|
||||||
self.signer(),
|
self.signer(),
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ impl fmt::Display for Scheduled {
|
||||||
Input::Fetched(fetch, _) => {
|
Input::Fetched(fetch, _) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{} <~ {} ({}): Fetched",
|
"{} <<~ {} ({}): Fetched (initiated={})",
|
||||||
self.node, fetch.remote, fetch.rid
|
self.node, fetch.remote, fetch.rid, fetch.initiated
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -410,10 +410,10 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
}
|
}
|
||||||
Input::Fetched(f, result) => {
|
Input::Fetched(f, result) => {
|
||||||
let result = Rc::try_unwrap(result).unwrap();
|
let result = Rc::try_unwrap(result).unwrap();
|
||||||
let mut repo = p.storage().repository(f.rid).unwrap();
|
if f.initiated {
|
||||||
|
let mut repo = p.storage().repository(f.rid).unwrap();
|
||||||
fetch(&mut repo, &f.remote, f.namespaces.clone()).unwrap();
|
fetch(&mut repo, &f.remote, f.namespaces.clone()).unwrap();
|
||||||
|
}
|
||||||
p.fetched(f, result);
|
p.fetched(f, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -608,17 +608,19 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Io::Fetch(fetch) => {
|
Io::Fetch(fetch) => {
|
||||||
|
let remote = fetch.remote;
|
||||||
|
|
||||||
if fetch.initiated {
|
if fetch.initiated {
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "sim",
|
target: "sim",
|
||||||
"{:05} {} ~> {} ({})",
|
"{:05} {} ~> {} ({}): Fetch outgoing",
|
||||||
self.elapsed().as_millis(), node, fetch.remote, fetch.rid
|
self.elapsed().as_millis(), node, remote, fetch.rid
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log::info!(
|
log::info!(
|
||||||
target: "sim",
|
target: "sim",
|
||||||
"{:05} {} <~ {} ({})",
|
"{:05} {} <~ {} ({}): Fetch incoming",
|
||||||
self.elapsed().as_millis(), node, fetch.remote, fetch.rid
|
self.elapsed().as_millis(), node, remote, fetch.rid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,7 +629,7 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
self.time + LocalDuration::from_secs(3),
|
self.time + LocalDuration::from_secs(3),
|
||||||
Scheduled {
|
Scheduled {
|
||||||
node,
|
node,
|
||||||
remote: fetch.remote,
|
remote,
|
||||||
input: Input::Fetched(
|
input: Input::Fetched(
|
||||||
fetch,
|
fetch,
|
||||||
Rc::new(Err(FetchError::Io(io::ErrorKind::Other.into()))),
|
Rc::new(Err(FetchError::Io(io::ErrorKind::Other.into()))),
|
||||||
|
|
@ -639,7 +641,7 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||||
self.time + LocalDuration::from_secs(3),
|
self.time + LocalDuration::from_secs(3),
|
||||||
Scheduled {
|
Scheduled {
|
||||||
node,
|
node,
|
||||||
remote: fetch.remote,
|
remote,
|
||||||
input: Input::Fetched(fetch, Rc::new(Ok(vec![]))),
|
input: Input::Fetched(fetch, Rc::new(Ok(vec![]))),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -694,7 +696,6 @@ fn fetch<W: WriteRepository>(
|
||||||
});
|
});
|
||||||
opts.remote_callbacks(callbacks);
|
opts.remote_callbacks(callbacks);
|
||||||
|
|
||||||
// Fetch from the remote into the staging copy.
|
|
||||||
let mut remote = repo.raw().remote_anonymous(
|
let mut remote = repo.raw().remote_anonymous(
|
||||||
radicle::storage::git::transport::remote::Url {
|
radicle::storage::git::transport::remote::Url {
|
||||||
node: *node,
|
node: *node,
|
||||||
|
|
|
||||||
|
|
@ -548,8 +548,8 @@ fn test_refs_announcement_relay() {
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.connect_to(&eve);
|
alice.connect_to(&eve);
|
||||||
alice.receive(eve.id(), Message::Subscribe(Subscribe::all()));
|
alice.receive(eve.id(), Message::Subscribe(Subscribe::all()));
|
||||||
|
|
||||||
alice.receive(bob.id(), bob.refs_announcement(bob_inv[0]));
|
alice.receive(bob.id(), bob.refs_announcement(bob_inv[0]));
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
alice.messages(eve.id()).next(),
|
alice.messages(eve.id()).next(),
|
||||||
Some(Message::Announcement(_)),
|
Some(Message::Announcement(_)),
|
||||||
|
|
@ -896,11 +896,13 @@ fn test_push_and_pull() {
|
||||||
assert!(eve.get(proj_id).unwrap().is_none());
|
assert!(eve.get(proj_id).unwrap().is_none());
|
||||||
assert!(bob.get(proj_id).unwrap().is_none());
|
assert!(bob.get(proj_id).unwrap().is_none());
|
||||||
|
|
||||||
// Alice announces her refs.
|
let (send, _) = chan::bounded(1);
|
||||||
|
// Alice announces her inventory.
|
||||||
// We now expect Eve to fetch Alice's project from Alice.
|
// We now expect Eve to fetch Alice's project from Alice.
|
||||||
// Then we expect Bob to fetch Alice's project from Eve.
|
// Then we expect Bob to fetch Alice's project from Eve.
|
||||||
alice.elapse(LocalDuration::from_secs(1)); // Make sure our announcement is fresh.
|
alice.elapse(LocalDuration::from_secs(1)); // Make sure our announcement is fresh.
|
||||||
alice.command(service::Command::AnnounceRefs(proj_id));
|
alice.command(service::Command::SyncInventory(send));
|
||||||
|
|
||||||
sim.run_while([&mut alice, &mut bob, &mut eve], |s| !s.is_settled());
|
sim.run_while([&mut alice, &mut bob, &mut eve], |s| !s.is_settled());
|
||||||
|
|
||||||
// TODO: Refs should be compared between the two peers.
|
// TODO: Refs should be compared between the two peers.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue