node: Update sync status for private repos
Only public repos were being updated on node initialization.
This commit is contained in:
parent
9f36320d83
commit
f78e5a4281
|
|
@ -102,6 +102,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
head,
|
head,
|
||||||
doc,
|
doc,
|
||||||
refs,
|
refs,
|
||||||
|
..
|
||||||
} in repos
|
} in repos
|
||||||
{
|
{
|
||||||
if doc.visibility.is_public() && options.private && !options.public {
|
if doc.visibility.is_public() && options.private && !options.public {
|
||||||
|
|
|
||||||
|
|
@ -587,11 +587,9 @@ where
|
||||||
assert_ne!(time, LocalTime::default());
|
assert_ne!(time, LocalTime::default());
|
||||||
|
|
||||||
let nid = self.node_id();
|
let nid = self.node_id();
|
||||||
let inventory = self.storage.inventory()?;
|
|
||||||
|
|
||||||
self.started_at = Some(time);
|
self.started_at = Some(time);
|
||||||
self.clock = time;
|
self.clock = time;
|
||||||
self.inventory = gossip::inventory(self.timestamp(), inventory.clone());
|
|
||||||
|
|
||||||
// Populate refs database. This is only useful as part of the upgrade process for nodes
|
// Populate refs database. This is only useful as part of the upgrade process for nodes
|
||||||
// that have been online since before the refs database was created.
|
// that have been online since before the refs database was created.
|
||||||
|
|
@ -622,28 +620,25 @@ where
|
||||||
)
|
)
|
||||||
.expect("Service::initialize: error adding local node to address database");
|
.expect("Service::initialize: error adding local node to address database");
|
||||||
|
|
||||||
// Ensure that our inventory is recorded in our routing table, and we are seeding
|
|
||||||
// all of it. It can happen that inventory is not properly seeded if for eg. the
|
|
||||||
// user creates a new repository while the node is stopped.
|
|
||||||
self.db
|
|
||||||
.routing_mut()
|
|
||||||
.insert(inventory.iter(), nid, time.into())?;
|
|
||||||
|
|
||||||
let announced = self
|
let announced = self
|
||||||
.db
|
.db
|
||||||
.seeds()
|
.seeds()
|
||||||
.seeded_by(&nid)?
|
.seeded_by(&nid)?
|
||||||
.collect::<Result<HashMap<_, _>, _>>()?;
|
.collect::<Result<HashMap<_, _>, _>>()?;
|
||||||
for rid in inventory {
|
for repo in self.storage.repositories()? {
|
||||||
let repo = self.storage.repository(rid)?;
|
let rid = repo.rid;
|
||||||
|
|
||||||
// If we're not seeding this repo, just skip it.
|
// If we're not seeding this repo, just skip it.
|
||||||
if !self.policies.is_seeding(&rid)? {
|
if !self.policies.is_seeding(&rid)? {
|
||||||
warn!(target: "service", "Local repository {rid} is not seeded");
|
warn!(target: "service", "Local repository {rid} is not seeded");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Add public repositories to inventory.
|
||||||
|
if repo.doc.visibility.is_public() {
|
||||||
|
self.storage.insert(rid);
|
||||||
|
}
|
||||||
// If we have no owned refs for this repo, then there's nothing to announce.
|
// If we have no owned refs for this repo, then there's nothing to announce.
|
||||||
let Ok(updated_at) = SyncedAt::load(&repo, nid) else {
|
let Some(updated_at) = repo.synced_at else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -672,6 +667,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let inventory = self.storage.inventory()?;
|
||||||
|
// Ensure that our inventory is recorded in our routing table, and we are seeding
|
||||||
|
// all of it. It can happen that inventory is not properly seeded if for eg. the
|
||||||
|
// user creates a new repository while the node is stopped.
|
||||||
|
self.db
|
||||||
|
.routing_mut()
|
||||||
|
.insert(inventory.iter(), nid, time.into())?;
|
||||||
|
self.inventory = gossip::inventory(self.timestamp(), inventory);
|
||||||
|
}
|
||||||
|
|
||||||
// Setup subscription filter for seeded repos.
|
// Setup subscription filter for seeded repos.
|
||||||
self.filter = Filter::new(
|
self.filter = Filter::new(
|
||||||
self.policies
|
self.policies
|
||||||
|
|
|
||||||
|
|
@ -838,7 +838,6 @@ fn test_refs_announcement_no_subscribe() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_refs_announcement_offline() {
|
fn test_refs_announcement_offline() {
|
||||||
logger::init(log::Level::Debug);
|
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
let mut alice = {
|
let mut alice = {
|
||||||
let signer = MockSigner::default();
|
let signer = MockSigner::default();
|
||||||
|
|
@ -854,10 +853,10 @@ fn test_refs_announcement_offline() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let inv = alice.inventory();
|
let mut inv = alice.inventory();
|
||||||
let rid = inv.first().unwrap();
|
let rid = *inv.first().unwrap();
|
||||||
let mut bob = Peer::new("bob", [8, 8, 8, 8]);
|
let mut bob = Peer::new("bob", [8, 8, 8, 8]);
|
||||||
bob.seed(rid, policy::Scope::All).unwrap();
|
bob.seed(&rid, policy::Scope::All).unwrap();
|
||||||
|
|
||||||
// Make sure alice's service wasn't initialized before.
|
// Make sure alice's service wasn't initialized before.
|
||||||
assert_eq!(*alice.clock(), LocalTime::default());
|
assert_eq!(*alice.clock(), LocalTime::default());
|
||||||
|
|
@ -868,25 +867,23 @@ fn test_refs_announcement_offline() {
|
||||||
|
|
||||||
// Alice announces the refs of all projects since she hasn't announced refs for these projects
|
// Alice announces the refs of all projects since she hasn't announced refs for these projects
|
||||||
// yet.
|
// yet.
|
||||||
let mut messages = alice.messages(bob.id());
|
for msg in alice.messages(bob.id()) {
|
||||||
for i in &inv {
|
|
||||||
let msg = messages.next();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
msg,
|
msg,
|
||||||
Some(Message::Announcement(Announcement {
|
Message::Announcement(Announcement {
|
||||||
node,
|
node,
|
||||||
message: AnnouncementMessage::Refs(RefsAnnouncement {
|
message: AnnouncementMessage::Refs(RefsAnnouncement {
|
||||||
rid,
|
rid,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
..
|
..
|
||||||
}))
|
})
|
||||||
if node == alice.id && rid == *i
|
if node == alice.id && inv.remove(&rid)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an issue without telling the node.
|
// Create an issue without telling the node.
|
||||||
let repo = alice.storage().repository(*rid).unwrap();
|
let repo = alice.storage().repository(rid).unwrap();
|
||||||
let old_refs = RefsAt::new(&repo, alice.id).unwrap();
|
let old_refs = RefsAt::new(&repo, alice.id).unwrap();
|
||||||
let mut issues = radicle::issue::Cache::no_cache(&repo).unwrap();
|
let mut issues = radicle::issue::Cache::no_cache(&repo).unwrap();
|
||||||
issues
|
issues
|
||||||
|
|
@ -935,7 +932,7 @@ fn test_refs_announcement_offline() {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(anns.len(), 1);
|
assert_eq!(anns.len(), 1);
|
||||||
assert_eq!(anns.first().unwrap().rid, *rid);
|
assert_eq!(anns.first().unwrap().rid, rid);
|
||||||
assert_eq!(anns.first().unwrap().refs.first().unwrap().at, new_refs.at);
|
assert_eq!(anns.first().unwrap().refs.first().unwrap().at, new_refs.at);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefStr, R
|
||||||
use crate::identity::{Did, PayloadError};
|
use crate::identity::{Did, PayloadError};
|
||||||
use crate::identity::{Doc, DocAt, DocError};
|
use crate::identity::{Doc, DocAt, DocError};
|
||||||
use crate::identity::{Identity, RepoId};
|
use crate::identity::{Identity, RepoId};
|
||||||
|
use crate::node::SyncedAt;
|
||||||
use crate::storage::git::NAMESPACES_GLOB;
|
use crate::storage::git::NAMESPACES_GLOB;
|
||||||
use crate::storage::refs::Refs;
|
use crate::storage::refs::Refs;
|
||||||
|
|
||||||
|
|
@ -42,6 +43,8 @@ pub struct RepositoryInfo<V> {
|
||||||
/// Local signed refs, if any.
|
/// Local signed refs, if any.
|
||||||
/// Repositories with this set to `None` are ones that are seeded but not forked.
|
/// Repositories with this set to `None` are ones that are seeded but not forked.
|
||||||
pub refs: Option<refs::SignedRefsAt>,
|
pub refs: Option<refs::SignedRefsAt>,
|
||||||
|
/// Sync time of the repository.
|
||||||
|
pub synced_at: Option<SyncedAt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes one or more namespaces.
|
/// Describes one or more namespaces.
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,17 @@ use once_cell::sync::Lazy;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::crypto::Unverified;
|
use crate::crypto::Unverified;
|
||||||
use crate::git;
|
|
||||||
use crate::identity::doc::DocError;
|
use crate::identity::doc::DocError;
|
||||||
use crate::identity::{doc::DocAt, Doc, RepoId};
|
use crate::identity::{doc::DocAt, Doc, RepoId};
|
||||||
use crate::identity::{Identity, Project};
|
use crate::identity::{Identity, Project};
|
||||||
|
use crate::node::SyncedAt;
|
||||||
use crate::storage::refs;
|
use crate::storage::refs;
|
||||||
use crate::storage::refs::{Refs, SignedRefs, SignedRefsAt};
|
use crate::storage::refs::{Refs, SignedRefs, SignedRefsAt};
|
||||||
use crate::storage::{
|
use crate::storage::{
|
||||||
Inventory, ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo,
|
Inventory, ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo,
|
||||||
SetHead, SignRepository, WriteRepository, WriteStorage,
|
SetHead, SignRepository, WriteRepository, WriteStorage,
|
||||||
};
|
};
|
||||||
|
use crate::{git, node};
|
||||||
|
|
||||||
pub use crate::git::{
|
pub use crate::git::{
|
||||||
ext, raw, refname, refspec, Oid, PatternStr, Qualified, RefError, RefString, UserInfo,
|
ext, raw, refname, refspec, Oid, PatternStr, Qualified, RefError, RefString, UserInfo,
|
||||||
|
|
@ -152,10 +153,10 @@ impl ReadStorage for Storage {
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||||
|
|
||||||
// If the cache hasn't been populated yet, we don't do anything, since this repo
|
|
||||||
// will be loaded when the cache is populated.
|
|
||||||
if let Some(ref mut repos) = *repos {
|
if let Some(ref mut repos) = *repos {
|
||||||
repos.insert(rid);
|
repos.insert(rid);
|
||||||
|
} else {
|
||||||
|
*repos = Some(BTreeSet::from_iter([rid]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,12 +212,17 @@ impl ReadStorage for Storage {
|
||||||
};
|
};
|
||||||
// Nb. This will be `None` if they were not found.
|
// Nb. This will be `None` if they were not found.
|
||||||
let refs = refs::SignedRefsAt::load(self.info.key, &repo)?;
|
let refs = refs::SignedRefsAt::load(self.info.key, &repo)?;
|
||||||
|
let synced_at = refs
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| node::SyncedAt::new(r.at, &repo))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
repos.push(RepositoryInfo {
|
repos.push(RepositoryInfo {
|
||||||
rid,
|
rid,
|
||||||
head,
|
head,
|
||||||
doc,
|
doc,
|
||||||
refs,
|
refs,
|
||||||
|
synced_at,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(repos)
|
Ok(repos)
|
||||||
|
|
@ -298,11 +304,17 @@ impl Storage {
|
||||||
rids.try_fold(Vec::new(), |mut infos, rid| {
|
rids.try_fold(Vec::new(), |mut infos, rid| {
|
||||||
let repo = self.repository(*rid)?;
|
let repo = self.repository(*rid)?;
|
||||||
let (_, head) = repo.head()?;
|
let (_, head) = repo.head()?;
|
||||||
|
let refs = refs::SignedRefsAt::load(self.info.key, &repo)?;
|
||||||
|
let synced_at = refs
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| SyncedAt::new(r.at, &repo))
|
||||||
|
.transpose()?;
|
||||||
let info = RepositoryInfo {
|
let info = RepositoryInfo {
|
||||||
rid: *rid,
|
rid: *rid,
|
||||||
head,
|
head,
|
||||||
doc: repo.identity_doc()?.into(),
|
doc: repo.identity_doc()?.into(),
|
||||||
refs: refs::SignedRefsAt::load(self.info.key, &repo)?,
|
refs,
|
||||||
|
synced_at,
|
||||||
};
|
};
|
||||||
infos.push(info);
|
infos.push(info);
|
||||||
Ok(infos)
|
Ok(infos)
|
||||||
|
|
|
||||||
|
|
@ -411,8 +411,8 @@ impl SignedRefsAt {
|
||||||
where
|
where
|
||||||
S: ReadRepository,
|
S: ReadRepository,
|
||||||
{
|
{
|
||||||
let at = match repo.reference_oid(&remote, &SIGREFS_BRANCH) {
|
let at = match RefsAt::new(repo, remote) {
|
||||||
Ok(at) => at,
|
Ok(RefsAt { at, .. }) => at,
|
||||||
Err(e) if git::is_not_found_err(&e) => return Ok(None),
|
Err(e) if git::is_not_found_err(&e) => return Ok(None),
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,13 @@ pub fn user() -> git::UserInfo {
|
||||||
/// Create a new storage with a project.
|
/// Create a new storage with a project.
|
||||||
pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage, rad::InitError> {
|
pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage, rad::InitError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let storage = Storage::open(path.join("storage"), user())?;
|
let storage = Storage::open(
|
||||||
|
path.join("storage"),
|
||||||
|
git::UserInfo {
|
||||||
|
alias: Alias::new("Radcliff"),
|
||||||
|
key: *signer.public_key(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
transport::local::register(storage.clone());
|
transport::local::register(storage.clone());
|
||||||
transport::remote::mock::register(signer.public_key(), storage.path());
|
transport::remote::mock::register(signer.public_key(), storage.path());
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ impl ReadStorage for MockStorage {
|
||||||
head: r.head().unwrap().1,
|
head: r.head().unwrap().1,
|
||||||
doc: r.doc.clone().into(),
|
doc: r.doc.clone().into(),
|
||||||
refs: None,
|
refs: None,
|
||||||
|
synced_at: None,
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue