cli: Consolidate policy/scope variants

* Policy::Track -> Policy::Allow
* Scope::Trusted -> Scope::Followed
This commit is contained in:
cloudhead 2023-12-01 13:13:23 +01:00
parent 4605348d68
commit f453090e27
No known key found for this signature in database
25 changed files with 128 additions and 132 deletions

View File

@ -153,8 +153,8 @@ You can see all replicated and locally-available repositories using:
$ rad ls $ rad ls
By default, your node will subscribe to updates from _all_ peers. This By default, your node will subscribe to updates from _all_ peers. This
behavior can be changed by passing *--scope trusted* when cloning. It can behavior can be changed by passing *--scope followed* when cloning. It can
also be changed later via the *rad track* command. also be changed later via the *rad seed* command.
=== Publishing a repository on Radicle === Publishing a repository on Radicle

View File

@ -1,7 +1,7 @@
Trying to clone a repository that is not in our routing table returns an error: Trying to clone a repository that is not in our routing table returns an error:
``` (fail) ``` (fail)
$ rad clone rad:zVNuptPuk5XauitpCWSNVCXGGfXW --scope trusted $ rad clone rad:zVNuptPuk5XauitpCWSNVCXGGfXW --scope followed
✓ Tracking relationship established for rad:zVNuptPuk5XauitpCWSNVCXGGfXW with scope 'trusted' ✓ Tracking relationship established for rad:zVNuptPuk5XauitpCWSNVCXGGfXW with scope 'followed'
✗ Error: no seeds found for rad:zVNuptPuk5XauitpCWSNVCXGGfXW ✗ Error: no seeds found for rad:zVNuptPuk5XauitpCWSNVCXGGfXW
``` ```

View File

@ -2,8 +2,8 @@ To create a local copy of a repository on the radicle network, we use the
`clone` command, followed by the identifier or *RID* of the repository: `clone` command, followed by the identifier or *RID* of the repository:
``` ```
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted $ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed
✓ Tracking relationship established for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted' ✓ Tracking relationship established for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi.. ✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
✓ Forking under z6Mkt67…v4N1tRk.. ✓ Forking under z6Mkt67…v4N1tRk..
✓ Creating checkout in ./heartwood.. ✓ Creating checkout in ./heartwood..

View File

@ -37,7 +37,7 @@ $ rad config
} }
}, },
"policy": "block", "policy": "block",
"scope": "trusted" "scope": "followed"
} }
} }
``` ```

View File

@ -2,8 +2,8 @@ Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice,
Bob tries to fetch it, and even though he's connected to Alice, it fails. Bob tries to fetch it, and even though he's connected to Alice, it fails.
``` ~bob ``` ~bob
$ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --scope trusted $ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --scope followed
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'trusted' ✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'followed'
$ rad ls $ rad ls
``` ```
``` ~bob (fail) ``` ~bob (fail)

View File

@ -3,7 +3,7 @@ To create your first radicle project, navigate to a git repository, and run
the `init` command: the `init` command:
``` ```
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --public --scope trusted $ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm --public --scope followed
Initializing public radicle 👾 project in . Initializing public radicle 👾 project in .

View File

@ -34,11 +34,11 @@ repository that was already created:
``` ```
$ rad node seeding $ rad node seeding
╭──────────────────────────────────────────────────────╮ ╭──────────────────────────────────────────────────────
│ RID Scope Policy │ │ RID Scope Policy │
├──────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji trusted track │ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji followed allow
╰──────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────────────
``` ```
If we wish to see which nodes we are following: If we wish to see which nodes we are following:
@ -48,7 +48,7 @@ $ rad node following
╭───────────────────────────────────────────────────────────────────────────╮ ╭───────────────────────────────────────────────────────────────────────────╮
│ DID Alias Policy │ │ DID Alias Policy │
├───────────────────────────────────────────────────────────────────────────┤ ├───────────────────────────────────────────────────────────────────────────┤
│ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk Bob track │ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk Bob allow
╰───────────────────────────────────────────────────────────────────────────╯ ╰───────────────────────────────────────────────────────────────────────────╯
``` ```

View File

@ -9,6 +9,6 @@ $ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias ev
Now let's track one of Eve's repositories: Now let's track one of Eve's repositories:
``` ```
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted --no-fetch $ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --no-fetch
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted' ✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
``` ```

View File

@ -176,7 +176,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let tracking = profile.tracking()?; let tracking = profile.tracking()?;
if let Some(repo) = tracking.repo_policy(&rid)? { if let Some(repo) = tracking.repo_policy(&rid)? {
let tracking = match repo.policy { let tracking = match repo.policy {
Policy::Track => term::format::positive("tracked"), Policy::Allow => term::format::positive("tracked"),
Policy::Block => term::format::negative("blocked"), Policy::Block => term::format::negative("blocked"),
}; };
println!( println!(

View File

@ -25,15 +25,15 @@ Usage
To delete a policy, use the `--delete` flag. To delete a policy, use the `--delete` flag.
When seeding a repository, a scope can be specified: this can be either `all` or When seeding a repository, a scope can be specified: this can be either `all` or
`trusted`. When using `all`, all remote nodes will be followed for that repository. `followed`. When using `all`, all remote nodes will be followed for that repository.
On the other hand, with `trusted`, only the repository delegates will be followed, On the other hand, with `followed`, only the repository delegates will be followed,
plus any remote that is explicitly followed via `rad follow <nid>`. plus any remote that is explicitly followed via `rad follow <nid>`.
Options Options
--delete, -d Delete the seeding policy --delete, -d Delete the seeding policy
--[no-]fetch Fetch repository after updating seeding policy --[no-]fetch Fetch repository after updating seeding policy
--scope <scope> Peer follow scope for a repository --scope <scope> Peer follow scope for this repository
--verbose, -v Verbose output --verbose, -v Verbose output
--help Print help --help Print help
"#, "#,

View File

@ -700,7 +700,7 @@ fn rad_clean() {
let mut bob = bob.spawn(); let mut bob = bob.spawn();
let mut eve = eve.spawn(); let mut eve = eve.spawn();
alice.handle.track_repo(acme, Scope::All).unwrap(); alice.handle.track_repo(acme, Scope::All).unwrap();
eve.handle.track_repo(acme, Scope::Trusted).unwrap(); eve.handle.track_repo(acme, Scope::Followed).unwrap();
bob.connect(&alice).converge([&alice]); bob.connect(&alice).converge([&alice]);
eve.connect(&alice).converge([&alice]); eve.connect(&alice).converge([&alice]);
@ -769,7 +769,7 @@ fn rad_clone() {
let mut alice = alice.spawn(); let mut alice = alice.spawn();
let mut bob = bob.spawn(); let mut bob = bob.spawn();
// Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors. // Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors.
alice.handle.track_repo(acme, Scope::Trusted).unwrap(); alice.handle.track_repo(acme, Scope::Followed).unwrap();
bob.connect(&alice).converge([&alice]); bob.connect(&alice).converge([&alice]);
@ -873,8 +873,8 @@ fn rad_clone_connect() {
let eve = eve.spawn(); let eve = eve.spawn();
alice.handle.track_repo(acme, Scope::Trusted).unwrap(); alice.handle.track_repo(acme, Scope::Followed).unwrap();
bob.handle.track_repo(acme, Scope::Trusted).unwrap(); bob.handle.track_repo(acme, Scope::Followed).unwrap();
alice.connect(&bob); alice.connect(&bob);
bob.routes_to(&[(acme, alice.id)]); bob.routes_to(&[(acme, alice.id)]);
eve.routes_to(&[(acme, alice.id), (acme, bob.id)]); eve.routes_to(&[(acme, alice.id), (acme, bob.id)]);
@ -1257,7 +1257,7 @@ fn test_replication_via_seed() {
let alice = environment.node(Config::test(Alias::new("alice"))); let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob"))); let bob = environment.node(Config::test(Alias::new("bob")));
let seed = environment.node(Config { let seed = environment.node(Config {
policy: Policy::Track, policy: Policy::Allow,
scope: Scope::All, scope: Scope::All,
..Config::test(Alias::new("seed")) ..Config::test(Alias::new("seed"))
}); });

View File

@ -13,7 +13,7 @@
//! 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. tracked
//! and delegate peers if the scope is trusted 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
//! the `rad/sigrefs` for each fetched peer in the //! the `rad/sigrefs` for each fetched peer in the
@ -241,7 +241,7 @@ impl ProtocolStage for SpecialRefs {
fn ls_refs(&self) -> Option<NonEmpty<BString>> { fn ls_refs(&self) -> Option<NonEmpty<BString>> {
match &self.tracked { match &self.tracked {
tracking::Tracked::All => Some(NonEmpty::new("refs/namespaces".into())), tracking::Tracked::All => Some(NonEmpty::new("refs/namespaces".into())),
tracking::Tracked::Trusted { remotes } => NonEmpty::collect( tracking::Tracked::Followed { remotes } => NonEmpty::collect(
remotes remotes
.iter() .iter()
.chain(self.delegates.iter()) .chain(self.delegates.iter())

View File

@ -10,7 +10,7 @@ pub use radicle::node::tracking::{Policy, Scope};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Tracked { pub enum Tracked {
All, All,
Trusted { remotes: HashSet<PublicKey> }, Followed { remotes: HashSet<PublicKey> },
} }
impl Tracked { impl Tracked {
@ -23,17 +23,17 @@ impl Tracked {
log::error!(target: "fetch", "Attempted to fetch untracked repo {rid}"); log::error!(target: "fetch", "Attempted to fetch untracked repo {rid}");
Err(error::Tracking::BlockedPolicy { rid }) Err(error::Tracking::BlockedPolicy { rid })
} }
Policy::Track => match entry.scope { Policy::Allow => match entry.scope {
Scope::All => Ok(Self::All), Scope::All => Ok(Self::All),
Scope::Trusted => { Scope::Followed => {
let nodes = config let nodes = config
.node_policies() .node_policies()
.map_err(|err| error::Tracking::FailedNodes { rid, err })?; .map_err(|err| error::Tracking::FailedNodes { rid, err })?;
let trusted: HashSet<_> = nodes let followed: HashSet<_> = nodes
.filter_map(|node| (node.policy == Policy::Track).then_some(node.id)) .filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
.collect(); .collect();
Ok(Tracked::Trusted { remotes: trusted }) Ok(Tracked::Followed { remotes: followed })
} }
}, },
} }

View File

@ -425,7 +425,7 @@ where
self.filter = Filter::new( self.filter = Filter::new(
self.tracking self.tracking
.repo_policies()? .repo_policies()?
.filter_map(|t| (t.policy == tracking::Policy::Track).then_some(t.id)), .filter_map(|t| (t.policy == tracking::Policy::Allow).then_some(t.id)),
); );
Ok(updated) Ok(updated)
} }
@ -570,7 +570,7 @@ where
self.filter = Filter::new( self.filter = Filter::new(
self.tracking self.tracking
.repo_policies()? .repo_policies()?
.filter_map(|t| (t.policy == tracking::Policy::Track).then_some(t.id)), .filter_map(|t| (t.policy == tracking::Policy::Allow).then_some(t.id)),
); );
// Try to establish some connections. // Try to establish some connections.
self.maintain_connections(); self.maintain_connections();
@ -1266,7 +1266,7 @@ where
"Service::handle_announcement: error accessing repo tracking configuration", "Service::handle_announcement: error accessing repo tracking configuration",
); );
if repo_entry.policy == tracking::Policy::Track { if repo_entry.policy == tracking::Policy::Allow {
let (fresh, stale) = match self.refs_status_of(message, &repo_entry.scope) { let (fresh, stale) = match self.refs_status_of(message, &repo_entry.scope) {
Ok(RefsStatus { fresh, stale }) => (fresh, stale), Ok(RefsStatus { fresh, stale }) => (fresh, stale),
Err(e) => { Err(e) => {
@ -1424,13 +1424,13 @@ where
// Second, check the scope. // Second, check the scope.
match scope { match scope {
tracking::Scope::All => Ok(refs), tracking::Scope::All => Ok(refs),
tracking::Scope::Trusted => { tracking::Scope::Followed => {
match self.tracking.namespaces_for(&self.storage, &message.rid) { match self.tracking.namespaces_for(&self.storage, &message.rid) {
Ok(Namespaces::All) => Ok(refs), Ok(Namespaces::All) => Ok(refs),
Ok(Namespaces::Trusted(mut trusted)) => { Ok(Namespaces::Followed(mut followed)) => {
// Get the set of trusted nodes except self. // Get the set of followed nodes except self.
trusted.remove(self.nid()); followed.remove(self.nid());
refs.fresh.retain(|r| trusted.contains(&r.remote)); refs.fresh.retain(|r| followed.contains(&r.remote));
Ok(refs) Ok(refs)
} }
@ -1817,7 +1817,7 @@ where
/// Return a new filter object, based on our tracking policy. /// Return a new filter object, based on our tracking policy.
fn filter(&self) -> Filter { fn filter(&self) -> Filter {
if self.config.policy == tracking::Policy::Track { if self.config.policy == tracking::Policy::Allow {
// TODO: Remove bits for blocked repos. // TODO: Remove bits for blocked repos.
Filter::default() Filter::default()
} else { } else {
@ -1920,7 +1920,7 @@ where
let missing = self let missing = self
.tracking .tracking
.repo_policies()? .repo_policies()?
.filter_map(|t| (t.policy == tracking::Policy::Track).then_some(t.id)) .filter_map(|t| (t.policy == tracking::Policy::Allow).then_some(t.id))
.filter(|rid| !inventory.contains(rid)); .filter(|rid| !inventory.contains(rid));
for rid in missing { for rid in missing {

View File

@ -420,7 +420,7 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
assert!(self assert!(self
.tracking .tracking
.track_repo(&id, node::tracking::Scope::Trusted) .track_repo(&id, node::tracking::Scope::Followed)
.unwrap()); .unwrap());
log::debug!( log::debug!(

View File

@ -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::Trusted).unwrap(); tracking.track_repo(&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();

View File

@ -422,7 +422,7 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
radicle::test::fetch( radicle::test::fetch(
&repo, &repo,
&nid, &nid,
Namespaces::Trusted(namespaces.clone()), Namespaces::Followed(namespaces.clone()),
) )
.unwrap(); .unwrap();
} }
@ -655,7 +655,7 @@ impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
Rc::new(Ok(fetch::FetchResult { Rc::new(Ok(fetch::FetchResult {
updated: vec![], updated: vec![],
namespaces: match namespaces { namespaces: match namespaces {
Namespaces::Trusted(hs) => hs, Namespaces::Followed(hs) => hs,
Namespaces::All => HashSet::new(), Namespaces::All => HashSet::new(),
}, },
})), })),

View File

@ -742,7 +742,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, tracking::Scope::Trusted).unwrap(); alice.track_repo(&rid, tracking::Scope::Followed).unwrap();
alice.connect_to(&bob); alice.connect_to(&bob);
// Alice receives Bob's refs. // Alice receives Bob's refs.
@ -755,11 +755,11 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
/// Alice and Bob both have the same repo. /// Alice and Bob both have the same repo.
/// ///
/// First, Alice will not fetch from Bob's `RefsAnnouncement` as Alice does not /// First, Alice will not fetch from Bob's `RefsAnnouncement` as Alice does not
/// track Bob as `Trusted`. /// track Bob as `Followed`.
/// ///
/// Later Alice tracks Bob, and will be able to fetch Bob's refs. /// Later Alice follows Bob, and will be able to fetch Bob's refs.
#[test] #[test]
fn test_refs_announcement_trusted() { fn test_refs_announcement_followed() {
logger::init(log::Level::Debug); logger::init(log::Level::Debug);
// Create MockStorage for Alice and Bob. Both will have repo with `rid`. // Create MockStorage for Alice and Bob. Both will have repo with `rid`.
@ -796,9 +796,9 @@ fn test_refs_announcement_trusted() {
}, },
); );
// Alice uses Scope::Trusted, 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, tracking::Scope::Trusted).unwrap(); alice.track_repo(&rid, tracking::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));
@ -1538,14 +1538,14 @@ fn test_push_and_pull() {
assert!(eve.get(proj_id).unwrap().is_none()); assert!(eve.get(proj_id).unwrap().is_none());
assert!(bob.get(proj_id).unwrap().is_none()); assert!(bob.get(proj_id).unwrap().is_none());
// Bob tracks 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::TrackRepo(
proj_id, proj_id,
tracking::Scope::default(), tracking::Scope::default(),
sender, sender,
)); ));
// Eve tracks 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::TrackRepo(
proj_id, proj_id,

View File

@ -284,7 +284,7 @@ fn test_replication_invalid() {
converge([&alice, &bob]); converge([&alice, &bob]);
alice.handle.track_node(*carol.public_key(), None).unwrap(); alice.handle.track_node(*carol.public_key(), None).unwrap();
alice.handle.track_repo(acme, Scope::Trusted).unwrap(); alice.handle.track_repo(acme, Scope::Followed).unwrap();
let result = alice.handle.fetch(acme, bob.id, DEFAULT_TIMEOUT).unwrap(); let result = alice.handle.fetch(acme, bob.id, DEFAULT_TIMEOUT).unwrap();
// Fetch is successful despite not fetching Carol's refs, since she isn't a delegate. // Fetch is successful despite not fetching Carol's refs, since she isn't a delegate.
@ -367,7 +367,7 @@ fn test_dont_fetch_owned_refs() {
alice.connect(&bob); alice.connect(&bob);
converge([&alice, &bob]); converge([&alice, &bob]);
assert!(bob.handle.track_repo(acme, Scope::Trusted).unwrap()); assert!(bob.handle.track_repo(acme, Scope::Followed).unwrap());
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());
@ -380,7 +380,7 @@ fn test_dont_fetch_owned_refs() {
} }
#[test] #[test]
fn test_fetch_trusted_remotes() { fn test_fetch_followed_remotes() {
logger::init(log::Level::Debug); logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
@ -402,18 +402,18 @@ fn test_fetch_trusted_remotes() {
alice.connect(&bob); alice.connect(&bob);
converge([&alice, &bob]); converge([&alice, &bob]);
let trusted = signers let followed = signers
.iter() .iter()
.map(|s| *s.public_key()) .map(|s| *s.public_key())
.take(2) .take(2)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
assert!( assert!(
trusted.len() < signers.len(), followed.len() < signers.len(),
"Bob is only trusting a subset of peers" "Bob is only trusting a subset of peers"
); );
assert!(bob.handle.track_repo(acme, Scope::Trusted).unwrap()); assert!(bob.handle.track_repo(acme, Scope::Followed).unwrap());
for nid in &trusted { for nid in &followed {
assert!(bob.handle.track_node(*nid, None).unwrap()); assert!(bob.handle.track_node(*nid, None).unwrap());
} }
@ -429,8 +429,8 @@ fn test_fetch_trusted_remotes() {
.collect::<Result<HashSet<_>, _>>() .collect::<Result<HashSet<_>, _>>()
.unwrap(); .unwrap();
assert!(bob_remotes.len() == trusted.len() + 1); assert!(bob_remotes.len() == followed.len() + 1);
assert!(bob_remotes.is_superset(&trusted)); assert!(bob_remotes.is_superset(&followed));
assert!(bob_remotes.contains(&alice.id)); assert!(bob_remotes.contains(&alice.id));
} }
@ -450,7 +450,7 @@ fn test_missing_remote() {
alice.connect(&bob); alice.connect(&bob);
converge([&alice, &bob]); converge([&alice, &bob]);
assert!(bob.handle.track_repo(acme, Scope::Trusted).unwrap()); assert!(bob.handle.track_repo(acme, Scope::Followed).unwrap());
assert!(bob.handle.track_node(*carol.public_key(), None).unwrap()); assert!(bob.handle.track_node(*carol.public_key(), None).unwrap());
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());
@ -477,7 +477,7 @@ fn test_fetch_preserve_owned_refs() {
alice.connect(&bob); alice.connect(&bob);
converge([&alice, &bob]); converge([&alice, &bob]);
assert!(bob.handle.track_repo(acme, Scope::Trusted).unwrap()); assert!(bob.handle.track_repo(acme, Scope::Followed).unwrap());
assert!(bob.handle.track_node(alice.id, None).unwrap()); assert!(bob.handle.track_node(alice.id, None).unwrap());
let result = bob.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap(); let result = bob.handle.fetch(acme, alice.id, DEFAULT_TIMEOUT).unwrap();

View File

@ -25,12 +25,12 @@ pub struct Node {
pub policy: Policy, pub policy: Policy,
} }
/// Tracking policy. /// Resource policy.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum Policy { pub enum Policy {
/// The resource is tracked. /// The resource is allowed.
Track, Allow,
/// The resource is blocked. /// The resource is blocked.
#[default] #[default]
Block, Block,
@ -39,7 +39,7 @@ pub enum Policy {
impl fmt::Display for Policy { impl fmt::Display for Policy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Track => write!(f, "track"), Self::Allow => write!(f, "allow"),
Self::Block => write!(f, "block"), Self::Block => write!(f, "block"),
} }
} }
@ -50,7 +50,7 @@ impl FromStr for Policy {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"track" => Ok(Self::Track), "allow" => Ok(Self::Allow),
"block" => Ok(Self::Block), "block" => Ok(Self::Block),
_ => Err(s.to_owned()), _ => Err(s.to_owned()),
} }
@ -64,7 +64,7 @@ impl sqlite::BindableWithIndex for Policy {
i: I, i: I,
) -> sqlite::Result<()> { ) -> sqlite::Result<()> {
match self { match self {
Self::Track => "track", Self::Allow => "allow",
Self::Block => "block", Self::Block => "block",
} }
.bind(stmt, i) .bind(stmt, i)
@ -78,7 +78,7 @@ impl TryFrom<&sqlite::Value> for Policy {
let message = Some("sql: invalid policy".to_owned()); let message = Some("sql: invalid policy".to_owned());
match value { match value {
sqlite::Value::String(s) if s == "track" => Ok(Policy::Track), sqlite::Value::String(s) if s == "allow" => Ok(Policy::Allow),
sqlite::Value::String(s) if s == "block" => Ok(Policy::Block), sqlite::Value::String(s) if s == "block" => Ok(Policy::Block),
_ => Err(sqlite::Error { _ => Err(sqlite::Error {
code: None, code: None,
@ -88,21 +88,21 @@ impl TryFrom<&sqlite::Value> for Policy {
} }
} }
/// Tracking scope of a repository tracking policy. /// Follow scope of a seeded repository.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum Scope { pub enum Scope {
/// Track remotes of nodes that are already tracked. /// Seed remotes that are explicitly followed.
#[default] #[default]
Trusted, Followed,
/// Track all remotes. /// Seed all remotes.
All, All,
} }
impl fmt::Display for Scope { impl fmt::Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Scope::Trusted => f.write_str("trusted"), Scope::Followed => f.write_str("followed"),
Scope::All => f.write_str("all"), Scope::All => f.write_str("all"),
} }
} }
@ -117,7 +117,7 @@ impl FromStr for Scope {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"trusted" => Ok(Self::Trusted), "followed" => Ok(Self::Followed),
"all" => Ok(Self::All), "all" => Ok(Self::All),
_ => Err(ParseScopeError(s.to_string())), _ => Err(ParseScopeError(s.to_string())),
} }
@ -131,7 +131,7 @@ impl sqlite::BindableWithIndex for Scope {
i: I, i: I,
) -> sqlite::Result<()> { ) -> sqlite::Result<()> {
let s = match self { let s = match self {
Self::Trusted => "trusted", Self::Followed => "followed",
Self::All => "all", Self::All => "all",
}; };
s.bind(stmt, i) s.bind(stmt, i)

View File

@ -16,21 +16,21 @@ pub use crate::node::tracking::{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 tracking 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 tracked")]
BlockedPolicy { rid: Id }, BlockedPolicy { rid: Id },
#[error("Failed to get tracking nodes for {rid}")] #[error("failed to get tracking nodes for {rid}")]
FailedNodes { FailedNodes {
rid: Id, rid: Id,
#[source] #[source]
err: Error, err: Error,
}, },
#[error("Failed to get delegates for {rid}")] #[error("failed to get delegates for {rid}")]
FailedDelegates { FailedDelegates {
rid: Id, rid: Id,
#[source] #[source]
@ -38,8 +38,8 @@ pub enum NamespacesError {
}, },
#[error(transparent)] #[error(transparent)]
Git(#[from] crate::git::raw::Error), Git(#[from] crate::git::raw::Error),
#[error("Could not find any trusted nodes for {rid}")] #[error("could not find any followed nodes for {rid}")]
NoTrusted { rid: Id }, NoFollowed { rid: Id },
} }
/// Tracking configuration. /// Tracking configuration.
@ -77,13 +77,13 @@ impl<T> Config<T> {
/// Check if a repository is tracked. /// Check if a repository is tracked.
pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> { pub fn is_repo_tracked(&self, id: &Id) -> Result<bool, Error> {
self.repo_policy(id) self.repo_policy(id)
.map(|entry| entry.policy == Policy::Track) .map(|entry| entry.policy == Policy::Allow)
} }
/// Check if a node is tracked. /// Check if a node is tracked.
pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> { pub fn is_node_tracked(&self, id: &NodeId) -> Result<bool, Error> {
self.node_policy(id) self.node_policy(id)
.map(|entry| entry.policy == Policy::Track) .map(|entry| entry.policy == Policy::Allow)
} }
/// Get a node's tracking information. /// Get a node's tracking information.
@ -120,14 +120,14 @@ impl<T> Config<T> {
error!(target: "service", "Attempted to fetch untracked repo {rid}"); error!(target: "service", "Attempted to fetch untracked repo {rid}");
Err(NamespacesError::BlockedPolicy { rid: *rid }) Err(NamespacesError::BlockedPolicy { rid: *rid })
} }
Policy::Track => match entry.scope { Policy::Allow => match entry.scope {
Scope::All => Ok(Namespaces::All), Scope::All => Ok(Namespaces::All),
Scope::Trusted => { Scope::Followed => {
let nodes = self let nodes = self
.node_policies() .node_policies()
.map_err(|err| FailedNodes { rid: *rid, err })?; .map_err(|err| FailedNodes { rid: *rid, err })?;
let mut trusted: HashSet<_> = nodes let mut followed: HashSet<_> = nodes
.filter_map(|node| (node.policy == Policy::Track).then_some(node.id)) .filter_map(|node| (node.policy == Policy::Allow).then_some(node.id))
.collect(); .collect();
if let Ok(repo) = storage.repository(*rid) { if let Ok(repo) = storage.repository(*rid) {
@ -135,15 +135,15 @@ impl<T> Config<T> {
.delegates() .delegates()
.map_err(|err| FailedDelegates { rid: *rid, err })? .map_err(|err| FailedDelegates { rid: *rid, err })?
.map(PublicKey::from); .map(PublicKey::from);
trusted.extend(delegates); followed.extend(delegates);
}; };
if trusted.is_empty() { if followed.is_empty() {
// Nb. returning All here because the // Nb. returning All here because the
// fetching logic will correctly determine // fetching logic will correctly determine
// trusted and delegate remotes. // followed and delegate remotes.
Ok(Namespaces::All) Ok(Namespaces::All)
} else { } else {
Ok(Namespaces::Trusted(trusted)) Ok(Namespaces::Followed(followed))
} }
} }
}, },

View File

@ -2,30 +2,30 @@
-- Service configuration schema. -- Service configuration schema.
-- --
-- Node tracking policy. -- Node follow policies.
create table if not exists "node-policies" ( create table if not exists "following" (
-- Node ID. -- Node ID.
"id" text primary key not null, "id" text primary key not null,
-- Node alias. May override the alias announced by the node. -- Node alias. May override the alias announced by the node.
"alias" text default '', "alias" text default '',
-- Tracking policy for this node. -- Tracking policy for this node.
"policy" text default 'track' "policy" text default 'allow'
-- --
) strict; ) strict;
-- Repository tracking policy. -- Repository seeding policies.
create table if not exists "repo-policies" ( create table if not exists "seeding" (
-- Repository ID. -- Repository ID.
"id" text primary key not null, "id" text primary key not null,
-- Tracking scope for this repository. -- Tracking scope for this repository.
-- --
-- Valid values are: -- Valid values are:
-- --
-- "trusted" track repository delegates and remotes in the `node-policies` table. -- "followed" seed repository delegates and remotes in the `following` table.
-- "all" track all remotes. -- "all" seed all remotes.
-- --
"scope" text default 'trusted', "scope" text default 'followed',
-- Tracking policy for this repository. -- Tracking policy for this repository.
"policy" text default 'track' "policy" text default 'allow'
-- --
) strict; ) strict;

View File

@ -118,7 +118,7 @@ impl Config<Write> {
/// Track a node. /// Track a node.
pub fn track_node(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> { pub fn track_node(&mut self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
let mut stmt = self.db.prepare( let mut stmt = self.db.prepare(
"INSERT INTO `node-policies` (id, alias) "INSERT INTO `following` (id, alias)
VALUES (?1, ?2) VALUES (?1, ?2)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET alias = ?2 WHERE alias != ?2", SET alias = ?2 WHERE alias != ?2",
@ -134,7 +134,7 @@ impl Config<Write> {
/// Track a repository. /// Track a repository.
pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> { pub fn track_repo(&mut self, id: &Id, scope: Scope) -> Result<bool, Error> {
let mut stmt = self.db.prepare( let mut stmt = self.db.prepare(
"INSERT INTO `repo-policies` (id, scope) "INSERT INTO `seeding` (id, scope)
VALUES (?1, ?2) VALUES (?1, ?2)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET scope = ?2 WHERE scope != ?2", SET scope = ?2 WHERE scope != ?2",
@ -150,7 +150,7 @@ impl Config<Write> {
/// Set a node's tracking policy. /// Set a node's tracking policy.
pub fn set_node_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> { pub fn set_node_policy(&mut self, id: &NodeId, policy: Policy) -> Result<bool, Error> {
let mut stmt = self.db.prepare( let mut stmt = self.db.prepare(
"INSERT INTO `node-policies` (id, policy) "INSERT INTO `following` (id, policy)
VALUES (?1, ?2) VALUES (?1, ?2)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET policy = ?2 WHERE policy != ?2", SET policy = ?2 WHERE policy != ?2",
@ -166,7 +166,7 @@ impl Config<Write> {
/// Set a repository's tracking policy. /// Set a repository's tracking policy.
pub fn set_repo_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> { pub fn set_repo_policy(&mut self, id: &Id, policy: Policy) -> Result<bool, Error> {
let mut stmt = self.db.prepare( let mut stmt = self.db.prepare(
"INSERT INTO `repo-policies` (id, policy) "INSERT INTO `seeding` (id, policy)
VALUES (?1, ?2) VALUES (?1, ?2)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET policy = ?2 WHERE policy != ?2", SET policy = ?2 WHERE policy != ?2",
@ -181,9 +181,7 @@ impl Config<Write> {
/// Untrack a node. /// Untrack a node.
pub fn untrack_node(&mut self, id: &NodeId) -> Result<bool, Error> { pub fn untrack_node(&mut self, id: &NodeId) -> Result<bool, Error> {
let mut stmt = self let mut stmt = self.db.prepare("DELETE FROM `following` WHERE id = ?")?;
.db
.prepare("DELETE FROM `node-policies` WHERE id = ?")?;
stmt.bind((1, id))?; stmt.bind((1, id))?;
stmt.next()?; stmt.next()?;
@ -193,9 +191,7 @@ impl Config<Write> {
/// Untrack a repository. /// Untrack a repository.
pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, Error> { pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, Error> {
let mut stmt = self let mut stmt = self.db.prepare("DELETE FROM `seeding` WHERE id = ?")?;
.db
.prepare("DELETE FROM `repo-policies` WHERE id = ?")?;
stmt.bind((1, id))?; stmt.bind((1, id))?;
stmt.next()?; stmt.next()?;
@ -212,7 +208,7 @@ impl<T> Config<T> {
Ok(matches!( Ok(matches!(
self.node_policy(id)?, self.node_policy(id)?,
Some(Node { Some(Node {
policy: Policy::Track, policy: Policy::Allow,
.. ..
}) })
)) ))
@ -223,7 +219,7 @@ impl<T> Config<T> {
Ok(matches!( Ok(matches!(
self.repo_policy(id)?, self.repo_policy(id)?,
Some(Repo { Some(Repo {
policy: Policy::Track, policy: Policy::Allow,
.. ..
}) })
)) ))
@ -233,7 +229,7 @@ impl<T> Config<T> {
pub fn node_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> { pub fn node_policy(&self, id: &NodeId) -> Result<Option<Node>, Error> {
let mut stmt = self let mut stmt = self
.db .db
.prepare("SELECT alias, policy FROM `node-policies` WHERE id = ?")?; .prepare("SELECT alias, policy FROM `following` WHERE id = ?")?;
stmt.bind((1, id))?; stmt.bind((1, id))?;
@ -259,7 +255,7 @@ impl<T> Config<T> {
pub fn repo_policy(&self, id: &Id) -> Result<Option<Repo>, Error> { pub fn repo_policy(&self, id: &Id) -> Result<Option<Repo>, Error> {
let mut stmt = self let mut stmt = self
.db .db
.prepare("SELECT scope, policy FROM `repo-policies` WHERE id = ?")?; .prepare("SELECT scope, policy FROM `seeding` WHERE id = ?")?;
stmt.bind((1, id))?; stmt.bind((1, id))?;
@ -277,7 +273,7 @@ impl<T> Config<T> {
pub fn node_policies(&self) -> Result<Box<dyn Iterator<Item = Node>>, Error> { pub fn node_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 `node-policies`")? .prepare("SELECT id, alias, policy FROM `following`")?
.into_iter(); .into_iter();
let mut entries = Vec::new(); let mut entries = Vec::new();
@ -301,7 +297,7 @@ impl<T> Config<T> {
pub fn repo_policies(&self) -> Result<Box<dyn Iterator<Item = Repo>>, Error> { pub fn repo_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 `repo-policies`")? .prepare("SELECT id, scope, policy FROM `seeding`")?
.into_iter(); .into_iter();
let mut entries = Vec::new(); let mut entries = Vec::new();
@ -412,8 +408,8 @@ mod test {
assert!(db.track_repo(&id, Scope::All).unwrap()); assert!(db.track_repo(&id, Scope::All).unwrap());
assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::All); assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::All);
assert!(db.track_repo(&id, Scope::Trusted).unwrap()); assert!(db.track_repo(&id, Scope::Followed).unwrap());
assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::Trusted); assert_eq!(db.repo_policy(&id).unwrap().unwrap().scope, Scope::Followed);
} }
#[test] #[test]
@ -422,7 +418,7 @@ mod test {
let mut db = Config::open(":memory:").unwrap(); let mut db = Config::open(":memory:").unwrap();
assert!(db.track_repo(&id, Scope::All).unwrap()); assert!(db.track_repo(&id, Scope::All).unwrap());
assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Track); assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Allow);
assert!(db.set_repo_policy(&id, Policy::Block).unwrap()); assert!(db.set_repo_policy(&id, Policy::Block).unwrap());
assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Block); assert_eq!(db.repo_policy(&id).unwrap().unwrap().policy, Policy::Block);
} }
@ -433,7 +429,7 @@ mod test {
let mut db = Config::open(":memory:").unwrap(); let mut db = Config::open(":memory:").unwrap();
assert!(db.track_node(&id, None).unwrap()); assert!(db.track_node(&id, None).unwrap());
assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Track); assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Allow);
assert!(db.set_node_policy(&id, Policy::Block).unwrap()); assert!(db.set_node_policy(&id, Policy::Block).unwrap());
assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Block); assert_eq!(db.node_policy(&id).unwrap().unwrap().policy, Policy::Block);
} }

View File

@ -36,8 +36,8 @@ pub enum Namespaces {
/// All namespaces. /// All namespaces.
#[default] #[default]
All, All,
/// The trusted set of namespaces. /// The followed set of namespaces.
Trusted(HashSet<PublicKey>), Followed(HashSet<PublicKey>),
} }
impl Namespaces { impl Namespaces {
@ -48,7 +48,7 @@ impl Namespaces {
dst: (*NAMESPACES_GLOB).clone(), dst: (*NAMESPACES_GLOB).clone(),
force: true, force: true,
}], }],
Namespaces::Trusted(pks) => pks Namespaces::Followed(pks) => pks
.iter() .iter()
.map(|pk| { .map(|pk| {
let ns = pk.to_namespace().with_pattern(git::refspec::STAR); let ns = pk.to_namespace().with_pattern(git::refspec::STAR);
@ -65,7 +65,7 @@ impl Namespaces {
impl FromIterator<PublicKey> for Namespaces { impl FromIterator<PublicKey> for Namespaces {
fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self {
Self::Trusted(iter.into_iter().collect()) Self::Followed(iter.into_iter().collect())
} }
} }

View File

@ -19,7 +19,7 @@ pub fn fetch<W: WriteRepository>(
) -> Result<Vec<RefUpdate>, crate::storage::FetchError> { ) -> Result<Vec<RefUpdate>, crate::storage::FetchError> {
let namespace = match namespaces.into() { let namespace = match namespaces.into() {
Namespaces::All => None, Namespaces::All => None,
Namespaces::Trusted(trusted) => trusted.into_iter().next(), Namespaces::Followed(followed) => followed.into_iter().next(),
}; };
let mut updates = Vec::new(); let mut updates = Vec::new();
let mut callbacks = git2::RemoteCallbacks::new(); let mut callbacks = git2::RemoteCallbacks::new();