node: Add CLI options

This commit is contained in:
Alexis Sellier 2022-09-26 11:25:20 +02:00
parent 116eaa79e6
commit 8605946703
No known key found for this signature in database
10 changed files with 150 additions and 14 deletions

7
Cargo.lock generated
View File

@ -488,6 +488,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]]
name = "libc"
version = "0.2.133"
@ -808,6 +814,7 @@ dependencies = [
"colored",
"crossbeam-channel",
"fastrand",
"lexopt",
"log",
"nakamoto-net",
"nakamoto-net-poll",

View File

@ -13,6 +13,7 @@ chrono = { version = "0.4.0" }
colored = { version = "1.9.0" }
crossbeam-channel = { version = "0.5.6" }
fastrand = { version = "1.8.0" }
lexopt = { version = "0.2.1" }
log = { version = "0.4.17", features = ["std"] }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }

View File

@ -22,6 +22,7 @@ pub mod prelude {
pub use crate::hash::Digest;
pub use crate::identity::{Did, Id};
pub use crate::service::filter::Filter;
pub use crate::service::message::Address;
pub use crate::service::{DisconnectReason, Envelope, Event, Message, Network, NodeId};
pub use crate::storage::refs::Refs;
pub use crate::storage::WriteStorage;

View File

@ -1,15 +1,69 @@
use std::thread;
use std::{env, net};
use std::{env, net, process};
use radicle_node::{client, control};
use radicle_node::prelude::Address;
use radicle_node::{client, control, git, service};
type Reactor = nakamoto_net_poll::Reactor<net::TcpStream>;
#[derive(Debug)]
struct Options {
connect: Vec<Address>,
listen: Vec<net::SocketAddr>,
git_url: git::Url,
}
impl Options {
fn from_env() -> Result<Self, lexopt::Error> {
use lexopt::prelude::*;
let mut parser = lexopt::Parser::from_env();
let mut connect = Vec::new();
let mut listen = Vec::new();
let mut git_url = None;
while let Some(arg) = parser.next()? {
match arg {
Long("connect") => {
let addr = parser.value()?.parse()?;
connect.push(addr);
}
Long("listen") => {
let addr = parser.value()?.parse()?;
listen.push(addr);
}
Long("git-url") => {
let url = git::Url::from_bytes(parser.value()?.into_string()?.as_bytes())
.map_err(|e| format!("invalid URL: {}", e))?;
git_url = Some(url);
}
Long("help") => {
println!("usage: radicle-node [--connect <addr>]..");
process::exit(0);
}
_ => return Err(arg.unexpected()),
}
}
Ok(Self {
connect,
listen,
git_url: git_url.ok_or("a Git URL must be specified with `--git-url`")?,
})
}
}
fn main() -> anyhow::Result<()> {
let options = Options::from_env()?;
let profile = radicle::Profile::load()?;
let client = client::Client::<Reactor>::new(profile)?;
let handle = client.handle();
let config = client::Config::default();
let config = client::Config {
service: service::Config {
connect: options.connect,
git_url: options.git_url,
..service::Config::default()
},
listen: options.listen,
};
let socket = env::var("RAD_SOCKET").unwrap_or_else(|_| control::DEFAULT_SOCKET_NAME.to_owned());
let t1 = thread::spawn(move || control::listen(socket, handle));

View File

@ -28,6 +28,7 @@ use crate::git;
use crate::git::Url;
use crate::identity::{Doc, Id};
use crate::service::config::ProjectTracking;
use crate::service::message::Address;
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
use crate::service::peer::{Session, SessionError, SessionState};
use crate::storage;
@ -464,8 +465,9 @@ where
}
pub fn attempted(&mut self, addr: &std::net::SocketAddr) {
let address = Address::from(*addr);
let ip = addr.ip();
let persistent = self.config.is_persistent(addr);
let persistent = self.config.is_persistent(&address);
let peer = self
.sessions
.entry(ip)
@ -481,6 +483,7 @@ where
link: Link,
) {
let ip = addr.ip();
let address = addr.into();
debug!("Connected to {} ({:?})", ip, link);
@ -505,7 +508,7 @@ where
} else {
self.sessions.insert(
ip,
Session::new(addr, Link::Inbound, self.config.is_persistent(&addr)),
Session::new(addr, Link::Inbound, self.config.is_persistent(&address)),
);
}
}
@ -516,6 +519,7 @@ where
reason: nakamoto::DisconnectReason<DisconnectReason>,
) {
let since = self.local_time();
let address = Address::from(*addr);
let ip = addr.ip();
debug!("Disconnected from {} ({})", ip, reason);
@ -524,7 +528,7 @@ where
peer.state = SessionState::Disconnected { since };
// Attempt to re-connect to persistent peers.
if self.config.is_persistent(addr) && peer.attempts() < MAX_CONNECTION_ATTEMPTS {
if self.config.is_persistent(&address) && peer.attempts() < MAX_CONNECTION_ATTEMPTS {
if reason.is_dial_err() {
return;
}

View File

@ -1,5 +1,3 @@
use std::net;
use crate::collections::HashSet;
use crate::git;
use crate::git::Url;
@ -65,7 +63,7 @@ pub enum RemoteTracking {
pub struct Config {
/// Peers to connect to on startup.
/// Connections to these peers will be maintained.
pub connect: Vec<net::SocketAddr>,
pub connect: Vec<Address>,
/// Peer-to-peer network.
pub network: Network,
/// Project tracking policy.
@ -99,7 +97,7 @@ impl Default for Config {
}
impl Config {
pub fn is_persistent(&self, addr: &net::SocketAddr) -> bool {
pub fn is_persistent(&self, addr: &Address) -> bool {
self.connect.contains(addr)
}

View File

@ -1,5 +1,8 @@
use std::str::FromStr;
use std::{fmt, io, net};
use thiserror::Error;
use crate::crypto;
use crate::git;
use crate::identity::Id;
@ -24,6 +27,12 @@ pub type NodeFeatures = [u8; 32];
// TODO: We should check the length and charset when deserializing.
pub struct Hostname(String);
impl fmt::Display for Hostname {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// Peer public protocol address.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Address {
@ -59,6 +68,52 @@ impl From<net::SocketAddr> for Address {
}
}
#[derive(Debug, Error)]
pub enum AddressParseError {
#[error("unsupported address type `{0}`")]
Unsupported(String),
}
impl FromStr for Address {
type Err = AddressParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(addr) = net::SocketAddr::from_str(s) {
match addr.ip() {
net::IpAddr::V4(ip) => Ok(Self::Ipv4 {
ip,
port: addr.port(),
}),
net::IpAddr::V6(ip) => Ok(Self::Ipv6 {
ip,
port: addr.port(),
}),
}
} else {
Err(Self::Err::Unsupported(s.to_owned()))
}
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ipv4 { ip, port } => {
write!(f, "{}:{}", ip, port)
}
Self::Ipv6 { ip, port } => {
write!(f, "{}:{}", ip, port)
}
Self::Hostname { host, port } => {
write!(f, "{}:{}", host, port)
}
Self::Onion { key, port, .. } => {
write!(f, "{}:{}", key, port)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Subscribe {
/// Subscribe to events matching this filter.

View File

@ -44,9 +44,21 @@ impl Reactor {
}
/// Connect to a peer.
pub fn connect(&mut self, addr: net::SocketAddr) {
pub fn connect(&mut self, addr: impl Into<Address>) {
// TODO: Make sure we don't try to connect more than once to the same address.
self.io.push_back(Io::Connect(addr));
match addr.into() {
Address::Ipv4 { ip, port } => {
self.io
.push_back(Io::Connect(net::SocketAddr::new(net::IpAddr::V4(ip), port)));
}
Address::Ipv6 { ip, port } => {
self.io
.push_back(Io::Connect(net::SocketAddr::new(net::IpAddr::V6(ip), port)));
}
other => {
log::error!("Unsupported address type `{}`", other);
}
}
}
/// Disconnect a peer.

View File

@ -125,6 +125,10 @@ where
}
}
pub fn address(&self) -> Address {
simulator::Peer::addr(self).into()
}
pub fn timestamp(&self) -> Timestamp {
self.service.clock().timestamp()
}

View File

@ -78,7 +78,7 @@ fn test_persistent_peer_connect() {
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
let eve = Peer::new("eve", [9, 9, 9, 9], MockStorage::empty());
let config = Config {
connect: vec![bob.addr(), eve.addr()],
connect: vec![bob.address(), eve.address()],
..Config::default()
};
let mut alice = Peer::config(
@ -302,7 +302,7 @@ fn test_persistent_peer_reconnect() {
let mut alice = Peer::config(
"alice",
Config {
connect: vec![bob.addr(), eve.addr()],
connect: vec![bob.address(), eve.address()],
..Config::default()
},
[7, 7, 7, 7],