radicle: Rename many more methods using new naming
This commit is contained in:
parent
1b026ae0ee
commit
6ca7da2768
|
|
@ -1,12 +1,13 @@
|
||||||
To configure our node's tracking policy, we can use the `rad track` command.
|
To configure our node's seeding and follow policy, we can use the `rad seed`
|
||||||
For example, let's track a remote node we know about, and alias it to "eve":
|
and `rad follow` commands.
|
||||||
|
For example, let's follow a remote node we know about, and alias it to "eve":
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
|
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias eve
|
||||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
|
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (eve)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now let's track one of Eve's repositories:
|
Now let's seed one of Eve's repositories:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --no-fetch
|
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --no-fetch
|
||||||
|
|
@ -22,7 +22,7 @@ Usage
|
||||||
local operator or a delegate of the repository.
|
local operator or a delegate of the repository.
|
||||||
|
|
||||||
Note that remotes will still be fetched as long as they are
|
Note that remotes will still be fetched as long as they are
|
||||||
tracked and/or the tracking scope is "all".
|
followed and/or the follow scope is "all".
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ pub fn follow(
|
||||||
let followed = match node.follow(nid, alias.clone()) {
|
let followed = match node.follow(nid, alias.clone()) {
|
||||||
Ok(updated) => updated,
|
Ok(updated) => updated,
|
||||||
Err(e) if e.is_connection_err() => {
|
Err(e) if e.is_connection_err() => {
|
||||||
let mut config = profile.tracking_mut()?;
|
let mut config = profile.policies_mut()?;
|
||||||
config.track_node(&nid, alias.as_deref())?
|
config.follow(&nid, alias.as_deref())?
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ pub struct Options {
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
pub set_upstream: bool,
|
pub set_upstream: bool,
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
pub track: bool,
|
pub seed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args for Options {
|
impl Args for Options {
|
||||||
|
|
@ -77,7 +77,7 @@ impl Args for Options {
|
||||||
let mut set_upstream = false;
|
let mut set_upstream = false;
|
||||||
let mut setup_signing = false;
|
let mut setup_signing = false;
|
||||||
let mut scope = Scope::All;
|
let mut scope = Scope::All;
|
||||||
let mut track = true;
|
let mut seed = true;
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
let mut visibility = None;
|
let mut visibility = None;
|
||||||
|
|
||||||
|
|
@ -129,8 +129,8 @@ impl Args for Options {
|
||||||
Long("no-confirm") => {
|
Long("no-confirm") => {
|
||||||
interactive = Interactive::No;
|
interactive = Interactive::No;
|
||||||
}
|
}
|
||||||
Long("no-track") => {
|
Long("no-seed") => {
|
||||||
track = false;
|
seed = false;
|
||||||
}
|
}
|
||||||
Long("private") => {
|
Long("private") => {
|
||||||
visibility = Some(Visibility::private([]));
|
visibility = Some(Visibility::private([]));
|
||||||
|
|
@ -161,7 +161,7 @@ impl Args for Options {
|
||||||
interactive,
|
interactive,
|
||||||
set_upstream,
|
set_upstream,
|
||||||
setup_signing,
|
setup_signing,
|
||||||
track,
|
seed,
|
||||||
visibility,
|
visibility,
|
||||||
verbose,
|
verbose,
|
||||||
},
|
},
|
||||||
|
|
@ -279,10 +279,10 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
term::blob(json::to_string_pretty(&proj)?);
|
term::blob(json::to_string_pretty(&proj)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's important to track our own repositories to make sure that our node signals
|
// It's important to seed our own repositories to make sure that our node signals
|
||||||
// interest for them. This ensures that messages relating to them are relayed to us.
|
// interest for them. This ensures that messages relating to them are relayed to us.
|
||||||
if options.track {
|
if options.seed {
|
||||||
cli::project::track(id, options.scope, &mut node, profile)?;
|
cli::project::seed(id, options.scope, &mut node, profile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
|
if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
|
||||||
|
|
|
||||||
|
|
@ -173,8 +173,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Target::Policy => {
|
Target::Policy => {
|
||||||
let tracking = profile.tracking()?;
|
let tracking = profile.policies()?;
|
||||||
if let Some(repo) = tracking.repo_policy(&rid)? {
|
if let Some(repo) = tracking.seed_policy(&rid)? {
|
||||||
let tracking = match repo.policy {
|
let tracking = match repo.policy {
|
||||||
Policy::Allow => term::format::positive("tracked"),
|
Policy::Allow => term::format::positive("tracked"),
|
||||||
Policy::Block => term::format::negative("blocked"),
|
Policy::Block => term::format::negative("blocked"),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Usage
|
||||||
rad ls [<option>...]
|
rad ls [<option>...]
|
||||||
|
|
||||||
By default, this command shows you all repositories that you have forked or initialized.
|
By default, this command shows you all repositories that you have forked or initialized.
|
||||||
If you wish to see all tracked repositories, use the `--all` option.
|
If you wish to see all seeded repositories, use the `--all` option.
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::terminal as term;
|
||||||
use term::Element;
|
use term::Element;
|
||||||
|
|
||||||
pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
|
pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
|
||||||
let store = profile.tracking()?;
|
let store = profile.policies()?;
|
||||||
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
||||||
t.push([
|
t.push([
|
||||||
term::format::default(String::from("RID")),
|
term::format::default(String::from("RID")),
|
||||||
|
|
@ -16,7 +16,7 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
|
||||||
]);
|
]);
|
||||||
t.divider();
|
t.divider();
|
||||||
|
|
||||||
for policy::Repo { id, scope, policy } in store.repo_policies()? {
|
for policy::Repo { id, scope, policy } in store.seed_policies()? {
|
||||||
let id = id.to_string();
|
let id = id.to_string();
|
||||||
let scope = scope.to_string();
|
let scope = scope.to_string();
|
||||||
let policy = policy.to_string();
|
let policy = policy.to_string();
|
||||||
|
|
@ -33,7 +33,7 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn following(profile: &Profile) -> anyhow::Result<()> {
|
pub fn following(profile: &Profile) -> anyhow::Result<()> {
|
||||||
let store = profile.tracking()?;
|
let store = profile.policies()?;
|
||||||
let aliases = profile.aliases();
|
let aliases = profile.aliases();
|
||||||
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
let mut t = term::Table::new(term::table::TableOptions::bordered());
|
||||||
t.push([
|
t.push([
|
||||||
|
|
@ -43,7 +43,7 @@ pub fn following(profile: &Profile) -> anyhow::Result<()> {
|
||||||
]);
|
]);
|
||||||
t.divider();
|
t.divider();
|
||||||
|
|
||||||
for policy::Node { id, alias, policy } in store.node_policies()? {
|
for policy::Node { id, alias, policy } in store.follow_policies()? {
|
||||||
t.push([
|
t.push([
|
||||||
term::format::highlight(Did::from(id).to_string()),
|
term::format::highlight(Did::from(id).to_string()),
|
||||||
match alias {
|
match alias {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ fn get_branch(git_ref: git::Qualified) -> git::RefString {
|
||||||
std::iter::once(head).chain(tail).collect()
|
std::iter::once(head).chain(tail).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the merge target for this patch. This can be any tracked remote's "default" branch,
|
/// Determine the merge target for this patch. This can be any followed remote's "default" branch,
|
||||||
/// as well as your own (eg. `rad/master`).
|
/// as well as your own (eg. `rad/master`).
|
||||||
pub fn get_merge_target(
|
pub fn get_merge_target(
|
||||||
storage: &Repository,
|
storage: &Repository,
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ pub fn update(
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
let updated = project::track(rid, scope, node, profile)?;
|
let updated = project::seed(rid, scope, node, profile)?;
|
||||||
let outcome = if updated { "updated" } else { "exists" };
|
let outcome = if updated { "updated" } else { "exists" };
|
||||||
|
|
||||||
term::success!(
|
term::success!(
|
||||||
|
|
@ -143,7 +143,7 @@ pub fn update(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(rid: Id, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
|
pub fn delete(rid: Id, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
|
||||||
if project::untrack(rid, node, profile)? {
|
if project::unseed(rid, node, profile)? {
|
||||||
term::success!("Seeding policy for {} removed", term::format::tertiary(rid));
|
term::success!("Seeding policy for {} removed", term::format::tertiary(rid));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
Operation::Synchronize(SyncMode::Repo { mode, direction }) => {
|
Operation::Synchronize(SyncMode::Repo { mode, direction }) => {
|
||||||
if [SyncDirection::Fetch, SyncDirection::Both].contains(&direction) {
|
if [SyncDirection::Fetch, SyncDirection::Both].contains(&direction) {
|
||||||
if !profile.tracking()?.is_repo_tracked(&rid)? {
|
if !profile.policies()?.is_repo_seeded(&rid)? {
|
||||||
anyhow::bail!("repository {rid} is not tracked");
|
anyhow::bail!("repository {rid} is not seeded");
|
||||||
}
|
}
|
||||||
let results = fetch(rid, mode.clone(), options.timeout, &mut node)?;
|
let results = fetch(rid, mode.clone(), options.timeout, &mut node)?;
|
||||||
let success = results.success().count();
|
let success = results.success().count();
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let unfollowed = match node.unfollow(nid) {
|
let unfollowed = match node.unfollow(nid) {
|
||||||
Ok(updated) => updated,
|
Ok(updated) => updated,
|
||||||
Err(e) if e.is_connection_err() => {
|
Err(e) if e.is_connection_err() => {
|
||||||
let mut config = profile.tracking_mut()?;
|
let mut config = profile.policies_mut()?;
|
||||||
config.untrack_node(&nid)?
|
config.unfollow(&nid)?
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@ impl<'a> SetupRemote<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track a repository by first trying to track through the node, and if the node isn't running,
|
/// Seed a repository by first trying to seed through the node, and if the node isn't running,
|
||||||
/// by updating the tracking database directly.
|
/// by updating the policy database directly.
|
||||||
pub fn track(
|
pub fn seed(
|
||||||
rid: Id,
|
rid: Id,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
|
|
@ -63,21 +63,21 @@ pub fn track(
|
||||||
match node.seed(rid, scope) {
|
match node.seed(rid, scope) {
|
||||||
Ok(updated) => Ok(updated),
|
Ok(updated) => Ok(updated),
|
||||||
Err(e) if e.is_connection_err() => {
|
Err(e) if e.is_connection_err() => {
|
||||||
let mut config = profile.tracking_mut()?;
|
let mut config = profile.policies_mut()?;
|
||||||
config.track_repo(&rid, scope).map_err(|e| e.into())
|
config.seed(&rid, scope).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Untrack a repository by first trying to untrack through the node, and if the node isn't running,
|
/// Unseed a repository by first trying to unseed through the node, and if the node isn't running,
|
||||||
/// by updating the tracking database directly.
|
/// by updating the policy database directly.
|
||||||
pub fn untrack(rid: Id, node: &mut Node, profile: &Profile) -> Result<bool, anyhow::Error> {
|
pub fn unseed(rid: Id, node: &mut Node, profile: &Profile) -> Result<bool, anyhow::Error> {
|
||||||
match node.unseed(rid) {
|
match node.unseed(rid) {
|
||||||
Ok(updated) => Ok(updated),
|
Ok(updated) => Ok(updated),
|
||||||
Err(e) if e.is_connection_err() => {
|
Err(e) if e.is_connection_err() => {
|
||||||
let mut config = profile.tracking_mut()?;
|
let mut config = profile.policies_mut()?;
|
||||||
config.untrack_repo(&rid).map_err(|e| e.into())
|
config.unseed(&rid).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -741,14 +741,14 @@ fn rad_clean() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_track() {
|
fn rad_seed_and_follow() {
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
let alice = environment.node(Config::test(Alias::new("alice")));
|
let alice = environment.node(Config::test(Alias::new("alice")));
|
||||||
let working = tempfile::tempdir().unwrap();
|
let working = tempfile::tempdir().unwrap();
|
||||||
let alice = alice.spawn();
|
let alice = alice.spawn();
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"examples/rad-track.md",
|
"examples/rad-seed-and-follow.md",
|
||||||
working.path(),
|
working.path(),
|
||||||
Some(&alice.home),
|
Some(&alice.home),
|
||||||
[],
|
[],
|
||||||
|
|
@ -897,7 +897,7 @@ fn rad_sync_without_node() {
|
||||||
let mut eve = environment.node(Config::test(Alias::new("eve")));
|
let mut eve = environment.node(Config::test(Alias::new("eve")));
|
||||||
|
|
||||||
let rid = Id::from_urn("rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5").unwrap();
|
let rid = Id::from_urn("rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5").unwrap();
|
||||||
eve.tracking.track_repo(&rid, Scope::All).unwrap();
|
eve.policies.seed(&rid, Scope::All).unwrap();
|
||||||
|
|
||||||
formula(&environment.tmp(), "examples/rad-sync-without-node.md")
|
formula(&environment.tmp(), "examples/rad-sync-without-node.md")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ use radicle::prelude::Doc;
|
||||||
use radicle::storage::git::Repository;
|
use radicle::storage::git::Repository;
|
||||||
use radicle::storage::ReadRepository;
|
use radicle::storage::ReadRepository;
|
||||||
|
|
||||||
use crate::tracking::{BlockList, Tracked};
|
use crate::policy::{Allowed, BlockList};
|
||||||
use crate::transport::{ConnectionStream, Transport};
|
use crate::transport::{ConnectionStream, Transport};
|
||||||
|
|
||||||
/// The handle used for pulling or cloning changes from a remote peer.
|
/// The handle used for pulling or cloning changes from a remote peer.
|
||||||
pub struct Handle<S> {
|
pub struct Handle<S> {
|
||||||
pub(crate) local: PublicKey,
|
pub(crate) local: PublicKey,
|
||||||
pub(crate) repo: Repository,
|
pub(crate) repo: Repository,
|
||||||
pub(crate) tracked: Tracked,
|
pub(crate) allowed: Allowed,
|
||||||
pub(crate) transport: Transport<S>,
|
pub(crate) transport: Transport<S>,
|
||||||
/// The set of keys we will ignore when fetching from a
|
/// The set of keys we will ignore when fetching from a
|
||||||
/// remote. This set can be constructed using the tracking
|
/// remote. This set can be constructed using the tracking
|
||||||
|
|
@ -34,7 +34,7 @@ impl<S> Handle<S> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
local: PublicKey,
|
local: PublicKey,
|
||||||
repo: Repository,
|
repo: Repository,
|
||||||
tracked: Tracked,
|
follow: Allowed,
|
||||||
blocked: BlockList,
|
blocked: BlockList,
|
||||||
connection: S,
|
connection: S,
|
||||||
) -> Result<Self, error::Init>
|
) -> Result<Self, error::Init>
|
||||||
|
|
@ -47,7 +47,7 @@ impl<S> Handle<S> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
local,
|
local,
|
||||||
repo,
|
repo,
|
||||||
tracked,
|
allowed: follow,
|
||||||
transport,
|
transport,
|
||||||
blocked,
|
blocked,
|
||||||
interrupt: Arc::new(AtomicBool::new(false)),
|
interrupt: Arc::new(AtomicBool::new(false)),
|
||||||
|
|
@ -78,8 +78,8 @@ impl<S> Handle<S> {
|
||||||
Ok(self.repo.identity_doc_at(head)?.doc)
|
Ok(self.repo.identity_doc_at(head)?.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tracked(&self) -> Tracked {
|
pub fn allowed(&self) -> Allowed {
|
||||||
self.tracked.clone()
|
self.allowed.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,13 +101,13 @@ pub mod error {
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Tracking {
|
pub enum Tracking {
|
||||||
#[error("failed to find tracking policy for {rid}")]
|
#[error("failed to find policy for {rid}")]
|
||||||
FailedPolicy {
|
FailedPolicy {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
#[source]
|
#[source]
|
||||||
err: policy::store::Error,
|
err: policy::store::Error,
|
||||||
},
|
},
|
||||||
#[error("cannot fetch {rid} as it is not tracked")]
|
#[error("cannot fetch {rid} as it is not seeded")]
|
||||||
BlockedPolicy { rid: Id },
|
BlockedPolicy { rid: Id },
|
||||||
#[error("failed to get tracking nodes for {rid}")]
|
#[error("failed to get tracking nodes for {rid}")]
|
||||||
FailedNodes {
|
FailedNodes {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod git;
|
pub mod git;
|
||||||
pub mod handle;
|
pub mod handle;
|
||||||
pub mod tracking;
|
pub mod policy;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
pub(crate) mod sigrefs;
|
pub(crate) mod sigrefs;
|
||||||
|
|
@ -10,8 +10,8 @@ mod stage;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
pub use handle::Handle;
|
pub use handle::Handle;
|
||||||
|
pub use policy::{Allowed, BlockList, Scope};
|
||||||
pub use state::{FetchLimit, FetchResult};
|
pub use state::{FetchLimit, FetchResult};
|
||||||
pub use tracking::{BlockList, Scope, Tracked};
|
|
||||||
pub use transport::Transport;
|
pub use transport::Transport;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,32 @@ use radicle::prelude::Id;
|
||||||
pub use radicle::node::policy::{Policy, Scope};
|
pub use radicle::node::policy::{Policy, Scope};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Tracked {
|
pub enum Allowed {
|
||||||
All,
|
All,
|
||||||
Followed { remotes: HashSet<PublicKey> },
|
Followed { remotes: HashSet<PublicKey> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tracked {
|
impl Allowed {
|
||||||
pub fn from_config(rid: Id, config: &Config<Read>) -> Result<Self, error::Tracking> {
|
pub fn from_config(rid: Id, config: &Config<Read>) -> Result<Self, error::Policy> {
|
||||||
let entry = config
|
let entry = config
|
||||||
.repo_policy(&rid)
|
.repo_policy(&rid)
|
||||||
.map_err(|err| error::Tracking::FailedPolicy { rid, err })?;
|
.map_err(|err| error::Policy::FailedPolicy { rid, err })?;
|
||||||
match entry.policy {
|
match entry.policy {
|
||||||
Policy::Block => {
|
Policy::Block => {
|
||||||
log::error!(target: "fetch", "Attempted to fetch untracked repo {rid}");
|
log::error!(target: "fetch", "Attempted to fetch non-seeded repo {rid}");
|
||||||
Err(error::Tracking::BlockedPolicy { rid })
|
Err(error::Policy::BlockedPolicy { rid })
|
||||||
}
|
}
|
||||||
Policy::Allow => match entry.scope {
|
Policy::Allow => match entry.scope {
|
||||||
Scope::All => Ok(Self::All),
|
Scope::All => Ok(Self::All),
|
||||||
Scope::Followed => {
|
Scope::Followed => {
|
||||||
let nodes = config
|
let nodes = config
|
||||||
.node_policies()
|
.follow_policies()
|
||||||
.map_err(|err| error::Tracking::FailedNodes { rid, err })?;
|
.map_err(|err| error::Policy::FailedNodes { rid, err })?;
|
||||||
let followed: HashSet<_> = nodes
|
let followed: HashSet<_> = nodes
|
||||||
.filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
|
.filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Tracked::Followed { remotes: followed })
|
Ok(Allowed::Followed { remotes: followed })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ impl BlockList {
|
||||||
|
|
||||||
pub fn from_config(config: &Config<Read>) -> Result<BlockList, error::Blocked> {
|
pub fn from_config(config: &Config<Read>) -> Result<BlockList, error::Blocked> {
|
||||||
Ok(config
|
Ok(config
|
||||||
.node_policies()?
|
.follow_policies()?
|
||||||
.filter_map(|entry| (entry.policy == Policy::Block).then_some(entry.id))
|
.filter_map(|entry| (entry.policy == Policy::Block).then_some(entry.id))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
@ -80,16 +80,16 @@ pub mod error {
|
||||||
pub struct Blocked(#[from] policy::config::Error);
|
pub struct Blocked(#[from] policy::config::Error);
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Tracking {
|
pub enum Policy {
|
||||||
#[error("failed to find tracking policy for {rid}")]
|
#[error("failed to find policy for {rid}")]
|
||||||
FailedPolicy {
|
FailedPolicy {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
#[source]
|
#[source]
|
||||||
err: policy::store::Error,
|
err: policy::store::Error,
|
||||||
},
|
},
|
||||||
#[error("cannot fetch {rid} as it is not tracked")]
|
#[error("cannot fetch {rid} as it is not seeded")]
|
||||||
BlockedPolicy { rid: Id },
|
BlockedPolicy { rid: Id },
|
||||||
#[error("failed to get tracking nodes for {rid}")]
|
#[error("failed to get followed nodes for {rid}")]
|
||||||
FailedNodes {
|
FailedNodes {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
#[source]
|
#[source]
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
//! as an anchor for the rest of the fetch, i.e. provides initial
|
//! as an anchor for the rest of the fetch, i.e. provides initial
|
||||||
//! delegate data for the repository.
|
//! delegate data for the repository.
|
||||||
//! 2. [`SpecialRefs`]: fetches the special references, `rad/id` and
|
//! 2. [`SpecialRefs`]: fetches the special references, `rad/id` and
|
||||||
//! `rad/sigrefs`, for each configured namespace, i.e. tracked
|
//! `rad/sigrefs`, for each configured namespace, i.e. followed
|
||||||
//! and delegate peers if the scope is "followed" and all peers is the
|
//! and delegate peers if the scope is "followed" and all peers is the
|
||||||
//! scope is all.
|
//! scope is all.
|
||||||
//! 3. [`DataRefs`]: fetches the `Oid`s for each reference listed in
|
//! 3. [`DataRefs`]: fetches the `Oid`s for each reference listed in
|
||||||
|
|
@ -40,12 +40,12 @@ use radicle::storage::refs::{RefsAt, Special};
|
||||||
use radicle::storage::ReadRepository;
|
use radicle::storage::ReadRepository;
|
||||||
|
|
||||||
use crate::git::refs::{Policy, Update, Updates};
|
use crate::git::refs::{Policy, Update, Updates};
|
||||||
|
use crate::policy::BlockList;
|
||||||
use crate::refs::{ReceivedRef, ReceivedRefname};
|
use crate::refs::{ReceivedRef, ReceivedRefname};
|
||||||
use crate::sigrefs;
|
use crate::sigrefs;
|
||||||
use crate::state::FetchState;
|
use crate::state::FetchState;
|
||||||
use crate::tracking::BlockList;
|
|
||||||
use crate::transport::WantsHaves;
|
use crate::transport::WantsHaves;
|
||||||
use crate::{refs, tracking};
|
use crate::{policy, refs};
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
use radicle::crypto::PublicKey;
|
use radicle::crypto::PublicKey;
|
||||||
|
|
@ -214,12 +214,11 @@ impl ProtocolStage for CanonicalId {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`ProtocolStage`] for fetching special refs from the set of
|
/// The [`ProtocolStage`] for fetching special refs from the set of
|
||||||
/// remotes in `tracked` and `delegates`.
|
/// remotes in `followed` and `delegates`.
|
||||||
///
|
///
|
||||||
/// This step asks for all tracked and delegate remote's `rad/id` and
|
/// This step asks for all followed and delegate remote's `rad/id` and
|
||||||
/// `rad/sigrefs`, iff the scope is
|
/// `rad/sigrefs`, iff the scope is [`policy::Scope::Followed`].
|
||||||
/// [`tracking::Scope::Trusted`]. Otherwise, it asks for all
|
/// Otherwise, it asks for all namespaces.
|
||||||
/// namespaces.
|
|
||||||
///
|
///
|
||||||
/// It ensures that all delegate refs were fetched.
|
/// It ensures that all delegate refs were fetched.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -229,7 +228,7 @@ pub struct SpecialRefs {
|
||||||
/// The node that is being fetched from.
|
/// The node that is being fetched from.
|
||||||
pub remote: PublicKey,
|
pub remote: PublicKey,
|
||||||
/// The set of nodes to be fetched.
|
/// The set of nodes to be fetched.
|
||||||
pub tracked: tracking::Tracked,
|
pub followed: policy::Allowed,
|
||||||
/// The set of delegates to be fetched, with the local node
|
/// The set of delegates to be fetched, with the local node
|
||||||
/// removed in the case of a `pull`.
|
/// removed in the case of a `pull`.
|
||||||
pub delegates: BTreeSet<PublicKey>,
|
pub delegates: BTreeSet<PublicKey>,
|
||||||
|
|
@ -239,9 +238,9 @@ pub struct SpecialRefs {
|
||||||
|
|
||||||
impl ProtocolStage for SpecialRefs {
|
impl ProtocolStage for SpecialRefs {
|
||||||
fn ls_refs(&self) -> Option<NonEmpty<BString>> {
|
fn ls_refs(&self) -> Option<NonEmpty<BString>> {
|
||||||
match &self.tracked {
|
match &self.followed {
|
||||||
tracking::Tracked::All => Some(NonEmpty::new("refs/namespaces".into())),
|
policy::Allowed::All => Some(NonEmpty::new("refs/namespaces".into())),
|
||||||
tracking::Tracked::Followed { remotes } => NonEmpty::collect(
|
policy::Allowed::Followed { remotes } => NonEmpty::collect(
|
||||||
remotes
|
remotes
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.delegates.iter())
|
.chain(self.delegates.iter())
|
||||||
|
|
|
||||||
|
|
@ -323,13 +323,13 @@ impl FetchState {
|
||||||
Ok(signed_refs)
|
Ok(signed_refs)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let tracked = handle.tracked();
|
let followed = handle.allowed();
|
||||||
log::trace!(target: "fetch", "Tracked nodes {:?}", tracked);
|
log::trace!(target: "fetch", "Followed nodes {:?}", followed);
|
||||||
let special_refs = stage::SpecialRefs {
|
let special_refs = stage::SpecialRefs {
|
||||||
blocked: handle.blocked.clone(),
|
blocked: handle.blocked.clone(),
|
||||||
remote,
|
remote,
|
||||||
delegates: delegates.clone(),
|
delegates: delegates.clone(),
|
||||||
tracked,
|
followed,
|
||||||
limit: limit.special,
|
limit: limit.special,
|
||||||
};
|
};
|
||||||
log::trace!(target: "fetch", "{special_refs:?}");
|
log::trace!(target: "fetch", "{special_refs:?}");
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl Context {
|
||||||
let issues = issue::Issues::open(&repo)?.counts()?;
|
let issues = issue::Issues::open(&repo)?.counts()?;
|
||||||
let patches = patch::Patches::open(&repo)?.counts()?;
|
let patches = patch::Patches::open(&repo)?.counts()?;
|
||||||
let db = &self.profile.database()?;
|
let db = &self.profile.database()?;
|
||||||
let trackings = db.count(&id).unwrap_or_default();
|
let seeding = db.count(&id).unwrap_or_default();
|
||||||
|
|
||||||
Ok(project::Info {
|
Ok(project::Info {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -74,7 +74,7 @@ impl Context {
|
||||||
issues,
|
issues,
|
||||||
patches,
|
patches,
|
||||||
id,
|
id,
|
||||||
trackings,
|
seeding,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ mod project {
|
||||||
pub patches: cob::patch::PatchCounts,
|
pub patches: cob::patch::PatchCounts,
|
||||||
pub issues: cob::issue::IssueCounts,
|
pub issues: cob::issue::IssueCounts,
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
pub trackings: usize,
|
pub seeding: usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ async fn delegates_projects_handler(
|
||||||
};
|
};
|
||||||
|
|
||||||
let delegates = id.doc.delegates;
|
let delegates = id.doc.delegates;
|
||||||
let trackings = db.count(&id.rid).unwrap_or_default();
|
let seeding = db.count(&id.rid).unwrap_or_default();
|
||||||
|
|
||||||
Some(Info {
|
Some(Info {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -88,7 +88,7 @@ async fn delegates_projects_handler(
|
||||||
issues,
|
issues,
|
||||||
patches,
|
patches,
|
||||||
id: id.rid,
|
id: id.rid,
|
||||||
trackings,
|
seeding,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.skip(page * per_page)
|
.skip(page * per_page)
|
||||||
|
|
@ -146,7 +146,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
|
"id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hello-world",
|
"name": "hello-world",
|
||||||
|
|
@ -168,7 +168,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": RID,
|
"id": RID,
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
@ -207,7 +207,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": RID,
|
"id": RID,
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,13 @@ async fn node_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
||||||
Ok::<_, Error>(Json(response))
|
Ok::<_, Error>(Json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return local tracking repos information.
|
/// Return local repo policies information.
|
||||||
/// `GET /node/policies/repos`
|
/// `GET /node/policies/repos`
|
||||||
async fn node_policies_repos_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
async fn node_policies_repos_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
||||||
let tracking = ctx.profile.tracking()?;
|
let policies = ctx.profile.policies()?;
|
||||||
let mut repos = Vec::new();
|
let mut repos = Vec::new();
|
||||||
|
|
||||||
for policy::Repo { id, scope, policy } in tracking.repo_policies()? {
|
for policy::Repo { id, scope, policy } in policies.seed_policies()? {
|
||||||
repos.push(json!({
|
repos.push(json!({
|
||||||
"id": id,
|
"id": id,
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
|
|
@ -68,7 +68,7 @@ async fn node_policies_repos_handler(State(ctx): State<Context>) -> impl IntoRes
|
||||||
Ok::<_, Error>(Json(repos))
|
Ok::<_, Error>(Json(repos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track a new repo.
|
/// Seed a new repo.
|
||||||
/// `PUT /node/policies/repos/:rid`
|
/// `PUT /node/policies/repos/:rid`
|
||||||
async fn node_policies_seed_handler(
|
async fn node_policies_seed_handler(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
|
|
@ -90,7 +90,7 @@ async fn node_policies_seed_handler(
|
||||||
Ok::<_, Error>((StatusCode::OK, Json(json!({ "success": true }))))
|
Ok::<_, Error>((StatusCode::OK, Json(json!({ "success": true }))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Untrack a repo.
|
/// Unseed a repo.
|
||||||
/// `DELETE /node/policies/repos/:rid`
|
/// `DELETE /node/policies/repos/:rid`
|
||||||
async fn node_policies_unseed_handler(
|
async fn node_policies_unseed_handler(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ async fn project_root_handler(
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let delegates = id.doc.delegates;
|
let delegates = id.doc.delegates;
|
||||||
let trackings = db.count(&id.rid).unwrap_or_default();
|
let seeding = db.count(&id.rid).unwrap_or_default();
|
||||||
|
|
||||||
Some(Info {
|
Some(Info {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -129,7 +129,7 @@ async fn project_root_handler(
|
||||||
issues,
|
issues,
|
||||||
patches,
|
patches,
|
||||||
id: id.rid,
|
id: id.rid,
|
||||||
trackings,
|
seeding,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.skip(page * per_page)
|
.skip(page * per_page)
|
||||||
|
|
@ -994,7 +994,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
|
"id": "rad:zLuTzcmoWMcdK37xqArS8eckp9vK",
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hello-world",
|
"name": "hello-world",
|
||||||
|
|
@ -1016,7 +1016,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": RID,
|
"id": RID,
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
@ -1051,7 +1051,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": RID,
|
"id": RID,
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
@ -1086,7 +1086,7 @@ mod routes {
|
||||||
"closed": 0,
|
"closed": 0,
|
||||||
},
|
},
|
||||||
"id": RID,
|
"id": RID,
|
||||||
"trackings": 0,
|
"seeding": 0,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
|
||||||
|
|
||||||
crate::logger::init().ok();
|
crate::logger::init().ok();
|
||||||
|
|
||||||
profile.tracking_mut().unwrap();
|
profile.policies_mut().unwrap();
|
||||||
profile.database_mut().unwrap(); // Create the database.
|
profile.database_mut().unwrap(); // Create the database.
|
||||||
|
|
||||||
let workdir = dir.join("hello-world-private");
|
let workdir = dir.join("hello-world-private");
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_track_untrack() {
|
fn test_seed_unseed() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
let socket = tmp.path().join("node.sock");
|
let socket = tmp.path().join("node.sock");
|
||||||
let proj = test::arbitrary::gen::<Id>(1);
|
let proj = test::arbitrary::gen::<Id>(1);
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,9 @@ pub enum Error {
|
||||||
/// A node database error.
|
/// A node database error.
|
||||||
#[error("node database error: {0}")]
|
#[error("node database error: {0}")]
|
||||||
Database(#[from] node::db::Error),
|
Database(#[from] node::db::Error),
|
||||||
/// A tracking database error.
|
/// A policies database error.
|
||||||
#[error("tracking database error: {0}")]
|
#[error("policies database error: {0}")]
|
||||||
Tracking(#[from] policy::Error),
|
Policy(#[from] policy::Error),
|
||||||
/// A gossip database error.
|
/// A gossip database error.
|
||||||
#[error("gossip database error: {0}")]
|
#[error("gossip database error: {0}")]
|
||||||
Gossip(#[from] gossip::Error),
|
Gossip(#[from] gossip::Error),
|
||||||
|
|
@ -145,11 +145,11 @@ impl Runtime {
|
||||||
log::info!(target: "node", "Opening node database..");
|
log::info!(target: "node", "Opening node database..");
|
||||||
let mut db: service::Stores<_> = home.database_mut()?.into();
|
let mut db: service::Stores<_> = home.database_mut()?.into();
|
||||||
|
|
||||||
log::info!(target: "node", "Opening tracking policy configuration..");
|
log::info!(target: "node", "Opening policy database..");
|
||||||
let tracking = home.tracking_mut()?;
|
let policies = home.policies_mut()?;
|
||||||
let tracking = policy::Config::new(policy, scope, tracking);
|
let policies = policy::Config::new(policy, scope, policies);
|
||||||
|
|
||||||
log::info!(target: "node", "Default tracking policy set to '{}'", &policy);
|
log::info!(target: "node", "Default seeding policy set to '{}'", &policy);
|
||||||
log::info!(target: "node", "Initializing service ({:?})..", network);
|
log::info!(target: "node", "Initializing service ({:?})..", network);
|
||||||
|
|
||||||
let announcement = if let Some(ann) = fs::read(node_dir.join(node::NODE_ANNOUNCEMENT_FILE))
|
let announcement = if let Some(ann) = fs::read(node_dir.join(node::NODE_ANNOUNCEMENT_FILE))
|
||||||
|
|
@ -202,7 +202,7 @@ impl Runtime {
|
||||||
clock,
|
clock,
|
||||||
db,
|
db,
|
||||||
storage.clone(),
|
storage.clone(),
|
||||||
tracking,
|
policies,
|
||||||
signer.clone(),
|
signer.clone(),
|
||||||
rng,
|
rng,
|
||||||
announcement,
|
announcement,
|
||||||
|
|
@ -237,7 +237,7 @@ impl Runtime {
|
||||||
let fetch = worker::FetchConfig {
|
let fetch = worker::FetchConfig {
|
||||||
policy,
|
policy,
|
||||||
scope,
|
scope,
|
||||||
tracking_db: home.node().join(node::TRACKING_DB_FILE),
|
policies_db: home.node().join(node::POLICIES_DB_FILE),
|
||||||
limit: FetchLimit::default(),
|
limit: FetchLimit::default(),
|
||||||
local: nid,
|
local: nid,
|
||||||
expiry: worker::garbage::Expiry::default(),
|
expiry: worker::garbage::Expiry::default(),
|
||||||
|
|
|
||||||
|
|
@ -203,25 +203,25 @@ impl radicle::node::Handle for Handle {
|
||||||
|
|
||||||
fn follow(&mut self, id: NodeId, alias: Option<Alias>) -> Result<bool, Error> {
|
fn follow(&mut self, id: NodeId, alias: Option<Alias>) -> Result<bool, Error> {
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
self.command(service::Command::TrackNode(id, alias, sender))?;
|
self.command(service::Command::Follow(id, alias, sender))?;
|
||||||
receiver.recv().map_err(Error::from)
|
receiver.recv().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfollow(&mut self, id: NodeId) -> Result<bool, Error> {
|
fn unfollow(&mut self, id: NodeId) -> Result<bool, Error> {
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
self.command(service::Command::UntrackNode(id, sender))?;
|
self.command(service::Command::Unfollow(id, sender))?;
|
||||||
receiver.recv().map_err(Error::from)
|
receiver.recv().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seed(&mut self, id: Id, scope: policy::Scope) -> Result<bool, Error> {
|
fn seed(&mut self, id: Id, scope: policy::Scope) -> Result<bool, Error> {
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
self.command(service::Command::TrackRepo(id, scope, sender))?;
|
self.command(service::Command::Seed(id, scope, sender))?;
|
||||||
receiver.recv().map_err(Error::from)
|
receiver.recv().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unseed(&mut self, id: Id) -> Result<bool, Error> {
|
fn unseed(&mut self, id: Id) -> Result<bool, Error> {
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
self.command(service::Command::UntrackRepo(id, sender))?;
|
self.command(service::Command::Unseed(id, sender))?;
|
||||||
receiver.recv().map_err(Error::from)
|
receiver.recv().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Seeds(#[from] seed::Error),
|
Seeds(#[from] seed::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Tracking(#[from] policy::Error),
|
Policy(#[from] policy::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Repository(#[from] radicle::storage::RepositoryError),
|
Repository(#[from] radicle::storage::RepositoryError),
|
||||||
#[error("namespaces error: {0}")]
|
#[error("namespaces error: {0}")]
|
||||||
|
|
@ -173,14 +173,14 @@ pub enum Command {
|
||||||
Seeds(Id, chan::Sender<Seeds>),
|
Seeds(Id, chan::Sender<Seeds>),
|
||||||
/// Fetch the given repository from the network.
|
/// Fetch the given repository from the network.
|
||||||
Fetch(Id, NodeId, time::Duration, chan::Sender<FetchResult>),
|
Fetch(Id, NodeId, time::Duration, chan::Sender<FetchResult>),
|
||||||
/// Track the given repository.
|
/// Seed the given repository.
|
||||||
TrackRepo(Id, Scope, chan::Sender<bool>),
|
Seed(Id, Scope, chan::Sender<bool>),
|
||||||
/// Untrack the given repository.
|
/// Unseed the given repository.
|
||||||
UntrackRepo(Id, chan::Sender<bool>),
|
Unseed(Id, chan::Sender<bool>),
|
||||||
/// Track the given node.
|
/// Follow the given node.
|
||||||
TrackNode(NodeId, Option<Alias>, chan::Sender<bool>),
|
Follow(NodeId, Option<Alias>, chan::Sender<bool>),
|
||||||
/// Untrack the given node.
|
/// Unfollow the given node.
|
||||||
UntrackNode(NodeId, chan::Sender<bool>),
|
Unfollow(NodeId, chan::Sender<bool>),
|
||||||
/// Query the internal service state.
|
/// Query the internal service state.
|
||||||
QueryState(Arc<QueryState>, chan::Sender<Result<(), CommandError>>),
|
QueryState(Arc<QueryState>, chan::Sender<Result<(), CommandError>>),
|
||||||
}
|
}
|
||||||
|
|
@ -196,10 +196,10 @@ impl fmt::Debug for Command {
|
||||||
Self::Config(_) => write!(f, "Config"),
|
Self::Config(_) => write!(f, "Config"),
|
||||||
Self::Seeds(id, _) => write!(f, "Seeds({id})"),
|
Self::Seeds(id, _) => write!(f, "Seeds({id})"),
|
||||||
Self::Fetch(id, node, _, _) => write!(f, "Fetch({id}, {node})"),
|
Self::Fetch(id, node, _, _) => write!(f, "Fetch({id}, {node})"),
|
||||||
Self::TrackRepo(id, scope, _) => write!(f, "TrackRepo({id}, {scope})"),
|
Self::Seed(id, scope, _) => write!(f, "Seed({id}, {scope})"),
|
||||||
Self::UntrackRepo(id, _) => write!(f, "UntrackRepo({id})"),
|
Self::Unseed(id, _) => write!(f, "Unseed({id})"),
|
||||||
Self::TrackNode(id, _, _) => write!(f, "TrackNode({id})"),
|
Self::Follow(id, _, _) => write!(f, "Follow({id})"),
|
||||||
Self::UntrackNode(id, _) => write!(f, "UntrackNode({id})"),
|
Self::Unfollow(id, _) => write!(f, "Unfollow({id})"),
|
||||||
Self::QueryState { .. } => write!(f, "QueryState(..)"),
|
Self::QueryState { .. } => write!(f, "QueryState(..)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +213,7 @@ pub enum CommandError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Routing(#[from] routing::Error),
|
Routing(#[from] routing::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Tracking(#[from] policy::Error),
|
Policy(#[from] policy::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error returned by [`Service::try_fetch`].
|
/// Error returned by [`Service::try_fetch`].
|
||||||
|
|
@ -304,8 +304,8 @@ pub struct Service<D, S, G> {
|
||||||
storage: S,
|
storage: S,
|
||||||
/// Node database.
|
/// Node database.
|
||||||
db: Stores<D>,
|
db: Stores<D>,
|
||||||
/// Tracking policy configuration.
|
/// Policy configuration.
|
||||||
tracking: policy::Config<Write>,
|
policies: policy::Config<Write>,
|
||||||
/// Peer sessions, currently or recently connected.
|
/// Peer sessions, currently or recently connected.
|
||||||
sessions: Sessions,
|
sessions: Sessions,
|
||||||
/// Clock. Tells the time.
|
/// Clock. Tells the time.
|
||||||
|
|
@ -322,7 +322,7 @@ pub struct Service<D, S, G> {
|
||||||
queue: VecDeque<(Id, NodeId)>,
|
queue: VecDeque<(Id, NodeId)>,
|
||||||
/// Request/connection rate limitter.
|
/// Request/connection rate limitter.
|
||||||
limiter: RateLimiter,
|
limiter: RateLimiter,
|
||||||
/// Current tracked repository bloom filter.
|
/// Current seeded repositories bloom filter.
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
/// Last time the service was idle.
|
/// Last time the service was idle.
|
||||||
last_idle: LocalTime,
|
last_idle: LocalTime,
|
||||||
|
|
@ -364,7 +364,7 @@ where
|
||||||
clock: LocalTime,
|
clock: LocalTime,
|
||||||
db: Stores<D>,
|
db: Stores<D>,
|
||||||
storage: S,
|
storage: S,
|
||||||
tracking: policy::Config<Write>,
|
policies: policy::Config<Write>,
|
||||||
signer: G,
|
signer: G,
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
node: NodeAnnouncement,
|
node: NodeAnnouncement,
|
||||||
|
|
@ -375,7 +375,7 @@ where
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
storage,
|
storage,
|
||||||
tracking,
|
policies,
|
||||||
signer,
|
signer,
|
||||||
rng,
|
rng,
|
||||||
node,
|
node,
|
||||||
|
|
@ -402,42 +402,37 @@ where
|
||||||
self.outbox.next()
|
self.outbox.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track a repository.
|
/// Seed a repository.
|
||||||
/// Returns whether or not the tracking policy was updated.
|
/// Returns whether or not the repo policy was updated.
|
||||||
pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, policy::Error> {
|
pub fn seed(&mut self, id: &Id, scope: Scope) -> Result<bool, policy::Error> {
|
||||||
let updated = self.tracking.track_repo(id, scope)?;
|
let updated = self.policies.seed(id, scope)?;
|
||||||
self.filter.insert(id);
|
self.filter.insert(id);
|
||||||
|
|
||||||
Ok(updated)
|
Ok(updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Untrack a repository.
|
/// Unseed a repository.
|
||||||
/// Returns whether or not the tracking policy was updated.
|
/// Returns whether or not the repo policy was updated.
|
||||||
/// Note that when untracking, we don't announce anything to the network. This is because by
|
/// Note that when unseeding, we don't announce anything to the network. This is because by
|
||||||
/// simply not announcing it anymore, it will eventually be pruned by nodes.
|
/// simply not announcing it anymore, it will eventually be pruned by nodes.
|
||||||
pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, policy::Error> {
|
pub fn unseed(&mut self, id: &Id) -> Result<bool, policy::Error> {
|
||||||
let updated = self.tracking.untrack_repo(id)?;
|
let updated = self.policies.unseed(id)?;
|
||||||
// Nb. This is potentially slow if we have lots of projects. We should probably
|
// Nb. This is potentially slow if we have lots of repos. We should probably
|
||||||
// only re-compute the filter when we've untracked a certain amount of projects
|
// only re-compute the filter when we've unseeded a certain amount of repos
|
||||||
// and the filter is really out of date.
|
// and the filter is really out of date.
|
||||||
//
|
//
|
||||||
// TODO: Share this code with initialization code.
|
// TODO: Share this code with initialization code.
|
||||||
self.filter = Filter::new(
|
self.filter = Filter::new(
|
||||||
self.tracking
|
self.policies
|
||||||
.repo_policies()?
|
.seed_policies()?
|
||||||
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
|
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
|
||||||
);
|
);
|
||||||
Ok(updated)
|
Ok(updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether we are tracking a certain repository.
|
/// Find the closest `n` peers by proximity in seeding graphs.
|
||||||
pub fn is_tracking(&self, id: &Id) -> Result<bool, policy::Error> {
|
|
||||||
self.tracking.is_repo_tracked(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the closest `n` peers by proximity in tracking graphs.
|
|
||||||
/// Returns a sorted list from the closest peer to the furthest.
|
/// Returns a sorted list from the closest peer to the furthest.
|
||||||
/// Peers with more trackings in common score score higher.
|
/// Peers with more seedings in common score score higher.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
|
pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
|
||||||
todo!()
|
todo!()
|
||||||
|
|
@ -463,9 +458,9 @@ where
|
||||||
&mut self.storage
|
&mut self.storage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the tracking policy.
|
/// Get the node policies.
|
||||||
pub fn tracking(&self) -> &policy::Config<Write> {
|
pub fn policies(&self) -> &policy::Config<Write> {
|
||||||
&self.tracking
|
&self.policies
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the local signer.
|
/// Get the local signer.
|
||||||
|
|
@ -520,8 +515,8 @@ where
|
||||||
for (id, addr) in addrs.into_iter().map(|ca| ca.into()) {
|
for (id, addr) in addrs.into_iter().map(|ca| ca.into()) {
|
||||||
self.connect(id, addr);
|
self.connect(id, addr);
|
||||||
}
|
}
|
||||||
// Ensure that our inventory is recorded in our routing table, and we are tracking
|
// 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 tracked if for eg. the
|
// 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.
|
// user creates a new repository while the node is stopped.
|
||||||
let rids = self.storage.inventory()?;
|
let rids = self.storage.inventory()?;
|
||||||
self.db.routing_mut().insert(&rids, nid, time.as_millis())?;
|
self.db.routing_mut().insert(&rids, nid, time.as_millis())?;
|
||||||
|
|
@ -534,8 +529,8 @@ where
|
||||||
for rid in rids {
|
for rid in rids {
|
||||||
let repo = self.storage.repository(rid)?;
|
let repo = self.storage.repository(rid)?;
|
||||||
|
|
||||||
if !self.is_tracking(&rid)? {
|
if !self.policies.is_seeding(&rid)? {
|
||||||
warn!(target: "service", "Local repository {rid} is not tracked");
|
warn!(target: "service", "Local repository {rid} is not seeded");
|
||||||
}
|
}
|
||||||
// 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 Ok(updated_at) = SyncedAt::load(&repo, nid) else {
|
||||||
|
|
@ -566,10 +561,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup subscription filter for tracked repos.
|
// Setup subscription filter for seeded repos.
|
||||||
self.filter = Filter::new(
|
self.filter = Filter::new(
|
||||||
self.tracking
|
self.policies
|
||||||
.repo_policies()?
|
.seed_policies()?
|
||||||
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
|
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id)),
|
||||||
);
|
);
|
||||||
// Try to establish some connections.
|
// Try to establish some connections.
|
||||||
|
|
@ -677,12 +672,12 @@ where
|
||||||
Command::Fetch(rid, seed, timeout, resp) => {
|
Command::Fetch(rid, seed, timeout, resp) => {
|
||||||
self.fetch(rid, &seed, timeout, Some(resp));
|
self.fetch(rid, &seed, timeout, Some(resp));
|
||||||
}
|
}
|
||||||
Command::TrackRepo(rid, scope, resp) => {
|
Command::Seed(rid, scope, resp) => {
|
||||||
// Update our tracking policy.
|
// Update our seeding policy.
|
||||||
let tracked = self
|
let seeded = self
|
||||||
.track_repo(&rid, scope)
|
.seed(&rid, scope)
|
||||||
.expect("Service::command: error tracking repository");
|
.expect("Service::command: error seeding repository");
|
||||||
resp.send(tracked).ok();
|
resp.send(seeded).ok();
|
||||||
|
|
||||||
// Let all our peers know that we're interested in this repo from now on.
|
// Let all our peers know that we're interested in this repo from now on.
|
||||||
self.outbox.broadcast(
|
self.outbox.broadcast(
|
||||||
|
|
@ -690,25 +685,25 @@ where
|
||||||
self.sessions.connected().map(|(_, s)| s),
|
self.sessions.connected().map(|(_, s)| s),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Command::UntrackRepo(id, resp) => {
|
Command::Unseed(id, resp) => {
|
||||||
let untracked = self
|
let updated = self
|
||||||
.untrack_repo(&id)
|
.unseed(&id)
|
||||||
.expect("Service::command: error untracking repository");
|
.expect("Service::command: error unseeding repository");
|
||||||
resp.send(untracked).ok();
|
resp.send(updated).ok();
|
||||||
}
|
}
|
||||||
Command::TrackNode(id, alias, resp) => {
|
Command::Follow(id, alias, resp) => {
|
||||||
let tracked = self
|
let seeded = self
|
||||||
.tracking
|
.policies
|
||||||
.track_node(&id, alias.as_deref())
|
.follow(&id, alias.as_deref())
|
||||||
.expect("Service::command: error tracking node");
|
.expect("Service::command: error following node");
|
||||||
resp.send(tracked).ok();
|
resp.send(seeded).ok();
|
||||||
}
|
}
|
||||||
Command::UntrackNode(id, resp) => {
|
Command::Unfollow(id, resp) => {
|
||||||
let untracked = self
|
let updated = self
|
||||||
.tracking
|
.policies
|
||||||
.untrack_node(&id)
|
.unfollow(&id)
|
||||||
.expect("Service::command: error untracking node");
|
.expect("Service::command: error unfollowing node");
|
||||||
resp.send(untracked).ok();
|
resp.send(updated).ok();
|
||||||
}
|
}
|
||||||
Command::AnnounceRefs(id, resp) => match self.announce_refs(id, [self.node_id()]) {
|
Command::AnnounceRefs(id, resp) => match self.announce_refs(id, [self.node_id()]) {
|
||||||
Ok(refs) => match refs.as_slice() {
|
Ok(refs) => match refs.as_slice() {
|
||||||
|
|
@ -833,7 +828,7 @@ where
|
||||||
from,
|
from,
|
||||||
subscribers: vec![],
|
subscribers: vec![],
|
||||||
});
|
});
|
||||||
let namespaces = self.tracking.namespaces_for(&self.storage, &rid)?;
|
let namespaces = self.policies.namespaces_for(&self.storage, &rid)?;
|
||||||
|
|
||||||
self.outbox
|
self.outbox
|
||||||
.fetch(session, rid, namespaces, refs_at, timeout);
|
.fetch(session, rid, namespaces, refs_at, timeout);
|
||||||
|
|
@ -1190,10 +1185,10 @@ where
|
||||||
sub.filter.insert(id);
|
sub.filter.insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're tracking and connected to the announcer, and we don't have
|
// If we're seeding and connected to the announcer, and we don't have
|
||||||
// the inventory, fetch it from the announcer.
|
// the inventory, fetch it from the announcer.
|
||||||
if self.tracking.is_repo_tracked(id).expect(
|
if self.policies.is_seeding(id).expect(
|
||||||
"Service::handle_announcement: error accessing tracking configuration",
|
"Service::handle_announcement: error accessing seeding configuration",
|
||||||
) {
|
) {
|
||||||
// Only if we do not have the repository locally do we fetch here.
|
// 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 we do have it, only fetch after receiving a ref announcement.
|
||||||
|
|
@ -1202,7 +1197,7 @@ where
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
debug!(target: "service", "Missing tracked inventory {id}; initiating fetch..");
|
debug!(target: "service", "Missing seeded inventory {id}; initiating fetch..");
|
||||||
self.fetch(*id, announcer, FETCH_TIMEOUT, None);
|
self.fetch(*id, announcer, FETCH_TIMEOUT, None);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -1262,8 +1257,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Buffer/throttle fetches.
|
// TODO: Buffer/throttle fetches.
|
||||||
let repo_entry = self.tracking.repo_policy(&message.rid).expect(
|
let repo_entry = self.policies.repo_policy(&message.rid).expect(
|
||||||
"Service::handle_announcement: error accessing repo tracking configuration",
|
"Service::handle_announcement: error accessing repo seeding configuration",
|
||||||
);
|
);
|
||||||
|
|
||||||
if repo_entry.policy == Policy::Allow {
|
if repo_entry.policy == Policy::Allow {
|
||||||
|
|
@ -1336,7 +1331,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
target: "service",
|
target: "service",
|
||||||
"Ignoring refs announcement from {announcer}: repository {} isn't tracked",
|
"Ignoring refs announcement from {announcer}: repository {} isn't seeded",
|
||||||
message.rid
|
message.rid
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1425,7 +1420,7 @@ where
|
||||||
match scope {
|
match scope {
|
||||||
policy::Scope::All => Ok(refs),
|
policy::Scope::All => Ok(refs),
|
||||||
policy::Scope::Followed => {
|
policy::Scope::Followed => {
|
||||||
match self.tracking.namespaces_for(&self.storage, &message.rid) {
|
match self.policies.namespaces_for(&self.storage, &message.rid) {
|
||||||
Ok(Namespaces::All) => Ok(refs),
|
Ok(Namespaces::All) => Ok(refs),
|
||||||
Ok(Namespaces::Followed(mut followed)) => {
|
Ok(Namespaces::Followed(mut followed)) => {
|
||||||
// Get the set of followed nodes except self.
|
// Get the set of followed nodes except self.
|
||||||
|
|
@ -1618,9 +1613,11 @@ where
|
||||||
info!(target: "service", "Routing table updated for {rid} with seed {from}");
|
info!(target: "service", "Routing table updated for {rid} with seed {from}");
|
||||||
self.emitter.emit(Event::SeedDiscovered { rid, nid: from });
|
self.emitter.emit(Event::SeedDiscovered { rid, nid: from });
|
||||||
|
|
||||||
if self.tracking.is_repo_tracked(&rid).expect(
|
if self
|
||||||
"Service::process_inventory: error accessing tracking configuration",
|
.policies
|
||||||
) {
|
.is_seeding(&rid)
|
||||||
|
.expect("Service::process_inventory: error accessing seeding configuration")
|
||||||
|
{
|
||||||
// TODO: We should fetch here if we're already connected, case this seed has
|
// TODO: We should fetch here if we're already connected, case this seed has
|
||||||
// refs we don't have.
|
// refs we don't have.
|
||||||
}
|
}
|
||||||
|
|
@ -1797,7 +1794,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, add peers we know about but have no information about the sync status.
|
// Then, add peers we know about but have no information about the sync status.
|
||||||
// These peers have announced that they track the repository via an inventory
|
// These peers have announced that they seed the repository via an inventory
|
||||||
// announcement, but we haven't received any ref announcements from them.
|
// announcement, but we haven't received any ref announcements from them.
|
||||||
for nid in self.db.routing().get(rid)? {
|
for nid in self.db.routing().get(rid)? {
|
||||||
if nid == self.node_id() {
|
if nid == self.node_id() {
|
||||||
|
|
@ -1815,7 +1812,7 @@ where
|
||||||
Ok(seeds)
|
Ok(seeds)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new filter object, based on our tracking policy.
|
/// Return a new filter object, based on our seeding policy.
|
||||||
fn filter(&self) -> Filter {
|
fn filter(&self) -> Filter {
|
||||||
if self.config.policy == Policy::Allow {
|
if self.config.policy == Policy::Allow {
|
||||||
// TODO: Remove bits for blocked repos.
|
// TODO: Remove bits for blocked repos.
|
||||||
|
|
@ -1914,12 +1911,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch all repositories that are tracked but missing from our inventory.
|
/// Fetch all repositories that are seeded but missing from our inventory.
|
||||||
fn fetch_missing_inventory(&mut self) -> Result<(), Error> {
|
fn fetch_missing_inventory(&mut self) -> Result<(), Error> {
|
||||||
let inventory = self.storage().inventory()?;
|
let inventory = self.storage().inventory()?;
|
||||||
let missing = self
|
let missing = self
|
||||||
.tracking
|
.policies
|
||||||
.repo_policies()?
|
.seed_policies()?
|
||||||
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id))
|
.filter_map(|t| (t.policy == Policy::Allow).then_some(t.id))
|
||||||
.filter(|rid| !inventory.contains(rid));
|
.filter(|rid| !inventory.contains(rid));
|
||||||
|
|
||||||
|
|
@ -1938,7 +1935,7 @@ where
|
||||||
// Another way to handle this would be to update our database, saying
|
// Another way to handle this would be to update our database, saying
|
||||||
// that we're trying to fetch a certain repo. We would then just
|
// that we're trying to fetch a certain repo. We would then just
|
||||||
// iterate over those entries in the above circumstances. This is
|
// iterate over those entries in the above circumstances. This is
|
||||||
// merely an optimization though, we can also iterate over all tracked
|
// merely an optimization though, we can also iterate over all seeded
|
||||||
// repos and check which ones are not in our inventory.
|
// repos and check which ones are not in our inventory.
|
||||||
debug!(target: "service", "No connected seeds found for {rid}..");
|
debug!(target: "service", "No connected seeds found for {rid}..");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use radicle::identity::{Id, Visibility};
|
||||||
use radicle::node::policy::store as policy;
|
use radicle::node::policy::store as policy;
|
||||||
use radicle::node::routing::Store;
|
use radicle::node::routing::Store;
|
||||||
use radicle::node::Database;
|
use radicle::node::Database;
|
||||||
use radicle::node::{Alias, TRACKING_DB_FILE};
|
use radicle::node::{Alias, POLICIES_DB_FILE};
|
||||||
use radicle::node::{ConnectOptions, Handle as _};
|
use radicle::node::{ConnectOptions, Handle as _};
|
||||||
use radicle::profile;
|
use radicle::profile;
|
||||||
use radicle::profile::Home;
|
use radicle::profile::Home;
|
||||||
|
|
@ -81,8 +81,8 @@ impl Environment {
|
||||||
let profile = self.profile(&config.alias);
|
let profile = self.profile(&config.alias);
|
||||||
let signer = MemorySigner::load(&profile.keystore, None).unwrap();
|
let signer = MemorySigner::load(&profile.keystore, None).unwrap();
|
||||||
|
|
||||||
let tracking_db = profile.home.node().join(TRACKING_DB_FILE);
|
let policies_db = profile.home.node().join(POLICIES_DB_FILE);
|
||||||
let tracking = policy::Config::open(tracking_db).unwrap();
|
let policies = policy::Store::open(policies_db).unwrap();
|
||||||
let db = profile.database_mut().unwrap();
|
let db = profile.database_mut().unwrap();
|
||||||
let db = service::Stores::from(db);
|
let db = service::Stores::from(db);
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl Environment {
|
||||||
config,
|
config,
|
||||||
signer,
|
signer,
|
||||||
db,
|
db,
|
||||||
tracking,
|
policies,
|
||||||
storage: profile.storage,
|
storage: profile.storage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ impl Environment {
|
||||||
let home = Home::new(self.tmp().join("home").join(alias).join(".radicle")).unwrap();
|
let home = Home::new(self.tmp().join("home").join(alias).join(".radicle")).unwrap();
|
||||||
let keystore = Keystore::new(&home.keys());
|
let keystore = Keystore::new(&home.keys());
|
||||||
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
|
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
|
||||||
let tracking_db = home.node().join(TRACKING_DB_FILE);
|
let policies_db = home.node().join(POLICIES_DB_FILE);
|
||||||
let alias = Alias::from_str(alias).unwrap();
|
let alias = Alias::from_str(alias).unwrap();
|
||||||
let config = profile::Config {
|
let config = profile::Config {
|
||||||
node: node::Config::new(alias.clone()),
|
node: node::Config::new(alias.clone()),
|
||||||
|
|
@ -122,7 +122,7 @@ impl Environment {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
policy::Config::open(tracking_db).unwrap();
|
policy::Store::open(policies_db).unwrap();
|
||||||
home.database_mut().unwrap(); // Just create the database.
|
home.database_mut().unwrap(); // Just create the database.
|
||||||
|
|
||||||
transport::local::register(storage.clone());
|
transport::local::register(storage.clone());
|
||||||
|
|
@ -149,7 +149,7 @@ pub struct Node<G> {
|
||||||
pub storage: Storage,
|
pub storage: Storage,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub db: service::Stores<Database>,
|
pub db: service::Stores<Database>,
|
||||||
pub tracking: policy::Config<policy::Write>,
|
pub policies: policy::Store<policy::Write>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle to a running node.
|
/// Handle to a running node.
|
||||||
|
|
@ -348,7 +348,7 @@ impl Node<MockSigner> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tracking = home.tracking_mut().unwrap();
|
let policies = home.policies_mut().unwrap();
|
||||||
let db = home.database_mut().unwrap();
|
let db = home.database_mut().unwrap();
|
||||||
let db = service::Stores::from(db);
|
let db = service::Stores::from(db);
|
||||||
|
|
||||||
|
|
@ -360,7 +360,7 @@ impl Node<MockSigner> {
|
||||||
storage,
|
storage,
|
||||||
config,
|
config,
|
||||||
db,
|
db,
|
||||||
tracking,
|
policies,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -419,8 +419,8 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(self
|
assert!(self
|
||||||
.tracking
|
.policies
|
||||||
.track_repo(&id, node::policy::Scope::Followed)
|
.seed(&id, node::policy::Scope::Followed)
|
||||||
.unwrap());
|
.unwrap());
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use crate::service::NodeId;
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
pub updates: Arc<Mutex<Vec<Id>>>,
|
pub updates: Arc<Mutex<Vec<Id>>>,
|
||||||
pub tracking_repos: Arc<Mutex<HashSet<Id>>>,
|
pub seeding: Arc<Mutex<HashSet<Id>>>,
|
||||||
pub tracking_nodes: Arc<Mutex<HashSet<NodeId>>>,
|
pub following: Arc<Mutex<HashSet<NodeId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl radicle::node::Handle for Handle {
|
impl radicle::node::Handle for Handle {
|
||||||
|
|
@ -61,15 +61,15 @@ impl radicle::node::Handle for Handle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seed(&mut self, id: Id, _scope: policy::Scope) -> Result<bool, Self::Error> {
|
fn seed(&mut self, id: Id, _scope: policy::Scope) -> Result<bool, Self::Error> {
|
||||||
Ok(self.tracking_repos.lock().unwrap().insert(id))
|
Ok(self.seeding.lock().unwrap().insert(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unseed(&mut self, id: Id) -> Result<bool, Self::Error> {
|
fn unseed(&mut self, id: Id) -> Result<bool, Self::Error> {
|
||||||
Ok(self.tracking_repos.lock().unwrap().remove(&id))
|
Ok(self.seeding.lock().unwrap().remove(&id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn follow(&mut self, id: NodeId, _alias: Option<Alias>) -> Result<bool, Self::Error> {
|
fn follow(&mut self, id: NodeId, _alias: Option<Alias>) -> Result<bool, Self::Error> {
|
||||||
Ok(self.tracking_nodes.lock().unwrap().insert(id))
|
Ok(self.following.lock().unwrap().insert(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe(
|
fn subscribe(
|
||||||
|
|
@ -80,7 +80,7 @@ impl radicle::node::Handle for Handle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfollow(&mut self, id: NodeId) -> Result<bool, Self::Error> {
|
fn unfollow(&mut self, id: NodeId) -> Result<bool, Self::Error> {
|
||||||
Ok(self.tracking_nodes.lock().unwrap().remove(&id))
|
Ok(self.following.lock().unwrap().remove(&id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn announce_refs(&mut self, id: Id) -> Result<RefsAt, Self::Error> {
|
fn announce_refs(&mut self, id: Id) -> Result<RefsAt, Self::Error> {
|
||||||
|
|
|
||||||
|
|
@ -162,8 +162,8 @@ where
|
||||||
storage: S,
|
storage: S,
|
||||||
mut config: Config<G>,
|
mut config: Config<G>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let tracking = policy::Store::<policy::store::Write>::memory().unwrap();
|
let policies = policy::Store::<policy::store::Write>::memory().unwrap();
|
||||||
let mut tracking = policy::Config::new(config.policy, config.scope, tracking);
|
let mut policies = policy::Config::new(config.policy, config.scope, policies);
|
||||||
let id = *config.signer.public_key();
|
let id = *config.signer.public_key();
|
||||||
let ip = ip.into();
|
let ip = ip.into();
|
||||||
let local_addr = net::SocketAddr::new(ip, config.rng.u16(..));
|
let local_addr = net::SocketAddr::new(ip, config.rng.u16(..));
|
||||||
|
|
@ -172,7 +172,7 @@ where
|
||||||
config.config.external_addresses.push(local_addr.into());
|
config.config.external_addresses.push(local_addr.into());
|
||||||
|
|
||||||
for rid in storage.inventory().unwrap() {
|
for rid in storage.inventory().unwrap() {
|
||||||
tracking.track_repo(&rid, Scope::Followed).unwrap();
|
policies.seed(&rid, Scope::Followed).unwrap();
|
||||||
}
|
}
|
||||||
let announcement = service::gossip::node(&config.config, config.local_time.as_secs());
|
let announcement = service::gossip::node(&config.config, config.local_time.as_secs());
|
||||||
let emitter: Emitter<Event> = Default::default();
|
let emitter: Emitter<Event> = Default::default();
|
||||||
|
|
@ -181,7 +181,7 @@ where
|
||||||
config.local_time,
|
config.local_time,
|
||||||
config.db,
|
config.db,
|
||||||
storage,
|
storage,
|
||||||
tracking,
|
policies,
|
||||||
config.signer,
|
config.signer,
|
||||||
config.rng.clone(),
|
config.rng.clone(),
|
||||||
announcement,
|
announcement,
|
||||||
|
|
|
||||||
|
|
@ -388,25 +388,21 @@ fn test_inventory_pruning() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tracking() {
|
fn test_seeding() {
|
||||||
let mut alice = Peer::new("alice", [7, 7, 7, 7]);
|
let mut alice = Peer::new("alice", [7, 7, 7, 7]);
|
||||||
let proj_id: identity::Id = test::arbitrary::gen(1);
|
let proj_id: identity::Id = test::arbitrary::gen(1);
|
||||||
|
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
alice.command(Command::TrackRepo(
|
alice.command(Command::Seed(proj_id, policy::Scope::default(), sender));
|
||||||
proj_id,
|
|
||||||
policy::Scope::default(),
|
|
||||||
sender,
|
|
||||||
));
|
|
||||||
let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
|
let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
|
||||||
assert!(policy_change);
|
assert!(policy_change);
|
||||||
assert!(alice.tracking().is_repo_tracked(&proj_id).unwrap());
|
assert!(alice.policies().is_seeding(&proj_id).unwrap());
|
||||||
|
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
alice.command(Command::UntrackRepo(proj_id, sender));
|
alice.command(Command::Unseed(proj_id, sender));
|
||||||
let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
|
let policy_change = receiver.recv().map_err(runtime::HandleError::from).unwrap();
|
||||||
assert!(policy_change);
|
assert!(policy_change);
|
||||||
assert!(!alice.tracking().is_repo_tracked(&proj_id).unwrap());
|
assert!(!alice.policies().is_seeding(&proj_id).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -504,7 +500,7 @@ fn test_announcement_rebroadcast_duplicates() {
|
||||||
anns.insert(bob.node_announcement());
|
anns.insert(bob.node_announcement());
|
||||||
|
|
||||||
for rid in rids {
|
for rid in rids {
|
||||||
alice.track_repo(&rid, policy::Scope::All).unwrap();
|
alice.seed(&rid, policy::Scope::All).unwrap();
|
||||||
anns.insert(carol.refs_announcement(rid));
|
anns.insert(carol.refs_announcement(rid));
|
||||||
anns.insert(bob.refs_announcement(rid));
|
anns.insert(bob.refs_announcement(rid));
|
||||||
}
|
}
|
||||||
|
|
@ -676,9 +672,9 @@ fn test_refs_announcement_relay() {
|
||||||
};
|
};
|
||||||
let bob_inv = bob.storage().inventory().unwrap();
|
let bob_inv = bob.storage().inventory().unwrap();
|
||||||
|
|
||||||
alice.track_repo(&bob_inv[0], policy::Scope::All).unwrap();
|
alice.seed(&bob_inv[0], policy::Scope::All).unwrap();
|
||||||
alice.track_repo(&bob_inv[1], policy::Scope::All).unwrap();
|
alice.seed(&bob_inv[1], policy::Scope::All).unwrap();
|
||||||
alice.track_repo(&bob_inv[2], policy::Scope::All).unwrap();
|
alice.seed(&bob_inv[2], policy::Scope::All).unwrap();
|
||||||
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()));
|
||||||
|
|
@ -742,7 +738,7 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
|
||||||
let bob_inv = bob.storage().inventory().unwrap();
|
let bob_inv = bob.storage().inventory().unwrap();
|
||||||
let rid = bob_inv[0];
|
let rid = bob_inv[0];
|
||||||
|
|
||||||
alice.track_repo(&rid, policy::Scope::Followed).unwrap();
|
alice.seed(&rid, policy::Scope::Followed).unwrap();
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
|
|
||||||
// Alice receives Bob's refs.
|
// Alice receives Bob's refs.
|
||||||
|
|
@ -798,7 +794,7 @@ fn test_refs_announcement_followed() {
|
||||||
|
|
||||||
// Alice uses Scope::Followed, and did not track Bob yet.
|
// Alice uses Scope::Followed, and did not track Bob yet.
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.track_repo(&rid, policy::Scope::Followed).unwrap();
|
alice.seed(&rid, policy::Scope::Followed).unwrap();
|
||||||
|
|
||||||
// Alice receives Bob's refs
|
// Alice receives Bob's refs
|
||||||
alice.receive(bob.id(), bob.refs_announcement(rid));
|
alice.receive(bob.id(), bob.refs_announcement(rid));
|
||||||
|
|
@ -811,7 +807,7 @@ fn test_refs_announcement_followed() {
|
||||||
|
|
||||||
// Alice starts to track Bob.
|
// Alice starts to track Bob.
|
||||||
let (sender, receiver) = chan::bounded(1);
|
let (sender, receiver) = chan::bounded(1);
|
||||||
alice.command(Command::TrackNode(
|
alice.command(Command::Follow(
|
||||||
bob.id,
|
bob.id,
|
||||||
Some(node::Alias::new("bob")),
|
Some(node::Alias::new("bob")),
|
||||||
sender,
|
sender,
|
||||||
|
|
@ -834,7 +830,7 @@ fn test_refs_announcement_no_subscribe() {
|
||||||
let eve = Peer::new("eve", [9, 9, 9, 9]);
|
let eve = Peer::new("eve", [9, 9, 9, 9]);
|
||||||
let id = arbitrary::gen(1);
|
let id = arbitrary::gen(1);
|
||||||
|
|
||||||
alice.track_repo(&id, policy::Scope::All).unwrap();
|
alice.seed(&id, policy::Scope::All).unwrap();
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.connect_to(&eve);
|
alice.connect_to(&eve);
|
||||||
alice.receive(bob.id(), bob.refs_announcement(rid));
|
alice.receive(bob.id(), bob.refs_announcement(rid));
|
||||||
|
|
@ -863,7 +859,7 @@ fn test_refs_announcement_offline() {
|
||||||
let inv = alice.inventory();
|
let 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.track_repo(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!(alice.initialize());
|
assert!(alice.initialize());
|
||||||
|
|
@ -1248,14 +1244,14 @@ fn test_maintain_connections_failed_attempt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_track_repo_subscribe() {
|
fn test_seed_repo_subscribe() {
|
||||||
let mut alice = Peer::new("alice", [7, 7, 7, 7]);
|
let mut alice = Peer::new("alice", [7, 7, 7, 7]);
|
||||||
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
||||||
let rid = arbitrary::gen::<Id>(1);
|
let rid = arbitrary::gen::<Id>(1);
|
||||||
let (send, recv) = chan::bounded(1);
|
let (send, recv) = chan::bounded(1);
|
||||||
|
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.command(Command::TrackRepo(rid, policy::Scope::default(), send));
|
alice.command(Command::Seed(rid, policy::Scope::default(), send));
|
||||||
assert!(recv.recv().unwrap());
|
assert!(recv.recv().unwrap());
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
|
|
@ -1275,7 +1271,7 @@ fn test_fetch_missing_inventory_on_gossip() {
|
||||||
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
||||||
let now = LocalTime::now();
|
let now = LocalTime::now();
|
||||||
|
|
||||||
alice.track_repo(&rid, node::policy::Scope::All).unwrap();
|
alice.seed(&rid, node::policy::Scope::All).unwrap();
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.receive(
|
alice.receive(
|
||||||
bob.id(),
|
bob.id(),
|
||||||
|
|
@ -1300,7 +1296,7 @@ fn test_fetch_missing_inventory_on_schedule() {
|
||||||
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
let bob = Peer::new("bob", [8, 8, 8, 8]);
|
||||||
let now = LocalTime::now();
|
let now = LocalTime::now();
|
||||||
|
|
||||||
alice.track_repo(&rid, node::policy::Scope::All).unwrap();
|
alice.seed(&rid, node::policy::Scope::All).unwrap();
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.receive(
|
alice.receive(
|
||||||
bob.id(),
|
bob.id(),
|
||||||
|
|
@ -1441,7 +1437,7 @@ fn test_refs_synced_event() {
|
||||||
});
|
});
|
||||||
let msg = ann.signed(bob.signer());
|
let msg = ann.signed(bob.signer());
|
||||||
|
|
||||||
alice.track_repo(&acme, policy::Scope::All).unwrap();
|
alice.seed(&acme, policy::Scope::All).unwrap();
|
||||||
alice.connect_to(&bob);
|
alice.connect_to(&bob);
|
||||||
alice.receive(bob.id, Message::Announcement(msg));
|
alice.receive(bob.id, Message::Announcement(msg));
|
||||||
|
|
||||||
|
|
@ -1540,14 +1536,14 @@ fn test_push_and_pull() {
|
||||||
|
|
||||||
// Bob seeds Alice's project.
|
// Bob seeds Alice's project.
|
||||||
let (sender, _) = chan::bounded(1);
|
let (sender, _) = chan::bounded(1);
|
||||||
bob.command(service::Command::TrackRepo(
|
bob.command(service::Command::Seed(
|
||||||
proj_id,
|
proj_id,
|
||||||
policy::Scope::default(),
|
policy::Scope::default(),
|
||||||
sender,
|
sender,
|
||||||
));
|
));
|
||||||
// Eve seeds Alice's project.
|
// Eve seeds Alice's project.
|
||||||
let (sender, _) = chan::bounded(1);
|
let (sender, _) = chan::bounded(1);
|
||||||
eve.command(service::Command::TrackRepo(
|
eve.command(service::Command::Seed(
|
||||||
proj_id,
|
proj_id,
|
||||||
policy::Scope::default(),
|
policy::Scope::default(),
|
||||||
sender,
|
sender,
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,8 @@ fn test_replication() {
|
||||||
let inventory = alice.storage.inventory().unwrap();
|
let inventory = alice.storage.inventory().unwrap();
|
||||||
assert!(inventory.is_empty());
|
assert!(inventory.is_empty());
|
||||||
|
|
||||||
let tracked = alice.handle.seed(acme, Scope::All).unwrap();
|
let updated = alice.handle.seed(acme, Scope::All).unwrap();
|
||||||
assert!(tracked);
|
assert!(updated);
|
||||||
|
|
||||||
let seeds = alice.handle.seeds(acme).unwrap();
|
let seeds = alice.handle.seeds(acme).unwrap();
|
||||||
assert!(seeds.is_connected(&bob.id));
|
assert!(seeds.is_connected(&bob.id));
|
||||||
|
|
@ -314,8 +314,8 @@ fn test_migrated_clone() {
|
||||||
alice.connect(&bob);
|
alice.connect(&bob);
|
||||||
converge([&alice, &bob]);
|
converge([&alice, &bob]);
|
||||||
|
|
||||||
let tracked = bob.handle.seed(acme, Scope::All).unwrap();
|
let updated = bob.handle.seed(acme, Scope::All).unwrap();
|
||||||
assert!(tracked);
|
assert!(updated);
|
||||||
|
|
||||||
let result = bob.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
|
let result = bob.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();
|
||||||
assert!(result.is_success());
|
assert!(result.is_success());
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ pub enum FetchError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Storage(#[from] radicle::storage::Error),
|
Storage(#[from] radicle::storage::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TrackingConfig(#[from] radicle::node::policy::store::Error),
|
PolicyStore(#[from] radicle::node::policy::store::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Tracked(#[from] radicle_fetch::tracking::error::Tracking),
|
Policy(#[from] radicle_fetch::policy::error::Policy),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Blocked(#[from] radicle_fetch::tracking::error::Blocked),
|
Blocked(#[from] radicle_fetch::policy::error::Blocked),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchError {
|
impl FetchError {
|
||||||
|
|
@ -156,8 +156,8 @@ pub struct FetchConfig {
|
||||||
pub policy: Policy,
|
pub policy: Policy,
|
||||||
/// Default scope, if a scope for a specific repository was not found.
|
/// Default scope, if a scope for a specific repository was not found.
|
||||||
pub scope: policy::Scope,
|
pub scope: policy::Scope,
|
||||||
/// Path to the tracking database.
|
/// Path to the policies database.
|
||||||
pub tracking_db: PathBuf,
|
pub policies_db: PathBuf,
|
||||||
/// Data limits when fetching from a remote.
|
/// Data limits when fetching from a remote.
|
||||||
pub limit: FetchLimit,
|
pub limit: FetchLimit,
|
||||||
/// Public key of the local peer.
|
/// Public key of the local peer.
|
||||||
|
|
@ -278,18 +278,18 @@ impl Worker {
|
||||||
let FetchConfig {
|
let FetchConfig {
|
||||||
policy,
|
policy,
|
||||||
scope,
|
scope,
|
||||||
tracking_db,
|
policies_db,
|
||||||
limit,
|
limit,
|
||||||
local,
|
local,
|
||||||
expiry,
|
expiry,
|
||||||
} = &self.fetch_config;
|
} = &self.fetch_config;
|
||||||
let policies = policy::Config::new(*policy, *scope, policy::Store::reader(tracking_db)?);
|
let policies = policy::Config::new(*policy, *scope, policy::Store::reader(policies_db)?);
|
||||||
// N.b. if the `rid` is blocked this will return an error, so
|
// N.b. if the `rid` is blocked this will return an error, so
|
||||||
// we won't continue with any further set up of the fetch.
|
// we won't continue with any further set up of the fetch.
|
||||||
let tracked = radicle_fetch::Tracked::from_config(rid, &policies)?;
|
let allowed = radicle_fetch::Allowed::from_config(rid, &policies)?;
|
||||||
let blocked = radicle_fetch::BlockList::from_config(&policies)?;
|
let blocked = radicle_fetch::BlockList::from_config(&policies)?;
|
||||||
|
|
||||||
let handle = fetch::Handle::new(rid, *local, &self.storage, tracked, blocked, channels)?;
|
let handle = fetch::Handle::new(rid, *local, &self.storage, allowed, blocked, channels)?;
|
||||||
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
||||||
|
|
||||||
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use radicle::prelude::Id;
|
||||||
use radicle::storage::refs::RefsAt;
|
use radicle::storage::refs::RefsAt;
|
||||||
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
|
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
|
||||||
use radicle::Storage;
|
use radicle::Storage;
|
||||||
use radicle_fetch::{BlockList, FetchLimit, Tracked};
|
use radicle_fetch::{Allowed, BlockList, FetchLimit};
|
||||||
|
|
||||||
use super::channels::ChannelsFlush;
|
use super::channels::ChannelsFlush;
|
||||||
|
|
||||||
|
|
@ -34,18 +34,18 @@ impl Handle {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
local: PublicKey,
|
local: PublicKey,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
tracked: Tracked,
|
follow: Allowed,
|
||||||
blocked: BlockList,
|
blocked: BlockList,
|
||||||
channels: ChannelsFlush,
|
channels: ChannelsFlush,
|
||||||
) -> Result<Self, error::Handle> {
|
) -> Result<Self, error::Handle> {
|
||||||
let exists = storage.contains(&rid)?;
|
let exists = storage.contains(&rid)?;
|
||||||
if exists {
|
if exists {
|
||||||
let repo = storage.repository(rid)?;
|
let repo = storage.repository(rid)?;
|
||||||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
let handle = radicle_fetch::Handle::new(local, repo, follow, blocked, channels)?;
|
||||||
Ok(Handle::Pull { handle })
|
Ok(Handle::Pull { handle })
|
||||||
} else {
|
} else {
|
||||||
let (repo, tmp) = storage.lock_repository(rid)?;
|
let (repo, tmp) = storage.lock_repository(rid)?;
|
||||||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
let handle = radicle_fetch::Handle::new(local, repo, follow, blocked, channels)?;
|
||||||
Ok(Handle::Clone { handle, tmp })
|
Ok(Handle::Clone { handle, tmp })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ pub const DEFAULT_TIMEOUT: time::Duration = time::Duration::from_secs(9);
|
||||||
pub const MAX_ALIAS_LENGTH: usize = 32;
|
pub const MAX_ALIAS_LENGTH: usize = 32;
|
||||||
/// Filename of node database under the node directory.
|
/// Filename of node database under the node directory.
|
||||||
pub const NODE_DB_FILE: &str = "node.db";
|
pub const NODE_DB_FILE: &str = "node.db";
|
||||||
/// Filename of tracking table database under the node directory.
|
/// Filename of policies database under the node directory.
|
||||||
pub const TRACKING_DB_FILE: &str = "tracking.db";
|
pub const POLICIES_DB_FILE: &str = "policies.db";
|
||||||
/// Filename of last node announcement, when running in debug mode.
|
/// Filename of last node announcement, when running in debug mode.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const NODE_ANNOUNCEMENT_FILE: &str = "announcement.wire.debug";
|
pub const NODE_ANNOUNCEMENT_FILE: &str = "announcement.wire.debug";
|
||||||
|
|
|
||||||
|
|
@ -211,10 +211,10 @@ pub struct Config {
|
||||||
/// Configured service limits.
|
/// Configured service limits.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub limits: Limits,
|
pub limits: Limits,
|
||||||
/// Default tracking policy.
|
/// Default seeding policy.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub policy: Policy,
|
pub policy: Policy,
|
||||||
/// Default tracking scope.
|
/// Default seeding scope.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ impl fmt::Display for Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("invalid tracking scope: {0:?}")]
|
#[error("invalid seeding scope: {0:?}")]
|
||||||
pub struct ParseScopeError(String);
|
pub struct ParseScopeError(String);
|
||||||
|
|
||||||
impl FromStr for Scope {
|
impl FromStr for Scope {
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,21 @@ use crate::prelude::{Id, NodeId};
|
||||||
use crate::storage::{Namespaces, ReadRepository as _, ReadStorage, RepositoryError};
|
use crate::storage::{Namespaces, ReadRepository as _, ReadStorage, RepositoryError};
|
||||||
|
|
||||||
pub use crate::node::policy::store;
|
pub use crate::node::policy::store;
|
||||||
pub use crate::node::policy::store::Config as Store;
|
|
||||||
pub use crate::node::policy::store::Error;
|
pub use crate::node::policy::store::Error;
|
||||||
|
pub use crate::node::policy::store::Store;
|
||||||
pub use crate::node::policy::{Alias, Node, Policy, Repo, Scope};
|
pub use crate::node::policy::{Alias, Node, Policy, Repo, Scope};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum NamespacesError {
|
pub enum NamespacesError {
|
||||||
#[error("failed to find tracking policy for {rid}")]
|
#[error("failed to find policy for {rid}")]
|
||||||
FailedPolicy {
|
FailedPolicy {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
#[source]
|
#[source]
|
||||||
err: Error,
|
err: Error,
|
||||||
},
|
},
|
||||||
#[error("cannot fetch {rid} as it is not tracked")]
|
#[error("cannot fetch {rid} as it is not seeded")]
|
||||||
BlockedPolicy { rid: Id },
|
BlockedPolicy { rid: Id },
|
||||||
#[error("failed to get tracking nodes for {rid}")]
|
#[error("failed to get node policies for {rid}")]
|
||||||
FailedNodes {
|
FailedNodes {
|
||||||
rid: Id,
|
rid: Id,
|
||||||
#[source]
|
#[source]
|
||||||
|
|
@ -42,7 +42,7 @@ pub enum NamespacesError {
|
||||||
NoFollowed { rid: Id },
|
NoFollowed { rid: Id },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tracking configuration.
|
/// Policies configuration.
|
||||||
pub struct Config<T> {
|
pub struct Config<T> {
|
||||||
/// Default policy, if a policy for a specific node or repository was not found.
|
/// Default policy, if a policy for a specific node or repository was not found.
|
||||||
policy: Policy,
|
policy: Policy,
|
||||||
|
|
@ -65,7 +65,7 @@ impl<T> fmt::Debug for Config<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Config<T> {
|
impl<T> Config<T> {
|
||||||
/// Create a new tracking configuration.
|
/// Create a new policy configuration.
|
||||||
pub fn new(policy: Policy, scope: Scope, store: Store<T>) -> Self {
|
pub fn new(policy: Policy, scope: Scope, store: Store<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
policy,
|
policy,
|
||||||
|
|
@ -74,32 +74,32 @@ impl<T> Config<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a repository is tracked.
|
/// Check if a repository is seeded.
|
||||||
pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> {
|
pub fn is_seeding(&self, id: &Id) -> Result<bool, Error> {
|
||||||
self.repo_policy(id)
|
self.repo_policy(id)
|
||||||
.map(|entry| entry.policy == Policy::Allow)
|
.map(|entry| entry.policy == Policy::Allow)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a node is tracked.
|
/// Check if a node is followed.
|
||||||
pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> {
|
pub fn is_following(&self, id: &NodeId) -> Result<bool, Error> {
|
||||||
self.node_policy(id)
|
self.node_policy(id)
|
||||||
.map(|entry| entry.policy == Policy::Allow)
|
.map(|entry| entry.policy == Policy::Allow)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a node's tracking information.
|
/// Get a node's following information.
|
||||||
/// Returns the default policy if the node isn't found.
|
/// Returns the default policy if the node isn't found.
|
||||||
pub fn node_policy(&self, id: &NodeId) -> Result<Node, Error> {
|
pub fn node_policy(&self, id: &NodeId) -> Result<Node, Error> {
|
||||||
Ok(self.store.node_policy(id)?.unwrap_or(Node {
|
Ok(self.store.follow_policy(id)?.unwrap_or(Node {
|
||||||
id: *id,
|
id: *id,
|
||||||
alias: None,
|
alias: None,
|
||||||
policy: self.policy,
|
policy: self.policy,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a repository's tracking information.
|
/// Get a repository's seediing information.
|
||||||
/// Returns the default policy if the repo isn't found.
|
/// Returns the default policy if the repo isn't found.
|
||||||
pub fn repo_policy(&self, id: &Id) -> Result<Repo, Error> {
|
pub fn repo_policy(&self, id: &Id) -> Result<Repo, Error> {
|
||||||
Ok(self.store.repo_policy(id)?.unwrap_or(Repo {
|
Ok(self.store.seed_policy(id)?.unwrap_or(Repo {
|
||||||
id: *id,
|
id: *id,
|
||||||
scope: self.scope,
|
scope: self.scope,
|
||||||
policy: self.policy,
|
policy: self.policy,
|
||||||
|
|
@ -124,7 +124,7 @@ impl<T> Config<T> {
|
||||||
Scope::All => Ok(Namespaces::All),
|
Scope::All => Ok(Namespaces::All),
|
||||||
Scope::Followed => {
|
Scope::Followed => {
|
||||||
let nodes = self
|
let nodes = self
|
||||||
.node_policies()
|
.follow_policies()
|
||||||
.map_err(|err| FailedNodes { rid: *rid, err })?;
|
.map_err(|err| FailedNodes { rid: *rid, err })?;
|
||||||
let mut followed: HashSet<_> = nodes
|
let mut followed: HashSet<_> = nodes
|
||||||
.filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
|
.filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
|
||||||
|
|
|
||||||
|
|
@ -32,23 +32,23 @@ pub struct Read;
|
||||||
pub struct Write;
|
pub struct Write;
|
||||||
|
|
||||||
/// Read only config.
|
/// Read only config.
|
||||||
pub type ConfigReader = Config<Read>;
|
pub type StoreReader = Store<Read>;
|
||||||
/// Read-write config.
|
/// Read-write config.
|
||||||
pub type ConfigWriter = Config<Write>;
|
pub type StoreWriter = Store<Write>;
|
||||||
|
|
||||||
/// Tracking configuration.
|
/// Policy configuration.
|
||||||
pub struct Config<T> {
|
pub struct Store<T> {
|
||||||
db: sql::Connection,
|
db: sql::Connection,
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Debug for Config<T> {
|
impl<T> fmt::Debug for Store<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Config(..)")
|
write!(f, "Store(..)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config<Read> {
|
impl Store<Read> {
|
||||||
const SCHEMA: &'static str = include_str!("schema.sql");
|
const SCHEMA: &'static str = include_str!("schema.sql");
|
||||||
|
|
||||||
/// Same as [`Self::open`], but in read-only mode. This is useful to have multiple
|
/// Same as [`Self::open`], but in read-only mode. This is useful to have multiple
|
||||||
|
|
@ -80,7 +80,7 @@ impl Config<Read> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config<Write> {
|
impl Store<Write> {
|
||||||
const SCHEMA: &'static str = include_str!("schema.sql");
|
const SCHEMA: &'static str = include_str!("schema.sql");
|
||||||
|
|
||||||
/// Open a policy store at the given path. Creates a new store if it
|
/// Open a policy store at the given path. Creates a new store if it
|
||||||
|
|
@ -108,15 +108,15 @@ impl Config<Write> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a read-only version of this store.
|
/// Get a read-only version of this store.
|
||||||
pub fn read_only(self) -> ConfigReader {
|
pub fn read_only(self) -> StoreReader {
|
||||||
Config {
|
Store {
|
||||||
db: self.db,
|
db: self.db,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track a node.
|
/// Follow a node.
|
||||||
pub fn track_node(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
|
pub fn follow(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare(
|
let mut stmt = self.db.prepare(
|
||||||
"INSERT INTO `following` (id, alias)
|
"INSERT INTO `following` (id, alias)
|
||||||
VALUES (?1, ?2)
|
VALUES (?1, ?2)
|
||||||
|
|
@ -131,8 +131,8 @@ impl Config<Write> {
|
||||||
Ok(self.db.change_count() > 0)
|
Ok(self.db.change_count() > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track a repository.
|
/// Seed a repository.
|
||||||
pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> {
|
pub fn seed(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare(
|
let mut stmt = self.db.prepare(
|
||||||
"INSERT INTO `seeding` (id, scope)
|
"INSERT INTO `seeding` (id, scope)
|
||||||
VALUES (?1, ?2)
|
VALUES (?1, ?2)
|
||||||
|
|
@ -147,8 +147,8 @@ impl Config<Write> {
|
||||||
Ok(self.db.change_count() > 0)
|
Ok(self.db.change_count() > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a node's tracking policy.
|
/// Set a node's follow policy.
|
||||||
pub fn set_node_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> {
|
pub fn set_follow_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare(
|
let mut stmt = self.db.prepare(
|
||||||
"INSERT INTO `following` (id, policy)
|
"INSERT INTO `following` (id, policy)
|
||||||
VALUES (?1, ?2)
|
VALUES (?1, ?2)
|
||||||
|
|
@ -163,8 +163,8 @@ impl Config<Write> {
|
||||||
Ok(self.db.change_count() > 0)
|
Ok(self.db.change_count() > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a repository's tracking policy.
|
/// Set a repository's seeding policy.
|
||||||
pub fn set_repo_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> {
|
pub fn set_seed_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare(
|
let mut stmt = self.db.prepare(
|
||||||
"INSERT INTO `seeding` (id, policy)
|
"INSERT INTO `seeding` (id, policy)
|
||||||
VALUES (?1, ?2)
|
VALUES (?1, ?2)
|
||||||
|
|
@ -179,8 +179,8 @@ impl Config<Write> {
|
||||||
Ok(self.db.change_count() > 0)
|
Ok(self.db.change_count() > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Untrack a node.
|
/// Unfollow a node.
|
||||||
pub fn untrack_node(&mut self, id: &NodeId) -> Result<bool, Error> {
|
pub fn unfollow(&mut self, id: &NodeId) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare("DELETE FROM `following` WHERE id = ?")?;
|
let mut stmt = self.db.prepare("DELETE FROM `following` WHERE id = ?")?;
|
||||||
|
|
||||||
stmt.bind((1, id))?;
|
stmt.bind((1, id))?;
|
||||||
|
|
@ -189,8 +189,8 @@ impl Config<Write> {
|
||||||
Ok(self.db.change_count() > 0)
|
Ok(self.db.change_count() > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Untrack a repository.
|
/// Unseed a repository.
|
||||||
pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, Error> {
|
pub fn unseed(&mut self, id: &Id) -> Result<bool, Error> {
|
||||||
let mut stmt = self.db.prepare("DELETE FROM `seeding` WHERE id = ?")?;
|
let mut stmt = self.db.prepare("DELETE FROM `seeding` WHERE id = ?")?;
|
||||||
|
|
||||||
stmt.bind((1, id))?;
|
stmt.bind((1, id))?;
|
||||||
|
|
@ -202,11 +202,11 @@ impl Config<Write> {
|
||||||
|
|
||||||
/// `Read` methods for `Config`. This implies that a
|
/// `Read` methods for `Config`. This implies that a
|
||||||
/// `Config<Write>` can access these functions as well.
|
/// `Config<Write>` can access these functions as well.
|
||||||
impl<T> Config<T> {
|
impl<T> Store<T> {
|
||||||
/// Check if a node is tracked.
|
/// Check if a node is followed.
|
||||||
pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> {
|
pub fn is_node_followed(&self, id: &NodeId) -> Result<bool, Error> {
|
||||||
Ok(matches!(
|
Ok(matches!(
|
||||||
self.node_policy(id)?,
|
self.follow_policy(id)?,
|
||||||
Some(Node {
|
Some(Node {
|
||||||
policy: Policy::Allow,
|
policy: Policy::Allow,
|
||||||
..
|
..
|
||||||
|
|
@ -214,10 +214,10 @@ impl<T> Config<T> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a repository is tracked.
|
/// Check if a repository is seeded.
|
||||||
pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> {
|
pub fn is_repo_seeded(&self, id: &Id) -> Result<bool, Error> {
|
||||||
Ok(matches!(
|
Ok(matches!(
|
||||||
self.repo_policy(id)?,
|
self.seed_policy(id)?,
|
||||||
Some(Repo {
|
Some(Repo {
|
||||||
policy: Policy::Allow,
|
policy: Policy::Allow,
|
||||||
..
|
..
|
||||||
|
|
@ -225,8 +225,8 @@ impl<T> Config<T> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a node's tracking policy.
|
/// Get a node's follow policy.
|
||||||
pub fn node_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> {
|
pub fn follow_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
.prepare("SELECT alias, policy FROM `following` WHERE id = ?")?;
|
.prepare("SELECT alias, policy FROM `following` WHERE id = ?")?;
|
||||||
|
|
@ -251,8 +251,8 @@ impl<T> Config<T> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a repository's tracking policy.
|
/// Get a repository's seeding policy.
|
||||||
pub fn repo_policy(&self, id: &Id) -> Result<Option<Repo>, Error> {
|
pub fn seed_policy(&self, id: &Id) -> Result<Option<Repo>, Error> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
.prepare("SELECT scope, policy FROM `seeding` WHERE id = ?")?;
|
.prepare("SELECT scope, policy FROM `seeding` WHERE id = ?")?;
|
||||||
|
|
@ -269,8 +269,8 @@ impl<T> Config<T> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get node tracking policies.
|
/// Get node follow policies.
|
||||||
pub fn node_policies(&self) -> Result<Box<dyn Iterator<Item = Node>>, Error> {
|
pub fn follow_policies(&self) -> Result<Box<dyn Iterator<Item = Node>>, Error> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
.prepare("SELECT id, alias, policy FROM `following`")?
|
.prepare("SELECT id, alias, policy FROM `following`")?
|
||||||
|
|
@ -293,8 +293,8 @@ impl<T> Config<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: see if sql can return iterator directly
|
// TODO: see if sql can return iterator directly
|
||||||
/// Get repository tracking policies.
|
/// Get repository seed policies.
|
||||||
pub fn repo_policies(&self) -> Result<Box<dyn Iterator<Item = Repo>>, Error> {
|
pub fn seed_policies(&self) -> Result<Box<dyn Iterator<Item = Repo>>, Error> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
.prepare("SELECT id, scope, policy FROM `seeding`")?
|
.prepare("SELECT id, scope, policy FROM `seeding`")?
|
||||||
|
|
@ -312,11 +312,11 @@ impl<T> Config<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AliasStore for Config<T> {
|
impl<T> AliasStore for Store<T> {
|
||||||
/// Retrieve `alias` of given node.
|
/// Retrieve `alias` of given node.
|
||||||
/// Calls `Self::node_policy` under the hood.
|
/// Calls `Self::node_policy` under the hood.
|
||||||
fn alias(&self, nid: &NodeId) -> Option<Alias> {
|
fn alias(&self, nid: &NodeId) -> Option<Alias> {
|
||||||
self.node_policy(nid)
|
self.follow_policy(nid)
|
||||||
.map(|node| node.and_then(|n| n.alias))
|
.map(|node| node.and_then(|n| n.alias))
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
}
|
}
|
||||||
|
|
@ -330,38 +330,38 @@ mod test {
|
||||||
use crate::test::arbitrary;
|
use crate::test::arbitrary;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_track_and_untrack_node() {
|
fn test_follow_and_unfollow_node() {
|
||||||
let id = arbitrary::gen::<NodeId>(1);
|
let id = arbitrary::gen::<NodeId>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_node(&id, Some("eve")).unwrap());
|
assert!(db.follow(&id, Some("eve")).unwrap());
|
||||||
assert!(db.is_node_tracked(&id).unwrap());
|
assert!(db.is_node_followed(&id).unwrap());
|
||||||
assert!(!db.track_node(&id, Some("eve")).unwrap());
|
assert!(!db.follow(&id, Some("eve")).unwrap());
|
||||||
assert!(db.untrack_node(&id).unwrap());
|
assert!(db.unfollow(&id).unwrap());
|
||||||
assert!(!db.is_node_tracked(&id).unwrap());
|
assert!(!db.is_node_followed(&id).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_track_and_untrack_repo() {
|
fn test_seed_and_unseed_repo() {
|
||||||
let id = arbitrary::gen::<Id>(1);
|
let id = arbitrary::gen::<Id>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_repo(&id, Scope::All).unwrap());
|
assert!(db.seed(&id, Scope::All).unwrap());
|
||||||
assert!(db.is_repo_tracked(&id).unwrap());
|
assert!(db.is_repo_seeded(&id).unwrap());
|
||||||
assert!(!db.track_repo(&id, Scope::All).unwrap());
|
assert!(!db.seed(&id, Scope::All).unwrap());
|
||||||
assert!(db.untrack_repo(&id).unwrap());
|
assert!(db.unseed(&id).unwrap());
|
||||||
assert!(!db.is_repo_tracked(&id).unwrap());
|
assert!(!db.is_repo_seeded(&id).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_node_policies() {
|
fn test_node_policies() {
|
||||||
let ids = arbitrary::vec::<NodeId>(3);
|
let ids = arbitrary::vec::<NodeId>(3);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
for id in &ids {
|
for id in &ids {
|
||||||
assert!(db.track_node(id, None).unwrap());
|
assert!(db.follow(id, None).unwrap());
|
||||||
}
|
}
|
||||||
let mut entries = db.node_policies().unwrap();
|
let mut entries = db.follow_policies().unwrap();
|
||||||
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[0]);
|
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[0]);
|
||||||
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[1]);
|
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[1]);
|
||||||
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[2]);
|
assert_matches!(entries.next(), Some(Node { id, .. }) if id == ids[2]);
|
||||||
|
|
@ -370,12 +370,12 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_repo_policies() {
|
fn test_repo_policies() {
|
||||||
let ids = arbitrary::vec::<Id>(3);
|
let ids = arbitrary::vec::<Id>(3);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
for id in &ids {
|
for id in &ids {
|
||||||
assert!(db.track_repo(id, Scope::All).unwrap());
|
assert!(db.seed(id, Scope::All).unwrap());
|
||||||
}
|
}
|
||||||
let mut entries = db.repo_policies().unwrap();
|
let mut entries = db.seed_policies().unwrap();
|
||||||
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[0]);
|
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[0]);
|
||||||
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[1]);
|
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[1]);
|
||||||
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[2]);
|
assert_matches!(entries.next(), Some(Repo { id, .. }) if id == ids[2]);
|
||||||
|
|
@ -384,19 +384,19 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update_alias() {
|
fn test_update_alias() {
|
||||||
let id = arbitrary::gen::<NodeId>(1);
|
let id = arbitrary::gen::<NodeId>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_node(&id, Some("eve")).unwrap());
|
assert!(db.follow(&id, Some("eve")).unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.node_policy(&id).unwrap().unwrap().alias,
|
db.follow_policy(&id).unwrap().unwrap().alias,
|
||||||
Some(Alias::from_str("eve").unwrap())
|
Some(Alias::from_str("eve").unwrap())
|
||||||
);
|
);
|
||||||
assert!(db.track_node(&id, None).unwrap());
|
assert!(db.follow(&id, None).unwrap());
|
||||||
assert_eq!(db.node_policy(&id).unwrap().unwrap().alias, None);
|
assert_eq!(db.follow_policy(&id).unwrap().unwrap().alias, None);
|
||||||
assert!(!db.track_node(&id, None).unwrap());
|
assert!(!db.follow(&id, None).unwrap());
|
||||||
assert!(db.track_node(&id, Some("alice")).unwrap());
|
assert!(db.follow(&id, Some("alice")).unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.node_policy(&id).unwrap().unwrap().alias,
|
db.follow_policy(&id).unwrap().unwrap().alias,
|
||||||
Some(Alias::new("alice"))
|
Some(Alias::new("alice"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -404,33 +404,39 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update_scope() {
|
fn test_update_scope() {
|
||||||
let id = arbitrary::gen::<Id>(1);
|
let id = arbitrary::gen::<Id>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_repo(&id, Scope::All).unwrap());
|
assert!(db.seed(&id, Scope::All).unwrap());
|
||||||
assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::All);
|
assert_eq!(db.seed_policy(&id).unwrap().unwrap().scope, Scope::All);
|
||||||
assert!(db.track_repo(&id, Scope::Followed).unwrap());
|
assert!(db.seed(&id, Scope::Followed).unwrap());
|
||||||
assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::Followed);
|
assert_eq!(db.seed_policy(&id).unwrap().unwrap().scope, Scope::Followed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_repo_policy() {
|
fn test_repo_policy() {
|
||||||
let id = arbitrary::gen::<Id>(1);
|
let id = arbitrary::gen::<Id>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_repo(&id, Scope::All).unwrap());
|
assert!(db.seed(&id, Scope::All).unwrap());
|
||||||
assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Allow);
|
assert_eq!(db.seed_policy(&id).unwrap().unwrap().policy, Policy::Allow);
|
||||||
assert!(db.set_repo_policy(&id, Policy::Block).unwrap());
|
assert!(db.set_seed_policy(&id, Policy::Block).unwrap());
|
||||||
assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Block);
|
assert_eq!(db.seed_policy(&id).unwrap().unwrap().policy, Policy::Block);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_node_policy() {
|
fn test_node_policy() {
|
||||||
let id = arbitrary::gen::<NodeId>(1);
|
let id = arbitrary::gen::<NodeId>(1);
|
||||||
let mut db = Config::open(":memory:").unwrap();
|
let mut db = Store::open(":memory:").unwrap();
|
||||||
|
|
||||||
assert!(db.track_node(&id, None).unwrap());
|
assert!(db.follow(&id, None).unwrap());
|
||||||
assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Allow);
|
assert_eq!(
|
||||||
assert!(db.set_node_policy(&id, Policy::Block).unwrap());
|
db.follow_policy(&id).unwrap().unwrap().policy,
|
||||||
assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Block);
|
Policy::Allow
|
||||||
|
);
|
||||||
|
assert!(db.set_follow_policy(&id, Policy::Block).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
db.follow_policy(&id).unwrap().unwrap().policy,
|
||||||
|
Policy::Block
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ pub enum Error {
|
||||||
#[error("profile key `{0}` is not registered with ssh-agent")]
|
#[error("profile key `{0}` is not registered with ssh-agent")]
|
||||||
KeyNotRegistered(PublicKey),
|
KeyNotRegistered(PublicKey),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TrackingStore(#[from] node::policy::store::Error),
|
PolicyStore(#[from] node::policy::store::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DatabaseStore(#[from] node::db::Error),
|
DatabaseStore(#[from] node::db::Error),
|
||||||
}
|
}
|
||||||
|
|
@ -341,22 +341,6 @@ impl Profile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a read-only handle to the tracking configuration of the node.
|
|
||||||
pub fn tracking(&self) -> Result<policy::store::ConfigReader, policy::store::Error> {
|
|
||||||
let path = self.home.node().join(node::TRACKING_DB_FILE);
|
|
||||||
let config = policy::store::Config::reader(path)?;
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a read-write handle to the tracking configuration of the node.
|
|
||||||
pub fn tracking_mut(&self) -> Result<policy::store::ConfigWriter, policy::store::Error> {
|
|
||||||
let path = self.home.node().join(node::TRACKING_DB_FILE);
|
|
||||||
let config = policy::store::Config::open(path)?;
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get radicle home.
|
/// Get radicle home.
|
||||||
pub fn home(&self) -> &Home {
|
pub fn home(&self) -> &Home {
|
||||||
&self.home
|
&self.home
|
||||||
|
|
@ -364,10 +348,10 @@ impl Profile {
|
||||||
|
|
||||||
/// Return a multi-source store for aliases.
|
/// Return a multi-source store for aliases.
|
||||||
pub fn aliases(&self) -> Aliases {
|
pub fn aliases(&self) -> Aliases {
|
||||||
let tracking = self.home.tracking().ok();
|
let policies = self.home.policies().ok();
|
||||||
let db = self.home.database().ok();
|
let db = self.home.database().ok();
|
||||||
|
|
||||||
Aliases { tracking, db }
|
Aliases { policies, db }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,15 +372,15 @@ impl std::ops::DerefMut for Profile {
|
||||||
/// Holds multiple alias stores, and will try
|
/// Holds multiple alias stores, and will try
|
||||||
/// them one by one when asking for an alias.
|
/// them one by one when asking for an alias.
|
||||||
pub struct Aliases {
|
pub struct Aliases {
|
||||||
tracking: Option<policy::store::ConfigReader>,
|
policies: Option<policy::store::StoreReader>,
|
||||||
db: Option<node::Database>,
|
db: Option<node::Database>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AliasStore for Aliases {
|
impl AliasStore for Aliases {
|
||||||
/// Retrieve `alias` of given node.
|
/// Retrieve `alias` of given node.
|
||||||
/// First looks in `tracking.db` and then `addresses.db`.
|
/// First looks in `policies.db` and then `addresses.db`.
|
||||||
fn alias(&self, nid: &NodeId) -> Option<Alias> {
|
fn alias(&self, nid: &NodeId) -> Option<Alias> {
|
||||||
self.tracking
|
self.policies
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|db| db.alias(nid))
|
.and_then(|db| db.alias(nid))
|
||||||
.or_else(|| self.db.as_ref().and_then(|db| db.alias(nid)))
|
.or_else(|| self.db.as_ref().and_then(|db| db.alias(nid)))
|
||||||
|
|
@ -487,18 +471,18 @@ impl Home {
|
||||||
.unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
|
.unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a read-only handle to the tracking configuration of the node.
|
/// Return a read-only handle to the policies of the node.
|
||||||
pub fn tracking(&self) -> Result<policy::store::ConfigReader, policy::store::Error> {
|
pub fn policies(&self) -> Result<policy::store::StoreReader, policy::store::Error> {
|
||||||
let path = self.node().join(node::TRACKING_DB_FILE);
|
let path = self.node().join(node::POLICIES_DB_FILE);
|
||||||
let config = policy::store::Config::reader(path)?;
|
let config = policy::store::Store::reader(path)?;
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a read-write handle to the tracking configuration of the node.
|
/// Return a read-write handle to the policies of the node.
|
||||||
pub fn tracking_mut(&self) -> Result<policy::store::ConfigWriter, policy::store::Error> {
|
pub fn policies_mut(&self) -> Result<policy::store::StoreWriter, policy::store::Error> {
|
||||||
let path = self.node().join(node::TRACKING_DB_FILE);
|
let path = self.node().join(node::POLICIES_DB_FILE);
|
||||||
let config = policy::store::Config::open(path)?;
|
let config = policy::store::Store::open(path)?;
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ pub struct RepositoryInfo<V> {
|
||||||
/// Identity document.
|
/// Identity document.
|
||||||
pub doc: Doc<V>,
|
pub doc: Doc<V>,
|
||||||
/// Local signed refs, if any.
|
/// Local signed refs, if any.
|
||||||
/// Repositories with this set to `None` are ones that are tracked 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue