From e3cb7d1fb2b02a04bf0c945309055bd7048aa531 Mon Sep 17 00:00:00 2001 From: Ben Striegel Date: Tue, 3 Dec 2019 17:10:23 -0500 Subject: [PATCH] refactor(ilp-node): make ilp-node able to compile entirely without Redis BREAKING CHANGE: the "redis_url" command-line option to the ilp-node binary has been renamed to "database_url" --- crates/ilp-node/Cargo.toml | 12 +- crates/ilp-node/src/lib.rs | 11 +- crates/ilp-node/src/main.rs | 23 ++- crates/ilp-node/src/node.rs | 171 +++++++----------- crates/ilp-node/src/redis_store.rs | 72 ++++++++ crates/ilp-node/tests/{ => redis}/btp.rs | 20 +- .../tests/{ => redis}/exchange_rates.rs | 13 +- .../ilp-node/tests/{ => redis}/prometheus.rs | 20 +- .../tests/{ => redis}/redis_helpers.rs | 6 +- crates/ilp-node/tests/redis/redis_tests.rs | 7 + .../tests/{ => redis}/test_helpers.rs | 0 .../ilp-node/tests/{ => redis}/three_nodes.rs | 30 ++- .../src/expiry_shortener_service.rs | 5 - crates/interledger-store/Cargo.toml | 2 +- crates/interledger/Cargo.toml | 1 + docker/run-testnet-bundle.js | 4 +- docs/configuration.md | 4 +- docs/manual-config.md | 2 +- 18 files changed, 223 insertions(+), 180 deletions(-) create mode 100644 crates/ilp-node/src/redis_store.rs rename crates/ilp-node/tests/{ => redis}/btp.rs (94%) rename crates/ilp-node/tests/{ => redis}/exchange_rates.rs (94%) rename crates/ilp-node/tests/{ => redis}/prometheus.rs (94%) rename crates/ilp-node/tests/{ => redis}/redis_helpers.rs (96%) create mode 100644 crates/ilp-node/tests/redis/redis_tests.rs rename crates/ilp-node/tests/{ => redis}/test_helpers.rs (100%) rename crates/ilp-node/tests/{ => redis}/three_nodes.rs (95%) diff --git a/crates/ilp-node/Cargo.toml b/crates/ilp-node/Cargo.toml index e00970dbe..fa65f7f5b 100644 --- a/crates/ilp-node/Cargo.toml +++ b/crates/ilp-node/Cargo.toml @@ -9,11 +9,18 @@ repository = "https://github.com/interledger-rs/interledger-rs" default-run = "ilp-node" [features] -default = ["balance-tracking"] +default = ["balance-tracking", "redis"] balance-tracking = [] # This is an experimental feature that enables submitting packet # records to Google Cloud PubSub. This may be removed in the future. google-pubsub = ["base64", "chrono", "parking_lot", "reqwest", "serde_json", "yup-oauth2"] +redis = ["redis_crate", "interledger/redis"] + +[[test]] +name = "redis_tests" +path = "tests/redis/redis_tests.rs" +required-features = ["redis"] + [dependencies] bytes = { version = "0.4.12", default-features = false } @@ -27,7 +34,7 @@ metrics = { version = "0.12.0", default-features = false, features = ["std"] } metrics-core = { version = "0.5.1", default-features = false } metrics-runtime = { version = "0.12.0", default-features = false, features = ["metrics-observer-prometheus"] } num-bigint = { version = "0.2.3", default-features = false, features = ["std"] } -redis = { version = "0.13.0", default-features = false, features = ["executor"] } +redis_crate = { package = "redis", version = "0.13.0", default-features = false, features = ["executor"], optional = true } ring = { version = "0.16.9", default-features = false } serde = { version = "1.0.101", default-features = false } tokio = { version = "0.1.22", default-features = false } @@ -53,7 +60,6 @@ approx = { version = "0.3.2", default-features = false } base64 = { version = "0.10.1", default-features = false } net2 = { version = "0.2.33", default-features = false } rand = { version = "0.7.2", default-features = false } -redis = { version = "0.13.0", default-features = false, features = ["executor"] } reqwest = { version = "0.9.22", default-features = false, features = ["default-tls"] } serde_json = { version = "1.0.41", default-features = false } tokio-retry = { version = "0.2.0", default-features = false } diff --git a/crates/ilp-node/src/lib.rs b/crates/ilp-node/src/lib.rs index ae408aa3b..e8f83bfb7 100644 --- a/crates/ilp-node/src/lib.rs +++ b/crates/ilp-node/src/lib.rs @@ -1,8 +1,15 @@ #![type_length_limit = "1152885"] -#[cfg(feature = "google-pubsub")] -mod google_pubsub; mod metrics; mod node; mod trace; + +#[cfg(feature = "google-pubsub")] +mod google_pubsub; +#[cfg(feature = "redis")] +mod redis_store; + pub use node::*; +#[allow(deprecated)] +#[cfg(feature = "redis")] +pub use redis_store::insert_account_with_redis_store; diff --git a/crates/ilp-node/src/main.rs b/crates/ilp-node/src/main.rs index 79bafb55e..f7f15b34d 100644 --- a/crates/ilp-node/src/main.rs +++ b/crates/ilp-node/src/main.rs @@ -1,9 +1,19 @@ #![type_length_limit = "1152885"] +mod metrics; +mod node; +mod trace; + +#[cfg(feature = "google-pubsub")] +mod google_pubsub; +#[cfg(feature = "redis")] +mod redis_store; + use clap::{crate_version, App, Arg, ArgMatches}; use config::{Config, Source}; use config::{FileFormat, Value}; use libc::{c_int, isatty}; +use node::InterledgerNode; use std::{ ffi::{OsStr, OsString}, io::Read, @@ -14,13 +24,6 @@ use tracing_subscriber::{ fmt::{time::ChronoUtc, Subscriber}, }; -#[cfg(feature = "google-pubsub")] -mod google_pubsub; -mod metrics; -mod node; -mod trace; -use node::InterledgerNode; - pub fn main() { Subscriber::builder() .with_timer(ChronoUtc::rfc3339()) @@ -69,8 +72,10 @@ pub fn main() { .takes_value(true) .required(true) .help("HTTP Authorization token for the node admin (sent as a Bearer token)"), - Arg::with_name("redis_url") - .long("redis_url") + Arg::with_name("database_url") + .long("database_url") + // temporary alias for backwards compatibility + .alias("redis_url") .takes_value(true) .default_value("redis://127.0.0.1:6379") .help("Redis URI (for example, \"redis://127.0.0.1:6379\" or \"unix:/tmp/redis.sock\")"), diff --git a/crates/ilp-node/src/node.rs b/crates/ilp-node/src/node.rs index 4d5d2c181..8ca4aeca3 100644 --- a/crates/ilp-node/src/node.rs +++ b/crates/ilp-node/src/node.rs @@ -1,20 +1,11 @@ +use crate::metrics::{incoming_metrics, outgoing_metrics}; +use crate::trace::{trace_forwarding, trace_incoming, trace_outgoing}; use bytes::Bytes; use futures::{ - future::{err, result, Either}, + future::{err, Either}, Future, }; use hex::FromHex; -#[doc(hidden)] -pub use interledger::api::AccountDetails; -pub use interledger::service_util::ExchangeRateProvider; -use std::sync::Arc; - -#[cfg(feature = "google-pubsub")] -use crate::google_pubsub::{create_google_pubsub_wrapper, PubsubConfig}; -use crate::metrics::{incoming_metrics, outgoing_metrics}; -use crate::trace::{trace_forwarding, trace_incoming, trace_outgoing}; -#[cfg(feature = "balance-tracking")] -use interledger::service_util::BalanceService; use interledger::{ api::{NodeApi, NodeStore}, btp::{btp_service_as_filter, connect_client, BtpOutgoingService, BtpStore}, @@ -30,40 +21,46 @@ use interledger::{ }, service_util::{ BalanceStore, EchoService, ExchangeRateFetcher, ExchangeRateService, ExchangeRateStore, - ExpiryShortenerService, MaxPacketAmountAccount, MaxPacketAmountService, RateLimitAccount, - RateLimitService, RateLimitStore, RoundTripTimeAccount, ValidatorService, + ExpiryShortenerService, MaxPacketAmountService, RateLimitService, RateLimitStore, + ValidatorService, }, settlement::{ api::{create_settlements_filter, SettlementMessageService}, core::{ idempotency::IdempotentStore, - types::{LeftoversStore, SettlementAccount, SettlementStore}, + types::{LeftoversStore, SettlementStore}, }, }, - store::{account::Account, redis::RedisStoreBuilder}, + store::account::Account, stream::{StreamNotificationsStore, StreamReceiverService}, }; use lazy_static::lazy_static; use metrics_core::{Builder, Drain, Observe}; use metrics_runtime; use num_bigint::BigUint; -use redis::{ConnectionInfo, IntoConnectionInfo}; -use ring::hmac; -use serde::{de::Error as DeserializeError, Deserialize, Deserializer, Serialize}; +use serde::{de::Error as DeserializeError, Deserialize, Deserializer}; +use std::sync::Arc; use std::{convert::TryFrom, net::SocketAddr, str, str::FromStr, time::Duration}; use tokio::spawn; use tracing::{debug, debug_span, error, info}; use tracing_futures::Instrument; use url::Url; -use uuid::Uuid; use warp::{ self, http::{Response, StatusCode}, Filter, }; -static REDIS_SECRET_GENERATION_STRING: &str = "ilp_redis_secret"; -static DEFAULT_REDIS_URL: &str = "redis://127.0.0.1:6379"; +#[cfg(feature = "google-pubsub")] +use crate::google_pubsub::{create_google_pubsub_wrapper, PubsubConfig}; +#[cfg(feature = "redis")] +use crate::redis_store::*; +#[cfg(feature = "balance-tracking")] +use interledger::service_util::BalanceService; + +#[doc(hidden)] +pub use interledger::service_util::ExchangeRateProvider; + lazy_static! { static ref DEFAULT_ILP_ADDRESS: Address = Address::from_str("local.host").unwrap(); } @@ -74,8 +71,15 @@ fn default_settlement_api_bind_address() -> SocketAddr { fn default_http_bind_address() -> SocketAddr { SocketAddr::from(([127, 0, 0, 1], 7770)) } -fn default_redis_url() -> ConnectionInfo { - DEFAULT_REDIS_URL.into_connection_info().unwrap() +// We allow unreachable code on the below function because there must always be exactly one default +// regardless of how many data sources the crate is compiled to support, +// but we don't know which will be enabled or in which quantities or configurations. +// This return-based pattern effectively gives us fallthrough behavior. +#[allow(unreachable_code)] +fn default_database_url() -> String { + #[cfg(feature = "redis")] + return default_redis_url(); + panic!("no backing store configured") } fn deserialize_optional_address<'de, D>(deserializer: D) -> Result, D::Error> @@ -116,21 +120,6 @@ where } } -fn deserialize_redis_connection<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Url::parse(&String::deserialize(deserializer)?) - .map_err(|err| DeserializeError::custom(format!("Invalid URL: {:?}", err)))? - .into_connection_info() - .map_err(|err| { - DeserializeError::custom(format!( - "Error converting into Redis connection info: {:?}", - err - )) - }) -} - /// Configuration for [Prometheus](https://prometheus.io) metrics collection. #[derive(Deserialize, Clone)] pub struct PrometheusConfig { @@ -195,7 +184,9 @@ impl ExchangeRateConfig { } /// An all-in-one Interledger node that includes sender and receiver functionality, -/// a connector, and a management API. The node uses Redis for persistence. +/// a connector, and a management API. +/// Will connect to the database at the given URL; see the crate features defined in +/// Cargo.toml to see a list of all supported stores. #[derive(Deserialize, Clone)] pub struct InterledgerNode { /// ILP address of the node @@ -207,13 +198,13 @@ pub struct InterledgerNode { pub secret_seed: [u8; 32], /// HTTP Authorization token for the node admin (sent as a Bearer token) pub admin_auth_token: String, - /// Redis URI (for example, "redis://127.0.0.1:6379" or "unix:/tmp/redis.sock") + /// Data store URI (for example, "redis://127.0.0.1:6379" or "redis+unix:/tmp/redis.sock") #[serde( - deserialize_with = "deserialize_redis_connection", - default = "default_redis_url", + default = "default_database_url", + // temporary alias for backwards compatibility alias = "redis_url" )] - pub redis_connection: ConnectionInfo, + pub database_url: String, /// IP address and port to listen for HTTP connections /// This is used for both the API and ILP over HTTP packets #[serde(default = "default_http_bind_address")] @@ -259,48 +250,42 @@ impl InterledgerNode { } } - fn serve_node(self) -> impl Future { - let redis_addr = self.redis_connection.addr.clone(); - let redis_secret = generate_redis_secret(&self.secret_seed); + fn serve_node(self) -> Box + Send + 'static> { let ilp_address = if let Some(address) = &self.ilp_address { address.clone() } else { DEFAULT_ILP_ADDRESS.clone() }; - debug!(target: "interledger-node", - "Starting Interledger node with ILP address: {}", - ilp_address - ); + // TODO: store a Url directly in InterledgerNode rather than a String? + let database_url = match Url::parse(&self.database_url) { + Ok(url) => url, + Err(e) => { + error!( + "The string '{}' could not be parsed as a URL: {}", + &self.database_url, e + ); + return Box::new(err(())); + } + }; - Box::new(RedisStoreBuilder::new(self.redis_connection.clone(), redis_secret) - .node_ilp_address(ilp_address.clone()) - .connect() - .map_err(move |err| error!(target: "interledger-node", "Error connecting to Redis: {:?} {:?}", redis_addr, err)) - .and_then(move |store| self.chain_services(store, ilp_address))) + match database_url.scheme() { + #[cfg(feature = "redis")] + "redis" | "redis+unix" => Box::new(serve_redis_node(self, ilp_address)), + other => { + error!("unsupported data source scheme: {}", other); + Box::new(err(())) + } + } } #[allow(clippy::cognitive_complexity)] - fn chain_services(self, store: S, ilp_address: Address) -> impl Future + pub(crate) fn chain_services( + self, + store: S, + ilp_address: Address, + ) -> impl Future where - // Should we use a generic rather than the concrete Account type? - // I spent long enough banging my head against this that I don't care - // anymore, but feel free to take a whack at it. - /*A: AccountTrait - + SettlementAccount - + HttpAccount - + BtpAccount - + CcpRoutingAccount - + RateLimitAccount - + RoundTripTimeAccount - + MaxPacketAmountAccount - + Send - + Sync - + 'static - + Serialize - + Clone,*/ - // Likewise, should this be generic? - //AT: ToString, S: NodeStore + BtpStore + HttpStore @@ -321,6 +306,11 @@ impl InterledgerNode { + Sync + 'static, { + debug!(target: "interledger-node", + "Starting Interledger node with ILP address: {}", + ilp_address + ); + let secret_seed = Bytes::from(&self.secret_seed[..]); let http_bind_address = self.http_bind_address; let settlement_api_bind_address = self.settlement_api_bind_address; @@ -513,7 +503,7 @@ impl InterledgerNode { }, ) }) -.in_current_span() + .in_current_span() } /// Starts a Prometheus metrics server that will listen on the configured address. @@ -576,35 +566,6 @@ impl InterledgerNode { pub fn run(self) { tokio_run(self.serve()); } - - #[doc(hidden)] - #[allow(dead_code)] - pub fn insert_account(&self, account: AccountDetails) -> impl Future { - let redis_secret = generate_redis_secret(&self.secret_seed); - result(self.redis_connection.clone().into_connection_info()) - .map_err(|err| error!(target: "interledger-node", "Invalid Redis connection details: {:?}", err)) - .and_then(move |redis_url| RedisStoreBuilder::new(redis_url, redis_secret).connect()) - .map_err(|err| error!(target: "interledger-node", "Error connecting to Redis: {:?}", err)) - .and_then(move |store| { - store - .insert_account(account) - .map_err(|_| error!(target: "interledger-node", "Unable to create account")) - .and_then(|account| { - debug!(target: "interledger-node", "Created account: {}", account.id()); - Ok(account.id()) - }) - }) - } -} - -fn generate_redis_secret(secret_seed: &[u8; 32]) -> [u8; 32] { - let mut redis_secret: [u8; 32] = [0; 32]; - let sig = hmac::sign( - &hmac::Key::new(hmac::HMAC_SHA256, secret_seed), - REDIS_SECRET_GENERATION_STRING.as_bytes(), - ); - redis_secret.copy_from_slice(sig.as_ref()); - redis_secret } #[doc(hidden)] diff --git a/crates/ilp-node/src/redis_store.rs b/crates/ilp-node/src/redis_store.rs new file mode 100644 index 000000000..ef9a957c0 --- /dev/null +++ b/crates/ilp-node/src/redis_store.rs @@ -0,0 +1,72 @@ +#![cfg(feature = "redis")] + +use crate::node::InterledgerNode; +use futures::{future::result, Future}; +pub use interledger::{ + api::{AccountDetails, NodeStore}, + packet::Address, + service::Account, + store::redis::RedisStoreBuilder, +}; +pub use redis_crate::{ConnectionInfo, IntoConnectionInfo}; +use ring::hmac; +use tracing::{debug, error}; +use uuid::Uuid; + +static REDIS_SECRET_GENERATION_STRING: &str = "ilp_redis_secret"; + +pub fn default_redis_url() -> String { + String::from("redis://127.0.0.1:6379") +} + +// This function could theoretically be defined as an inherent method on InterledgerNode itself. +// However, we define it in this module in order to consolidate conditionally-compiled code +// into as few discrete units as possible. +pub fn serve_redis_node( + node: InterledgerNode, + ilp_address: Address, +) -> impl Future { + let redis_connection_info = node.database_url.clone().into_connection_info().unwrap(); + let redis_addr = redis_connection_info.addr.clone(); + let redis_secret = generate_redis_secret(&node.secret_seed); + Box::new(RedisStoreBuilder::new(redis_connection_info, redis_secret) + .node_ilp_address(ilp_address.clone()) + .connect() + .map_err(move |err| error!(target: "interledger-node", "Error connecting to Redis: {:?} {:?}", redis_addr, err)) + .and_then(move |store| node.chain_services(store, ilp_address))) +} + +pub fn generate_redis_secret(secret_seed: &[u8; 32]) -> [u8; 32] { + let mut redis_secret: [u8; 32] = [0; 32]; + let sig = hmac::sign( + &hmac::Key::new(hmac::HMAC_SHA256, secret_seed), + REDIS_SECRET_GENERATION_STRING.as_bytes(), + ); + redis_secret.copy_from_slice(sig.as_ref()); + redis_secret +} + +#[doc(hidden)] +#[allow(dead_code)] +#[deprecated(note = "use HTTP API instead")] +pub fn insert_account_with_redis_store( + node: &InterledgerNode, + account: AccountDetails, +) -> impl Future { + let redis_secret = generate_redis_secret(&node.secret_seed); + result(node.database_url.clone().into_connection_info()) + .map_err( + |err| error!(target: "interledger-node", "Invalid Redis connection details: {:?}", err), + ) + .and_then(move |redis_url| RedisStoreBuilder::new(redis_url, redis_secret).connect()) + .map_err(|err| error!(target: "interledger-node", "Error connecting to Redis: {:?}", err)) + .and_then(move |store| { + store + .insert_account(account) + .map_err(|_| error!(target: "interledger-node", "Unable to create account")) + .and_then(|account| { + debug!(target: "interledger-node", "Created account: {}", account.id()); + Ok(account.id()) + }) + }) +} diff --git a/crates/ilp-node/tests/btp.rs b/crates/ilp-node/tests/redis/btp.rs similarity index 94% rename from crates/ilp-node/tests/btp.rs rename to crates/ilp-node/tests/redis/btp.rs index 02633bbba..2ccbaebe8 100644 --- a/crates/ilp-node/tests/btp.rs +++ b/crates/ilp-node/tests/redis/btp.rs @@ -1,3 +1,5 @@ +use crate::redis_helpers::*; +use crate::test_helpers::*; use futures::{future::join_all, Future}; use ilp_node::InterledgerNode; use serde_json::{self, json}; @@ -5,12 +7,6 @@ use tokio::runtime::Builder as RuntimeBuilder; use tracing::error_span; use tracing_futures::Instrument; -mod redis_helpers; -use redis_helpers::*; - -mod test_helpers; -use test_helpers::*; - #[test] fn two_nodes_btp() { // Nodes 1 and 2 are peers, Node 2 is the parent of Node 2 @@ -23,10 +19,10 @@ fn two_nodes_btp() { let mut connection_info2 = context.get_client_connection_info(); connection_info2.db = 2; - let node_a_http = get_open_port(Some(3010)); - let node_a_settlement = get_open_port(Some(3011)); - let node_b_http = get_open_port(Some(3020)); - let node_b_settlement = get_open_port(Some(3021)); + let node_a_http = get_open_port(None); + let node_a_settlement = get_open_port(None); + let node_b_http = get_open_port(None); + let node_b_settlement = get_open_port(None); let mut runtime = RuntimeBuilder::new() .panic_handler(|err| std::panic::resume_unwind(err)) @@ -65,7 +61,7 @@ fn two_nodes_btp() { let node_a: InterledgerNode = serde_json::from_value(json!({ "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info1), + "database_url": connection_info_to_string(connection_info1), "http_bind_address": format!("127.0.0.1:{}", node_a_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node_a_settlement), "secret_seed": random_secret(), @@ -80,7 +76,7 @@ fn two_nodes_btp() { "ilp_address": "example.parent", "default_spsp_account": "bob_on_b", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info2), + "database_url": connection_info_to_string(connection_info2), "http_bind_address": format!("127.0.0.1:{}", node_b_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node_b_settlement), "secret_seed": random_secret(), diff --git a/crates/ilp-node/tests/exchange_rates.rs b/crates/ilp-node/tests/redis/exchange_rates.rs similarity index 94% rename from crates/ilp-node/tests/exchange_rates.rs rename to crates/ilp-node/tests/redis/exchange_rates.rs index dd20d221a..803100f3a 100644 --- a/crates/ilp-node/tests/exchange_rates.rs +++ b/crates/ilp-node/tests/redis/exchange_rates.rs @@ -1,3 +1,5 @@ +use crate::redis_helpers::*; +use crate::test_helpers::*; use futures::Future; use ilp_node::InterledgerNode; use reqwest::r#async::Client; @@ -9,11 +11,6 @@ use tokio_retry::{strategy::FibonacciBackoff, Retry}; use tracing::error; use tracing_subscriber; -mod redis_helpers; -use redis_helpers::*; -mod test_helpers; -use test_helpers::*; - #[test] fn coincap() { install_tracing_subscriber(); @@ -24,13 +21,13 @@ fn coincap() { .build() .unwrap(); - let http_port = get_open_port(Some(3010)); + let http_port = get_open_port(None); let node: InterledgerNode = serde_json::from_value(json!({ "ilp_address": "example.one", "default_spsp_account": "one", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(context.get_client_connection_info()), + "database_url": connection_info_to_string(context.get_client_connection_info()), "http_bind_address": format!("127.0.0.1:{}", http_port), "settlement_api_bind_address": format!("127.0.0.1:{}", get_open_port(None)), "secret_seed": random_secret(), @@ -107,7 +104,7 @@ fn cryptocompare() { "ilp_address": "example.one", "default_spsp_account": "one", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(context.get_client_connection_info()), + "database_url": connection_info_to_string(context.get_client_connection_info()), "http_bind_address": format!("127.0.0.1:{}", http_port), "settlement_api_bind_address": format!("127.0.0.1:{}", get_open_port(None)), "secret_seed": random_secret(), diff --git a/crates/ilp-node/tests/prometheus.rs b/crates/ilp-node/tests/redis/prometheus.rs similarity index 94% rename from crates/ilp-node/tests/prometheus.rs rename to crates/ilp-node/tests/redis/prometheus.rs index 33e69f023..a9631314b 100644 --- a/crates/ilp-node/tests/prometheus.rs +++ b/crates/ilp-node/tests/redis/prometheus.rs @@ -1,15 +1,11 @@ +use crate::redis_helpers::*; +use crate::test_helpers::*; use futures::{future::join_all, Future}; use ilp_node::InterledgerNode; use reqwest::r#async::Client; use serde_json::{self, json}; use tokio::runtime::Builder as RuntimeBuilder; -mod redis_helpers; -use redis_helpers::*; - -mod test_helpers; -use test_helpers::*; - #[test] fn prometheus() { // Nodes 1 and 2 are peers, Node 2 is the parent of Node 2 @@ -22,10 +18,10 @@ fn prometheus() { let mut connection_info2 = context.get_client_connection_info(); connection_info2.db = 2; - let node_a_http = get_open_port(Some(3010)); - let node_a_settlement = get_open_port(Some(3011)); - let node_b_http = get_open_port(Some(3020)); - let node_b_settlement = get_open_port(Some(3021)); + let node_a_http = get_open_port(None); + let node_a_settlement = get_open_port(None); + let node_b_http = get_open_port(None); + let node_b_settlement = get_open_port(None); let prometheus_port = get_open_port(None); let mut runtime = RuntimeBuilder::new() @@ -72,7 +68,7 @@ fn prometheus() { let node_a: InterledgerNode = serde_json::from_value(json!({ "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info1), + "database_url": connection_info_to_string(connection_info1), "http_bind_address": format!("127.0.0.1:{}", node_a_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node_a_settlement), "secret_seed": random_secret(), @@ -88,7 +84,7 @@ fn prometheus() { "ilp_address": "example.parent", "default_spsp_account": "bob_on_b", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info2), + "database_url": connection_info_to_string(connection_info2), "http_bind_address": format!("127.0.0.1:{}", node_b_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node_b_settlement), "secret_seed": random_secret(), diff --git a/crates/ilp-node/tests/redis_helpers.rs b/crates/ilp-node/tests/redis/redis_helpers.rs similarity index 96% rename from crates/ilp-node/tests/redis_helpers.rs rename to crates/ilp-node/tests/redis/redis_helpers.rs index 555697a54..0c61f4475 100644 --- a/crates/ilp-node/tests/redis_helpers.rs +++ b/crates/ilp-node/tests/redis/redis_helpers.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] use futures::Future; -use redis::{self, ConnectionAddr, ConnectionInfo, RedisError}; +use redis_crate::{self as redis, ConnectionAddr, ConnectionInfo, RedisError}; use std::env; use std::fs; use std::path::PathBuf; @@ -15,7 +15,9 @@ use tokio::timer::Delay; pub fn connection_info_to_string(info: ConnectionInfo) -> String { match info.addr.as_ref() { ConnectionAddr::Tcp(url, port) => format!("redis://{}:{}/{}", url, port, info.db), - ConnectionAddr::Unix(path) => format!("unix:{}?db={}", path.to_str().unwrap(), info.db), + ConnectionAddr::Unix(path) => { + format!("redis+unix:{}?db={}", path.to_str().unwrap(), info.db) + } } } diff --git a/crates/ilp-node/tests/redis/redis_tests.rs b/crates/ilp-node/tests/redis/redis_tests.rs new file mode 100644 index 000000000..0ec67ff68 --- /dev/null +++ b/crates/ilp-node/tests/redis/redis_tests.rs @@ -0,0 +1,7 @@ +mod btp; +mod exchange_rates; +mod prometheus; +mod three_nodes; + +mod redis_helpers; +mod test_helpers; diff --git a/crates/ilp-node/tests/test_helpers.rs b/crates/ilp-node/tests/redis/test_helpers.rs similarity index 100% rename from crates/ilp-node/tests/test_helpers.rs rename to crates/ilp-node/tests/redis/test_helpers.rs diff --git a/crates/ilp-node/tests/three_nodes.rs b/crates/ilp-node/tests/redis/three_nodes.rs similarity index 95% rename from crates/ilp-node/tests/three_nodes.rs rename to crates/ilp-node/tests/redis/three_nodes.rs index 6529f1c18..caf90cb8d 100644 --- a/crates/ilp-node/tests/three_nodes.rs +++ b/crates/ilp-node/tests/redis/three_nodes.rs @@ -1,16 +1,14 @@ +use crate::redis_helpers::*; +use crate::test_helpers::*; use futures::{future::join_all, stream::*, sync::mpsc, Future}; use ilp_node::InterledgerNode; +use interledger::packet::Address; +use interledger::stream::StreamDelivery; use serde_json::json; +use std::str::FromStr; use tokio::runtime::Builder as RuntimeBuilder; use tracing::{debug, error_span}; use tracing_futures::Instrument; -mod redis_helpers; -use redis_helpers::*; -mod test_helpers; -use interledger::packet::Address; -use interledger::stream::StreamDelivery; -use std::str::FromStr; -use test_helpers::*; const LOG_TARGET: &str = "interledger-tests-three-nodes"; @@ -28,12 +26,12 @@ fn three_nodes() { let mut connection_info3 = context.get_client_connection_info(); connection_info3.db = 3; - let node1_http = get_open_port(Some(3010)); - let node1_settlement = get_open_port(Some(3011)); - let node2_http = get_open_port(Some(3020)); - let node2_settlement = get_open_port(Some(3021)); - let node3_http = get_open_port(Some(3030)); - let node3_settlement = get_open_port(Some(3031)); + let node1_http = get_open_port(None); + let node1_settlement = get_open_port(None); + let node2_http = get_open_port(None); + let node2_settlement = get_open_port(None); + let node3_http = get_open_port(None); + let node3_settlement = get_open_port(None); let mut runtime = RuntimeBuilder::new() .panic_handler(|err| std::panic::resume_unwind(err)) @@ -101,7 +99,7 @@ fn three_nodes() { "ilp_address": "example.alice", "default_spsp_account": "alice_on_a", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info1), + "database_url": connection_info_to_string(connection_info1), "http_bind_address": format!("127.0.0.1:{}", node1_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node1_settlement), "secret_seed": random_secret(), @@ -115,7 +113,7 @@ fn three_nodes() { let node2: InterledgerNode = serde_json::from_value(json!({ "ilp_address": "example.bob", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info2), + "database_url": connection_info_to_string(connection_info2), "http_bind_address": format!("127.0.0.1:{}", node2_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node2_settlement), "secret_seed": random_secret(), @@ -129,7 +127,7 @@ fn three_nodes() { let node3: InterledgerNode = serde_json::from_value(json!({ "default_spsp_account": "charlie_on_c", "admin_auth_token": "admin", - "redis_connection": connection_info_to_string(connection_info3), + "database_url": connection_info_to_string(connection_info3), "http_bind_address": format!("127.0.0.1:{}", node3_http), "settlement_api_bind_address": format!("127.0.0.1:{}", node3_settlement), "secret_seed": random_secret(), diff --git a/crates/interledger-service-util/src/expiry_shortener_service.rs b/crates/interledger-service-util/src/expiry_shortener_service.rs index dd66bec27..db5658447 100644 --- a/crates/interledger-service-util/src/expiry_shortener_service.rs +++ b/crates/interledger-service-util/src/expiry_shortener_service.rs @@ -39,11 +39,6 @@ impl ExpiryShortenerService { } } -// TODO: the bounds in the module give really bad error messages; -// consider the above ExpiryShortenerService impl which is unbounded, -// so any type will implement it, but to actually do anything useful -// the following bounds must be specified, leading to seemingly-unrelated -// errors regarding OutgoingService in generic code impl OutgoingService for ExpiryShortenerService where O: OutgoingService, diff --git a/crates/interledger-store/Cargo.toml b/crates/interledger-store/Cargo.toml index 8659ea3ce..3133cbce3 100644 --- a/crates/interledger-store/Cargo.toml +++ b/crates/interledger-store/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" repository = "https://github.com/interledger-rs/interledger-rs" [features] -default = ["redis"] +default = [] redis = ["redis_crate"] [lib] diff --git a/crates/interledger/Cargo.toml b/crates/interledger/Cargo.toml index 7cf77bd15..f490e5e2e 100644 --- a/crates/interledger/Cargo.toml +++ b/crates/interledger/Cargo.toml @@ -35,6 +35,7 @@ spsp = ["interledger-spsp", "stream"] store = ["interledger-store"] stream = ["interledger-stream", "ildcp"] trace = ["interledger-service/trace"] +redis = ["interledger-store/redis"] [dependencies] interledger-api = { path = "../interledger-api", version = "^0.3.0", optional = true, default-features = false } diff --git a/docker/run-testnet-bundle.js b/docker/run-testnet-bundle.js index f6887f167..b84936515 100644 --- a/docker/run-testnet-bundle.js +++ b/docker/run-testnet-bundle.js @@ -130,7 +130,7 @@ function runNode({ httpBindAddress, adminAuthToken, secretSeed, nodeName }) { `--http_bind_address=${httpBindAddress}`, `--admin_auth_token=${adminAuthToken}`, `--secret_seed=${secretSeed}`, - '--redis_url=unix:/tmp/redis.sock', + '--redis_url=redis+unix:/tmp/redis.sock', `--default_spsp_account=${nodeName}` ], { @@ -170,7 +170,7 @@ function runEthSettlementEngine({ ethKey, ethUrl }) { `--private_key=${ethKey}`, '--poll_frequency=15000', '--confirmations=0', - '--redis_url=unix:/tmp/redis.sock?db=2', + '--redis_url=redis+unix:/tmp/redis.sock?db=2', '--chain_id=4' ], { env: { diff --git a/docs/configuration.md b/docs/configuration.md index 78e7ba10b..0e05dbeff 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -75,9 +75,9 @@ The configuration parameters are explained in the following format. - [ILP Addresses v2.0.0](https://github.com/interledger/rfcs/blob/master/0015-ilp-addresses/0015-ilp-addresses.md) - `g.my-node` - The ILP address of your node. The format should conform to the RFC above. If you are running a child node, you don't need to specify this. -- redis_url +- database_url - URL - - `redis://127.0.0.1:6379`, `unix:/tmp/redis.sock` + - `redis://127.0.0.1:6379`, `redis+unix:/tmp/redis.sock` - A URL of redis that the node connects to in order to store its data. - http_bind_address - Socket Address (`address:port`) diff --git a/docs/manual-config.md b/docs/manual-config.md index 360fb9f79..27f20bcf5 100644 --- a/docs/manual-config.md +++ b/docs/manual-config.md @@ -183,7 +183,7 @@ First, save your config file as `config.json` which contains: { "secret_seed": "", "admin_auth_token": "", - "redis_url": "redis://127.0.0.1:6379/", + "database_url": "redis://127.0.0.1:6379/", "http_bind_address": "127.0.0.1:7770", "settlement_api_bind_address": "127.0.0.1:7771" }