diff --git a/node-launchpad/src/components/home.rs b/node-launchpad/src/components/home.rs index ed723da0cf..5471e665dc 100644 --- a/node-launchpad/src/components/home.rs +++ b/node-launchpad/src/components/home.rs @@ -446,6 +446,7 @@ fn add_and_start_nodes( None, None, None, + false, None, None, None, diff --git a/sn_node_manager/src/add_services/mod.rs b/sn_node_manager/src/add_services/mod.rs index cb69c552b1..d78c2b4a9d 100644 --- a/sn_node_manager/src/add_services/mod.rs +++ b/sn_node_manager/src/add_services/mod.rs @@ -78,6 +78,18 @@ pub async fn add_node( } } + if let Some(port_option) = &options.node_port { + check_port_availability(port_option, &node_registry.nodes)?; + } + + if let Some(port_option) = &options.metrics_port { + check_port_availability(port_option, &node_registry.nodes)?; + } + + if let Some(port_option) = &options.rpc_port { + check_port_availability(port_option, &node_registry.nodes)?; + } + let safenode_file_name = options .safenode_src_path .file_name() @@ -202,6 +214,8 @@ pub async fn add_node( listen_addr: None, local: options.local, log_dir_path: service_log_dir_path.clone(), + metrics_port, + node_port, number: node_number, reward_balance: None, rpc_socket_addr, @@ -415,3 +429,32 @@ fn increment_port_option(port: Option) -> Option { } None } + +fn check_port_availability(port_option: &PortRange, nodes: &[NodeServiceData]) -> Result<()> { + let mut all_ports = Vec::new(); + for node in nodes { + if let Some(port) = node.metrics_port { + all_ports.push(port); + } + if let Some(port) = node.node_port { + all_ports.push(port); + } + all_ports.push(node.rpc_socket_addr.port()); + } + + match port_option { + PortRange::Single(port) => { + if all_ports.iter().any(|p| *p == *port) { + return Err(eyre!("Port {port} is being used by another service")); + } + } + PortRange::Range(start, end) => { + for i in *start..=*end { + if all_ports.iter().any(|p| *p == i) { + return Err(eyre!("Port {i} is being used by another service")); + } + } + } + } + Ok(()) +} diff --git a/sn_node_manager/src/add_services/tests.rs b/sn_node_manager/src/add_services/tests.rs index ff2fc636bd..f56a9d58c7 100644 --- a/sn_node_manager/src/add_services/tests.rs +++ b/sn_node_manager/src/add_services/tests.rs @@ -208,6 +208,8 @@ async fn add_genesis_node_should_return_an_error_if_there_is_already_a_genesis_n listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, pid: None, peer_id: None, @@ -804,6 +806,8 @@ async fn add_new_node_should_add_another_service() -> Result<()> { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, pid: None, peer_id: None, @@ -1007,23 +1011,7 @@ async fn add_node_should_use_custom_ports_for_one_service() -> Result<()> { node_logs_dir.assert(predicate::path::is_dir()); assert_eq!(node_registry.nodes.len(), 1); - assert_eq!(node_registry.nodes[0].version, latest_version); - assert_eq!(node_registry.nodes[0].service_name, "safenode1"); - assert_eq!(node_registry.nodes[0].user, Some(get_username())); - assert_eq!(node_registry.nodes[0].number, 1); - assert_eq!( - node_registry.nodes[0].rpc_socket_addr, - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12001) - ); - assert_eq!( - node_registry.nodes[0].log_dir_path, - node_logs_dir.to_path_buf().join("safenode1") - ); - assert_eq!( - node_registry.nodes[0].data_dir_path, - node_data_dir.to_path_buf().join("safenode1") - ); - assert_matches!(node_registry.nodes[0].status, ServiceStatus::Added); + assert_eq!(node_registry.nodes[0].node_port, Some(custom_port)); Ok(()) } @@ -1230,10 +1218,175 @@ async fn add_node_should_use_a_custom_port_range() -> Result<()> { 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].node_port, Some(12000)); + assert_eq!(node_registry.nodes[1].node_port, Some(12001)); + assert_eq!(node_registry.nodes[2].node_port, Some(12002)); Ok(()) } +#[tokio::test] +async fn add_node_should_return_an_error_if_duplicate_custom_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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: None, + node_port: Some(12000), + number: 1, + 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 { + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: None, + node_port: Some(PortRange::Single(12000)), + 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_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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: None, + node_port: Some(12000), + number: 1, + 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 { + bootstrap_peers: vec![], + count: Some(3), + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: None, + node_port: Some(PortRange::Range(12000, 12002)), + 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_port_and_node_count_do_not_match() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; @@ -1559,14 +1712,177 @@ async fn add_node_should_use_a_custom_port_range_for_metrics_server() -> Result< ) .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(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_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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: Some(12000), + node_port: None, + number: 1, + 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 { + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: Some(PortRange::Single(12000)), + 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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: Some(12000), + node_port: None, + number: 1, + 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 { + bootstrap_peers: vec![], + count: Some(3), + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: Some(PortRange::Range(12000, 12002)), + 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_rpc_server() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; @@ -1763,6 +2079,169 @@ async fn add_node_should_use_a_custom_port_range_for_the_rpc_server() -> Result< Ok(()) } +#[tokio::test] +async fn add_node_should_return_an_error_if_duplicate_custom_rpc_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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: None, + node_port: None, + number: 1, + 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 { + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: None, + node_port: None, + rpc_address: None, + rpc_port: Some(PortRange::Single(8081)), + 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 8081 is being used by another service"); + Ok(()) + } + } +} + +#[tokio::test] +async fn add_node_should_return_an_error_if_duplicate_custom_rpc_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 { + faucet: None, + save_path: node_reg_path.to_path_buf(), + 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"), + metrics_port: None, + node_port: None, + number: 1, + 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 { + bootstrap_peers: vec![], + count: None, + delete_safenode_src: true, + env_variables: None, + genesis: false, + home_network: false, + local: false, + metrics_port: None, + node_port: None, + rpc_address: None, + rpc_port: Some(PortRange::Range(8081, 8082)), + 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 8081 is being used by another service"); + Ok(()) + } + } +} + #[tokio::test] async fn add_faucet_should_add_a_faucet_service() -> Result<()> { let tmp_data_dir = assert_fs::TempDir::new()?; diff --git a/sn_node_manager/src/cmd/node.rs b/sn_node_manager/src/cmd/node.rs index fb7c86a994..1a150722ba 100644 --- a/sn_node_manager/src/cmd/node.rs +++ b/sn_node_manager/src/cmd/node.rs @@ -426,6 +426,7 @@ pub async fn upgrade( ) .await?; + println!("listen addresses: {:?}", node_registry.nodes[0].listen_addr); if !use_force { let node_versions = node_registry .nodes diff --git a/sn_node_manager/src/lib.rs b/sn_node_manager/src/lib.rs index ea86f7ef7d..f15e67ee47 100644 --- a/sn_node_manager/src/lib.rs +++ b/sn_node_manager/src/lib.rs @@ -552,6 +552,7 @@ mod tests { }; use sn_transfers::NanoTokens; use std::{ + ffi::OsString, net::{IpAddr, Ipv4Addr, SocketAddr}, path::{Path, PathBuf}, str::FromStr, @@ -636,6 +637,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: None, pid: None, @@ -723,6 +726,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -781,6 +786,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -875,6 +882,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -948,6 +957,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: None, pid: None, @@ -1027,6 +1038,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: None, pid: None, @@ -1075,6 +1088,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1118,6 +1133,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: None, pid: None, @@ -1159,6 +1176,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1203,6 +1222,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: None, pid: None, @@ -1258,6 +1279,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1376,6 +1399,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1457,6 +1482,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1580,6 +1607,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1716,6 +1745,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1848,6 +1879,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -1980,6 +2013,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, peer_id: Some(PeerId::from_str( "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", @@ -2036,6 +2071,735 @@ mod tests { Ok(()) } + #[tokio::test] + async fn upgrade_should_retain_the_upnp_flag() -> Result<()> { + let current_version = "0.1.0"; + let target_version = "0.2.0"; + + let tmp_data_dir = assert_fs::TempDir::new()?; + let current_install_dir = tmp_data_dir.child("safenode_install"); + current_install_dir.create_dir_all()?; + + let current_node_bin = current_install_dir.child("safenode"); + current_node_bin.write_binary(b"fake safenode binary")?; + let target_node_bin = tmp_data_dir.child("safenode"); + target_node_bin.write_binary(b"fake safenode binary")?; + + let mut mock_service_control = MockServiceControl::new(); + let mut mock_rpc_client = MockRpcClient::new(); + + // before binary upgrade + mock_service_control + .expect_is_service_process_running() + .with(eq(1000)) + .times(1) + .returning(|_| true); + mock_service_control + .expect_stop() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + + // after binary upgrade + mock_service_control + .expect_uninstall() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_install() + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:8081"), + OsString::from("--root-dir"), + OsString::from("/var/safenode-manager/services/safenode1"), + OsString::from("--log-output-dest"), + OsString::from("/var/log/safenode/safenode1"), + OsString::from("--upnp"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: current_node_bin.to_path_buf(), + username: Some("safe".to_string()), + working_directory: None, + }), + eq(false), + ) + .times(1) + .returning(|_, _| Ok(())); + + // after service restart + mock_service_control + .expect_start() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_wait() + .with(eq(3000)) + .times(1) + .returning(|_| ()); + mock_service_control + .expect_get_process_pid() + .with(eq(current_node_bin.to_path_buf().clone())) + .times(1) + .returning(|_| Ok(100)); + mock_rpc_client.expect_node_info().times(1).returning(|| { + Ok(NodeInfo { + pid: 2000, + peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?, + data_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + log_path: PathBuf::from("/var/log/safenode/safenode1"), + version: target_version.to_string(), + uptime: std::time::Duration::from_secs(1), // the service was just started + }) + }); + mock_rpc_client + .expect_network_info() + .times(1) + .returning(|| { + Ok(NetworkInfo { + connected_peers: Vec::new(), + listeners: Vec::new(), + }) + }); + + let mut service_data = 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"), + metrics_port: None, + node_port: None, + number: 1, + peer_id: Some(PeerId::from_str( + "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", + )?), + pid: Some(1000), + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: current_node_bin.to_path_buf(), + service_name: "safenode1".to_string(), + status: ServiceStatus::Running, + upnp: true, + user: Some("safe".to_string()), + user_mode: false, + version: current_version.to_string(), + }; + let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client)); + let mut service_manager = ServiceManager::new( + service, + Box::new(mock_service_control), + VerbosityLevel::Normal, + ); + + service_manager + .upgrade(UpgradeOptions { + bootstrap_peers: Vec::new(), + env_variables: None, + force: false, + start_service: true, + target_bin_path: target_node_bin.to_path_buf(), + target_version: Version::parse(target_version).unwrap(), + }) + .await?; + + assert!(service_manager.service.service_data.upnp); + + Ok(()) + } + + #[tokio::test] + async fn upgrade_should_retain_the_home_network_flag() -> Result<()> { + let current_version = "0.1.0"; + let target_version = "0.2.0"; + + let tmp_data_dir = assert_fs::TempDir::new()?; + let current_install_dir = tmp_data_dir.child("safenode_install"); + current_install_dir.create_dir_all()?; + + let current_node_bin = current_install_dir.child("safenode"); + current_node_bin.write_binary(b"fake safenode binary")?; + let target_node_bin = tmp_data_dir.child("safenode"); + target_node_bin.write_binary(b"fake safenode binary")?; + + let mut mock_service_control = MockServiceControl::new(); + let mut mock_rpc_client = MockRpcClient::new(); + + // before binary upgrade + mock_service_control + .expect_is_service_process_running() + .with(eq(1000)) + .times(1) + .returning(|_| true); + mock_service_control + .expect_stop() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + + // after binary upgrade + mock_service_control + .expect_uninstall() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_install() + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:8081"), + OsString::from("--root-dir"), + OsString::from("/var/safenode-manager/services/safenode1"), + OsString::from("--log-output-dest"), + OsString::from("/var/log/safenode/safenode1"), + OsString::from("--home-network"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: current_node_bin.to_path_buf(), + username: Some("safe".to_string()), + working_directory: None, + }), + eq(false), + ) + .times(1) + .returning(|_, _| Ok(())); + + // after service restart + mock_service_control + .expect_start() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_wait() + .with(eq(3000)) + .times(1) + .returning(|_| ()); + mock_service_control + .expect_get_process_pid() + .with(eq(current_node_bin.to_path_buf().clone())) + .times(1) + .returning(|_| Ok(100)); + mock_rpc_client.expect_node_info().times(1).returning(|| { + Ok(NodeInfo { + pid: 2000, + peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?, + data_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + log_path: PathBuf::from("/var/log/safenode/safenode1"), + version: target_version.to_string(), + uptime: std::time::Duration::from_secs(1), // the service was just started + }) + }); + mock_rpc_client + .expect_network_info() + .times(1) + .returning(|| { + Ok(NetworkInfo { + connected_peers: Vec::new(), + listeners: Vec::new(), + }) + }); + + let mut service_data = NodeServiceData { + connected_peers: None, + data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + genesis: false, + home_network: true, + listen_addr: None, + local: false, + log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, + number: 1, + peer_id: Some(PeerId::from_str( + "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", + )?), + pid: Some(1000), + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: current_node_bin.to_path_buf(), + service_name: "safenode1".to_string(), + status: ServiceStatus::Running, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: current_version.to_string(), + }; + let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client)); + let mut service_manager = ServiceManager::new( + service, + Box::new(mock_service_control), + VerbosityLevel::Normal, + ); + + service_manager + .upgrade(UpgradeOptions { + bootstrap_peers: Vec::new(), + env_variables: None, + force: false, + start_service: true, + target_bin_path: target_node_bin.to_path_buf(), + target_version: Version::parse(target_version).unwrap(), + }) + .await?; + + assert!(service_manager.service.service_data.home_network); + + Ok(()) + } + + #[tokio::test] + async fn upgrade_should_retain_custom_node_ports() -> Result<()> { + let current_version = "0.1.0"; + let target_version = "0.2.0"; + + let tmp_data_dir = assert_fs::TempDir::new()?; + let current_install_dir = tmp_data_dir.child("safenode_install"); + current_install_dir.create_dir_all()?; + + let current_node_bin = current_install_dir.child("safenode"); + current_node_bin.write_binary(b"fake safenode binary")?; + let target_node_bin = tmp_data_dir.child("safenode"); + target_node_bin.write_binary(b"fake safenode binary")?; + + let mut mock_service_control = MockServiceControl::new(); + let mut mock_rpc_client = MockRpcClient::new(); + + // before binary upgrade + mock_service_control + .expect_is_service_process_running() + .with(eq(1000)) + .times(1) + .returning(|_| true); + mock_service_control + .expect_stop() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + + // after binary upgrade + mock_service_control + .expect_uninstall() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_install() + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:8081"), + OsString::from("--root-dir"), + OsString::from("/var/safenode-manager/services/safenode1"), + OsString::from("--log-output-dest"), + OsString::from("/var/log/safenode/safenode1"), + OsString::from("--port"), + OsString::from("12000"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: current_node_bin.to_path_buf(), + username: Some("safe".to_string()), + working_directory: None, + }), + eq(false), + ) + .times(1) + .returning(|_, _| Ok(())); + + // after service restart + mock_service_control + .expect_start() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_wait() + .with(eq(3000)) + .times(1) + .returning(|_| ()); + mock_service_control + .expect_get_process_pid() + .with(eq(current_node_bin.to_path_buf().clone())) + .times(1) + .returning(|_| Ok(100)); + mock_rpc_client.expect_node_info().times(1).returning(|| { + Ok(NodeInfo { + pid: 2000, + peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?, + data_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + log_path: PathBuf::from("/var/log/safenode/safenode1"), + version: target_version.to_string(), + uptime: std::time::Duration::from_secs(1), // the service was just started + }) + }); + mock_rpc_client + .expect_network_info() + .times(1) + .returning(|| { + Ok(NetworkInfo { + connected_peers: Vec::new(), + listeners: Vec::new(), + }) + }); + + let mut service_data = 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"), + metrics_port: None, + number: 1, + node_port: Some(12000), + peer_id: Some(PeerId::from_str( + "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", + )?), + pid: Some(1000), + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: current_node_bin.to_path_buf(), + service_name: "safenode1".to_string(), + status: ServiceStatus::Running, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: current_version.to_string(), + }; + let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client)); + let mut service_manager = ServiceManager::new( + service, + Box::new(mock_service_control), + VerbosityLevel::Normal, + ); + + service_manager + .upgrade(UpgradeOptions { + bootstrap_peers: Vec::new(), + env_variables: None, + force: false, + start_service: true, + target_bin_path: target_node_bin.to_path_buf(), + target_version: Version::parse(target_version).unwrap(), + }) + .await?; + + assert_eq!(service_manager.service.service_data.node_port, Some(12000)); + + Ok(()) + } + + #[tokio::test] + async fn upgrade_should_retain_custom_metrics_ports() -> Result<()> { + let current_version = "0.1.0"; + let target_version = "0.2.0"; + + let tmp_data_dir = assert_fs::TempDir::new()?; + let current_install_dir = tmp_data_dir.child("safenode_install"); + current_install_dir.create_dir_all()?; + + let current_node_bin = current_install_dir.child("safenode"); + current_node_bin.write_binary(b"fake safenode binary")?; + let target_node_bin = tmp_data_dir.child("safenode"); + target_node_bin.write_binary(b"fake safenode binary")?; + + let mut mock_service_control = MockServiceControl::new(); + let mut mock_rpc_client = MockRpcClient::new(); + + // before binary upgrade + mock_service_control + .expect_is_service_process_running() + .with(eq(1000)) + .times(1) + .returning(|_| true); + mock_service_control + .expect_stop() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + + // after binary upgrade + mock_service_control + .expect_uninstall() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_install() + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:8081"), + OsString::from("--root-dir"), + OsString::from("/var/safenode-manager/services/safenode1"), + OsString::from("--log-output-dest"), + OsString::from("/var/log/safenode/safenode1"), + OsString::from("--metrics-port"), + OsString::from("12000"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: current_node_bin.to_path_buf(), + username: Some("safe".to_string()), + working_directory: None, + }), + eq(false), + ) + .times(1) + .returning(|_, _| Ok(())); + + // after service restart + mock_service_control + .expect_start() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_wait() + .with(eq(3000)) + .times(1) + .returning(|_| ()); + mock_service_control + .expect_get_process_pid() + .with(eq(current_node_bin.to_path_buf().clone())) + .times(1) + .returning(|_| Ok(100)); + mock_rpc_client.expect_node_info().times(1).returning(|| { + Ok(NodeInfo { + pid: 2000, + peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?, + data_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + log_path: PathBuf::from("/var/log/safenode/safenode1"), + version: target_version.to_string(), + uptime: std::time::Duration::from_secs(1), // the service was just started + }) + }); + mock_rpc_client + .expect_network_info() + .times(1) + .returning(|| { + Ok(NetworkInfo { + connected_peers: Vec::new(), + listeners: Vec::new(), + }) + }); + + let mut service_data = 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"), + metrics_port: Some(12000), + node_port: None, + number: 1, + peer_id: Some(PeerId::from_str( + "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", + )?), + pid: Some(1000), + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: current_node_bin.to_path_buf(), + service_name: "safenode1".to_string(), + status: ServiceStatus::Running, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: current_version.to_string(), + }; + let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client)); + let mut service_manager = ServiceManager::new( + service, + Box::new(mock_service_control), + VerbosityLevel::Normal, + ); + + service_manager + .upgrade(UpgradeOptions { + bootstrap_peers: Vec::new(), + env_variables: None, + force: false, + start_service: true, + target_bin_path: target_node_bin.to_path_buf(), + target_version: Version::parse(target_version).unwrap(), + }) + .await?; + + assert_eq!( + service_manager.service.service_data.metrics_port, + Some(12000) + ); + + Ok(()) + } + + #[tokio::test] + async fn upgrade_should_retain_custom_rpc_ports() -> Result<()> { + let current_version = "0.1.0"; + let target_version = "0.2.0"; + + let tmp_data_dir = assert_fs::TempDir::new()?; + let current_install_dir = tmp_data_dir.child("safenode_install"); + current_install_dir.create_dir_all()?; + + let current_node_bin = current_install_dir.child("safenode"); + current_node_bin.write_binary(b"fake safenode binary")?; + let target_node_bin = tmp_data_dir.child("safenode"); + target_node_bin.write_binary(b"fake safenode binary")?; + + let mut mock_service_control = MockServiceControl::new(); + let mut mock_rpc_client = MockRpcClient::new(); + + // before binary upgrade + mock_service_control + .expect_is_service_process_running() + .with(eq(1000)) + .times(1) + .returning(|_| true); + mock_service_control + .expect_stop() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + + // after binary upgrade + mock_service_control + .expect_uninstall() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_install() + .with( + eq(ServiceInstallCtx { + args: vec![ + OsString::from("--rpc"), + OsString::from("127.0.0.1:8081"), + OsString::from("--root-dir"), + OsString::from("/var/safenode-manager/services/safenode1"), + OsString::from("--log-output-dest"), + OsString::from("/var/log/safenode/safenode1"), + OsString::from("--metrics-port"), + OsString::from("12000"), + ], + contents: None, + environment: None, + label: "safenode1".parse()?, + program: current_node_bin.to_path_buf(), + username: Some("safe".to_string()), + working_directory: None, + }), + eq(false), + ) + .times(1) + .returning(|_, _| Ok(())); + + // after service restart + mock_service_control + .expect_start() + .with(eq("safenode1"), eq(false)) + .times(1) + .returning(|_, _| Ok(())); + mock_service_control + .expect_wait() + .with(eq(3000)) + .times(1) + .returning(|_| ()); + mock_service_control + .expect_get_process_pid() + .with(eq(current_node_bin.to_path_buf().clone())) + .times(1) + .returning(|_| Ok(100)); + mock_rpc_client.expect_node_info().times(1).returning(|| { + Ok(NodeInfo { + pid: 2000, + peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?, + data_path: PathBuf::from("/var/safenode-manager/services/safenode1"), + log_path: PathBuf::from("/var/log/safenode/safenode1"), + version: target_version.to_string(), + uptime: std::time::Duration::from_secs(1), // the service was just started + }) + }); + mock_rpc_client + .expect_network_info() + .times(1) + .returning(|| { + Ok(NetworkInfo { + connected_peers: Vec::new(), + listeners: Vec::new(), + }) + }); + + let mut service_data = 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"), + metrics_port: Some(12000), + node_port: None, + number: 1, + peer_id: Some(PeerId::from_str( + "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR", + )?), + pid: Some(1000), + reward_balance: Some(NanoTokens::zero()), + rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081), + safenode_path: current_node_bin.to_path_buf(), + service_name: "safenode1".to_string(), + status: ServiceStatus::Running, + upnp: false, + user: Some("safe".to_string()), + user_mode: false, + version: current_version.to_string(), + }; + let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client)); + let mut service_manager = ServiceManager::new( + service, + Box::new(mock_service_control), + VerbosityLevel::Normal, + ); + + service_manager + .upgrade(UpgradeOptions { + bootstrap_peers: Vec::new(), + env_variables: None, + force: false, + start_service: true, + target_bin_path: target_node_bin.to_path_buf(), + target_version: Version::parse(target_version).unwrap(), + }) + .await?; + + assert_eq!( + service_manager.service.service_data.rpc_socket_addr, + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081) + ); + + Ok(()) + } + #[tokio::test] async fn remove_should_remove_an_added_node() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; @@ -2061,6 +2825,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: log_dir.to_path_buf(), + metrics_port: None, + node_port: None, number: 1, pid: None, peer_id: None, @@ -2110,6 +2876,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, pid: Some(1000), peer_id: Some(PeerId::from_str( @@ -2167,6 +2935,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: PathBuf::from("/var/log/safenode/safenode1"), + metrics_port: None, + node_port: None, number: 1, pid: Some(1000), peer_id: Some(PeerId::from_str( @@ -2226,6 +2996,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: log_dir.to_path_buf(), + metrics_port: None, + node_port: None, number: 1, pid: None, peer_id: None, @@ -2283,6 +3055,8 @@ mod tests { listen_addr: None, local: false, log_dir_path: log_dir.to_path_buf(), + metrics_port: None, + node_port: None, number: 1, pid: None, peer_id: None, diff --git a/sn_node_manager/src/local.rs b/sn_node_manager/src/local.rs index a7f9774a3c..f72549c330 100644 --- a/sn_node_manager/src/local.rs +++ b/sn_node_manager/src/local.rs @@ -336,6 +336,8 @@ pub async fn run_node( listen_addr: Some(listen_addrs), local: true, log_dir_path: node_info.log_path, + metrics_port: None, + node_port: None, number: run_options.number, peer_id: Some(peer_id), pid: Some(node_info.pid), diff --git a/sn_node_manager/src/rpc.rs b/sn_node_manager/src/rpc.rs index e91ffa1fde..fefd4c6c7e 100644 --- a/sn_node_manager/src/rpc.rs +++ b/sn_node_manager/src/rpc.rs @@ -188,6 +188,8 @@ pub async fn restart_node_service( listen_addr: None, local: current_node_clone.local, log_dir_path, + metrics_port: None, + node_port: None, number: new_node_number as u16, peer_id: None, pid: None, diff --git a/sn_service_management/src/lib.rs b/sn_service_management/src/lib.rs index 55df089f80..dc8c1b32b2 100644 --- a/sn_service_management/src/lib.rs +++ b/sn_service_management/src/lib.rs @@ -53,7 +53,7 @@ pub enum UpgradeResult { Error(String), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UpgradeOptions { pub bootstrap_peers: Vec, pub env_variables: Option>, diff --git a/sn_service_management/src/node.rs b/sn_service_management/src/node.rs index 6e41934a32..5c198a2e3d 100644 --- a/sn_service_management/src/node.rs +++ b/sn_service_management/src/node.rs @@ -60,10 +60,21 @@ impl<'a> ServiceStateActions for NodeService<'a> { if self.service_data.local { args.push(OsString::from("--local")); } - if let Some(node_port) = self.service_data.get_safenode_port() { + if self.service_data.upnp { + args.push(OsString::from("--upnp")); + } + if self.service_data.home_network { + args.push(OsString::from("--home-network")); + } + + if let Some(node_port) = self.service_data.node_port { args.push(OsString::from("--port")); args.push(OsString::from(node_port.to_string())); } + if let Some(metrics_port) = self.service_data.metrics_port { + args.push(OsString::from("--metrics-port")); + args.push(OsString::from(metrics_port.to_string())); + } if !options.bootstrap_peers.is_empty() { let peers_str = options @@ -77,13 +88,13 @@ impl<'a> ServiceStateActions for NodeService<'a> { } Ok(ServiceInstallCtx { - label: label.clone(), - program: self.service_data.safenode_path.to_path_buf(), args, contents: None, + environment: options.env_variables, + label: label.clone(), + program: self.service_data.safenode_path.to_path_buf(), username: self.service_data.user.clone(), working_directory: None, - environment: options.env_variables, }) } @@ -160,6 +171,10 @@ pub struct NodeServiceData { pub listen_addr: Option>, pub local: bool, pub log_dir_path: PathBuf, + #[serde(default)] + pub metrics_port: Option, + #[serde(default)] + pub node_port: Option, pub number: u16, #[serde( serialize_with = "serialize_peer_id", @@ -245,8 +260,10 @@ impl NodeServiceData { pub fn get_safenode_port(&self) -> Option { // assuming the listening addr contains /ip4/127.0.0.1/udp/56215/quic-v1/p2p/ if let Some(multi_addrs) = &self.listen_addr { + println!("Listening addresses are defined"); for addr in multi_addrs { if let Some(port) = get_port_from_multiaddr(addr) { + println!("Found port: {}", port); return Some(port); } }