cli: Implement `rad node start` with support for deamonizing
Signed-off-by: xphoniex <dj.2dixx@gmail.com>
This commit is contained in:
parent
362f26a0c8
commit
ac12a4deee
|
|
@ -22,12 +22,14 @@ pub const HELP: Help = Help {
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
rad node status [<option>...]
|
rad node status [<option>...]
|
||||||
rad node start [<option>...]
|
rad node start [--daemon|-d] [<option>...] [-- <node-option>...]
|
||||||
rad node stop [<option>...]
|
rad node stop [<option>...]
|
||||||
rad node connect <nid> <addr> [<option>...]
|
rad node connect <nid> <addr> [<option>...]
|
||||||
rad node routing [<option>...]
|
rad node routing [<option>...]
|
||||||
rad node tracking [--repos|--nodes] [<option>...]
|
rad node tracking [--repos|--nodes] [<option>...]
|
||||||
|
|
||||||
|
For `<node-option>` see `radicle-node --help`.
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
--help Print help
|
--help Print help
|
||||||
|
|
@ -41,12 +43,20 @@ pub struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
Connect { nid: NodeId, addr: Address },
|
Connect {
|
||||||
|
nid: NodeId,
|
||||||
|
addr: Address,
|
||||||
|
},
|
||||||
Routing,
|
Routing,
|
||||||
Start,
|
Start {
|
||||||
|
daemon: bool,
|
||||||
|
options: Vec<OsString>,
|
||||||
|
},
|
||||||
Status,
|
Status,
|
||||||
Stop,
|
Stop,
|
||||||
Tracking { mode: TrackingMode },
|
Tracking {
|
||||||
|
mode: TrackingMode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -71,6 +81,8 @@ impl Args for Options {
|
||||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
||||||
use lexopt::prelude::*;
|
use lexopt::prelude::*;
|
||||||
|
|
||||||
|
let mut daemon = false;
|
||||||
|
let mut options = vec![];
|
||||||
let mut parser = lexopt::Parser::from_args(args);
|
let mut parser = lexopt::Parser::from_args(args);
|
||||||
let mut op: Option<OperationName> = None;
|
let mut op: Option<OperationName> = None;
|
||||||
let mut tracking_mode = TrackingMode::default();
|
let mut tracking_mode = TrackingMode::default();
|
||||||
|
|
@ -111,6 +123,12 @@ impl Args for Options {
|
||||||
Long("nodes") if matches!(op, Some(OperationName::Tracking)) => {
|
Long("nodes") if matches!(op, Some(OperationName::Tracking)) => {
|
||||||
tracking_mode = TrackingMode::Nodes
|
tracking_mode = TrackingMode::Nodes
|
||||||
}
|
}
|
||||||
|
Long("daemon") | Short('d') if matches!(op, Some(OperationName::Start)) => {
|
||||||
|
daemon = true;
|
||||||
|
}
|
||||||
|
Value(val) if matches!(op, Some(OperationName::Start)) => {
|
||||||
|
options.push(val);
|
||||||
|
}
|
||||||
_ => return Err(anyhow!(arg.unexpected())),
|
_ => return Err(anyhow!(arg.unexpected())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +139,7 @@ impl Args for Options {
|
||||||
addr: addr.ok_or_else(|| anyhow!("an address must be provided"))?,
|
addr: addr.ok_or_else(|| anyhow!("an address must be provided"))?,
|
||||||
},
|
},
|
||||||
OperationName::Routing => Operation::Routing,
|
OperationName::Routing => Operation::Routing,
|
||||||
OperationName::Start => Operation::Start,
|
OperationName::Start => Operation::Start { daemon, options },
|
||||||
OperationName::Status => Operation::Status,
|
OperationName::Status => Operation::Status,
|
||||||
OperationName::Stop => Operation::Stop,
|
OperationName::Stop => Operation::Stop,
|
||||||
OperationName::Tracking => Operation::Tracking {
|
OperationName::Tracking => Operation::Tracking {
|
||||||
|
|
@ -145,7 +163,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
radicle::node::routing::Table::reader(profile.home.node().join(ROUTING_DB_FILE))?;
|
radicle::node::routing::Table::reader(profile.home.node().join(ROUTING_DB_FILE))?;
|
||||||
routing::run(&store)?;
|
routing::run(&store)?;
|
||||||
}
|
}
|
||||||
Operation::Start => control::start()?,
|
Operation::Start { daemon, options } => control::start(daemon, options)?,
|
||||||
Operation::Status => {
|
Operation::Status => {
|
||||||
let node = Node::new(profile.socket());
|
let node = Node::new(profile.socket());
|
||||||
control::status(&node);
|
control::status(&node);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,34 @@
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
use radicle::node::{Address, Handle as _, NodeId};
|
use radicle::node::{Address, Handle as _, NodeId};
|
||||||
use radicle::Node;
|
use radicle::Node;
|
||||||
|
|
||||||
use crate::terminal as term;
|
use crate::terminal as term;
|
||||||
|
|
||||||
pub fn start() -> anyhow::Result<()> {
|
pub fn start(daemon: bool, options: Vec<OsString>) -> anyhow::Result<()> {
|
||||||
todo!()
|
if daemon {
|
||||||
|
let home = radicle::profile::home()?;
|
||||||
|
let log = OpenOptions::new()
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(home.node().join("node.log"))?;
|
||||||
|
process::Command::new("radicle-node")
|
||||||
|
.args(options)
|
||||||
|
.stdin(process::Stdio::null())
|
||||||
|
.stdout(process::Stdio::from(log))
|
||||||
|
.stderr(process::Stdio::null())
|
||||||
|
.spawn()?;
|
||||||
|
} else {
|
||||||
|
let mut child = process::Command::new("radicle-node")
|
||||||
|
.args(options)
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
child.wait()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(node: Node) -> anyhow::Result<()> {
|
pub fn stop(node: Node) -> anyhow::Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! Logging module.
|
//! Logging module.
|
||||||
use std::io;
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
@ -18,35 +18,28 @@ impl Log for Logger {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
let target = record.target();
|
let target = record.target();
|
||||||
|
|
||||||
if record.level() == Level::Error {
|
let message = format!(
|
||||||
write(record, target, io::stderr());
|
"{:<5} {:<8} {}",
|
||||||
} else {
|
record.level(),
|
||||||
write(record, target, io::stdout());
|
target.cyan(),
|
||||||
}
|
record.args()
|
||||||
|
);
|
||||||
|
|
||||||
fn write(record: &log::Record, target: &str, mut stream: impl io::Write) {
|
let message = format!(
|
||||||
let message = format!(
|
"{} {}",
|
||||||
"{:<5} {:<8} {}",
|
Local::now().to_rfc3339_opts(SecondsFormat::Millis, true),
|
||||||
record.level(),
|
message,
|
||||||
target.cyan(),
|
);
|
||||||
record.args()
|
|
||||||
);
|
|
||||||
|
|
||||||
let message = format!(
|
let message = match record.level() {
|
||||||
"{} {}",
|
Level::Error => message.red(),
|
||||||
Local::now().to_rfc3339_opts(SecondsFormat::Millis, true),
|
Level::Warn => message.yellow(),
|
||||||
message,
|
Level::Info => message.normal(),
|
||||||
);
|
Level::Debug => message.dimmed(),
|
||||||
|
Level::Trace => message.white().dimmed(),
|
||||||
|
};
|
||||||
|
|
||||||
let message = match record.level() {
|
writeln!(io::stdout(), "{message}").expect("write shouldn't fail");
|
||||||
Level::Error => message.red(),
|
|
||||||
Level::Warn => message.yellow(),
|
|
||||||
Level::Info => message.normal(),
|
|
||||||
Level::Debug => message.dimmed(),
|
|
||||||
Level::Trace => message.white().dimmed(),
|
|
||||||
};
|
|
||||||
writeln!(stream, "{message}").expect("write shouldn't fail");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue