diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b96129832..29145aaa32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,7 +189,7 @@ jobs: # uses: obi1kenobi/cargo-semver-checks-action@v2 uses: n0-computer/cargo-semver-checks-action@feat-baseline with: - package: iroh, iroh-base, iroh-dns-server, iroh-net-bench, iroh-node-util, iroh-relay, iroh-net-report + package: iroh, iroh-base, iroh-dns-server, iroh-net-bench, iroh-relay, iroh-net-report baseline-rev: ${{ env.HEAD_COMMIT_SHA }} use-cache: false diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 50065d513e..1bacb65108 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -23,7 +23,7 @@ env: RUSTFLAGS: -Dwarnings RUSTDOCFLAGS: -Dwarnings SCCACHE_CACHE_SIZE: "10G" - CRATES_LIST: "iroh,iroh-node-util,iroh-net-bench,iroh-test,iroh-dns-server,iroh-relay,iroh-net-report" + CRATES_LIST: "iroh,iroh-net-bench,iroh-test,iroh-dns-server,iroh-relay,iroh-net-report" IROH_FORCE_STAGING_RELAYS: "1" jobs: diff --git a/Cargo.lock b/Cargo.lock index d80d3dd591..fd9c5b57b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,17 +574,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" -[[package]] -name = "clipboard-win" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - [[package]] name = "cobs" version = "0.2.3" @@ -597,16 +586,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" -[[package]] -name = "colored" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" -dependencies = [ - "lazy_static", - "windows-sys 0.48.0", -] - [[package]] name = "combine" version = "4.6.7" @@ -617,18 +596,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "comfy-table" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" -dependencies = [ - "crossterm", - "strum", - "strum_macros", - "unicode-width 0.2.0", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -749,15 +716,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -783,28 +741,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.6.0", - "crossterm_winapi", - "parking_lot", - "rustix", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crunchy" version = "0.2.2" @@ -893,41 +829,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.90", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.90", -] - [[package]] name = "dashmap" version = "5.5.3" @@ -1156,18 +1057,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "educe" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "either" version = "1.13.0" @@ -1211,12 +1100,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "enum-as-inner" version = "0.6.1" @@ -1229,19 +1112,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "enum-ordinalize" -version = "3.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "enumflags2" version = "0.7.10" @@ -1293,16 +1163,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - [[package]] name = "event-listener" version = "4.0.3" @@ -1335,17 +1195,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" -[[package]] -name = "fd-lock" -version = "3.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "ff" version = "0.13.0" @@ -1705,7 +1554,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.7.0", + "indexmap", "slab", "tokio", "tokio-util", @@ -1722,12 +1571,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.5" @@ -1926,15 +1769,6 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "hostname" version = "0.3.1" @@ -2020,25 +1854,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "human-time" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259822ea527bd0d5ebf3108d84e98ac4f20769e2e8b2f3ab76e1dd6e21de7f3c" -dependencies = [ - "human-time-macros", -] - -[[package]] -name = "human-time-macros" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04129f85bfd960234ed3fa53212a4904881e024322e804ac5a48da239e44a09" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "hyper" version = "1.5.1" @@ -2238,12 +2053,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.4.0" @@ -2306,17 +2115,6 @@ dependencies = [ "xmltree", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.7.0" @@ -2325,7 +2123,6 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", - "serde", ] [[package]] @@ -2636,35 +2433,6 @@ dependencies = [ "url", ] -[[package]] -name = "iroh-node-util" -version = "0.28.0" -dependencies = [ - "anyhow", - "clap", - "colored", - "comfy-table", - "derive_more", - "dirs-next", - "futures-lite 2.5.0", - "human-time", - "iroh", - "nested_enum_utils", - "quic-rpc", - "quic-rpc-derive", - "rustyline", - "serde", - "serde-error", - "serde_with", - "strum", - "tempfile", - "time", - "tokio", - "tracing", - "tracing-appender", - "tracing-subscriber", -] - [[package]] name = "iroh-quinn" version = "0.12.0" @@ -3059,18 +2827,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "nested_enum_utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f256ef99e7ac37428ef98c89bef9d84b590172de4bbfbe81b68a4cd3abadb32" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "netdev" version = "0.31.0" @@ -3215,15 +2971,6 @@ dependencies = [ "wmi", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nix" version = "0.26.4" @@ -3992,40 +3739,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "quic-rpc" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc623a188942fc875926f7baeb2cb08ed4288b64f29072656eb051e360ee7623" -dependencies = [ - "anyhow", - "derive_more", - "educe", - "flume", - "futures-lite 2.5.0", - "futures-sink", - "futures-util", - "hex", - "pin-project", - "serde", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "quic-rpc-derive" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbef4c942978f74ef296ae40d43d4375c9d730b65a582688a358108cfd5c0cf7" -dependencies = [ - "proc-macro2", - "quic-rpc", - "quote", - "syn 1.0.109", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -4103,16 +3816,6 @@ dependencies = [ "pest_derive", ] -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -4552,29 +4255,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "rustyline" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix 0.26.4", - "radix_trie", - "scopeguard", - "unicode-segmentation", - "unicode-width 0.1.14", - "utf8parse", - "winapi", -] - [[package]] name = "ryu" version = "1.0.18" @@ -4679,15 +4359,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-error" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342110fb7a5d801060c885da03bf91bfa7c7ca936deafcc64bb6706375605d47" -dependencies = [ - "serde", -] - [[package]] name = "serde_bencode" version = "0.2.4" @@ -4770,36 +4441,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.7.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "serdect" version = "0.2.0" @@ -4988,12 +4629,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - [[package]] name = "strsim" version = "0.11.1" @@ -5493,7 +5128,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.7.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -5587,18 +5222,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror 1.0.69", - "time", - "tracing-subscriber", -] - [[package]] name = "tracing-attributes" version = "0.1.28" @@ -5750,12 +5373,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-width" version = "0.1.14" diff --git a/Cargo.toml b/Cargo.toml index a21f829818..926ab00a8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "iroh/bench", "iroh-relay", "iroh-net-report", - "iroh-node-util", ] resolver = "2" diff --git a/iroh-node-util/Cargo.toml b/iroh-node-util/Cargo.toml deleted file mode 100644 index bb28e56bac..0000000000 --- a/iroh-node-util/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "iroh-node-util" -description = "Utilities to build binaries containing an iroh endpoint" -readme = "README.md" -license = "MIT OR Apache-2.0" -version = "0.28.0" -edition = "2021" -authors = ["n0 team"] -repository = "https://github.com/n0-computer/iroh" -keywords = ["quic", "networking", "holepunching", "p2p"] - -# Sadly this also needs to be updated in .github/workflows/ci.yml -rust-version = "1.76" - -[lints] -workspace = true - -[dependencies] -anyhow = "1" -clap = { version = "4", features = ["derive"], optional = true } -tokio = "1" -iroh = { version = "0.28.1", path = "../iroh" } -tempfile = "3" -strum = "0.26" -nested_enum_utils = "0.1.0" -quic-rpc = "0.15.0" -quic-rpc-derive = "0.15.0" -serde = "1" -serde-error = "0.1.3" -futures-lite = "2.5.0" -tracing = "0.1.40" - -# config -dirs-next = { version = "2.0.0", optional = true } - -# logging -derive_more = { version = "1.0.0", features = ["display"], optional = true } -rustyline = { version = "12.0.0", optional = true } -serde_with = { version = "3.7.0", optional = true } -tracing-appender = { version = "0.2.3", optional = true } -tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } - -# cli -colored = { version = "2.0.4", optional = true } -comfy-table = { version = "7.0.1", optional = true } -time = { version = "0.3", features = ["formatting"], optional = true } -human-time = { version = "0.1.6", optional = true } - -[features] -logging = ["dep:derive_more", "dep:serde_with", "dep:rustyline", "dep:tracing-appender", "dep:tracing-subscriber"] -config = ["dep:dirs-next"] -cli = ["dep:clap", "dep:colored", "dep:comfy-table", "dep:time", "dep:human-time"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "iroh_docsrs"] diff --git a/iroh-node-util/README.md b/iroh-node-util/README.md deleted file mode 100644 index c1b74200c2..0000000000 --- a/iroh-node-util/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# iroh-node-util - -This crate provides utilities to build binaries containing an iroh endpoint. - -# License - -This project is licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in this project by you, as defined in the Apache-2.0 license, -shall be dual licensed as above, without any additional terms or conditions. - diff --git a/iroh-node-util/src/cli.rs b/iroh-node-util/src/cli.rs deleted file mode 100644 index 409d601699..0000000000 --- a/iroh-node-util/src/cli.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Cli commands. -pub mod net; -pub mod node; diff --git a/iroh-node-util/src/cli/net.rs b/iroh-node-util/src/cli/net.rs deleted file mode 100644 index deedbca1e7..0000000000 --- a/iroh-node-util/src/cli/net.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Define the net subcommands. -use std::{net::SocketAddr, time::Duration}; - -use anyhow::Result; -use clap::Subcommand; -use colored::Colorize; -use comfy_table::{presets::NOTHING, Cell, Table}; -use futures_lite::{Stream, StreamExt}; -use human_time::ToHumanTimeString; -use iroh::{ - endpoint::{DirectAddrInfo, RemoteInfo}, - NodeAddr, NodeId, RelayUrl, -}; - -/// Commands to manage the iroh network. -#[derive(Subcommand, Debug, Clone)] -#[allow(clippy::large_enum_variant, missing_docs)] -pub enum NetCommands { - /// Get information about the different remote nodes. - RemoteList, - /// Get information about a particular remote node. - Remote { node_id: NodeId }, - /// Get the node addr of this node. - NodeAddr, - /// Add this node addr to the known nodes. - AddNodeAddr { - node_id: NodeId, - relay: Option, - addresses: Vec, - }, - /// Get the relay server we are connected to. - HomeRelay, -} - -impl NetCommands { - /// Runs the net command given the iroh client. - pub async fn run(self, client: &crate::rpc::client::net::Client) -> Result<()> { - match self { - Self::RemoteList => { - let connections = client.remote_info_iter().await?; - let timestamp = time::OffsetDateTime::now_utc() - .format(&time::format_description::well_known::Rfc2822) - .unwrap_or_else(|_| String::from("failed to get current time")); - - println!( - " {}: {}\n\n{}", - "current time".bold(), - timestamp, - fmt_remote_infos(connections).await - ); - } - Self::Remote { node_id } => { - let info = client.remote_info(node_id).await?; - match info { - Some(info) => println!("{}", fmt_info(info)), - None => println!("Not Found"), - } - } - Self::NodeAddr => { - let addr = client.node_addr().await?; - println!("Node ID: {}", addr.node_id); - let relay = addr - .info - .relay_url - .map(|s| s.to_string()) - .unwrap_or_else(|| "Not Available".to_string()); - println!("Home Relay: {}", relay); - println!("Direct Addresses ({}):", addr.info.direct_addresses.len()); - for da in &addr.info.direct_addresses { - println!(" {}", da); - } - } - Self::AddNodeAddr { - node_id, - relay, - addresses, - } => { - let mut addr = NodeAddr::new(node_id).with_direct_addresses(addresses); - if let Some(relay) = relay { - addr = addr.with_relay_url(relay); - } - client.add_node_addr(addr).await?; - } - Self::HomeRelay => { - let relay = client.home_relay().await?; - let relay = relay - .map(|s| s.to_string()) - .unwrap_or_else(|| "Not Available".to_string()); - println!("Home Relay: {}", relay); - } - } - Ok(()) - } -} - -/// Formats the remote information into a `Table`. -async fn fmt_remote_infos( - mut infos: impl Stream> + Unpin, -) -> String { - let mut table = Table::new(); - table.load_preset(NOTHING).set_header( - ["node id", "relay", "conn type", "latency", "last used"] - .into_iter() - .map(bold_cell), - ); - while let Some(Ok(info)) = infos.next().await { - let node_id: Cell = info.node_id.to_string().into(); - let relay_url = info - .relay_url - .map_or(String::new(), |url_info| url_info.relay_url.to_string()) - .into(); - let conn_type = info.conn_type.to_string().into(); - let latency = match info.latency { - Some(latency) => latency.to_human_time_string(), - None => String::from("unknown"), - } - .into(); - let last_used = info - .last_used - .map(fmt_how_long_ago) - .map(Cell::new) - .unwrap_or_else(never); - table.add_row([node_id, relay_url, conn_type, latency, last_used]); - } - table.to_string() -} - -/// Formats the remote information into a `String`. -fn fmt_info(info: RemoteInfo) -> String { - let RemoteInfo { - node_id, - relay_url, - addrs, - conn_type, - latency, - last_used, - } = info; - let timestamp = time::OffsetDateTime::now_utc() - .format(&time::format_description::well_known::Rfc2822) - .unwrap_or_else(|_| String::from("failed to get current time")); - let mut table = Table::new(); - table.load_preset(NOTHING); - table.add_row([bold_cell("current time"), timestamp.into()]); - table.add_row([bold_cell("node id"), node_id.to_string().into()]); - let relay_url = relay_url - .map(|r| r.relay_url.to_string()) - .unwrap_or_else(|| String::from("unknown")); - table.add_row([bold_cell("relay url"), relay_url.into()]); - table.add_row([bold_cell("connection type"), conn_type.to_string().into()]); - table.add_row([bold_cell("latency"), fmt_latency(latency).into()]); - table.add_row([ - bold_cell("last used"), - last_used - .map(fmt_how_long_ago) - .map(Cell::new) - .unwrap_or_else(never), - ]); - table.add_row([bold_cell("known addresses"), addrs.len().into()]); - - let general_info = table.to_string(); - - let addrs_info = fmt_addrs(addrs); - - format!("{general_info}\n\n{addrs_info}") -} - -/// Formats the [`DirectAddrInfo`] into a [`Table`]. -fn direct_addr_row(info: DirectAddrInfo) -> comfy_table::Row { - let DirectAddrInfo { - addr, - latency, - last_control, - last_payload, - last_alive, - .. - } = info; - - let last_control = match last_control { - None => never(), - Some((how_long_ago, kind)) => { - format!("{kind} ( {} )", fmt_how_long_ago(how_long_ago)).into() - } - }; - let last_payload = last_payload - .map(fmt_how_long_ago) - .map(Cell::new) - .unwrap_or_else(never); - - let last_alive = last_alive - .map(fmt_how_long_ago) - .map(Cell::new) - .unwrap_or_else(never); - - [ - addr.into(), - fmt_latency(latency).into(), - last_control, - last_payload, - last_alive, - ] - .into() -} - -/// Formats a collection o [`DirectAddrInfo`] into a [`Table`]. -fn fmt_addrs(addrs: Vec) -> comfy_table::Table { - let mut table = Table::new(); - table.load_preset(NOTHING).set_header( - vec!["addr", "latency", "last control", "last data", "last alive"] - .into_iter() - .map(bold_cell), - ); - table.add_rows(addrs.into_iter().map(direct_addr_row)); - table -} - -/// Creates a cell with the dimmed text "never". -fn never() -> Cell { - Cell::new("never").add_attribute(comfy_table::Attribute::Dim) -} - -/// Formats a [`Duration`] into a human-readable `String`. -fn fmt_how_long_ago(duration: Duration) -> String { - duration - .to_human_time_string() - .split_once(',') - .map(|(first, _rest)| first) - .unwrap_or("-") - .to_string() -} - -/// Formats the latency into a human-readable `String`. -fn fmt_latency(latency: Option) -> String { - match latency { - Some(latency) => latency.to_human_time_string(), - None => String::from("unknown"), - } -} - -/// Creates a bold cell with the given text. -fn bold_cell(s: &str) -> Cell { - Cell::new(s).add_attribute(comfy_table::Attribute::Bold) -} diff --git a/iroh-node-util/src/cli/node.rs b/iroh-node-util/src/cli/node.rs deleted file mode 100644 index aabca5ce3b..0000000000 --- a/iroh-node-util/src/cli/node.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Node commands -use clap::Subcommand; - -use crate::rpc::client::node; - -/// Commands to manage the iroh RPC. -#[derive(Subcommand, Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum NodeCommands { - /// Get statistics and metrics from the running node. - Stats, - /// Get status of the running node. - Status, - /// Shutdown the running node. - Shutdown { - /// Shutdown mode. - /// - /// Hard shutdown will immediately terminate the process, soft shutdown will wait - /// for all connections to close. - #[clap(long, default_value_t = false)] - force: bool, - }, -} - -impl NodeCommands { - /// Run the RPC command given the iroh client and the console environment. - pub async fn run(self, node: &node::Client) -> anyhow::Result<()> { - match self { - Self::Stats => { - let stats = node.stats().await?; - for (name, details) in stats.iter() { - println!( - "{:23} : {:>6} ({})", - name, details.value, details.description - ); - } - Ok(()) - } - Self::Shutdown { force } => { - node.shutdown(force).await?; - Ok(()) - } - Self::Status => { - let response = node.status().await?; - println!("Listening addresses: {:#?}", response.listen_addrs); - println!("Node ID: {}", response.addr.node_id); - println!("Version: {}", response.version); - if let Some(addr) = response.rpc_addr { - println!("RPC Addr: {}", addr); - } - Ok(()) - } - } - } -} diff --git a/iroh-node-util/src/config.rs b/iroh-node-util/src/config.rs deleted file mode 100644 index 77d6ba6866..0000000000 --- a/iroh-node-util/src/config.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Utilities to get default paths for configuration, data, and cache directories. -use std::{env, path::PathBuf}; - -use anyhow::{anyhow, Result}; - -/// Returns the path to the user's config directory for the given binary. -/// -/// This is determined by the following steps: -/// - If the environment variable `_CONFIG_DIR` is set, return that. -/// - If the operating environment provides a config directory, return `$CONFIG_DIR/`. -/// - Otherwise, return an error. -/// -/// The default directories are as follows: -/// -/// | Platform | Value | Example | -/// | -------- | ------------------------------------- | -------------------------------- | -/// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config/iroh | /home/alice/.config/iroh | -/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh | -/// | Windows | `{FOLDERID_RoamingAppData}`/iroh | C:\Users\Alice\AppData\Roaming\iroh | -pub fn config_root(bin: &'static str) -> Result { - let env_config_dir = format!("{}_CONFIG_DIR", bin.to_uppercase()); - if let Some(val) = env::var_os(env_config_dir) { - return Ok(PathBuf::from(val)); - } - let cfg = dirs_next::config_dir() - .ok_or_else(|| anyhow!("operating environment provides no directory for configuration"))?; - Ok(cfg.join(bin)) -} - -/// Returns the path to the user's data directory for the given binary. -/// -/// This is determined by the following steps: -/// - If the environment variable `_DATA_DIR` is set, return that. -/// - If the operating environment provides a data directory, return `$DATA_DIR/`. -/// - Otherwise, return an error. -/// -/// The default directories are as follows: -/// -/// | Platform | Value | Example | -/// | -------- | --------------------------------------------- | ---------------------------------------- | -/// | Linux | `$XDG_DATA_HOME`/iroh or `$HOME`/.local/share/iroh | /home/alice/.local/share/iroh | -/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh | -/// | Windows | `{FOLDERID_RoamingAppData}/iroh` | C:\Users\Alice\AppData\Roaming\iroh | -pub fn data_root(bin: &'static str) -> Result { - let env_data_dir = format!("{}_DATA_DIR", bin.to_uppercase()); - let path = if let Some(val) = env::var_os(env_data_dir) { - PathBuf::from(val) - } else { - let path = dirs_next::data_dir().ok_or_else(|| { - anyhow!("operating environment provides no directory for application data") - })?; - path.join(bin) - }; - let path = if !path.is_absolute() { - std::env::current_dir()?.join(path) - } else { - path - }; - Ok(path) -} - -/// Returns the path to the user's cache directory for the given binary. -/// -/// This is determined by the following steps: -/// - If the environment variable `_CACHE_DIR` is set, return that. -/// - If the operating environment provides a cache directory, return `$CACHE_DIR/`. -/// - Otherwise, return an error. -/// -/// The default directories are as follows: -/// -/// | Platform | Value | Example | -/// | -------- | --------------------------------------------- | ---------------------------------------- | -/// | Linux | `$XDG_CACHE_HOME`/iroh or `$HOME`/.cache/iroh | /home/.cache/iroh | -/// | macOS | `$HOME`/Library/Caches/iroh | /Users/Alice/Library/Caches/iroh | -/// | Windows | `{FOLDERID_LocalAppData}/iroh` | C:\Users\Alice\AppData\Roaming\iroh | -pub fn cache_root(bin: &'static str) -> Result { - let env_cache_dir = format!("{}_CACHE_DIR", bin.to_uppercase()); - if let Some(val) = env::var_os(env_cache_dir) { - return Ok(PathBuf::from(val)); - } - let path = dirs_next::cache_dir().ok_or_else(|| { - anyhow!("operating environment provides no directory for application data") - })?; - Ok(path.join(bin)) -} diff --git a/iroh-node-util/src/fs.rs b/iroh-node-util/src/fs.rs deleted file mode 100644 index 575ddcead8..0000000000 --- a/iroh-node-util/src/fs.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Utilities for filesystem operations. - -use std::path::PathBuf; - -use anyhow::Context; -use iroh::key::SecretKey; -use tokio::io::AsyncWriteExt; - -/// Loads a [`SecretKey`] from the provided file, or stores a newly generated one -/// at the given location. -pub async fn load_secret_key(key_path: PathBuf) -> anyhow::Result { - if key_path.exists() { - let keystr = tokio::fs::read(key_path).await?; - let secret_key = SecretKey::try_from_openssh(keystr).context("invalid keyfile")?; - Ok(secret_key) - } else { - let secret_key = SecretKey::generate(); - let ser_key = secret_key.to_openssh()?; - - // Try to canonicalize if possible - let key_path = key_path.canonicalize().unwrap_or(key_path); - let key_path_parent = key_path.parent().ok_or_else(|| { - anyhow::anyhow!("no parent directory found for '{}'", key_path.display()) - })?; - tokio::fs::create_dir_all(&key_path_parent).await?; - - // write to tempfile - let (file, temp_file_path) = tempfile::NamedTempFile::new_in(key_path_parent) - .context("unable to create tempfile")? - .into_parts(); - let mut file = tokio::fs::File::from_std(file); - file.write_all(ser_key.as_bytes()) - .await - .context("unable to write keyfile")?; - file.flush().await?; - drop(file); - - // move file - tokio::fs::rename(temp_file_path, key_path) - .await - .context("failed to rename keyfile")?; - - Ok(secret_key) - } -} diff --git a/iroh-node-util/src/lib.rs b/iroh-node-util/src/lib.rs deleted file mode 100644 index f8c8853d68..0000000000 --- a/iroh-node-util/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Utilities for building iroh nodes. -#![deny(missing_docs, rustdoc::broken_intra_doc_links)] -#![cfg_attr(iroh_docsrs, feature(doc_cfg))] -#[cfg_attr(iroh_docsrs, doc(cfg(feature = "cli")))] -#[cfg(feature = "cli")] -pub mod cli; -#[cfg_attr(iroh_docsrs, doc(cfg(feature = "config")))] -#[cfg(feature = "config")] -pub mod config; -#[cfg_attr(iroh_docsrs, doc(cfg(feature = "logging")))] -#[cfg(feature = "logging")] -pub mod logging; -pub mod rpc; - -pub mod fs; - -use std::path::PathBuf; - -use anyhow::Context; -use iroh::key::SecretKey; -use tokio::io::AsyncWriteExt; - -/// Loads a [`SecretKey`] from the provided file, or stores a newly generated one -/// at the given location. -pub async fn load_secret_key(key_path: PathBuf) -> anyhow::Result { - if key_path.exists() { - let keystr = tokio::fs::read(key_path).await?; - let secret_key = SecretKey::try_from_openssh(keystr).context("invalid keyfile")?; - Ok(secret_key) - } else { - let secret_key = SecretKey::generate(); - let ser_key = secret_key.to_openssh()?; - - // Try to canonicalize if possible - let key_path = key_path.canonicalize().unwrap_or(key_path); - let key_path_parent = key_path.parent().ok_or_else(|| { - anyhow::anyhow!("no parent directory found for '{}'", key_path.display()) - })?; - tokio::fs::create_dir_all(&key_path_parent).await?; - - // write to tempfile - let (file, temp_file_path) = tempfile::NamedTempFile::new_in(key_path_parent) - .context("unable to create tempfile")? - .into_parts(); - let mut file = tokio::fs::File::from_std(file); - file.write_all(ser_key.as_bytes()) - .await - .context("unable to write keyfile")?; - file.flush().await?; - drop(file); - - // move file - tokio::fs::rename(temp_file_path, key_path) - .await - .context("failed to rename keyfile")?; - - Ok(secret_key) - } -} diff --git a/iroh-node-util/src/logging.rs b/iroh-node-util/src/logging.rs deleted file mode 100644 index e657ef461b..0000000000 --- a/iroh-node-util/src/logging.rs +++ /dev/null @@ -1,192 +0,0 @@ -//! Utilities for logging -use std::{env, path::Path}; - -use derive_more::FromStr; -use serde::{Deserialize, Serialize}; -use serde_with::{DeserializeFromStr, SerializeDisplay}; -use tracing_appender::{non_blocking, rolling}; -use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, Layer}; - -/// `RUST_LOG` statement used by default in file logging. -// rustyline is annoying -pub(crate) const DEFAULT_FILE_RUST_LOG: &str = "rustyline=warn,debug"; - -/// Parse `_FILE_RUST_LOG` as [`tracing_subscriber::EnvFilter`]. Returns `None` if not -/// present. -pub fn env_file_rust_log(bin: &'static str) -> Option> { - let env_file_rust_log = format!("{}_FILE_RUST_LOG", bin.to_uppercase()); - match env::var(env_file_rust_log) { - Ok(s) => Some(crate::logging::EnvFilter::from_str(&s).map_err(Into::into)), - Err(e) => match e { - env::VarError::NotPresent => None, - e @ env::VarError::NotUnicode(_) => Some(Err(e.into())), - }, - } -} - -/// Initialize logging both in the terminal and file based. -/// -/// The terminal based logging layer will: -/// - use the default [`fmt::format::Format`]. -/// - log to [`std::io::Stderr`] -/// -/// The file base logging layer will: -/// - use the default [`fmt::format::Format`] save for: -/// - including line numbers. -/// - not using ansi colors. -/// - create log files in the [`FileLogging::dir`] directory. If not provided, the `logs` dir -/// inside the given `logs_root` is used. -/// - rotate files every [`FileLogging::rotation`]. -/// - keep at most [`FileLogging::max_files`] log files. -/// - use the filtering defined by [`FileLogging::rust_log`]. When not provided, the default -/// `DEFAULT_FILE_RUST_LOG` is used. -/// - create log files with the name `iroh-.log` (ex: iroh-2024-02-02.log) -pub fn init_terminal_and_file_logging( - file_log_config: &FileLogging, - logs_root: &Path, -) -> anyhow::Result { - let terminal_layer = fmt::layer() - .with_writer(std::io::stderr) - .with_filter(tracing_subscriber::EnvFilter::from_default_env()); - let (file_layer, guard) = { - let FileLogging { - rust_log, - max_files, - rotation, - dir, - } = file_log_config; - - let filter = rust_log.layer(); - - let (file_logger, guard) = { - let file_appender = if *max_files == 0 || &filter.to_string() == "off" { - fmt::writer::OptionalWriter::none() - } else { - let rotation = match rotation { - Rotation::Hourly => rolling::Rotation::HOURLY, - Rotation::Daily => rolling::Rotation::DAILY, - Rotation::Never => rolling::Rotation::NEVER, - }; - - // prefer the directory set in the config file over the default - let logs_path = dir.clone().unwrap_or_else(|| logs_root.join("logs")); - - let file_appender = rolling::Builder::new() - .rotation(rotation) - .max_log_files(*max_files) - .filename_prefix("iroh") - .filename_suffix("log") - .build(logs_path)?; - fmt::writer::OptionalWriter::some(file_appender) - }; - non_blocking(file_appender) - }; - - let layer = fmt::Layer::new() - .with_ansi(false) - .with_line_number(true) - .with_writer(file_logger) - .with_filter(filter); - (layer, guard) - }; - tracing_subscriber::registry() - .with(file_layer) - .with(terminal_layer) - .try_init()?; - Ok(guard) -} - -/// Initialize logging in the terminal. -/// -/// This will: -/// - use the default [`fmt::format::Format`]. -/// - log to [`std::io::Stderr`] -pub fn init_terminal_logging() -> anyhow::Result<()> { - let terminal_layer = fmt::layer() - .with_writer(std::io::stderr) - .with_filter(tracing_subscriber::EnvFilter::from_default_env()); - tracing_subscriber::registry() - .with(terminal_layer) - .try_init()?; - Ok(()) -} - -/// Configuration for the logfiles. -// Please note that this is documented in the `iroh.computer` repository under -// `src/app/docs/reference/config/page.mdx`. Any changes to this need to be updated there. -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] -#[serde(default, deny_unknown_fields)] -pub struct FileLogging { - /// RUST_LOG directive to filter file logs. - pub rust_log: EnvFilter, - /// Maximum number of files to keep. - pub max_files: usize, - /// How often should a new log file be produced. - pub rotation: Rotation, - /// Where to store log files. - pub dir: Option, -} - -impl Default for FileLogging { - fn default() -> Self { - Self { - rust_log: EnvFilter::default(), - max_files: 4, - rotation: Rotation::default(), - dir: None, - } - } -} - -/// Wrapper to obtain a [`tracing_subscriber::EnvFilter`] that satisfies required bounds. -#[derive( - Debug, Clone, PartialEq, Eq, SerializeDisplay, DeserializeFromStr, derive_more::Display, -)] -#[display("{_0}")] -pub struct EnvFilter(String); - -impl FromStr for EnvFilter { - type Err = ::Err; - - fn from_str(s: &str) -> Result { - // validate the RUST_LOG statement - let _valid_env = tracing_subscriber::EnvFilter::from_str(s)?; - Ok(EnvFilter(s.into())) - } -} - -impl Default for EnvFilter { - fn default() -> Self { - Self(DEFAULT_FILE_RUST_LOG.into()) - } -} - -impl EnvFilter { - pub(crate) fn layer(&self) -> tracing_subscriber::EnvFilter { - tracing_subscriber::EnvFilter::from_str(&self.0).expect("validated RUST_LOG statement") - } -} - -/// How often should a new file be created for file logs. -/// -/// Akin to [`tracing_appender::rolling::Rotation`]. -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)] -#[serde(rename_all = "lowercase")] -#[allow(missing_docs)] -pub enum Rotation { - #[default] - Hourly, - Daily, - Never, -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Tests that the default file logging `RUST_LOG` statement produces a valid layer. - #[test] - fn test_default_file_rust_log() { - let _ = EnvFilter::default().layer(); - } -} diff --git a/iroh-node-util/src/rpc.rs b/iroh-node-util/src/rpc.rs deleted file mode 100644 index 359ff66ac6..0000000000 --- a/iroh-node-util/src/rpc.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! RPC client, server and protocol to control iroh endpoints and iroh nodes -pub mod client; -pub mod proto; -pub mod server; diff --git a/iroh-node-util/src/rpc/client.rs b/iroh-node-util/src/rpc/client.rs deleted file mode 100644 index c261fc0b80..0000000000 --- a/iroh-node-util/src/rpc/client.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Client to interact with iroh nodes and endpoints. -use anyhow::Result; -use futures_lite::{Stream, StreamExt}; - -pub mod net; -pub mod node; - -fn flatten( - s: impl Stream, E2>>, -) -> impl Stream> -where - E1: std::error::Error + Send + Sync + 'static, - E2: std::error::Error + Send + Sync + 'static, -{ - s.map(|res| match res { - Ok(Ok(res)) => Ok(res), - Ok(Err(err)) => Err(err.into()), - Err(err) => Err(err.into()), - }) -} diff --git a/iroh-node-util/src/rpc/client/net.rs b/iroh-node-util/src/rpc/client/net.rs deleted file mode 100644 index cf25c7642e..0000000000 --- a/iroh-node-util/src/rpc/client/net.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! API to manage the iroh networking stack. -//! -//! The main entry point is the [`Client`]. -//! -//! The client can be used to get information about the node, such as the -//! [node id](Client::node_id) or [node address](Client::node_addr). -//! -//! It can also be used to provide additional information to the node, e.g. -//! using the [add_node_addr](Client::add_node_addr) method. -use std::net::SocketAddr; - -use anyhow::Result; -use futures_lite::{Stream, StreamExt}; -use iroh::{endpoint::RemoteInfo, relay::RelayUrl, NodeAddr, NodeId}; -use quic_rpc::RpcClient; -use serde::{Deserialize, Serialize}; - -use super::flatten; -use crate::rpc::proto::{ - net::{ - AddAddrRequest, AddrRequest, IdRequest, RelayRequest, RemoteInfoRequest, - RemoteInfoResponse, RemoteInfosIterRequest, - }, - RpcService, -}; - -/// Iroh net Client. -/// -/// Cheaply clonable and threadsafe. Use the iroh `net::Client` to access the -/// iroh net methods from a different thread, process, or remote machine. -/// -/// The `node::Client` api allows you to get information *about* the iroh node, -/// its status, and connection status to other nodes. It also allows you to -/// provide address information about *other* nodes to your node. -#[derive(Debug, Clone)] -#[repr(transparent)] -pub struct Client { - pub(super) rpc: RpcClient, -} - -impl Client { - /// Creates a new net client - pub fn new(rpc: RpcClient) -> Self { - Self { rpc } - } - - /// Fetches information about currently known remote nodes. - /// - /// This streams a *current snapshot*. It does not keep the stream open after finishing - /// transferring the snapshot. - /// - /// See also [`Endpoint::remote_info_iter`](iroh::Endpoint::remote_info_iter). - pub async fn remote_info_iter(&self) -> Result>> { - let stream = self.rpc.server_streaming(RemoteInfosIterRequest {}).await?; - Ok(flatten(stream).map(|res| res.map(|res| res.info))) - } - - /// Fetches node information about a remote iroh node identified by its [`NodeId`]. - /// - /// See also [`Endpoint::remote_info`](iroh::Endpoint::remote_info). - pub async fn remote_info(&self, node_id: NodeId) -> Result> { - let RemoteInfoResponse { info } = self.rpc.rpc(RemoteInfoRequest { node_id }).await??; - Ok(info) - } - - /// Fetches the node id of this node. - /// - /// See also [`Endpoint::node_id`](iroh::Endpoint::node_id). - pub async fn node_id(&self) -> Result { - let id = self.rpc.rpc(IdRequest).await??; - Ok(id) - } - - /// Fetches the [`NodeAddr`] for this node. - /// - /// See also [`Endpoint::node_addr`](iroh::Endpoint::node_addr). - pub async fn node_addr(&self) -> Result { - let addr = self.rpc.rpc(AddrRequest).await??; - Ok(addr) - } - - /// Adds a known node address to this node. - /// - /// See also [`Endpoint::add_node_addr`](iroh::Endpoint::add_node_addr). - pub async fn add_node_addr(&self, addr: NodeAddr) -> Result<()> { - self.rpc.rpc(AddAddrRequest { addr }).await??; - Ok(()) - } - - /// Returns the relay server we are connected to. - /// - /// See also [`Endpoint::home_relay`](iroh::Endpoint::home_relay). - pub async fn home_relay(&self) -> Result> { - let relay = self.rpc.rpc(RelayRequest).await??; - Ok(relay) - } -} - -/// The response to a version request -#[derive(Debug, Serialize, Deserialize)] -pub struct NodeStatus { - /// The node id and socket addresses of this node. - pub addr: NodeAddr, - /// The bound listening addresses of the node - pub listen_addrs: Vec, - /// The version of the node - pub version: String, - /// RPC address, if currently listening. - pub rpc_addr: Option, -} diff --git a/iroh-node-util/src/rpc/client/node.rs b/iroh-node-util/src/rpc/client/node.rs deleted file mode 100644 index 8d6d7b8a62..0000000000 --- a/iroh-node-util/src/rpc/client/node.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Client to interact with an iroh node. -//! -//! The main entry point is [`Client`]. -use std::collections::BTreeMap; - -use anyhow::Result; -use quic_rpc::RpcClient; - -use super::net::NodeStatus; -use crate::rpc::proto::{node::*, RpcService}; - -/// Client to interact with an iroh node. -#[derive(Debug, Clone)] -#[repr(transparent)] -pub struct Client { - pub(super) rpc: RpcClient, -} - -impl Client { - /// Creates a new node client - pub fn new(rpc: RpcClient) -> Self { - Self { rpc } - } - - /// Shuts down the node. - /// - /// If `force` is true, the node will be shut down instantly without - /// waiting for things to stop gracefully. - pub async fn shutdown(&self, force: bool) -> Result<()> { - self.rpc.rpc(ShutdownRequest { force }).await?; - Ok(()) - } - - /// Fetches statistics of the running node. - pub async fn stats(&self) -> Result> { - let res = self.rpc.rpc(StatsRequest {}).await??; - Ok(res.stats) - } - - /// Fetches status information about this node. - pub async fn status(&self) -> Result { - let response = self.rpc.rpc(StatusRequest).await??; - Ok(response) - } -} diff --git a/iroh-node-util/src/rpc/proto.rs b/iroh-node-util/src/rpc/proto.rs deleted file mode 100644 index 23ea56af4f..0000000000 --- a/iroh-node-util/src/rpc/proto.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! RPC protocol definitions for controlling iroh endpoints and iroh nodes -use nested_enum_utils::enum_conversions; -use serde::{Deserialize, Serialize}; - -pub mod net; -pub mod node; - -pub(crate) type RpcError = serde_error::Error; -pub(crate) type RpcResult = Result; - -/// Request, either net or node -#[derive(Debug, Serialize, Deserialize)] -#[enum_conversions] -#[allow(missing_docs)] -pub enum Request { - Net(net::Request), - Node(node::Request), -} - -/// Response, either net or node -#[derive(Debug, Serialize, Deserialize)] -#[enum_conversions] -#[allow(missing_docs)] -pub enum Response { - Net(net::Response), - Node(node::Response), -} - -/// The RPC service -#[derive(Debug, Clone, Copy)] -pub struct RpcService {} - -impl quic_rpc::Service for RpcService { - type Req = Request; - type Res = Response; -} diff --git a/iroh-node-util/src/rpc/proto/net.rs b/iroh-node-util/src/rpc/proto/net.rs deleted file mode 100644 index 790103948e..0000000000 --- a/iroh-node-util/src/rpc/proto/net.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! RPC calls to control an iroh endpoint. -#![allow(missing_docs)] -use iroh::{endpoint::RemoteInfo, key::PublicKey, relay::RelayUrl, NodeAddr, NodeId}; -use nested_enum_utils::enum_conversions; -use quic_rpc_derive::rpc_requests; -use serde::{Deserialize, Serialize}; - -use super::{RpcResult, RpcService}; - -#[derive(strum::Display, Debug, Serialize, Deserialize)] -#[enum_conversions(super::Request)] -#[rpc_requests(RpcService)] -pub enum Request { - #[rpc(response = RpcResult)] - Id(IdRequest), - #[rpc(response = RpcResult)] - Addr(AddrRequest), - #[rpc(response = RpcResult<()>)] - AddAddr(AddAddrRequest), - #[rpc(response = RpcResult>)] - Relay(RelayRequest), - #[server_streaming(response = RpcResult)] - RemoteInfosIter(RemoteInfosIterRequest), - #[rpc(response = RpcResult)] - RemoteInfo(RemoteInfoRequest), - #[server_streaming(response = WatchResponse)] - Watch(NodeWatchRequest), -} - -#[allow(missing_docs)] -#[derive(strum::Display, Debug, Serialize, Deserialize)] -#[enum_conversions(super::Response)] -pub enum Response { - Id(RpcResult), - Addr(RpcResult), - Relay(RpcResult>), - RemoteInfosIter(RpcResult), - RemoteInfo(RpcResult), - Watch(WatchResponse), - Unit(RpcResult<()>), -} - -/// List network path information about all the remote nodes known by this node. -/// -/// There may never have been connections to these nodes, and connections may not even be -/// possible. Nodes can also become known due to discovery mechanisms -/// or be added manually. -#[derive(Debug, Serialize, Deserialize)] -pub struct RemoteInfosIterRequest; - -/// A response to a [`Request::RemoteInfosIter`]. -#[derive(Debug, Serialize, Deserialize)] -pub struct RemoteInfosIterResponse { - /// Information about a node. - pub info: RemoteInfo, -} - -/// Get information about a specific remote node. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RemoteInfoRequest { - /// The node identifier - pub node_id: PublicKey, -} - -/// A response to a [`Request::RemoteInfo`] request -#[derive(Debug, Serialize, Deserialize)] -pub struct RemoteInfoResponse { - /// Information about a node - pub info: Option, -} - -/// A request to get information the identity of the node. -#[derive(Serialize, Deserialize, Debug)] -pub struct IdRequest; - -#[derive(Serialize, Deserialize, Debug)] -pub struct AddrRequest; - -#[derive(Serialize, Deserialize, Debug)] -pub struct AddAddrRequest { - pub addr: NodeAddr, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RelayRequest; - -/// A request to watch for the node status -#[derive(Serialize, Deserialize, Debug)] -pub struct NodeWatchRequest; - -/// The response to a watch request -#[derive(Serialize, Deserialize, Debug)] -pub struct WatchResponse { - /// The version of the node - pub version: String, -} diff --git a/iroh-node-util/src/rpc/proto/node.rs b/iroh-node-util/src/rpc/proto/node.rs deleted file mode 100644 index 353334cb54..0000000000 --- a/iroh-node-util/src/rpc/proto/node.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! RPC calls to control a generic node. -use std::collections::BTreeMap; - -use nested_enum_utils::enum_conversions; -use quic_rpc_derive::rpc_requests; -use serde::{Deserialize, Serialize}; - -use super::{RpcResult, RpcService}; -use crate::rpc::client::net::NodeStatus; - -#[allow(missing_docs)] -#[derive(strum::Display, Debug, Serialize, Deserialize)] -#[enum_conversions(super::Request)] -#[rpc_requests(RpcService)] -pub enum Request { - #[rpc(response = RpcResult)] - Status(StatusRequest), - #[rpc(response = RpcResult)] - Stats(StatsRequest), - #[rpc(response = ())] - Shutdown(ShutdownRequest), -} - -#[allow(missing_docs)] -#[derive(strum::Display, Debug, Serialize, Deserialize)] -#[enum_conversions(super::Response)] -pub enum Response { - Status(RpcResult), - Stats(RpcResult), - Shutdown(()), -} - -/// A request to shutdown the node -#[derive(Serialize, Deserialize, Debug)] -pub struct ShutdownRequest { - /// Force shutdown - pub force: bool, -} - -/// A request to get information about the status of the node. -#[derive(Serialize, Deserialize, Debug)] -pub struct StatusRequest; - -/// Get stats for the running Iroh node -#[derive(Serialize, Deserialize, Debug)] -pub struct StatsRequest {} - -/// Counter stats -#[derive(Serialize, Deserialize, Debug)] -pub struct CounterStats { - /// The counter value - pub value: u64, - /// The counter description - pub description: String, -} - -/// Response to [`StatsRequest`] -#[derive(Serialize, Deserialize, Debug)] -pub struct StatsResponse { - /// Map of statistics - pub stats: BTreeMap, -} diff --git a/iroh-node-util/src/rpc/server.rs b/iroh-node-util/src/rpc/server.rs deleted file mode 100644 index fc7681e671..0000000000 --- a/iroh-node-util/src/rpc/server.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! Server implementation to handle node and net rpc requests -use std::{collections::BTreeMap, net::SocketAddr, sync::Arc, time::Duration}; - -use anyhow::{anyhow, Result}; -use futures_lite::{Stream, StreamExt}; -use iroh::{Endpoint, NodeAddr, NodeId, RelayUrl}; -use quic_rpc::server::{ChannelTypes, RpcChannel, RpcServerError}; -use tracing::{debug, info}; - -use super::proto::{net, node::CounterStats, Request}; -use crate::rpc::{ - client::net::NodeStatus, - proto::{ - node::{self, ShutdownRequest, StatsRequest, StatsResponse, StatusRequest}, - RpcError, RpcResult, RpcService, - }, -}; - -/// Trait that provides fields used by the rpc handler for the net and node requests. -pub trait AbstractNode: Send + Sync + 'static { - /// Get the endpoint of the node - fn endpoint(&self) -> &Endpoint; - - /// Shutdown the node, used by the node shutdown rpc call - fn shutdown(&self); - - /// Rpc address of the node, used by the node status rpc call - fn rpc_addr(&self) -> Option { - None - } - - /// Stats for the node stats rpc call - fn stats(&self) -> anyhow::Result> { - anyhow::bail!("metrics are disabled"); - } -} - -struct Handler(Arc); - -/// Handle rpc requests for the node and net services -pub async fn handle_rpc_request>( - node: Arc, - msg: Request, - chan: RpcChannel, -) -> Result<(), RpcServerError> { - use Request::*; - match msg { - Node(msg) => Handler(node).handle_node_request(msg, chan).await, - Net(msg) => Handler(node).handle_net_request(msg, chan).await, - } -} - -impl Handler { - fn endpoint(&self) -> &Endpoint { - self.0.endpoint() - } - - async fn handle_node_request>( - self, - msg: node::Request, - chan: RpcChannel, - ) -> Result<(), RpcServerError> { - use node::Request::*; - debug!("handling node request: {msg}"); - match msg { - Status(msg) => chan.rpc(msg, self, Self::node_status).await, - Shutdown(msg) => chan.rpc(msg, self, Self::node_shutdown).await, - Stats(msg) => chan.rpc(msg, self, Self::node_stats).await, - } - } - - async fn handle_net_request>( - self, - msg: net::Request, - chan: RpcChannel, - ) -> Result<(), RpcServerError> { - use net::Request::*; - debug!("handling net request: {msg}"); - match msg { - Watch(msg) => chan.server_streaming(msg, self, Self::node_watch).await, - Id(msg) => chan.rpc(msg, self, Self::node_id).await, - Addr(msg) => chan.rpc(msg, self, Self::node_addr).await, - Relay(msg) => chan.rpc(msg, self, Self::node_relay).await, - RemoteInfosIter(msg) => { - chan.server_streaming(msg, self, Self::remote_infos_iter) - .await - } - RemoteInfo(msg) => chan.rpc(msg, self, Self::remote_info).await, - AddAddr(msg) => chan.rpc(msg, self, Self::node_add_addr).await, - } - } - - #[allow(clippy::unused_async)] - async fn node_shutdown(self, request: ShutdownRequest) { - if request.force { - info!("hard shutdown requested"); - std::process::exit(0); - } else { - // trigger a graceful shutdown - info!("graceful shutdown requested"); - self.0.shutdown(); - } - } - - #[allow(clippy::unused_async)] - async fn node_stats(self, _req: StatsRequest) -> RpcResult { - Ok(StatsResponse { - stats: self.0.stats().map_err(|e| RpcError::new(&*e))?, - }) - } - - async fn node_status(self, _: StatusRequest) -> RpcResult { - Ok(NodeStatus { - addr: self - .endpoint() - .node_addr() - .await - .map_err(|e| RpcError::new(&*e))?, - listen_addrs: self.local_endpoint_addresses().await.unwrap_or_default(), - version: env!("CARGO_PKG_VERSION").to_string(), - rpc_addr: self.0.rpc_addr(), - }) - } - - async fn local_endpoint_addresses(&self) -> Result> { - let endpoints = self - .endpoint() - .direct_addresses() - .next() - .await - .ok_or(anyhow!("no endpoints found"))?; - Ok(endpoints.into_iter().map(|x| x.addr).collect()) - } - - async fn node_addr(self, _: net::AddrRequest) -> RpcResult { - let addr = self - .endpoint() - .node_addr() - .await - .map_err(|e| RpcError::new(&*e))?; - Ok(addr) - } - - fn remote_infos_iter( - self, - _: net::RemoteInfosIterRequest, - ) -> impl Stream> + Send + 'static { - let mut infos: Vec<_> = self.endpoint().remote_info_iter().collect(); - infos.sort_by_key(|n| n.node_id.to_string()); - futures_lite::stream::iter( - infos - .into_iter() - .map(|info| Ok(net::RemoteInfosIterResponse { info })), - ) - } - - #[allow(clippy::unused_async)] - async fn node_id(self, _: net::IdRequest) -> RpcResult { - Ok(self.endpoint().secret_key().public()) - } - - // This method is called as an RPC method, which have to be async - #[allow(clippy::unused_async)] - async fn remote_info(self, req: net::RemoteInfoRequest) -> RpcResult { - let net::RemoteInfoRequest { node_id } = req; - let info = self.endpoint().remote_info(node_id); - Ok(net::RemoteInfoResponse { info }) - } - - // This method is called as an RPC method, which have to be async - #[allow(clippy::unused_async)] - async fn node_add_addr(self, req: net::AddAddrRequest) -> RpcResult<()> { - let net::AddAddrRequest { addr } = req; - self.endpoint() - .add_node_addr(addr) - .map_err(|e| RpcError::new(&*e))?; - Ok(()) - } - - #[allow(clippy::unused_async)] - async fn node_relay(self, _: net::RelayRequest) -> RpcResult> { - Ok(self.endpoint().home_relay()) - } - - fn node_watch(self, _: net::NodeWatchRequest) -> impl Stream + Send { - futures_lite::stream::unfold((), |()| async move { - tokio::time::sleep(HEALTH_POLL_WAIT).await; - Some(( - net::WatchResponse { - version: env!("CARGO_PKG_VERSION").to_string(), - }, - (), - )) - }) - } -} - -const HEALTH_POLL_WAIT: Duration = Duration::from_secs(1);