node: Add CLI options
This commit is contained in:
parent
116eaa79e6
commit
8605946703
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -125,6 +125,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> Address {
|
||||
simulator::Peer::addr(self).into()
|
||||
}
|
||||
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
self.service.clock().timestamp()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
Loading…
Reference in New Issue