Add commnads to query internal service state
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
efdc8c52c7
commit
15f525299e
|
|
@ -1,4 +1,5 @@
|
||||||
use std::net;
|
use std::net;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crossbeam_channel as chan;
|
use crossbeam_channel as chan;
|
||||||
use nakamoto_net::Waker;
|
use nakamoto_net::Waker;
|
||||||
|
|
@ -6,7 +7,8 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
use crate::service;
|
use crate::service;
|
||||||
use crate::service::{CommandError, FetchLookup};
|
use crate::service::{CommandError, FetchLookup, QueryState};
|
||||||
|
use crate::service::{NodeId, Session};
|
||||||
|
|
||||||
/// An error resulting from a handle method.
|
/// An error resulting from a handle method.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
@ -88,6 +90,45 @@ impl<W: Waker> traits::Handle for Handle<W> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn routing(&self) -> Result<chan::Receiver<(Id, Vec<NodeId>)>, Error> {
|
||||||
|
let (sender, receiver) = chan::unbounded();
|
||||||
|
let query: Arc<QueryState> = Arc::new(move |state| {
|
||||||
|
for (id, nodes) in state.routing().iter() {
|
||||||
|
if sender.send((*id, nodes.iter().cloned().collect())).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let (err_sender, err_receiver) = chan::bounded(1);
|
||||||
|
self.command(service::Command::QueryState(query, err_sender))?;
|
||||||
|
err_receiver.recv()??;
|
||||||
|
|
||||||
|
Ok(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sessions(&self) -> Result<chan::Receiver<(NodeId, Session)>, Error> {
|
||||||
|
// TODO: This can be implemented once we have real peer sessions.
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inventory(&self) -> Result<chan::Receiver<Id>, Error> {
|
||||||
|
let (sender, receiver) = chan::unbounded();
|
||||||
|
let query: Arc<QueryState> = Arc::new(move |state| {
|
||||||
|
for id in state.inventory()?.iter() {
|
||||||
|
if sender.send(*id).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let (err_sender, err_receiver) = chan::bounded(1);
|
||||||
|
self.command(service::Command::QueryState(query, err_sender))?;
|
||||||
|
err_receiver.recv()??;
|
||||||
|
|
||||||
|
Ok(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
/// Ask the client to shutdown.
|
/// Ask the client to shutdown.
|
||||||
fn shutdown(self) -> Result<(), Error> {
|
fn shutdown(self) -> Result<(), Error> {
|
||||||
self.shutdown.send(())?;
|
self.shutdown.send(())?;
|
||||||
|
|
@ -114,5 +155,11 @@ pub mod traits {
|
||||||
fn command(&self, cmd: service::Command) -> Result<(), Error>;
|
fn command(&self, cmd: service::Command) -> Result<(), Error>;
|
||||||
/// Ask the client to shutdown.
|
/// Ask the client to shutdown.
|
||||||
fn shutdown(self) -> Result<(), Error>;
|
fn shutdown(self) -> Result<(), Error>;
|
||||||
|
/// Query the routing table.
|
||||||
|
fn routing(&self) -> Result<chan::Receiver<(Id, Vec<NodeId>)>, Error>;
|
||||||
|
/// Query the peer session state.
|
||||||
|
fn sessions(&self) -> Result<chan::Receiver<(NodeId, Session)>, Error>;
|
||||||
|
/// Query the inventory.
|
||||||
|
fn inventory(&self) -> Result<chan::Receiver<Id>, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,6 @@ enum DrainError {
|
||||||
InvalidCommandArg(String),
|
InvalidCommandArg(String),
|
||||||
#[error("unknown command `{0}`")]
|
#[error("unknown command `{0}`")]
|
||||||
UnknownCommand(String),
|
UnknownCommand(String),
|
||||||
#[error("invalid command")]
|
|
||||||
InvalidCommand,
|
|
||||||
#[error("client error: {0}")]
|
#[error("client error: {0}")]
|
||||||
Client(#[from] client::handle::Error),
|
Client(#[from] client::handle::Error),
|
||||||
#[error("i/o error: {0}")]
|
#[error("i/o error: {0}")]
|
||||||
|
|
@ -101,7 +99,38 @@ fn drain<H: Handle>(stream: &UnixStream, handle: &H) -> Result<(), DrainError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((cmd, _)) => return Err(DrainError::UnknownCommand(cmd.to_owned())),
|
Some((cmd, _)) => return Err(DrainError::UnknownCommand(cmd.to_owned())),
|
||||||
None => return Err(DrainError::InvalidCommand),
|
|
||||||
|
// Commands with no arguments.
|
||||||
|
None => match line.as_str() {
|
||||||
|
"routing" => match handle.routing() {
|
||||||
|
Ok(c) => {
|
||||||
|
let mut writer = LineWriter::new(stream);
|
||||||
|
|
||||||
|
for (id, seeds) in c.iter() {
|
||||||
|
let seeds = seeds
|
||||||
|
.into_iter()
|
||||||
|
.map(String::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
writeln!(writer, "{id} {seeds}",)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => return Err(DrainError::Client(e)),
|
||||||
|
},
|
||||||
|
"inventory" => match handle.inventory() {
|
||||||
|
Ok(c) => {
|
||||||
|
let mut writer = LineWriter::new(stream);
|
||||||
|
|
||||||
|
for id in c.iter() {
|
||||||
|
writeln!(writer, "{id}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => return Err(DrainError::Client(e)),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(DrainError::UnknownCommand(line));
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ pub mod reactor;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{fmt, net, net::IpAddr};
|
use std::{fmt, net, net::IpAddr};
|
||||||
|
|
||||||
use crossbeam_channel as chan;
|
use crossbeam_channel as chan;
|
||||||
|
|
@ -30,12 +31,13 @@ use crate::identity::{Doc, Id};
|
||||||
use crate::service::config::ProjectTracking;
|
use crate::service::config::ProjectTracking;
|
||||||
use crate::service::message::{Address, Announcement, AnnouncementMessage};
|
use crate::service::message::{Address, Announcement, AnnouncementMessage};
|
||||||
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
|
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
|
||||||
use crate::service::peer::{Session, SessionError, SessionState};
|
use crate::service::peer::{SessionError, SessionState};
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::{Inventory, ReadRepository, RefUpdate, WriteRepository, WriteStorage};
|
use crate::storage::{Inventory, ReadRepository, RefUpdate, WriteRepository, WriteStorage};
|
||||||
|
|
||||||
pub use crate::service::config::{Config, Network};
|
pub use crate::service::config::{Config, Network};
|
||||||
pub use crate::service::message::{Envelope, Message};
|
pub use crate::service::message::{Envelope, Message};
|
||||||
|
pub use crate::service::peer::Session;
|
||||||
|
|
||||||
use self::gossip::Gossip;
|
use self::gossip::Gossip;
|
||||||
use self::message::{InventoryAnnouncement, NodeFeatures};
|
use self::message::{InventoryAnnouncement, NodeFeatures};
|
||||||
|
|
@ -109,19 +111,44 @@ pub enum FetchResult {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Function used to query internal service state.
|
||||||
|
pub type QueryState = dyn Fn(&dyn ServiceState) -> Result<(), CommandError> + Send + Sync;
|
||||||
|
|
||||||
/// Commands sent to the service by the operator.
|
/// Commands sent to the service by the operator.
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
/// Announce repository references for given project id to peers.
|
||||||
AnnounceRefs(Id),
|
AnnounceRefs(Id),
|
||||||
|
/// Connect to node with the given address.
|
||||||
Connect(net::SocketAddr),
|
Connect(net::SocketAddr),
|
||||||
|
/// Fetch the given project from the network.
|
||||||
Fetch(Id, chan::Sender<FetchLookup>),
|
Fetch(Id, chan::Sender<FetchLookup>),
|
||||||
|
/// Track the given project.
|
||||||
Track(Id, chan::Sender<bool>),
|
Track(Id, chan::Sender<bool>),
|
||||||
|
/// Untrack the given project.
|
||||||
Untrack(Id, chan::Sender<bool>),
|
Untrack(Id, chan::Sender<bool>),
|
||||||
|
/// Query the internal service state.
|
||||||
|
QueryState(Arc<QueryState>, chan::Sender<Result<(), CommandError>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Command {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::AnnounceRefs(id) => write!(f, "AnnounceRefs({})", id),
|
||||||
|
Self::Connect(addr) => write!(f, "Connect({})", addr),
|
||||||
|
Self::Fetch(id, _) => write!(f, "Fetch({})", id),
|
||||||
|
Self::Track(id, _) => write!(f, "Track({})", id),
|
||||||
|
Self::Untrack(id, _) => write!(f, "Untrack({})", id),
|
||||||
|
Self::QueryState { .. } => write!(f, "QueryState(..)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command-related errors.
|
/// Command-related errors.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum CommandError {}
|
pub enum CommandError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Storage(#[from] storage::Error),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Service<A, S, G> {
|
pub struct Service<A, S, G> {
|
||||||
|
|
@ -161,6 +188,21 @@ pub struct Service<A, S, G> {
|
||||||
start_time: LocalTime,
|
start_time: LocalTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A, S, G> Service<A, S, G>
|
||||||
|
where
|
||||||
|
G: crypto::Signer,
|
||||||
|
{
|
||||||
|
/// Get the local node id.
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
*self.signer.public_key()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the local service time.
|
||||||
|
pub fn local_time(&self) -> LocalTime {
|
||||||
|
self.clock.local_time()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<A, S, G> Service<A, S, G>
|
impl<A, S, G> Service<A, S, G>
|
||||||
where
|
where
|
||||||
A: address_book::Store,
|
A: address_book::Store,
|
||||||
|
|
@ -201,10 +243,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_id(&self) -> NodeId {
|
|
||||||
*self.signer.public_key()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn seeds(&self, id: &Id) -> Box<dyn Iterator<Item = (&NodeId, &Session)> + '_> {
|
pub fn seeds(&self, id: &Id) -> Box<dyn Iterator<Item = (&NodeId, &Session)> + '_> {
|
||||||
if let Some(peers) = self.routing.get(id) {
|
if let Some(peers) = self.routing.get(id) {
|
||||||
Box::new(
|
Box::new(
|
||||||
|
|
@ -255,16 +293,6 @@ where
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the connected peers.
|
|
||||||
pub fn sessions(&self) -> &Sessions {
|
|
||||||
&self.sessions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current inventory.
|
|
||||||
pub fn inventory(&self) -> Result<Inventory, storage::Error> {
|
|
||||||
self.storage.inventory()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the storage instance.
|
/// Get the storage instance.
|
||||||
pub fn storage(&self) -> &S {
|
pub fn storage(&self) -> &S {
|
||||||
&self.storage
|
&self.storage
|
||||||
|
|
@ -275,36 +303,11 @@ where
|
||||||
&mut self.storage
|
&mut self.storage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a project from storage, using the local node's key.
|
|
||||||
pub fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::Error> {
|
|
||||||
self.storage.get(&self.node_id(), proj)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the local signer.
|
/// Get the local signer.
|
||||||
pub fn signer(&self) -> &G {
|
pub fn signer(&self) -> &G {
|
||||||
&self.signer
|
&self.signer
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the clock.
|
|
||||||
pub fn clock(&self) -> &RefClock {
|
|
||||||
&self.clock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the local service time.
|
|
||||||
pub fn local_time(&self) -> LocalTime {
|
|
||||||
self.clock.local_time()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get service configuration.
|
|
||||||
pub fn config(&self) -> &Config {
|
|
||||||
&self.config
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to routing table.
|
|
||||||
pub fn routing(&self) -> &Routing {
|
|
||||||
&self.routing
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get I/O reactor.
|
/// Get I/O reactor.
|
||||||
pub fn reactor(&mut self) -> &mut Reactor {
|
pub fn reactor(&mut self) -> &mut Reactor {
|
||||||
&mut self.reactor
|
&mut self.reactor
|
||||||
|
|
@ -463,6 +466,9 @@ where
|
||||||
|
|
||||||
self.reactor.broadcast(ann, peers);
|
self.reactor.broadcast(ann, peers);
|
||||||
}
|
}
|
||||||
|
Command::QueryState(query, sender) => {
|
||||||
|
sender.send(query(self)).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -789,6 +795,52 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gives read access to the service state.
|
||||||
|
pub trait ServiceState {
|
||||||
|
/// Get the connected peers.
|
||||||
|
fn sessions(&self) -> &Sessions;
|
||||||
|
/// Get the current inventory.
|
||||||
|
fn inventory(&self) -> Result<Inventory, storage::Error>;
|
||||||
|
/// Get a project from storage, using the local node's key.
|
||||||
|
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::Error>;
|
||||||
|
/// Get the clock.
|
||||||
|
fn clock(&self) -> &RefClock;
|
||||||
|
/// Get service configuration.
|
||||||
|
fn config(&self) -> &Config;
|
||||||
|
/// Get reference to routing table.
|
||||||
|
fn routing(&self) -> &Routing;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, S, G> ServiceState for Service<A, S, G>
|
||||||
|
where
|
||||||
|
G: Signer,
|
||||||
|
S: ReadStorage,
|
||||||
|
{
|
||||||
|
fn sessions(&self) -> &Sessions {
|
||||||
|
&self.sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inventory(&self) -> Result<Inventory, storage::Error> {
|
||||||
|
self.storage.inventory()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::Error> {
|
||||||
|
self.storage.get(&self.node_id(), proj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock(&self) -> &RefClock {
|
||||||
|
&self.clock
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn routing(&self) -> &Routing {
|
||||||
|
&self.routing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DisconnectReason {
|
pub enum DisconnectReason {
|
||||||
User,
|
User,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::service::message::*;
|
use crate::service::message::*;
|
||||||
use crate::service::*;
|
use crate::service::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum SessionState {
|
pub enum SessionState {
|
||||||
/// Initial peer state. For outgoing peers this
|
/// Initial peer state. For outgoing peers this
|
||||||
|
|
@ -38,7 +38,7 @@ pub enum SessionError {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A peer session. Each connected peer will have one session.
|
/// A peer session. Each connected peer will have one session.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
/// Peer address.
|
/// Peer address.
|
||||||
pub addr: net::SocketAddr,
|
pub addr: net::SocketAddr,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crossbeam_channel as chan;
|
||||||
|
|
||||||
use crate::client::handle::traits;
|
use crate::client::handle::traits;
|
||||||
use crate::client::handle::Error;
|
use crate::client::handle::Error;
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
|
|
@ -34,6 +36,18 @@ impl traits::Handle for Handle {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn routing(&self) -> Result<chan::Receiver<(Id, Vec<service::NodeId>)>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sessions(&self) -> Result<chan::Receiver<(service::NodeId, service::Session)>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inventory(&self) -> Result<chan::Receiver<Id>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn shutdown(self) -> Result<(), Error> {
|
fn shutdown(self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::service::filter::Filter;
|
||||||
use crate::service::message::*;
|
use crate::service::message::*;
|
||||||
use crate::service::peer::*;
|
use crate::service::peer::*;
|
||||||
use crate::service::reactor::Io;
|
use crate::service::reactor::Io;
|
||||||
|
use crate::service::ServiceState as _;
|
||||||
use crate::service::*;
|
use crate::service::*;
|
||||||
use crate::storage::git::Storage;
|
use crate::storage::git::Storage;
|
||||||
use crate::storage::ReadStorage;
|
use crate::storage::ReadStorage;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue