rename binaries for clarity, and first pass of connect_to_servers test util

This commit is contained in:
James Wilson
2021-07-07 12:49:03 +01:00
parent 8bf412cad9
commit f2adead2e9
25 changed files with 212 additions and 48 deletions
+25 -24
View File
@@ -1259,29 +1259,6 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "shard"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"common",
"futures",
"hex",
"http",
"log",
"primitive-types",
"serde",
"serde_json",
"simple_logger",
"soketto",
"structopt",
"thiserror",
"tokio",
"tokio-util",
"warp",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@@ -1395,7 +1372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "telemetry"
name = "telemetry_core"
version = "0.1.0"
dependencies = [
"anyhow",
@@ -1423,6 +1400,29 @@ dependencies = [
"warp",
]
[[package]]
name = "telemetry_shard"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"common",
"futures",
"hex",
"http",
"log",
"primitive-types",
"serde",
"serde_json",
"simple_logger",
"soketto",
"structopt",
"thiserror",
"tokio",
"tokio-util",
"warp",
]
[[package]]
name = "tempfile"
version = "3.2.0"
@@ -1441,6 +1441,7 @@ dependencies = [
name = "test_utils"
version = "0.1.0"
dependencies = [
"anyhow",
"futures",
"http",
"log",
+2 -2
View File
@@ -1,8 +1,8 @@
[workspace]
members = [
"common",
"telemetry",
"shard",
"telemetry_core",
"telemetry_shard",
"test_utils"
]
+2 -2
View File
@@ -2,8 +2,8 @@
This folder contains the rust crates and documentation specific to the telemetry backend. A description of the folders:
- [telemetry](./telemetry): The Telemetry Core. This aggregates data received from shards and allows UI feeds to connect and receive this information.
- [shard](./shard): A Shard. It's expected that multiple of these will run. Nodes will connect to Shard instances and send JSON telemetry to them, and Shard instances will each connect to the Telemetry Core and relay on relevant data to it.
- [telemetry_core](./telemetry_core): The Telemetry Core. This aggregates data received from shards and allows UI feeds to connect and receive this information.
- [telemetry_shard](./telemetry_shard): A Shard. It's expected that multiple of these will run. Nodes will connect to Shard instances and send JSON telemetry to them, and Shard instances will each connect to the Telemetry Core and relay on relevant data to it.
- [common](./common): common code shared between the telemetry shard and core
- [docs](./docs): Material supporting the documentation lives here
@@ -1,5 +1,5 @@
[package]
name = "telemetry"
name = "telemetry_core"
version = "0.1.0"
authors = ["Parity Technologies Ltd. <admin@parity.io>"]
edition = "2018"
@@ -1,5 +1,5 @@
[package]
name = "shard"
name = "telemetry_shard"
version = "0.1.0"
authors = ["Parity Technologies Ltd. <admin@parity.io>"]
edition = "2018"
+1
View File
@@ -7,6 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.41"
futures = "0.3.15"
http = "0.2.4"
log = "0.4.14"
+178 -16
View File
@@ -1,21 +1,183 @@
use crate::ws_client::{ Sender, Receiver };
use crate::ws_client;
use tokio::process::Command;
use tokio::io::BufReader;
use tokio::io::{ AsyncRead, AsyncBufReadExt };
use tokio::time::Duration;
use anyhow::{ anyhow, Context };
/// We either say where to conenct to, or we start the binaries
/// ourselves. Either way, we hand back a `Connection` object
/// which allows us to talk to the running instances.
pub enum Opts {
StartProcesses {
shard_command: Option<String>,
num_shards: usize,
telemetry_command: Option<String>
},
ConnectToExisting {
shard_uris: Vec<http::Uri>,
telemetry_uri: http::Uri
}
pub struct StartProcesses {
/// Optional command to run to start a shard (instead of `telemetry_shard`).
/// The `--listen` argument will be appended here and shouldn't be provided.
pub shard_command: Option<Command>,
/// Optional command to run to start a telemetry core process (instead of `telemetry_core`).
/// The `--listen` argument will be appended here and shouldn't be provided.
pub telemetry_command: Option<Command>,
/// How many connections should we establish to each shard? We'll start
/// up a shard for each entry here.
pub num_shard_connections: Vec<usize>,
/// How many connections should we establish to the telemetry_core feed?
pub num_feed_connections: usize,
}
pub struct ConnectToExisting {
/// URI to shard /submit endpoint
pub shards: Vec<ConnectionDetails>,
/// URI to core /feed endpoint
pub feed: ConnectionDetails,
}
pub struct ConnectionDetails {
pub uri: http::Uri,
pub num_connections: usize
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Can't establsih connection: {0}")]
ConnectionError(#[from] ws_client::ConnectError),
#[error("Can't establsih connection: {0}")]
JoinError(#[from] tokio::task::JoinError),
#[error("Can't establsih connection: {0}")]
IoError(#[from] std::io::Error),
#[error("Could not obtain port for process: {0}")]
ErrorObtainingPort(anyhow::Error)
}
pub struct Connection {
shard_sockets: Vec<(Sender, Receiver)>,
telemetry_socket: Vec<(Sender, Receiver)>
/// Connections to each of the shard submit URIs
pub shard_connections: Vec<Vec<(ws_client::Sender, ws_client::Receiver)>>,
/// Connections to the telemetry feed URI
pub feed_connections: Vec<(ws_client::Sender, ws_client::Receiver)>
}
impl Connection {
/// Start telemetry_core and telemetry_shard processes and establish connections to them.
pub async fn start_processes(opts: StartProcesses) -> Result<Connection, Error> {
let mut core_cmd = opts.telemetry_command
.unwrap_or(Command::new("telemetry_core"))
.arg("--listen")
.arg("127.0.0.1:0") // 0 to have a port picked by the kernel
.arg("--log")
.arg("info")
.stdout(std::process::Stdio::piped())
.stdin(std::process::Stdio::piped())
.spawn()?;
// Find out the port that this is running on
let core_port = get_port(core_cmd.stdout.take().expect("core stdout"))
.await
.map_err(|e| Error::ErrorObtainingPort(e))?;
let mut shard_cmd = opts.shard_command.unwrap_or(Command::new("telemetry_shard"));
shard_cmd
.arg("--listen")
.arg("127.0.0.1:0") // 0 to have a port picked by the kernel
.arg("--log")
.arg("info")
.arg("--core")
.arg(format!("127.0.0.1:{}", core_port))
.stdout(std::process::Stdio::piped())
.stdin(std::process::Stdio::piped());
// Start shards and find out the ports that they are running on
let mut shard_ports: Vec<u16> = vec![];
for _ in 0..opts.num_shard_connections.len() {
let mut shard_process = shard_cmd.spawn()?;
let shard_port = get_port(shard_process.stdout.take().expect("shard stdout"))
.await
.map_err(|e| Error::ErrorObtainingPort(e))?;
shard_ports.push(shard_port);
}
// now that we've started the processes, establish connections to them:
let shard_uris: Vec<http::Uri> = shard_ports
.into_iter()
.map(|port| format!("http://127.0.0.1:{}/submit", port).parse().expect("valid submit URI"))
.collect();
let feed_uri = format!("http://127.0.0.1:{}/feed", core_port)
.parse()
.expect("valid feed URI");
ConnectToExisting {
feed: ConnectionDetails {
uri: feed_uri,
num_connections: opts.num_feed_connections
},
shards: opts.num_shard_connections
.into_iter()
.zip(shard_uris)
.map(|(n, uri)| ConnectionDetails {
uri,
num_connections: n
})
.collect()
};
todo!();
}
/// Establshes the requested connections to existing processes.
pub async fn connect_to_existing(opts: ConnectToExisting) -> Result<Connection, Error> {
let shard_details = opts.shards;
// connect to shards in the background:
let shard_groups_fut = tokio::spawn(async move {
let mut shard_results = vec![];
for details in &shard_details {
shard_results.push(connect_to_uri(&details.uri, details.num_connections).await);
}
let result_shard_groups: Result<Vec<_>,_> = shard_results.into_iter().collect();
result_shard_groups
});
// In the meantime, connect feeds:
let feed_connections = connect_to_uri(&opts.feed.uri, opts.feed.num_connections).await?;
// Now feeds are done, wait until shards also connected (this will have been progressing anyway):
let shard_connections = shard_groups_fut.await??;
Ok(Connection {
shard_connections,
feed_connections,
})
}
}
async fn get_port<R: AsyncRead + Unpin>(reader: R) -> Result<u16, anyhow::Error> {
let reader = BufReader::new(reader);
let mut reader_lines = reader.lines();
loop {
let line = tokio::time::timeout(
Duration::from_secs(1),
reader_lines.next_line()
).await;
let line = match line {
// timeout expired; couldn't get port:
Err(_) => return Err(anyhow!("Timeout expired waiting to discover port")),
// Something went wrong reading line; bail:
Ok(Err(e)) => return Err(anyhow!("Could not read line from stdout: {}", e)),
// No more output; process ended? bail:
Ok(Ok(None)) => return Err(anyhow!("No more output from stdout; has the process ended?")),
// All OK, and a line is given back; phew!
Ok(Ok(Some(line))) => line
};
let (_, port_str) = match line.rsplit_once("listening on http://127.0.0.1:") {
Some(m) => m,
None => continue
};
return port_str.parse().with_context(|| "Could not parse output to port");
}
}
async fn connect_to_uri(uri: &http::Uri, num_connections: usize) -> Result<Vec<(ws_client::Sender, ws_client::Receiver)>, ws_client::ConnectError> {
let connect_futs = (0..num_connections).map(|_| ws_client::connect(uri));
let sockets: Result<Vec<_>,_> = futures::future::join_all(connect_futs).await.into_iter().collect();
sockets
}
+2 -2
View File
@@ -1,3 +1,3 @@
/// A wrapper around soketto to simplify the process of establishing connections
mod ws_client;
mod connect_to_servers;
pub mod ws_client;
pub mod connect_to_servers;