From 1fd6625a81eba21739eb51be2d1c422e57be9d28 Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Fri, 31 May 2024 11:47:55 +0530 Subject: [PATCH 1/8] fix(manager): add metrics port if not set --- sn_node_manager/src/add_services/mod.rs | 10 +- sn_node_manager/src/add_services/tests.rs | 579 +++++++++++++++++++--- 2 files changed, 522 insertions(+), 67 deletions(-) diff --git a/sn_node_manager/src/add_services/mod.rs b/sn_node_manager/src/add_services/mod.rs index 64e34cd2c1..e896ceaf9f 100644 --- a/sn_node_manager/src/add_services/mod.rs +++ b/sn_node_manager/src/add_services/mod.rs @@ -144,6 +144,12 @@ pub async fn add_node( } else { service_control.get_available_port()? }; + let metrics_free_port = if let Some(port) = metrics_port { + Some(port) + } else { + Some(service_control.get_available_port()?) + }; + let rpc_socket_addr = if let Some(addr) = options.rpc_address { SocketAddr::new(IpAddr::V4(addr), rpc_free_port) } else { @@ -214,7 +220,7 @@ pub async fn add_node( local: options.local, log_dir_path: service_log_dir_path.clone(), log_format: options.log_format, - metrics_port, + metrics_port: metrics_free_port, name: service_name.clone(), node_port, owner: options.owner.clone(), @@ -245,7 +251,7 @@ pub async fn add_node( local: options.local, log_dir_path: service_log_dir_path.clone(), log_format: options.log_format, - metrics_port, + metrics_port: metrics_free_port, node_port, number: node_number, reward_balance: None, diff --git a/sn_node_manager/src/add_services/tests.rs b/sn_node_manager/src/add_services/tests.rs index d27f16c291..898d224196 100644 --- a/sn_node_manager/src/add_services/tests.rs +++ b/sn_node_manager/src/add_services/tests.rs @@ -105,10 +105,11 @@ async fn add_genesis_node_should_use_latest_version_and_add_one_service() -> Res let mut mock_service_control = MockServiceControl::new(); let mut seq = Sequence::new(); + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -121,7 +122,7 @@ async fn add_genesis_node_should_use_latest_version_and_add_one_service() -> Res local: true, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -393,10 +394,11 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( let mut seq = Sequence::new(); // Expected calls for first installation + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -409,7 +411,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -431,10 +433,11 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( .in_sequence(&mut seq); // Expected calls for second installation + let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8083)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -446,7 +449,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode2"), log_format: None, - metrics_port: None, + metrics_port: Some(15003), name: "safenode2".to_string(), node_port: None, owner: None, @@ -468,10 +471,11 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( .in_sequence(&mut seq); // Expected calls for third installation + let mut ports = vec![Ok(8085), Ok(15005)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8085)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -483,7 +487,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_format: None, log_dir_path: node_logs_dir.to_path_buf().join("safenode3"), - metrics_port: None, + metrics_port: Some(15005), name: "safenode3".to_string(), node_port: None, owner: None, @@ -623,10 +627,11 @@ async fn add_node_should_update_the_bootstrap_peers_inside_node_registry() -> Re let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -639,7 +644,7 @@ async fn add_node_should_update_the_bootstrap_peers_inside_node_registry() -> Re local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -753,10 +758,11 @@ async fn add_node_should_update_the_environment_variables_inside_node_registry() let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -768,7 +774,7 @@ async fn add_node_should_update_the_environment_variables_inside_node_registry() local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -900,10 +906,11 @@ async fn add_new_node_should_add_another_service() -> Result<()> { safenode_download_path.write_binary(b"fake safenode bin")?; let mut seq = Sequence::new(); + let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8083)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -915,7 +922,7 @@ async fn add_new_node_should_add_another_service() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode2"), log_format: None, - metrics_port: None, + metrics_port: Some(15003), name: "safenode2".to_string(), node_port: None, rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8083), @@ -1021,10 +1028,11 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -1036,7 +1044,7 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: Some(custom_port), owner: None, @@ -1128,10 +1136,11 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { let mut seq = Sequence::new(); // First service + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(15000)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1140,7 +1149,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15000"), + OsString::from("127.0.0.1:8081"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1159,6 +1168,8 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12000"), + OsString::from("--metrics-server-port"), + OsString::from("15001"), ], autostart: false, contents: None, @@ -1177,10 +1188,11 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { .in_sequence(&mut seq); // Second service + let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(15001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1189,7 +1201,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15001"), + OsString::from("127.0.0.1:8083"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1208,6 +1220,8 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12001"), + OsString::from("--metrics-server-port"), + OsString::from("15003"), ], autostart: false, contents: None, @@ -1226,10 +1240,11 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { .in_sequence(&mut seq); // Third service + let mut ports = vec![Ok(8085), Ok(15005)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(15002)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1238,7 +1253,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15002"), + OsString::from("127.0.0.1:8085"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1257,6 +1272,8 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12002"), + OsString::from("--metrics-server-port"), + OsString::from("15005"), ], autostart: false, contents: None, @@ -1665,10 +1682,11 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< let mut seq = Sequence::new(); // First service + let mut ports = vec![Ok(8081)].into_iter(); mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(15000)) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1677,7 +1695,7 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15000"), + OsString::from("127.0.0.1:8081"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1714,10 +1732,11 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< .in_sequence(&mut seq); // Second service + let mut ports = vec![Ok(8083)].into_iter(); mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(15001)) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1726,7 +1745,7 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15001"), + OsString::from("127.0.0.1:8083"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1763,10 +1782,11 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< .in_sequence(&mut seq); // Third service + let mut ports = vec![Ok(8085)].into_iter(); mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(15002)) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1775,7 +1795,7 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:15002"), + OsString::from("127.0.0.1:8085"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -2033,7 +2053,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metrics_port_in_ran } #[tokio::test] -async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result<()> { +async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; let node_reg_path = tmp_data_dir.child("node_reg.json"); @@ -2060,6 +2080,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< let mut seq = Sequence::new(); + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(20000)) + .in_sequence(&mut seq); + // First service mock_service_control .expect_install() @@ -2085,6 +2111,8 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), + OsString::from("--metrics-server-port"), + OsString::from("15000"), ], autostart: false, contents: None, @@ -2102,6 +2130,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .returning(|_, _| Ok(())) .in_sequence(&mut seq); + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(20001)) + .in_sequence(&mut seq); + // Second service mock_service_control .expect_install() @@ -2127,6 +2161,8 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), + OsString::from("--metrics-server-port"), + OsString::from("15001"), ], autostart: false, contents: None, @@ -2144,6 +2180,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .returning(|_, _| Ok(())) .in_sequence(&mut seq); + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(20002)) + .in_sequence(&mut seq); + // Third service mock_service_control .expect_install() @@ -2169,6 +2211,402 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), + OsString::from("--metrics-server-port"), + OsString::from("15002"), + ], + contents: None, + environment: None, + label: "safenode3".parse()?, + program: node_data_dir + .to_path_buf() + .join("safenode3") + .join(SAFENODE_FILE_NAME), + username: Some(get_username()), + working_directory: None, + }), + eq(false), + ) + .returning(|_, _| Ok(())) + .in_sequence(&mut seq); + + add_node( + AddNodeServiceOptions { + auto_set_nat_flags: false, + bootstrap_peers: vec![], + count: Some(3), + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + log_format: None, + metrics_port: Some(PortRange::Range(15000, 15002)), + owner: None, + node_port: None, + rpc_address: None, + rpc_port: None, + safenode_dir_path: temp_dir.to_path_buf(), + safenode_src_path: safenode_download_path.to_path_buf(), + service_data_dir_path: node_data_dir.to_path_buf(), + service_log_dir_path: node_logs_dir.to_path_buf(), + upnp: false, + user: Some(get_username()), + user_mode: false, + version: latest_version.to_string(), + }, + &mut node_registry, + &mock_service_control, + VerbosityLevel::Normal, + ) + .await?; + + safenode_download_path.assert(predicate::path::missing()); + node_data_dir.assert(predicate::path::is_dir()); + node_logs_dir.assert(predicate::path::is_dir()); + assert_eq!(node_registry.nodes.len(), 3); + assert_eq!(node_registry.nodes[0].metrics_port, Some(15000)); + assert_eq!(node_registry.nodes[1].metrics_port, Some(15001)); + assert_eq!(node_registry.nodes[2].metrics_port, Some(15002)); + + Ok(()) +} + +#[tokio::test] +async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used() -> Result<()> { + let tmp_data_dir = assert_fs::TempDir::new()?; + let node_reg_path = tmp_data_dir.child("node_reg.json"); + + let mut node_registry = NodeRegistry { + auditor: None, + faucet: None, + save_path: node_reg_path.to_path_buf(), + nat_status: None, + nodes: vec![NodeServiceData { + connected_peers: None, + data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + genesis: false, + home_network: false, + listen_addr: None, + local: false, + log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + log_format: None, + metrics_port: Some(15001), + node_port: None, + number: 1, + owner: None, + peer_id: None, + pid: None, + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"), + service_name: "safenode1".to_string(), + status: ServiceStatus::Added, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: "0.98.1".to_string(), + }], + bootstrap_peers: vec![], + environment_variables: None, + daemon: None, + }; + let latest_version = "0.96.4"; + let temp_dir = assert_fs::TempDir::new()?; + let node_data_dir = temp_dir.child("data"); + node_data_dir.create_dir_all()?; + let node_logs_dir = temp_dir.child("logs"); + node_logs_dir.create_dir_all()?; + let safenode_download_path = temp_dir.child(SAFENODE_FILE_NAME); + safenode_download_path.write_binary(b"fake safenode bin")?; + + let result = add_node( + AddNodeServiceOptions { + auto_set_nat_flags: false, + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + log_format: None, + metrics_port: Some(PortRange::Single(15001)), + owner: None, + node_port: None, + rpc_address: None, + rpc_port: None, + safenode_dir_path: temp_dir.to_path_buf(), + safenode_src_path: safenode_download_path.to_path_buf(), + service_data_dir_path: node_data_dir.to_path_buf(), + service_log_dir_path: node_logs_dir.to_path_buf(), + upnp: false, + user: Some(get_username()), + user_mode: false, + version: latest_version.to_string(), + }, + &mut node_registry, + &MockServiceControl::new(), + VerbosityLevel::Normal, + ) + .await; + + match result { + Ok(_) => panic!("This test is supposed to result in a failure"), + Err(e) => { + assert_eq!(e.to_string(), "Port 15001 is being used by another service"); + Ok(()) + } + } +} + +#[tokio::test] +async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_range_is_used( +) -> Result<()> { + let tmp_data_dir = assert_fs::TempDir::new()?; + let node_reg_path = tmp_data_dir.child("node_reg.json"); + + let mut node_registry = NodeRegistry { + auditor: None, + faucet: None, + save_path: node_reg_path.to_path_buf(), + nat_status: None, + nodes: vec![NodeServiceData { + connected_peers: None, + data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + genesis: false, + home_network: false, + listen_addr: None, + local: false, + log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + log_format: None, + metrics_port: Some(15001), + node_port: None, + number: 1, + owner: None, + peer_id: None, + pid: None, + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"), + service_name: "safenode1".to_string(), + status: ServiceStatus::Added, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: "0.98.1".to_string(), + }], + bootstrap_peers: vec![], + environment_variables: None, + daemon: None, + }; + let latest_version = "0.96.4"; + let temp_dir = assert_fs::TempDir::new()?; + let node_data_dir = temp_dir.child("data"); + node_data_dir.create_dir_all()?; + let node_logs_dir = temp_dir.child("logs"); + node_logs_dir.create_dir_all()?; + let safenode_download_path = temp_dir.child(SAFENODE_FILE_NAME); + safenode_download_path.write_binary(b"fake safenode bin")?; + + let result = add_node( + AddNodeServiceOptions { + auto_set_nat_flags: false, + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + log_format: None, + metrics_port: Some(PortRange::Range(15001, 15002)), + owner: None, + node_port: None, + rpc_address: None, + rpc_port: None, + safenode_dir_path: temp_dir.to_path_buf(), + safenode_src_path: safenode_download_path.to_path_buf(), + service_data_dir_path: node_data_dir.to_path_buf(), + service_log_dir_path: node_logs_dir.to_path_buf(), + upnp: false, + user: Some(get_username()), + user_mode: false, + version: latest_version.to_string(), + }, + &mut node_registry, + &MockServiceControl::new(), + VerbosityLevel::Normal, + ) + .await; + + match result { + Ok(_) => panic!("This test is supposed to result in a failure"), + Err(e) => { + assert_eq!(e.to_string(), "Port 15001 is being used by another service"); + Ok(()) + } + } +} + +#[tokio::test] +async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result<()> { + let tmp_data_dir = assert_fs::TempDir::new()?; + let node_reg_path = tmp_data_dir.child("node_reg.json"); + + let mut mock_service_control = MockServiceControl::new(); + + let mut node_registry = NodeRegistry { + auditor: None, + faucet: None, + save_path: node_reg_path.to_path_buf(), + nat_status: None, + nodes: vec![], + bootstrap_peers: vec![], + environment_variables: None, + daemon: None, + }; + let latest_version = "0.96.4"; + let temp_dir = assert_fs::TempDir::new()?; + let node_data_dir = temp_dir.child("data"); + node_data_dir.create_dir_all()?; + let node_logs_dir = temp_dir.child("logs"); + node_logs_dir.create_dir_all()?; + let safenode_download_path = temp_dir.child(SAFENODE_FILE_NAME); + safenode_download_path.write_binary(b"fake safenode bin")?; + + let mut seq = Sequence::new(); + + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(15001)) + .in_sequence(&mut seq); + + // First service + mock_service_control + .expect_install() + .times(1) + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:20000"), + OsString::from("--root-dir"), + OsString::from( + node_data_dir + .to_path_buf() + .join("safenode1") + .to_string_lossy() + .to_string(), + ), + OsString::from("--log-output-dest"), + OsString::from( + node_logs_dir + .to_path_buf() + .join("safenode1") + .to_string_lossy() + .to_string(), + ), + OsString::from("--metrics-server-port"), + OsString::from("15001"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: node_data_dir + .to_path_buf() + .join("safenode1") + .join(SAFENODE_FILE_NAME), + username: Some(get_username()), + working_directory: None, + }), + eq(false), + ) + .returning(|_, _| Ok(())) + .in_sequence(&mut seq); + + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(15003)) + .in_sequence(&mut seq); + + // Second service + mock_service_control + .expect_install() + .times(1) + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:20001"), + OsString::from("--root-dir"), + OsString::from( + node_data_dir + .to_path_buf() + .join("safenode2") + .to_string_lossy() + .to_string(), + ), + OsString::from("--log-output-dest"), + OsString::from( + node_logs_dir + .to_path_buf() + .join("safenode2") + .to_string_lossy() + .to_string(), + ), + OsString::from("--metrics-server-port"), + OsString::from("15003"), + ], + contents: None, + environment: None, + label: "safenode2".parse()?, + program: node_data_dir + .to_path_buf() + .join("safenode2") + .join(SAFENODE_FILE_NAME), + username: Some(get_username()), + working_directory: None, + }), + eq(false), + ) + .returning(|_, _| Ok(())) + .in_sequence(&mut seq); + + mock_service_control + .expect_get_available_port() + .times(1) + .returning(|| Ok(15005)) + .in_sequence(&mut seq); + + // Third service + mock_service_control + .expect_install() + .times(1) + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:20002"), + OsString::from("--root-dir"), + OsString::from( + node_data_dir + .to_path_buf() + .join("safenode3") + .to_string_lossy() + .to_string(), + ), + OsString::from("--log-output-dest"), + OsString::from( + node_logs_dir + .to_path_buf() + .join("safenode3") + .to_string_lossy() + .to_string(), + ), + OsString::from("--metrics-server-port"), + OsString::from("15005"), ], autostart: false, contents: None, @@ -2446,10 +2884,11 @@ async fn add_node_should_disable_upnp_and_home_network_if_nat_status_is_public() let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -2462,7 +2901,7 @@ async fn add_node_should_disable_upnp_and_home_network_if_nat_status_is_public() local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -2548,10 +2987,11 @@ async fn add_node_should_enable_upnp_if_nat_status_is_upnp() -> Result<()> { let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -2564,7 +3004,7 @@ async fn add_node_should_enable_upnp_if_nat_status_is_upnp() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -2650,10 +3090,11 @@ async fn add_node_should_enable_home_network_if_nat_status_is_private() -> Resul let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -2666,7 +3107,7 @@ async fn add_node_should_enable_home_network_if_nat_status_is_private() -> Resul local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -2753,10 +3194,11 @@ async fn add_node_should_return_an_error_if_nat_status_is_none_but_auto_set_nat_ let mut seq = Sequence::new(); + let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(12001)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let result = add_node( @@ -3366,10 +3808,11 @@ async fn add_node_should_not_delete_the_source_binary_if_path_arg_is_used() -> R let mut seq = Sequence::new(); // Expected calls for first installation + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3382,7 +3825,7 @@ async fn add_node_should_not_delete_the_source_binary_if_path_arg_is_used() -> R local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -3470,10 +3913,11 @@ async fn add_node_should_apply_the_home_network_flag_if_it_is_used() -> Result<( let mut seq = Sequence::new(); // Expected calls for first installation + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3486,7 +3930,7 @@ async fn add_node_should_apply_the_home_network_flag_if_it_is_used() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -3574,10 +4018,11 @@ async fn add_node_should_add_the_node_in_user_mode() -> Result<()> { let mut seq = Sequence::new(); // Expected calls for first installation + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3590,7 +4035,7 @@ async fn add_node_should_add_the_node_in_user_mode() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -3675,10 +4120,11 @@ async fn add_node_should_add_the_node_with_upnp_enabled() -> Result<()> { let mut seq = Sequence::new(); + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3691,7 +4137,7 @@ async fn add_node_should_add_the_node_with_upnp_enabled() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: None, + metrics_port: Some(15001), name: "safenode1".to_string(), node_port: None, owner: None, @@ -3777,10 +4223,11 @@ async fn add_node_should_assign_an_owner() -> Result<()> { let mut mock_service_control = MockServiceControl::new(); let mut seq = Sequence::new(); + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) - .returning(|| Ok(8081)) + .times(2) + .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control @@ -3806,6 +4253,8 @@ async fn add_node_should_assign_an_owner() -> Result<()> { .to_string_lossy() .to_string(), ), + OsString::from("--metrics-server-port"), + OsString::from("15001"), OsString::from("--owner"), OsString::from("discord_username"), ], From fa5ebe3b7f6105619ca16fc3d4a936d6a12a7508 Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Fri, 31 May 2024 21:22:59 +0530 Subject: [PATCH 2/8] chore(network): set metrics server to run on localhost --- sn_networking/src/metrics_service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sn_networking/src/metrics_service.rs b/sn_networking/src/metrics_service.rs index 77bed120d2..a7c6ca6172 100644 --- a/sn_networking/src/metrics_service.rs +++ b/sn_networking/src/metrics_service.rs @@ -19,8 +19,8 @@ use std::{ const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0"; pub(crate) fn run_metrics_server(registry: Registry, port: u16) { - // The server should not bind to localhost/127.0.0.1 as it will not accept connections from containers. - let addr = ([0, 0, 0, 0], port).into(); + // todo: containers don't work with localhost. + let addr = ([127, 0, 0, 1], port).into(); tokio::spawn(async move { let server = Server::bind(&addr).serve(MakeMetricService::new(registry)); From 4a73c3ecc0655e1f8421b72107d62dc448a7884f Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Fri, 31 May 2024 21:59:00 +0530 Subject: [PATCH 3/8] feat(node): expose cumulative forwarded reward as metric and cache it locally --- sn_node/src/metrics.rs | 19 +++++-- sn_node/src/node.rs | 94 ++++++++++++++++++++++++++++++++--- sn_node/src/put_validation.rs | 4 +- 3 files changed, 103 insertions(+), 14 deletions(-) diff --git a/sn_node/src/metrics.rs b/sn_node/src/metrics.rs index ed23e550b6..b139d4d265 100644 --- a/sn_node/src/metrics.rs +++ b/sn_node/src/metrics.rs @@ -33,7 +33,8 @@ pub(crate) struct NodeMetrics { peer_removed_from_routing_table: Counter, // wallet - pub(crate) reward_wallet_balance: Gauge, + pub(crate) current_reward_wallet_balance: Gauge, + pub(crate) total_forwarded_rewards: Gauge, } #[derive(EncodeLabelSet, Hash, Clone, Eq, PartialEq, Debug)] @@ -94,11 +95,18 @@ impl NodeMetrics { peer_removed_from_routing_table.clone(), ); - let reward_wallet_balance = Gauge::default(); + let current_reward_wallet_balance = Gauge::default(); sub_registry.register( - "reward_wallet_balance", + "current_reward_wallet_balance", "The number of Nanos in the node reward wallet", - reward_wallet_balance.clone(), + current_reward_wallet_balance.clone(), + ); + + let total_forwarded_rewards = Gauge::default(); + sub_registry.register( + "total_forwarded_rewards", + "The cumulative number of Nanos forwarded by the node", + total_forwarded_rewards.clone(), ); Self { @@ -108,7 +116,8 @@ impl NodeMetrics { replication_keys_to_fetch, peer_added_to_routing_table, peer_removed_from_routing_table, - reward_wallet_balance, + current_reward_wallet_balance, + total_forwarded_rewards, } } diff --git a/sn_node/src/node.rs b/sn_node/src/node.rs index 954a5f15b3..fd2394ffd0 100644 --- a/sn_node/src/node.rs +++ b/sn_node/src/node.rs @@ -15,10 +15,11 @@ use super::{ #[cfg(feature = "open-metrics")] use crate::metrics::NodeMetrics; use crate::RunningNode; - use bytes::Bytes; use libp2p::{identity::Keypair, Multiaddr, PeerId}; #[cfg(feature = "open-metrics")] +use prometheus_client::metrics::gauge::Gauge; +#[cfg(feature = "open-metrics")] use prometheus_client::registry::Registry; use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; use sn_networking::{ @@ -53,16 +54,20 @@ use sn_networking::PutRecordCfg; use sn_protocol::storage::{try_serialize_record, RecordKind, SpendAddress}; /// Interval to trigger replication of all records to all peers. -/// This is the max time it should take. Minimum interval at any ndoe will be half this +/// This is the max time it should take. Minimum interval at any node will be half this pub const PERIODIC_REPLICATION_INTERVAL_MAX_S: u64 = 45; /// Max number of attempts that chunk proof verification will be carried out against certain target, /// before classifying peer as a bad peer. const MAX_CHUNK_PROOF_VERIFY_ATTEMPTS: usize = 3; -/// Invertal between chunk proof verfication to be retired against the same target. +/// Interval between chunk proof verification to be retired against the same target. const CHUNK_PROOF_VERIFY_RETRY_INTERVAL: Duration = Duration::from_secs(15); +#[cfg(feature = "reward-forward")] +/// Track the forward balance by storing the balance in a file. This is useful to restore the balance between restarts. +const FORWARDED_BALANCE_FILE_NAME: &str = "forwarded_balance"; + /// Helper to build and run a Node pub struct NodeBuilder { keypair: Keypair, @@ -219,6 +224,20 @@ impl Node { let peers_connected = Arc::new(AtomicUsize::new(0)); let mut cmds_receiver = self.node_cmds.subscribe(); + // read the forwarded balance from the file and set the metric. + // This is done initially because reward forwarding takes a while to kick in + #[cfg(all(feature = "reward-forward", feature = "open-metrics"))] + let node_copy = self.clone(); + #[cfg(all(feature = "reward-forward", feature = "open-metrics"))] + let _handle = spawn(async move { + let root_dir = node_copy.network.root_dir_path; + let balance = read_forwarded_balance_value(&root_dir); + + if let Some(ref node_metrics) = node_copy.node_metrics { + let _ = node_metrics.total_forwarded_rewards.set(balance as i64); + } + }); + let _handle = spawn(swarm_driver.run()); let _handle = spawn(async move { // use a random inactivity timeout to ensure that the nodes do not sync when messages @@ -315,9 +334,16 @@ impl Node { let network = self.network.clone(); let forwarding_reason = owner.clone(); + #[cfg(feature = "open-metrics")] + let total_forwarded_rewards = self.node_metrics.as_ref().map(|metrics|metrics.total_forwarded_rewards.clone()); + let _handle = spawn(async move { - let _ = Self::try_forward_blance(network, forwarding_reason); - info!("Periodic blance forward took {:?}", start.elapsed()); + + #[cfg(feature = "open-metrics")] + let _ = Self::try_forward_balance(network, forwarding_reason, total_forwarded_rewards); + #[cfg(not(feature = "open-metrics"))] + let _ = Self::try_forward_balance(network, forwarding_reason); + info!("Periodic balance forward took {:?}", start.elapsed()); }); } @@ -765,8 +791,39 @@ impl Node { } } + #[cfg(not(feature = "open-metrics"))] + fn try_forward_balance(network: Network, forward_reason: String) -> Result<()> { + if let Err(err) = Self::try_forward_balance_inner(network, forward_reason) { + error!("Error while trying to forward balance: {err:?}"); + return Err(err); + } + Ok(()) + } + + #[cfg(feature = "open-metrics")] + fn try_forward_balance( + network: Network, + forward_reason: String, + forwarded_balance_metric: Option, + ) -> Result<()> { + match Self::try_forward_balance_inner(network, forward_reason) { + Ok(cumulative_forwarded_amount) => { + if let Some(forwarded_balance_metric) = forwarded_balance_metric { + let _ = forwarded_balance_metric.set(cumulative_forwarded_amount as i64); + } + } + Err(err) => { + error!("Error while trying to forward balance: {err:?}"); + return Err(err); + } + }; + + Ok(()) + } + /// Forward received rewards to another address - fn try_forward_blance(network: Network, forward_reason: String) -> Result<()> { + /// Returns the cumulative amount forwarded + fn try_forward_balance_inner(network: Network, forward_reason: String) -> Result { let mut spend_requests = vec![]; { // load wallet @@ -777,6 +834,10 @@ impl Node { spend_requests.extend(wallet.prepare_forward_signed_spend(payee, forward_reason)?); } + let total_forwarded_amount = spend_requests + .iter() + .map(|s| s.token().as_nano()) + .sum::(); let record_kind = RecordKind::Spend; let put_cfg = PutRecordCfg { @@ -787,7 +848,7 @@ impl Node { }; info!( - "Reward forwarding sending {} spends in this iteration.", + "Reward forwarding sending {} spends in this iteration. Total forwarded amount: {total_forwarded_amount}", spend_requests.len() ); @@ -833,7 +894,24 @@ impl Node { std::thread::sleep(Duration::from_millis(500)); } - Ok(()) + // write the balance to a file + let balance_file_path = network.root_dir_path.join(FORWARDED_BALANCE_FILE_NAME); + let old_balance = read_forwarded_balance_value(&balance_file_path); + let updated_balance = old_balance + total_forwarded_amount; + if let Err(err) = std::fs::write(&balance_file_path, updated_balance.to_string()) { + error!( + "Failed to write the updated balance to the file {balance_file_path:?} with {err:?}" + ); + } + + Ok(updated_balance) + } +} + +fn read_forwarded_balance_value(balance_file_path: &PathBuf) -> u64 { + match std::fs::read_to_string(balance_file_path) { + Ok(balance) => balance.parse::().unwrap_or(0), + Err(_) => 0, } } diff --git a/sn_node/src/put_validation.rs b/sn_node/src/put_validation.rs index 6988a8a9ce..cd309595a3 100644 --- a/sn_node/src/put_validation.rs +++ b/sn_node/src/put_validation.rs @@ -510,7 +510,9 @@ impl Node { #[cfg(feature = "open-metrics")] if let Some(node_metrics) = &self.node_metrics { - let _ = node_metrics.reward_wallet_balance.set(new_balance as i64); + let _ = node_metrics + .current_reward_wallet_balance + .set(new_balance as i64); } if royalties_cash_notes_r.is_empty() { From fb1d9d1028fc08aaec9c5e8cf342e6ca30862e2c Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Mon, 3 Jun 2024 19:40:05 +0530 Subject: [PATCH 4/8] chore(launchpad): update log folder structure --- node-launchpad/src/utils.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/node-launchpad/src/utils.rs b/node-launchpad/src/utils.rs index aad31d5fb8..7de61f7072 100644 --- a/node-launchpad/src/utils.rs +++ b/node-launchpad/src/utils.rs @@ -77,11 +77,9 @@ pub fn initialize_panic_handler() -> Result<()> { pub fn initialize_logging() -> Result<()> { let timestamp = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S").to_string(); - let log_path = get_launchpad_data_dir_path()? - .join("logs") - .join(format!("log_{timestamp}")); + let log_path = get_launchpad_data_dir_path()?.join("logs"); std::fs::create_dir_all(&log_path)?; - let log_file = std::fs::File::create(log_path.join("launchpad.log")) + let log_file = std::fs::File::create(log_path.join(format!("launchpad_{timestamp}.log"))) .context(format!("Failed to create file {log_path:?}"))?; std::env::set_var( "RUST_LOG", From b977bde5158b920736c1b9dddf6e67cd1864d8bc Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Mon, 3 Jun 2024 20:17:00 +0530 Subject: [PATCH 5/8] feat(launchpad): obtain stats from the metrics endpoint --- Cargo.lock | 14 +++ node-launchpad/Cargo.toml | 4 + node-launchpad/src/action.rs | 10 +- node-launchpad/src/components/home.rs | 134 +++------------------- node-launchpad/src/lib.rs | 1 + node-launchpad/src/node_stats.rs | 159 ++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 122 deletions(-) create mode 100644 node-launchpad/src/node_stats.rs diff --git a/Cargo.lock b/Cargo.lock index 08d2d55cb6..62096282aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4714,8 +4714,10 @@ dependencies = [ "log", "nix 0.28.0", "pretty_assertions", + "prometheus-parse", "rand 0.8.5", "ratatui", + "reqwest 0.12.4", "serde", "serde_json", "signal-hook", @@ -5556,6 +5558,18 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "prometheus-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "811031bea65e5a401fb2e1f37d802cca6601e204ac463809a3189352d13b78a5" +dependencies = [ + "chrono", + "itertools 0.12.1", + "once_cell", + "regex", +] + [[package]] name = "proptest" version = "1.4.0" diff --git a/node-launchpad/Cargo.toml b/node-launchpad/Cargo.toml index ab31f02066..d0865cb826 100644 --- a/node-launchpad/Cargo.toml +++ b/node-launchpad/Cargo.toml @@ -42,8 +42,12 @@ libc = "0.2.148" log = "0.4.20" nix = { version = "0.28.0", features = ["user"] } pretty_assertions = "1.4.0" +prometheus-parse = "0.2.5" rand = "0.8.5" ratatui = { version = "0.26.0", features = ["serde", "macros", "unstable-widget-ref"] } +reqwest = { version = "0.12.2", default-features = false, features = [ + "rustls-tls-manual-roots", +] } serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" signal-hook = "0.3.17" diff --git a/node-launchpad/src/action.rs b/node-launchpad/src/action.rs index 4bc6fc48eb..2b63773da4 100644 --- a/node-launchpad/src/action.rs +++ b/node-launchpad/src/action.rs @@ -6,7 +6,10 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use crate::mode::{InputMode, Scene}; +use crate::{ + mode::{InputMode, Scene}, + node_stats::NodeStats, +}; use serde::{Deserialize, Serialize}; use strum::Display; @@ -41,10 +44,7 @@ pub enum HomeActions { SuccessfullyDetectedNatStatus, ErrorWhileRunningNatDetection, - NodesStatsObtained { - wallet_balance: u64, - space_used: u64, - }, + NodesStatsObtained(NodeStats), TriggerBetaProgramme, TriggerManageNodes, diff --git a/node-launchpad/src/components/home.rs b/node-launchpad/src/components/home.rs index 95dd82c861..1039c184df 100644 --- a/node-launchpad/src/components/home.rs +++ b/node-launchpad/src/components/home.rs @@ -15,21 +15,16 @@ use crate::{ action::{Action, HomeActions}, config::Config, mode::{InputMode, Scene}, + node_stats::NodeStats, style::{clear_area, COOL_GREY, EUCALYPTUS, GHOST_WHITE, LIGHT_PERIWINKLE, VERY_LIGHT_AZURE}, }; use color_eyre::eyre::{OptionExt, Result}; -use fs_extra::dir::get_size; -use futures::StreamExt; use rand::seq::SliceRandom; use ratatui::{prelude::*, widgets::*}; use sn_node_manager::{config::get_node_registry_path, VerbosityLevel}; use sn_peers_acquisition::{get_bootstrap_peers_from_url, PeersArgs}; -use sn_service_management::{ - rpc::{RpcActions, RpcClient}, - NodeRegistry, NodeServiceData, ServiceStatus, -}; +use sn_service_management::{NodeRegistry, NodeServiceData, ServiceStatus}; use std::{ - net::SocketAddr, path::PathBuf, time::{Duration, Instant}, }; @@ -51,7 +46,8 @@ pub struct Home { node_services: Vec, is_nat_status_determined: bool, error_while_running_nat_detection: usize, - node_stats: NodesStats, + node_stats: NodeStats, + node_stats_last_update: Instant, node_table_state: TableState, nodes_to_start: usize, discord_username: String, @@ -85,7 +81,8 @@ impl Home { node_services: Default::default(), is_nat_status_determined: false, error_while_running_nat_detection: 0, - node_stats: NodesStats::new(), + node_stats: NodeStats::default(), + node_stats_last_update: Instant::now(), nodes_to_start: allocated_disk_space, node_table_state: Default::default(), lock_registry: None, @@ -100,10 +97,10 @@ impl Home { /// Tries to trigger the update of node stats if the last update was more than `NODE_STAT_UPDATE_INTERVAL` ago. /// The result is sent via the HomeActions::NodesStatsObtained action. fn try_update_node_stats(&mut self, force_update: bool) -> Result<()> { - if self.node_stats.last_update.elapsed() > NODE_STAT_UPDATE_INTERVAL || force_update { - self.node_stats.last_update = Instant::now(); + if self.node_stats_last_update.elapsed() > NODE_STAT_UPDATE_INTERVAL || force_update { + self.node_stats_last_update = Instant::now(); - NodesStats::fetch_all_node_stats(&self.node_services, self.get_actions_sender()?); + NodeStats::fetch_all_node_stats(&self.node_services, self.get_actions_sender()?); } Ok(()) } @@ -282,12 +279,8 @@ impl Component for Home { Action::Tick => { self.try_update_node_stats(false)?; } - Action::HomeActions(HomeActions::NodesStatsObtained { - wallet_balance, - space_used, - }) => { - self.node_stats.wallet_balance = wallet_balance; - self.node_stats.space_used = space_used; + Action::HomeActions(HomeActions::NodesStatsObtained(stats)) => { + self.node_stats = stats; } Action::HomeActions(HomeActions::StartNodesCompleted) | Action::HomeActions(HomeActions::StopNodesCompleted) => { @@ -425,14 +418,14 @@ impl Component for Home { let stats_rows = vec![Row::new(vec![ self.node_stats.wallet_balance.to_string(), space_used_value, - // self.node_stats.memory_usage.to_string(), - // self.node_stats.network_usage.to_string(), + self.node_stats.memory_usage_mb.to_string(), + self.node_stats.network_usage.to_string(), ])]; let stats_width = [ Constraint::Min(15), Constraint::Min(10), - // Constraint::Min(10), - // Constraint::Min(10), + Constraint::Min(10), + Constraint::Min(10), ]; let stats_table = Table::new(stats_rows, stats_width) .column_spacing(2) @@ -440,8 +433,8 @@ impl Component for Home { Row::new(vec![ "Wallet Balance", space_used_header.as_str(), - // "Memory usage", - // "Network Usage", + "Memory usage (MB)", + "Network Usage", ]) .style(Style::new().bold().fg(GHOST_WHITE)), ) @@ -454,7 +447,6 @@ impl Component for Home { .style(Style::default().fg(VERY_LIGHT_AZURE)), ); f.render_widget(stats_table, layer_zero[1]); - // "todo: display a table".to_string() }; // ==== Node Status ===== @@ -671,95 +663,3 @@ fn reset_nodes(action_sender: UnboundedSender) { } }); } - -/// The stats of all the running nodes -/// todo: certain stats like wallet balance, space used can be calculated even if the node is offline. -struct NodesStats { - pub wallet_balance: u64, - pub space_used: u64, - // pub memory_usage: usize, - // pub network_usage: usize, - pub last_update: Instant, -} - -impl NodesStats { - pub fn new() -> Self { - Self { - wallet_balance: 0, - space_used: 0, - // memory_usage: 0, - // network_usage: 0, - last_update: Instant::now(), - } - } - - pub fn fetch_all_node_stats(nodes: &[NodeServiceData], action_sender: UnboundedSender) { - let node_details = nodes - .iter() - .filter_map(|node| { - if node.status == ServiceStatus::Running { - Some(( - node.service_name.clone(), - node.rpc_socket_addr, - node.data_dir_path.clone(), - )) - } else { - None - } - }) - .collect::>(); - - tokio::task::spawn_local(async move { - Self::fetch_all_node_stats_inner(node_details, action_sender).await; - }); - } - - async fn fetch_all_node_stats_inner( - node_details: Vec<(String, SocketAddr, PathBuf)>, - action_sender: UnboundedSender, - ) { - let mut stream = futures::stream::iter(node_details) - .map(|(service_name, rpc_addr, data_dir)| async move { - ( - Self::fetch_stat_per_node(rpc_addr, data_dir).await, - service_name, - ) - }) - .buffer_unordered(5); - - let mut all_wallet_balance = 0; - let mut all_space_used = 0; - - while let Some((result, service_name)) = stream.next().await { - match result { - Ok((wallet_balance, space_used)) => { - info!("Wallet balance: {wallet_balance}, Space used: {space_used}"); - all_wallet_balance += wallet_balance; - all_space_used += space_used; - } - Err(err) => { - error!("Error while fetching stats from {service_name:?}: {err:?}"); - } - } - } - - if let Err(err) = action_sender.send(Action::HomeActions(HomeActions::NodesStatsObtained { - wallet_balance: all_wallet_balance, - space_used: all_space_used, - })) { - error!("Error while sending action: {err:?}"); - } - } - - // todo: get all the stats - async fn fetch_stat_per_node(rpc_addr: SocketAddr, data_dir: PathBuf) -> Result<(u64, u64)> { - let now = Instant::now(); - let rpc_client = RpcClient::from_socket_addr(rpc_addr); - let wallet_balance = rpc_client.node_info().await?.wallet_balance; - - let space_used = get_size(data_dir)?; - - debug!("Fetched stats from {rpc_addr:?} in {:?}", now.elapsed()); - Ok((wallet_balance, space_used)) - } -} diff --git a/node-launchpad/src/lib.rs b/node-launchpad/src/lib.rs index 98ede28787..8e9ba7b8d4 100644 --- a/node-launchpad/src/lib.rs +++ b/node-launchpad/src/lib.rs @@ -11,6 +11,7 @@ pub mod app; pub mod components; pub mod config; pub mod mode; +pub mod node_stats; pub mod style; pub mod tui; pub mod utils; diff --git a/node-launchpad/src/node_stats.rs b/node-launchpad/src/node_stats.rs new file mode 100644 index 0000000000..52e205cbf9 --- /dev/null +++ b/node-launchpad/src/node_stats.rs @@ -0,0 +1,159 @@ +// Copyright 2024 MaidSafe.net limited. +// +// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. +// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed +// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. Please review the Licences for the specific language governing +// permissions and limitations relating to use of the SAFE Network Software. + +use color_eyre::Result; +use fs_extra::dir::get_size; +use futures::StreamExt; +use serde::{Deserialize, Serialize}; +use sn_service_management::{NodeServiceData, ServiceStatus}; +use std::{path::PathBuf, time::Instant}; +use tokio::sync::mpsc::UnboundedSender; + +use crate::action::{Action, HomeActions}; + +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct NodeStats { + pub wallet_balance: u64, + pub forwarded_rewards: u64, + pub space_used: u64, + pub memory_usage_mb: usize, + pub network_usage: usize, +} + +impl NodeStats { + fn merge(&mut self, other: &NodeStats) { + self.wallet_balance += other.wallet_balance; + self.forwarded_rewards += other.forwarded_rewards; + self.space_used += other.space_used; + self.memory_usage_mb += other.memory_usage_mb; + self.network_usage += other.network_usage; + } + + pub fn fetch_all_node_stats(nodes: &[NodeServiceData], action_sender: UnboundedSender) { + let node_details = nodes + .iter() + .filter_map(|node| { + if node.status == ServiceStatus::Running { + if let Some(metrics_port) = node.metrics_port { + Some(( + node.service_name.clone(), + metrics_port, + node.data_dir_path.clone(), + )) + } else { + error!( + "No metrics port found for {:?}. Skipping stat fetch.", + node.service_name + ); + None + } + } else { + None + } + }) + .collect::>(); + if node_details.is_empty() { + info!("No running nodes to fetch stats from."); + return; + } else { + info!("Fetching stats from {} nodes", node_details.len()); + tokio::task::spawn_local(async move { + Self::fetch_all_node_stats_inner(node_details, action_sender).await; + }); + } + } + + async fn fetch_all_node_stats_inner( + node_details: Vec<(String, u16, PathBuf)>, + action_sender: UnboundedSender, + ) { + let mut stream = futures::stream::iter(node_details) + .map(|(service_name, metrics_port, data_dir)| async move { + ( + Self::fetch_stat_per_node(metrics_port, data_dir).await, + service_name, + ) + }) + .buffer_unordered(5); + + let mut all_node_stats = NodeStats::default(); + + while let Some((result, service_name)) = stream.next().await { + match result { + Ok(stats) => { + info!("Obtained node stats from {service_name:?}"); + all_node_stats.merge(&stats); + } + Err(err) => { + error!("Error while fetching stats from {service_name:?}: {err:?}"); + } + } + } + + if let Err(err) = action_sender.send(Action::HomeActions(HomeActions::NodesStatsObtained( + all_node_stats, + ))) { + error!("Error while sending action: {err:?}"); + } + } + + async fn fetch_stat_per_node(metrics_port: u16, data_dir: PathBuf) -> Result { + let now = Instant::now(); + + let body = reqwest::get(&format!("http://localhost:{metrics_port}/metrics")) + .await? + .text() + .await?; + let lines: Vec<_> = body.lines().map(|s| Ok(s.to_owned())).collect(); + let all_metrics = prometheus_parse::Scrape::parse(lines.into_iter())?; + + let mut stats = NodeStats { + wallet_balance: 0, + space_used: get_size(data_dir)?, + memory_usage_mb: 0, + network_usage: 0, + forwarded_rewards: 0, + }; + for sample in all_metrics.samples.iter() { + if sample.metric == "sn_node_current_reward_wallet_balance" { + match sample.value { + prometheus_parse::Value::Counter(val) + | prometheus_parse::Value::Gauge(val) + | prometheus_parse::Value::Untyped(val) => { + stats.wallet_balance = val as u64; + } + _ => {} + } + } else if sample.metric == "sn_networking_process_memory_used_mb" { + match sample.value { + prometheus_parse::Value::Counter(val) + | prometheus_parse::Value::Gauge(val) + | prometheus_parse::Value::Untyped(val) => { + stats.memory_usage_mb = val as usize; + } + _ => {} + } + } else if sample.metric == "sn_node_total_forwarded_rewards" { + match sample.value { + prometheus_parse::Value::Counter(val) + | prometheus_parse::Value::Gauge(val) + | prometheus_parse::Value::Untyped(val) => { + stats.forwarded_rewards = val as u64; + } + _ => {} + } + } + + debug!( + "Fetched stats from metrics_port {metrics_port:?} in {:?}", + now.elapsed() + ); + } + Ok(stats) + } +} From 7ee168f6d8fdfd8bdc3b7915ac44d0b3624066dd Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Mon, 3 Jun 2024 21:31:51 +0530 Subject: [PATCH 6/8] fix(launchpad): modify the device status panel --- node-launchpad/src/components/home.rs | 94 ++++++++++++--------------- node-launchpad/src/node_stats.rs | 23 +------ 2 files changed, 45 insertions(+), 72 deletions(-) diff --git a/node-launchpad/src/components/home.rs b/node-launchpad/src/components/home.rs index 1039c184df..c511a82d32 100644 --- a/node-launchpad/src/components/home.rs +++ b/node-launchpad/src/components/home.rs @@ -6,17 +6,16 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use super::{ - manage_nodes::{GB, MB}, - utils::centered_rect_fixed, - Component, Frame, -}; +use super::{manage_nodes::GB_PER_NODE, utils::centered_rect_fixed, Component, Frame}; use crate::{ action::{Action, HomeActions}, config::Config, mode::{InputMode, Scene}, node_stats::NodeStats, - style::{clear_area, COOL_GREY, EUCALYPTUS, GHOST_WHITE, LIGHT_PERIWINKLE, VERY_LIGHT_AZURE}, + style::{ + clear_area, COOL_GREY, EUCALYPTUS, GHOST_WHITE, LIGHT_PERIWINKLE, VERY_LIGHT_AZURE, + VIVID_SKY_BLUE, + }, }; use color_eyre::eyre::{OptionExt, Result}; use rand::seq::SliceRandom; @@ -27,11 +26,12 @@ use sn_service_management::{NodeRegistry, NodeServiceData, ServiceStatus}; use std::{ path::PathBuf, time::{Duration, Instant}, + vec, }; use tokio::sync::mpsc::UnboundedSender; const NODE_START_INTERVAL: usize = 10; -const NODE_STAT_UPDATE_INTERVAL: Duration = Duration::from_secs(15); +const NODE_STAT_UPDATE_INTERVAL: Duration = Duration::from_secs(5); const NAT_DETECTION_SERVERS_LIST_URL: &str = "https://sn-testnet.s3.eu-west-2.amazonaws.com/nat-detection-servers"; /// If nat detection fails for more than 3 times, we don't want to waste time running during every node start. @@ -400,52 +400,44 @@ impl Component for Home { ); } else { // display stats as a table - let (space_used_value, space_used_header) = { - // if space used within 1GB, display in mb - if self.node_stats.space_used as f64 / (MB as f64) < (MB as f64) { - ( - format!("{:.2}", self.node_stats.space_used as f64 / MB as f64), - "Space Used (MB)".to_string(), - ) - } else { - // else display in gb - ( - format!("{:.2}", self.node_stats.space_used as f64 / GB as f64), - "Space Used (GB)".to_string(), - ) - } + + let storage_allocated_row = Row::new(vec![ + Cell::new("Storage Allocated".to_string()).fg(GHOST_WHITE), + Cell::new(format!("{} GB", self.nodes_to_start * GB_PER_NODE)).fg(GHOST_WHITE), + ]); + let memory_use_val = if self.node_stats.memory_usage_mb as f64 / 1024 as f64 > 1.0 { + format!( + "{:.2} GB", + self.node_stats.memory_usage_mb as f64 / 1024 as f64 + ) + } else { + format!("{} MB", self.node_stats.memory_usage_mb) }; - let stats_rows = vec![Row::new(vec![ - self.node_stats.wallet_balance.to_string(), - space_used_value, - self.node_stats.memory_usage_mb.to_string(), - self.node_stats.network_usage.to_string(), - ])]; - let stats_width = [ - Constraint::Min(15), - Constraint::Min(10), - Constraint::Min(10), - Constraint::Min(10), + + let memory_use_row = Row::new(vec![ + Cell::new("Memory Use".to_string()).fg(GHOST_WHITE), + Cell::new(memory_use_val).fg(GHOST_WHITE), + ]); + let total_nanos_earned_row = Row::new(vec![ + Cell::new("Total Nanos Earned".to_string()).fg(VIVID_SKY_BLUE), + Cell::new(self.node_stats.forwarded_rewards.to_string()) + .fg(VIVID_SKY_BLUE) + .bold(), + ]); + let stats_rows = vec![ + storage_allocated_row, + memory_use_row.bottom_margin(2), + total_nanos_earned_row, ]; - let stats_table = Table::new(stats_rows, stats_width) - .column_spacing(2) - .header( - Row::new(vec![ - "Wallet Balance", - space_used_header.as_str(), - "Memory usage (MB)", - "Network Usage", - ]) - .style(Style::new().bold().fg(GHOST_WHITE)), - ) - .block( - Block::default() - .title("Device Status") - .title_style(Style::default().fg(GHOST_WHITE)) - .borders(Borders::ALL) - .padding(Padding::uniform(1)) - .style(Style::default().fg(VERY_LIGHT_AZURE)), - ); + let stats_width = [Constraint::Max(25), Constraint::Min(5)]; + let stats_table = Table::new(stats_rows, stats_width).block( + Block::default() + .title("Device Status") + .title_style(Style::default().fg(GHOST_WHITE)) + .borders(Borders::ALL) + .padding(Padding::uniform(1)) + .style(Style::default().fg(VERY_LIGHT_AZURE)), + ); f.render_widget(stats_table, layer_zero[1]); }; diff --git a/node-launchpad/src/node_stats.rs b/node-launchpad/src/node_stats.rs index 52e205cbf9..d20cf3e9d0 100644 --- a/node-launchpad/src/node_stats.rs +++ b/node-launchpad/src/node_stats.rs @@ -7,7 +7,6 @@ // permissions and limitations relating to use of the SAFE Network Software. use color_eyre::Result; -use fs_extra::dir::get_size; use futures::StreamExt; use serde::{Deserialize, Serialize}; use sn_service_management::{NodeServiceData, ServiceStatus}; @@ -18,20 +17,14 @@ use crate::action::{Action, HomeActions}; #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct NodeStats { - pub wallet_balance: u64, pub forwarded_rewards: u64, - pub space_used: u64, pub memory_usage_mb: usize, - pub network_usage: usize, } impl NodeStats { fn merge(&mut self, other: &NodeStats) { - self.wallet_balance += other.wallet_balance; self.forwarded_rewards += other.forwarded_rewards; - self.space_used += other.space_used; self.memory_usage_mb += other.memory_usage_mb; - self.network_usage += other.network_usage; } pub fn fetch_all_node_stats(nodes: &[NodeServiceData], action_sender: UnboundedSender) { @@ -102,7 +95,7 @@ impl NodeStats { } } - async fn fetch_stat_per_node(metrics_port: u16, data_dir: PathBuf) -> Result { + async fn fetch_stat_per_node(metrics_port: u16, _data_dir: PathBuf) -> Result { let now = Instant::now(); let body = reqwest::get(&format!("http://localhost:{metrics_port}/metrics")) @@ -113,23 +106,11 @@ impl NodeStats { let all_metrics = prometheus_parse::Scrape::parse(lines.into_iter())?; let mut stats = NodeStats { - wallet_balance: 0, - space_used: get_size(data_dir)?, memory_usage_mb: 0, - network_usage: 0, forwarded_rewards: 0, }; for sample in all_metrics.samples.iter() { - if sample.metric == "sn_node_current_reward_wallet_balance" { - match sample.value { - prometheus_parse::Value::Counter(val) - | prometheus_parse::Value::Gauge(val) - | prometheus_parse::Value::Untyped(val) => { - stats.wallet_balance = val as u64; - } - _ => {} - } - } else if sample.metric == "sn_networking_process_memory_used_mb" { + if sample.metric == "sn_networking_process_memory_used_mb" { match sample.value { prometheus_parse::Value::Counter(val) | prometheus_parse::Value::Gauge(val) From 43bbf388e23cc4d8b30af52a269709af6d108284 Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Mon, 3 Jun 2024 23:16:14 +0530 Subject: [PATCH 7/8] feat(manager): provide option to start metrics server using random ports --- sn_node_manager/src/add_services/config.rs | 1 + sn_node_manager/src/add_services/mod.rs | 4 +- sn_node_manager/src/add_services/tests.rs | 563 +++++---------------- sn_node_manager/src/bin/cli/main.rs | 10 +- sn_node_manager/src/cmd/node.rs | 4 + 5 files changed, 146 insertions(+), 436 deletions(-) diff --git a/sn_node_manager/src/add_services/config.rs b/sn_node_manager/src/add_services/config.rs index 0031ac4ee1..79295aac6d 100644 --- a/sn_node_manager/src/add_services/config.rs +++ b/sn_node_manager/src/add_services/config.rs @@ -132,6 +132,7 @@ pub struct AddNodeServiceOptions { pub bootstrap_peers: Vec, pub count: Option, pub delete_safenode_src: bool, + pub enable_metrics_server: bool, pub env_variables: Option>, pub genesis: bool, pub home_network: bool, diff --git a/sn_node_manager/src/add_services/mod.rs b/sn_node_manager/src/add_services/mod.rs index e896ceaf9f..87ca6ae120 100644 --- a/sn_node_manager/src/add_services/mod.rs +++ b/sn_node_manager/src/add_services/mod.rs @@ -146,8 +146,10 @@ pub async fn add_node( }; let metrics_free_port = if let Some(port) = metrics_port { Some(port) - } else { + } else if options.enable_metrics_server { Some(service_control.get_available_port()?) + } else { + None }; let rpc_socket_addr = if let Some(addr) = options.rpc_address { diff --git a/sn_node_manager/src/add_services/tests.rs b/sn_node_manager/src/add_services/tests.rs index 898d224196..4fd76695a9 100644 --- a/sn_node_manager/src/add_services/tests.rs +++ b/sn_node_manager/src/add_services/tests.rs @@ -105,11 +105,10 @@ async fn add_genesis_node_should_use_latest_version_and_add_one_service() -> Res let mut mock_service_control = MockServiceControl::new(); let mut seq = Sequence::new(); - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -122,7 +121,7 @@ async fn add_genesis_node_should_use_latest_version_and_add_one_service() -> Res local: true, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -149,6 +148,7 @@ async fn add_genesis_node_should_use_latest_version_and_add_one_service() -> Res bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: true, home_network: false, @@ -264,6 +264,7 @@ async fn add_genesis_node_should_return_an_error_if_there_is_already_a_genesis_n bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: true, home_network: false, @@ -331,6 +332,7 @@ async fn add_genesis_node_should_return_an_error_if_count_is_greater_than_1() -> bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: true, home_network: false, @@ -394,11 +396,10 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( let mut seq = Sequence::new(); // Expected calls for first installation - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -411,7 +412,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -433,11 +434,10 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( .in_sequence(&mut seq); // Expected calls for second installation - let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8083)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -449,7 +449,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode2"), log_format: None, - metrics_port: Some(15003), + metrics_port: None, name: "safenode2".to_string(), node_port: None, owner: None, @@ -471,11 +471,10 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( .in_sequence(&mut seq); // Expected calls for third installation - let mut ports = vec![Ok(8085), Ok(15005)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8085)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -487,7 +486,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( local: false, log_format: None, log_dir_path: node_logs_dir.to_path_buf().join("safenode3"), - metrics_port: Some(15005), + metrics_port: None, name: "safenode3".to_string(), node_port: None, owner: None, @@ -515,6 +514,7 @@ async fn add_node_should_use_latest_version_and_add_three_services() -> Result<( bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -627,11 +627,10 @@ async fn add_node_should_update_the_bootstrap_peers_inside_node_registry() -> Re let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -644,7 +643,7 @@ async fn add_node_should_update_the_bootstrap_peers_inside_node_registry() -> Re local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -671,6 +670,7 @@ async fn add_node_should_update_the_bootstrap_peers_inside_node_registry() -> Re bootstrap_peers: new_peers.clone(), count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, local: false, genesis: false, @@ -758,11 +758,10 @@ async fn add_node_should_update_the_environment_variables_inside_node_registry() let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -774,7 +773,7 @@ async fn add_node_should_update_the_environment_variables_inside_node_registry() local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -801,6 +800,7 @@ async fn add_node_should_update_the_environment_variables_inside_node_registry() bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: env_variables.clone(), genesis: false, home_network: false, @@ -906,11 +906,10 @@ async fn add_new_node_should_add_another_service() -> Result<()> { safenode_download_path.write_binary(b"fake safenode bin")?; let mut seq = Sequence::new(); - let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8083)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -922,7 +921,7 @@ async fn add_new_node_should_add_another_service() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode2"), log_format: None, - metrics_port: Some(15003), + metrics_port: None, name: "safenode2".to_string(), node_port: None, rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8083), @@ -950,6 +949,7 @@ async fn add_new_node_should_add_another_service() -> Result<()> { bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1028,11 +1028,10 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { autostart: false, @@ -1044,7 +1043,7 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: Some(custom_port), owner: None, @@ -1072,6 +1071,7 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1136,11 +1136,10 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { let mut seq = Sequence::new(); // First service - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(15000)) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1149,7 +1148,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:8081"), + OsString::from("127.0.0.1:15000"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1168,8 +1167,6 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12000"), - OsString::from("--metrics-server-port"), - OsString::from("15001"), ], autostart: false, contents: None, @@ -1188,11 +1185,10 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { .in_sequence(&mut seq); // Second service - let mut ports = vec![Ok(8083), Ok(15003)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(15001)) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1201,7 +1197,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:8083"), + OsString::from("127.0.0.1:15001"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1220,8 +1216,6 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12001"), - OsString::from("--metrics-server-port"), - OsString::from("15003"), ], autostart: false, contents: None, @@ -1240,11 +1234,10 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { .in_sequence(&mut seq); // Third service - let mut ports = vec![Ok(8085), Ok(15005)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(15002)) .in_sequence(&mut seq); mock_service_control .expect_install() @@ -1253,7 +1246,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:8085"), + OsString::from("127.0.0.1:15002"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -1272,8 +1265,6 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { ), OsString::from("--port"), OsString::from("12002"), - OsString::from("--metrics-server-port"), - OsString::from("15005"), ], autostart: false, contents: None, @@ -1298,6 +1289,7 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1390,6 +1382,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_port_is_used() -> R bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1480,6 +1473,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_port_in_range_is_us bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1545,6 +1539,7 @@ async fn add_node_should_return_an_error_if_port_and_node_count_do_not_match() - bootstrap_peers: vec![], count: Some(2), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1615,6 +1610,7 @@ async fn add_node_should_return_an_error_if_multiple_services_are_specified_with bootstrap_peers: vec![], count: Some(2), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -1654,7 +1650,7 @@ async fn add_node_should_return_an_error_if_multiple_services_are_specified_with } #[tokio::test] -async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result<()> { +async fn add_node_should_set_random_ports_if_enable_metrics_server_is_true() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; let node_reg_path = tmp_data_dir.child("node_reg.json"); @@ -1682,10 +1678,10 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< let mut seq = Sequence::new(); // First service - let mut ports = vec![Ok(8081)].into_iter(); + let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(1) + .times(2) .returning(move || ports.next().unwrap()) .in_sequence(&mut seq); mock_service_control @@ -1713,7 +1709,7 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< .to_string(), ), OsString::from("--metrics-server-port"), - OsString::from("12000"), + OsString::from("15001"), ], autostart: false, contents: None, @@ -1731,119 +1727,20 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< .returning(|_, _| Ok(())) .in_sequence(&mut seq); - // Second service - let mut ports = vec![Ok(8083)].into_iter(); - mock_service_control - .expect_get_available_port() - .times(1) - .returning(move || ports.next().unwrap()) - .in_sequence(&mut seq); - mock_service_control - .expect_install() - .times(1) - .with( - eq(ServiceInstallCtx { - args: vec![ - OsString::from("--rpc"), - OsString::from("127.0.0.1:8083"), - OsString::from("--root-dir"), - OsString::from( - node_data_dir - .to_path_buf() - .join("safenode2") - .to_string_lossy() - .to_string(), - ), - OsString::from("--log-output-dest"), - OsString::from( - node_logs_dir - .to_path_buf() - .join("safenode2") - .to_string_lossy() - .to_string(), - ), - OsString::from("--metrics-server-port"), - OsString::from("12001"), - ], - autostart: false, - contents: None, - environment: None, - label: "safenode2".parse()?, - program: node_data_dir - .to_path_buf() - .join("safenode2") - .join(SAFENODE_FILE_NAME), - username: Some(get_username()), - working_directory: None, - }), - eq(false), - ) - .returning(|_, _| Ok(())) - .in_sequence(&mut seq); - - // Third service - let mut ports = vec![Ok(8085)].into_iter(); - mock_service_control - .expect_get_available_port() - .times(1) - .returning(move || ports.next().unwrap()) - .in_sequence(&mut seq); - mock_service_control - .expect_install() - .times(1) - .with( - eq(ServiceInstallCtx { - args: vec![ - OsString::from("--rpc"), - OsString::from("127.0.0.1:8085"), - OsString::from("--root-dir"), - OsString::from( - node_data_dir - .to_path_buf() - .join("safenode3") - .to_string_lossy() - .to_string(), - ), - OsString::from("--log-output-dest"), - OsString::from( - node_logs_dir - .to_path_buf() - .join("safenode3") - .to_string_lossy() - .to_string(), - ), - OsString::from("--metrics-server-port"), - OsString::from("12002"), - ], - autostart: false, - contents: None, - environment: None, - label: "safenode3".parse()?, - program: node_data_dir - .to_path_buf() - .join("safenode3") - .join(SAFENODE_FILE_NAME), - username: Some(get_username()), - working_directory: None, - }), - eq(false), - ) - .returning(|_, _| Ok(())) - .in_sequence(&mut seq); - add_node( AddNodeServiceOptions { auto_restart: false, auto_set_nat_flags: false, bootstrap_peers: vec![], - count: Some(3), + count: None, delete_safenode_src: true, + enable_metrics_server: true, env_variables: None, genesis: false, home_network: false, local: false, log_format: None, - metrics_port: Some(PortRange::Range(12000, 12002)), + metrics_port: None, owner: None, node_port: None, rpc_address: None, @@ -1863,197 +1760,12 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< ) .await?; - assert_eq!(node_registry.nodes.len(), 3); - assert_eq!(node_registry.nodes[0].metrics_port, Some(12000)); - assert_eq!(node_registry.nodes[1].metrics_port, Some(12001)); - assert_eq!(node_registry.nodes[2].metrics_port, Some(12002)); - + assert_eq!(node_registry.nodes[0].metrics_port, Some(15001)); Ok(()) } #[tokio::test] -async fn add_node_should_return_an_error_if_duplicate_custom_metrics_port_is_used() -> Result<()> { - let tmp_data_dir = assert_fs::TempDir::new()?; - let node_reg_path = tmp_data_dir.child("node_reg.json"); - - let mut node_registry = NodeRegistry { - auditor: None, - faucet: None, - save_path: node_reg_path.to_path_buf(), - nat_status: None, - nodes: vec![NodeServiceData { - auto_restart: false, - connected_peers: None, - data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), - genesis: false, - home_network: false, - listen_addr: None, - local: false, - log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), - log_format: None, - metrics_port: Some(12000), - node_port: None, - number: 1, - owner: None, - peer_id: None, - pid: None, - reward_balance: Some(NanoTokens::zero()), - rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), - safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"), - service_name: "safenode1".to_string(), - status: ServiceStatus::Added, - upnp: false, - user: Some("safe".to_string()), - user_mode: false, - version: "0.98.1".to_string(), - }], - bootstrap_peers: vec![], - environment_variables: None, - daemon: None, - }; - let latest_version = "0.96.4"; - let temp_dir = assert_fs::TempDir::new()?; - let node_data_dir = temp_dir.child("data"); - node_data_dir.create_dir_all()?; - let node_logs_dir = temp_dir.child("logs"); - node_logs_dir.create_dir_all()?; - let safenode_download_path = temp_dir.child(SAFENODE_FILE_NAME); - safenode_download_path.write_binary(b"fake safenode bin")?; - - let result = add_node( - AddNodeServiceOptions { - auto_restart: false, - auto_set_nat_flags: false, - bootstrap_peers: vec![], - count: None, - delete_safenode_src: true, - env_variables: None, - genesis: false, - home_network: false, - local: false, - log_format: None, - metrics_port: Some(PortRange::Single(12000)), - owner: None, - node_port: None, - rpc_address: None, - rpc_port: None, - safenode_dir_path: temp_dir.to_path_buf(), - safenode_src_path: safenode_download_path.to_path_buf(), - service_data_dir_path: node_data_dir.to_path_buf(), - service_log_dir_path: node_logs_dir.to_path_buf(), - upnp: false, - user: Some(get_username()), - user_mode: false, - version: latest_version.to_string(), - }, - &mut node_registry, - &MockServiceControl::new(), - VerbosityLevel::Normal, - ) - .await; - - match result { - Ok(_) => panic!("This test is supposed to result in a failure"), - Err(e) => { - assert_eq!(e.to_string(), "Port 12000 is being used by another service"); - Ok(()) - } - } -} - -#[tokio::test] -async fn add_node_should_return_an_error_if_duplicate_custom_metrics_port_in_range_is_used( -) -> Result<()> { - let tmp_data_dir = assert_fs::TempDir::new()?; - let node_reg_path = tmp_data_dir.child("node_reg.json"); - - let mut node_registry = NodeRegistry { - auditor: None, - faucet: None, - save_path: node_reg_path.to_path_buf(), - nat_status: None, - nodes: vec![NodeServiceData { - auto_restart: false, - connected_peers: None, - data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), - genesis: false, - home_network: false, - listen_addr: None, - local: false, - log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), - log_format: None, - metrics_port: Some(12000), - node_port: None, - number: 1, - owner: None, - peer_id: None, - pid: None, - reward_balance: Some(NanoTokens::zero()), - rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), - safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"), - service_name: "safenode1".to_string(), - status: ServiceStatus::Added, - upnp: false, - user: Some("safe".to_string()), - user_mode: false, - version: "0.98.1".to_string(), - }], - bootstrap_peers: vec![], - environment_variables: None, - daemon: None, - }; - let latest_version = "0.96.4"; - let temp_dir = assert_fs::TempDir::new()?; - let node_data_dir = temp_dir.child("data"); - node_data_dir.create_dir_all()?; - let node_logs_dir = temp_dir.child("logs"); - node_logs_dir.create_dir_all()?; - let safenode_download_path = temp_dir.child(SAFENODE_FILE_NAME); - safenode_download_path.write_binary(b"fake safenode bin")?; - - let result = add_node( - AddNodeServiceOptions { - auto_restart: false, - auto_set_nat_flags: false, - bootstrap_peers: vec![], - count: Some(3), - delete_safenode_src: true, - env_variables: None, - genesis: false, - home_network: false, - local: false, - log_format: None, - metrics_port: Some(PortRange::Range(12000, 12002)), - owner: None, - node_port: None, - rpc_address: None, - rpc_port: None, - safenode_dir_path: temp_dir.to_path_buf(), - safenode_src_path: safenode_download_path.to_path_buf(), - service_data_dir_path: node_data_dir.to_path_buf(), - service_log_dir_path: node_logs_dir.to_path_buf(), - upnp: false, - user: Some(get_username()), - user_mode: false, - version: latest_version.to_string(), - }, - &mut node_registry, - &MockServiceControl::new(), - VerbosityLevel::Normal, - ) - .await; - - match result { - Ok(_) => panic!("This test is supposed to result in a failure"), - Err(e) => { - assert_eq!(e.to_string(), "Port 12000 is being used by another service"); - Ok(()) - } - } -} - -#[tokio::test] -async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Result<()> { +async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; let node_reg_path = tmp_data_dir.child("node_reg.json"); @@ -2080,13 +1792,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res let mut seq = Sequence::new(); + // First service mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(20000)) + .returning(|| Ok(15000)) .in_sequence(&mut seq); - - // First service mock_service_control .expect_install() .times(1) @@ -2094,7 +1805,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:20000"), + OsString::from("127.0.0.1:15000"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -2112,7 +1823,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res .to_string(), ), OsString::from("--metrics-server-port"), - OsString::from("15000"), + OsString::from("12000"), ], autostart: false, contents: None, @@ -2130,13 +1841,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res .returning(|_, _| Ok(())) .in_sequence(&mut seq); + // Second service mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(20001)) + .returning(|| Ok(15001)) .in_sequence(&mut seq); - - // Second service mock_service_control .expect_install() .times(1) @@ -2144,7 +1854,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:20001"), + OsString::from("127.0.0.1:15001"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -2162,7 +1872,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res .to_string(), ), OsString::from("--metrics-server-port"), - OsString::from("15001"), + OsString::from("12001"), ], autostart: false, contents: None, @@ -2180,13 +1890,12 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res .returning(|_, _| Ok(())) .in_sequence(&mut seq); + // Third service mock_service_control .expect_get_available_port() .times(1) - .returning(|| Ok(20002)) + .returning(|| Ok(15002)) .in_sequence(&mut seq); - - // Third service mock_service_control .expect_install() .times(1) @@ -2194,7 +1903,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res eq(ServiceInstallCtx { args: vec![ OsString::from("--rpc"), - OsString::from("127.0.0.1:20002"), + OsString::from("127.0.0.1:15002"), OsString::from("--root-dir"), OsString::from( node_data_dir @@ -2212,8 +1921,9 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res .to_string(), ), OsString::from("--metrics-server-port"), - OsString::from("15002"), + OsString::from("12002"), ], + autostart: false, contents: None, environment: None, label: "safenode3".parse()?, @@ -2231,16 +1941,18 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res add_node( AddNodeServiceOptions { + auto_restart: false, auto_set_nat_flags: false, bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, local: false, log_format: None, - metrics_port: Some(PortRange::Range(15000, 15002)), + metrics_port: Some(PortRange::Range(12000, 12002)), owner: None, node_port: None, rpc_address: None, @@ -2260,19 +1972,16 @@ async fn add_node_should_use_a_custom_port_range_for_the_metrics_server() -> Res ) .await?; - safenode_download_path.assert(predicate::path::missing()); - node_data_dir.assert(predicate::path::is_dir()); - node_logs_dir.assert(predicate::path::is_dir()); assert_eq!(node_registry.nodes.len(), 3); - assert_eq!(node_registry.nodes[0].metrics_port, Some(15000)); - assert_eq!(node_registry.nodes[1].metrics_port, Some(15001)); - assert_eq!(node_registry.nodes[2].metrics_port, Some(15002)); + assert_eq!(node_registry.nodes[0].metrics_port, Some(12000)); + assert_eq!(node_registry.nodes[1].metrics_port, Some(12001)); + assert_eq!(node_registry.nodes[2].metrics_port, Some(12002)); Ok(()) } #[tokio::test] -async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used() -> Result<()> { +async fn add_node_should_return_an_error_if_duplicate_custom_metrics_port_is_used() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; let node_reg_path = tmp_data_dir.child("node_reg.json"); @@ -2282,6 +1991,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used save_path: node_reg_path.to_path_buf(), nat_status: None, nodes: vec![NodeServiceData { + auto_restart: false, connected_peers: None, data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), genesis: false, @@ -2290,7 +2000,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: Some(12000), node_port: None, number: 1, owner: None, @@ -2321,16 +2031,18 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used let result = add_node( AddNodeServiceOptions { + auto_restart: false, auto_set_nat_flags: false, bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, local: false, log_format: None, - metrics_port: Some(PortRange::Single(15001)), + metrics_port: Some(PortRange::Single(12000)), owner: None, node_port: None, rpc_address: None, @@ -2353,14 +2065,14 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_is_used match result { Ok(_) => panic!("This test is supposed to result in a failure"), Err(e) => { - assert_eq!(e.to_string(), "Port 15001 is being used by another service"); + assert_eq!(e.to_string(), "Port 12000 is being used by another service"); Ok(()) } } } #[tokio::test] -async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_range_is_used( +async fn add_node_should_return_an_error_if_duplicate_custom_metrics_port_in_range_is_used( ) -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; let node_reg_path = tmp_data_dir.child("node_reg.json"); @@ -2371,6 +2083,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_rang save_path: node_reg_path.to_path_buf(), nat_status: None, nodes: vec![NodeServiceData { + auto_restart: false, connected_peers: None, data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), genesis: false, @@ -2379,7 +2092,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_rang local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: Some(12000), node_port: None, number: 1, owner: None, @@ -2410,16 +2123,18 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_rang let result = add_node( AddNodeServiceOptions { + auto_restart: false, auto_set_nat_flags: false, bootstrap_peers: vec![], - count: None, + count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, local: false, log_format: None, - metrics_port: Some(PortRange::Range(15001, 15002)), + metrics_port: Some(PortRange::Range(12000, 12002)), owner: None, node_port: None, rpc_address: None, @@ -2442,7 +2157,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_metric_port_in_rang match result { Ok(_) => panic!("This test is supposed to result in a failure"), Err(e) => { - assert_eq!(e.to_string(), "Port 15001 is being used by another service"); + assert_eq!(e.to_string(), "Port 12000 is being used by another service"); Ok(()) } } @@ -2476,12 +2191,6 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< let mut seq = Sequence::new(); - mock_service_control - .expect_get_available_port() - .times(1) - .returning(|| Ok(15001)) - .in_sequence(&mut seq); - // First service mock_service_control .expect_install() @@ -2507,9 +2216,8 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), - OsString::from("--metrics-server-port"), - OsString::from("15001"), ], + autostart: false, contents: None, environment: None, label: "safenode1".parse()?, @@ -2525,12 +2233,6 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .returning(|_, _| Ok(())) .in_sequence(&mut seq); - mock_service_control - .expect_get_available_port() - .times(1) - .returning(|| Ok(15003)) - .in_sequence(&mut seq); - // Second service mock_service_control .expect_install() @@ -2556,9 +2258,8 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), - OsString::from("--metrics-server-port"), - OsString::from("15003"), ], + autostart: false, contents: None, environment: None, label: "safenode2".parse()?, @@ -2574,12 +2275,6 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .returning(|_, _| Ok(())) .in_sequence(&mut seq); - mock_service_control - .expect_get_available_port() - .times(1) - .returning(|| Ok(15005)) - .in_sequence(&mut seq); - // Third service mock_service_control .expect_install() @@ -2605,8 +2300,6 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< .to_string_lossy() .to_string(), ), - OsString::from("--metrics-server-port"), - OsString::from("15005"), ], autostart: false, contents: None, @@ -2631,6 +2324,7 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< bootstrap_peers: vec![], count: Some(3), delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -2731,6 +2425,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_rpc_port_is_used() bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -2822,6 +2517,7 @@ async fn add_node_should_return_an_error_if_duplicate_custom_rpc_port_in_range_i bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -2884,11 +2580,10 @@ async fn add_node_should_disable_upnp_and_home_network_if_nat_status_is_public() let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -2901,7 +2596,7 @@ async fn add_node_should_disable_upnp_and_home_network_if_nat_status_is_public() local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -2928,6 +2623,7 @@ async fn add_node_should_disable_upnp_and_home_network_if_nat_status_is_public() bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, local: false, genesis: false, @@ -2987,11 +2683,10 @@ async fn add_node_should_enable_upnp_if_nat_status_is_upnp() -> Result<()> { let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3004,7 +2699,7 @@ async fn add_node_should_enable_upnp_if_nat_status_is_upnp() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -3031,6 +2726,7 @@ async fn add_node_should_enable_upnp_if_nat_status_is_upnp() -> Result<()> { bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, local: false, genesis: false, @@ -3090,11 +2786,10 @@ async fn add_node_should_enable_home_network_if_nat_status_is_private() -> Resul let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3107,7 +2802,7 @@ async fn add_node_should_enable_home_network_if_nat_status_is_private() -> Resul local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -3134,6 +2829,7 @@ async fn add_node_should_enable_home_network_if_nat_status_is_private() -> Resul bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, local: false, genesis: false, @@ -3194,11 +2890,10 @@ async fn add_node_should_return_an_error_if_nat_status_is_none_but_auto_set_nat_ let mut seq = Sequence::new(); - let mut ports = vec![Ok(12001), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(12001)) .in_sequence(&mut seq); let result = add_node( @@ -3208,6 +2903,7 @@ async fn add_node_should_return_an_error_if_nat_status_is_none_but_auto_set_nat_ bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, local: false, genesis: false, @@ -3808,11 +3504,10 @@ async fn add_node_should_not_delete_the_source_binary_if_path_arg_is_used() -> R let mut seq = Sequence::new(); // Expected calls for first installation - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3825,7 +3520,7 @@ async fn add_node_should_not_delete_the_source_binary_if_path_arg_is_used() -> R local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -3853,6 +3548,7 @@ async fn add_node_should_not_delete_the_source_binary_if_path_arg_is_used() -> R bootstrap_peers: vec![], count: Some(1), delete_safenode_src: false, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -3913,11 +3609,10 @@ async fn add_node_should_apply_the_home_network_flag_if_it_is_used() -> Result<( let mut seq = Sequence::new(); // Expected calls for first installation - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -3930,7 +3625,7 @@ async fn add_node_should_apply_the_home_network_flag_if_it_is_used() -> Result<( local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -3958,6 +3653,7 @@ async fn add_node_should_apply_the_home_network_flag_if_it_is_used() -> Result<( bootstrap_peers: vec![], count: Some(1), delete_safenode_src: false, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: true, @@ -4018,11 +3714,10 @@ async fn add_node_should_add_the_node_in_user_mode() -> Result<()> { let mut seq = Sequence::new(); // Expected calls for first installation - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -4035,7 +3730,7 @@ async fn add_node_should_add_the_node_in_user_mode() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -4063,6 +3758,7 @@ async fn add_node_should_add_the_node_in_user_mode() -> Result<()> { bootstrap_peers: vec![], count: Some(1), delete_safenode_src: false, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: true, @@ -4120,11 +3816,10 @@ async fn add_node_should_add_the_node_with_upnp_enabled() -> Result<()> { let mut seq = Sequence::new(); - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); let install_ctx = InstallNodeServiceCtxBuilder { @@ -4137,7 +3832,7 @@ async fn add_node_should_add_the_node_with_upnp_enabled() -> Result<()> { local: false, log_dir_path: node_logs_dir.to_path_buf().join("safenode1"), log_format: None, - metrics_port: Some(15001), + metrics_port: None, name: "safenode1".to_string(), node_port: None, owner: None, @@ -4165,6 +3860,7 @@ async fn add_node_should_add_the_node_with_upnp_enabled() -> Result<()> { bootstrap_peers: vec![], count: Some(1), delete_safenode_src: false, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: true, @@ -4223,11 +3919,10 @@ async fn add_node_should_assign_an_owner() -> Result<()> { let mut mock_service_control = MockServiceControl::new(); let mut seq = Sequence::new(); - let mut ports = vec![Ok(8081), Ok(15001)].into_iter(); mock_service_control .expect_get_available_port() - .times(2) - .returning(move || ports.next().unwrap()) + .times(1) + .returning(|| Ok(8081)) .in_sequence(&mut seq); mock_service_control @@ -4253,8 +3948,6 @@ async fn add_node_should_assign_an_owner() -> Result<()> { .to_string_lossy() .to_string(), ), - OsString::from("--metrics-server-port"), - OsString::from("15001"), OsString::from("--owner"), OsString::from("discord_username"), ], @@ -4282,6 +3975,7 @@ async fn add_node_should_assign_an_owner() -> Result<()> { bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, @@ -4398,6 +4092,7 @@ async fn add_node_should_auto_restart() -> Result<()> { bootstrap_peers: vec![], count: None, delete_safenode_src: true, + enable_metrics_server: false, env_variables: None, genesis: false, home_network: false, diff --git a/sn_node_manager/src/bin/cli/main.rs b/sn_node_manager/src/bin/cli/main.rs index a79c24bcee..e3b291b272 100644 --- a/sn_node_manager/src/bin/cli/main.rs +++ b/sn_node_manager/src/bin/cli/main.rs @@ -82,6 +82,11 @@ pub enum SubCmd { /// - Windows: C:\ProgramData\safenode\services #[clap(long, verbatim_doc_comment)] data_dir_path: Option, + /// Set this flag to enable the metrics server. The ports will be selected at random. + /// + /// If you want to specify the ports, use the --metrics-port argument. + #[clap(long)] + enable_metrics_server: bool, /// Provide environment variables for the safenode service. /// /// Useful to set log levels. Variables should be comma separated without spaces. @@ -121,7 +126,8 @@ pub enum SubCmd { /// This argument should only be used with a safenode binary that has the open-metrics /// feature enabled. /// - /// If not used, ports will be selected at random. + /// If not set, metrics server will not be started. Use --enable-metrics-server to start + /// the metrics server without specifying a port. /// /// If multiple services are being added and this argument is used, you must specify a /// range. For example, '12000-12004'. The length of the range must match the number of @@ -870,6 +876,7 @@ async fn main() -> Result<()> { auto_set_nat_flags, count, data_dir_path, + enable_metrics_server, env_variables, home_network, local, @@ -892,6 +899,7 @@ async fn main() -> Result<()> { auto_set_nat_flags, count, data_dir_path, + enable_metrics_server, env_variables, home_network, local, diff --git a/sn_node_manager/src/cmd/node.rs b/sn_node_manager/src/cmd/node.rs index b74322c2f8..fcd32c7b64 100644 --- a/sn_node_manager/src/cmd/node.rs +++ b/sn_node_manager/src/cmd/node.rs @@ -40,6 +40,7 @@ pub async fn add( auto_set_nat_flags: bool, count: Option, data_dir_path: Option, + enable_metrics_server: bool, env_variables: Option>, home_network: bool, local: bool, @@ -127,6 +128,7 @@ pub async fn add( bootstrap_peers, count, delete_safenode_src: src_path.is_none(), + enable_metrics_server, env_variables, genesis: is_first, home_network, @@ -510,6 +512,7 @@ pub async fn maintain_n_running_nodes( auto_set_nat_flags: bool, max_nodes_to_run: u16, data_dir_path: Option, + enable_metrics_server: bool, env_variables: Option>, home_network: bool, local: bool, @@ -602,6 +605,7 @@ pub async fn maintain_n_running_nodes( auto_set_nat_flags, Some(to_add_count as u16), data_dir_path, + enable_metrics_server, env_variables, home_network, local, From a006223a2cd77b815a7f2c0c33e641d251e325c4 Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Tue, 4 Jun 2024 00:36:22 +0530 Subject: [PATCH 8/8] chore(launchpad): clippy fixes --- node-launchpad/src/components/home.rs | 5 +++-- node-launchpad/src/node_stats.rs | 9 ++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/node-launchpad/src/components/home.rs b/node-launchpad/src/components/home.rs index c511a82d32..02fa39654d 100644 --- a/node-launchpad/src/components/home.rs +++ b/node-launchpad/src/components/home.rs @@ -405,10 +405,10 @@ impl Component for Home { Cell::new("Storage Allocated".to_string()).fg(GHOST_WHITE), Cell::new(format!("{} GB", self.nodes_to_start * GB_PER_NODE)).fg(GHOST_WHITE), ]); - let memory_use_val = if self.node_stats.memory_usage_mb as f64 / 1024 as f64 > 1.0 { + let memory_use_val = if self.node_stats.memory_usage_mb as f64 / 1024_f64 > 1.0 { format!( "{:.2} GB", - self.node_stats.memory_usage_mb as f64 / 1024 as f64 + self.node_stats.memory_usage_mb as f64 / 1024_f64 ) } else { format!("{} MB", self.node_stats.memory_usage_mb) @@ -610,6 +610,7 @@ fn maintain_n_running_nodes( true, count, None, + true, None, false, false, diff --git a/node-launchpad/src/node_stats.rs b/node-launchpad/src/node_stats.rs index d20cf3e9d0..59bd72f6ed 100644 --- a/node-launchpad/src/node_stats.rs +++ b/node-launchpad/src/node_stats.rs @@ -50,14 +50,13 @@ impl NodeStats { } }) .collect::>(); - if node_details.is_empty() { - info!("No running nodes to fetch stats from."); - return; - } else { - info!("Fetching stats from {} nodes", node_details.len()); + if !node_details.is_empty() { + debug!("Fetching stats from {} nodes", node_details.len()); tokio::task::spawn_local(async move { Self::fetch_all_node_stats_inner(node_details, action_sender).await; }); + } else { + debug!("No running nodes to fetch stats from."); } }