radicle: Introduce `radicle::node::config::Scope`
The `SeedingPolicy` type now requires a `Scope`, however existing configurations allow it to not be specified. Introduce a `radicle::node::config::Scope` type that allows for an optional `policy::Scope`. The default value resolves to `policy::Scope::All`. This preserves backwards compatibility for existing configurations.
This commit is contained in:
parent
306d0afbfe
commit
cee3659ed5
|
|
@ -72,6 +72,7 @@ pretty_assertions = { workspace = true }
|
|||
qcheck = { workspace = true }
|
||||
qcheck-macros = { workspace = true }
|
||||
radicle-cob = { workspace = true, features = ["stable-commit-ids", "test"] }
|
||||
radicle-core = {workspace = true, features = ["qcheck"] }
|
||||
radicle-crypto = { workspace = true, features = ["test"] }
|
||||
radicle-git-metadata = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json as json;
|
||||
|
||||
use crate::node;
|
||||
use crate::node::policy::{Scope, SeedingPolicy};
|
||||
use crate::node::policy::SeedingPolicy;
|
||||
use crate::node::{Address, Alias, NodeId};
|
||||
|
||||
use super::policy;
|
||||
|
||||
/// Peer-to-peer protocol version.
|
||||
pub type ProtocolVersion = u8;
|
||||
|
||||
|
|
@ -364,13 +366,13 @@ pub enum AddressConfig {
|
|||
|
||||
/// Default seeding policy. Applies when no repository policies for the given repo are found.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "default")]
|
||||
#[serde(tag = "default", rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum DefaultSeedingPolicy {
|
||||
/// Allow seeding.
|
||||
Allow {
|
||||
/// Seeding scope.
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Scope::is_implicit")]
|
||||
scope: Scope,
|
||||
},
|
||||
/// Block seeding.
|
||||
|
|
@ -378,6 +380,53 @@ pub enum DefaultSeedingPolicy {
|
|||
Block,
|
||||
}
|
||||
|
||||
/// [`Scope`] provides a schema for [`policy::Scope`], where the inner scope is
|
||||
/// optional. It is introduced to allow ease migration to a future
|
||||
/// version of [`DefaultSeedingPolicy::Allow`], where no or different defaults
|
||||
/// apply to [`DefaultSeedingPolicy::Allow::scope`].
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(transparent)]
|
||||
pub struct Scope(Option<policy::Scope>);
|
||||
|
||||
impl Scope {
|
||||
/// Construct the implicit scope, where the default value,
|
||||
/// [`policy::Scope::All`], is chosen for the final scope value.
|
||||
pub fn implicit() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
|
||||
/// Construct the explicit scope, where the given [`policy::Scope`] is used.
|
||||
pub fn explicit(scope: policy::Scope) -> Self {
|
||||
Self(Some(scope))
|
||||
}
|
||||
|
||||
/// Resolve this [`Scope`] to its [`policy::Scope`] value.
|
||||
///
|
||||
/// If the scope is implicit, then [`policy::Scope::All`] is returned.
|
||||
pub fn into_inner(self) -> policy::Scope {
|
||||
self.0.unwrap_or(policy::Scope::All)
|
||||
}
|
||||
|
||||
/// Returns `true` when the scope is implicit, i.e. no [`policy::Scope`] was
|
||||
/// given.
|
||||
pub fn is_implicit(&self) -> bool {
|
||||
self.0.is_none()
|
||||
}
|
||||
|
||||
/// Construct the explicit [`Scope`] where the inner scope is
|
||||
/// [`policy::Scope::All`].
|
||||
fn all() -> Self {
|
||||
Self::explicit(policy::Scope::All)
|
||||
}
|
||||
|
||||
/// Construct the explicit [`Scope`] where the inner scope is
|
||||
/// [`policy::Scope::Followed`].
|
||||
fn followed() -> Self {
|
||||
Self::explicit(policy::Scope::Followed)
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultSeedingPolicy {
|
||||
/// Is this an "allow" policy.
|
||||
pub fn is_allow(&self) -> bool {
|
||||
|
|
@ -386,7 +435,16 @@ impl DefaultSeedingPolicy {
|
|||
|
||||
/// Seed everything from anyone.
|
||||
pub fn permissive() -> Self {
|
||||
Self::Allow { scope: Scope::All }
|
||||
Self::Allow {
|
||||
scope: Scope::all(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Seed only delegate changes.
|
||||
pub fn followed() -> Self {
|
||||
Self::Allow {
|
||||
scope: Scope::followed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +452,9 @@ impl From<DefaultSeedingPolicy> for SeedingPolicy {
|
|||
fn from(policy: DefaultSeedingPolicy) -> Self {
|
||||
match policy {
|
||||
DefaultSeedingPolicy::Block => Self::Block,
|
||||
DefaultSeedingPolicy::Allow { scope } => Self::Allow { scope },
|
||||
DefaultSeedingPolicy::Allow { scope } => SeedingPolicy::Allow {
|
||||
scope: scope.into_inner(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -646,6 +706,10 @@ wrapper!(
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod test {
|
||||
use super::{DefaultSeedingPolicy, Scope};
|
||||
use crate::node::policy;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn partial() {
|
||||
use super::Config;
|
||||
|
|
@ -683,4 +747,85 @@ mod test {
|
|||
);
|
||||
assert_eq!(config.limits.connection.outbound.0, 1337);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_migrating_scope() {
|
||||
let seeding_policy: DefaultSeedingPolicy = serde_json::from_value(json!({
|
||||
"default": "allow"
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
seeding_policy,
|
||||
DefaultSeedingPolicy::Allow { scope: Scope(None) }
|
||||
);
|
||||
|
||||
let seeding_policy: DefaultSeedingPolicy = serde_json::from_value(json!({
|
||||
"default": "allow",
|
||||
"scope": null
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
seeding_policy,
|
||||
DefaultSeedingPolicy::Allow { scope: Scope(None) }
|
||||
);
|
||||
|
||||
let seeding_policy: DefaultSeedingPolicy = serde_json::from_value(json!({
|
||||
"default": "allow",
|
||||
"scope": "all"
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
seeding_policy,
|
||||
DefaultSeedingPolicy::Allow {
|
||||
scope: Scope(Some(policy::Scope::All))
|
||||
}
|
||||
);
|
||||
|
||||
let seeding_policy: DefaultSeedingPolicy = serde_json::from_value(json!({
|
||||
"default": "allow",
|
||||
"scope": "followed"
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
seeding_policy,
|
||||
DefaultSeedingPolicy::Allow {
|
||||
scope: Scope(Some(policy::Scope::Followed))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_migrating_scope() {
|
||||
assert_eq!(
|
||||
json!({
|
||||
"default": "allow"
|
||||
}),
|
||||
serde_json::to_value(DefaultSeedingPolicy::Allow { scope: Scope(None) }).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
json!({
|
||||
"default": "allow",
|
||||
"scope": "all"
|
||||
}),
|
||||
serde_json::to_value(DefaultSeedingPolicy::Allow {
|
||||
scope: Scope(Some(policy::Scope::All))
|
||||
})
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
json!({
|
||||
"default": "allow",
|
||||
"scope": "followed"
|
||||
}),
|
||||
serde_json::to_value(DefaultSeedingPolicy::Allow {
|
||||
scope: Scope(Some(policy::Scope::Followed))
|
||||
})
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,7 +192,9 @@ impl Config {
|
|||
) {
|
||||
log::warn!(target: "radicle", "Overwriting `seedingPolicy` configuration");
|
||||
cfg.node.seeding_policy = match policy {
|
||||
Policy::Allow => DefaultSeedingPolicy::Allow { scope },
|
||||
Policy::Allow => DefaultSeedingPolicy::Allow {
|
||||
scope: node::config::Scope::explicit(scope),
|
||||
},
|
||||
Policy::Block => DefaultSeedingPolicy::Block,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue