1#[macro_use]
10extern crate tracing;
11
12pub mod add_services;
13pub mod cmd;
14pub mod config;
15pub mod error;
16pub mod helpers;
17pub mod local;
18pub mod rpc;
19pub mod rpc_client;
20
21pub const DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S: u64 = 300;
22
23#[derive(Clone, Copy, PartialEq, Debug)]
24pub enum VerbosityLevel {
25 Minimal,
26 Normal,
27 Full,
28}
29
30impl From<u8> for VerbosityLevel {
31 fn from(verbosity: u8) -> Self {
32 match verbosity {
33 1 => VerbosityLevel::Minimal,
34 2 => VerbosityLevel::Normal,
35 3 => VerbosityLevel::Full,
36 _ => VerbosityLevel::Normal,
37 }
38 }
39}
40
41use crate::error::{Error, Result};
42use colored::Colorize;
43use semver::Version;
44use sn_evm::AttoTokens;
45use sn_service_management::rpc::RpcActions;
46use sn_service_management::{
47 control::ServiceControl, error::Error as ServiceError, rpc::RpcClient, NodeRegistry,
48 NodeService, NodeServiceData, ServiceStateActions, ServiceStatus, UpgradeOptions,
49 UpgradeResult,
50};
51use sn_transfers::HotWallet;
52use tracing::debug;
53
54pub const DAEMON_DEFAULT_PORT: u16 = 12500;
55pub const DAEMON_SERVICE_NAME: &str = "safenodemand";
56
57const RPC_START_UP_DELAY_MS: u64 = 3000;
58
59pub struct ServiceManager<T: ServiceStateActions + Send> {
60 pub service: T,
61 pub service_control: Box<dyn ServiceControl + Send>,
62 pub verbosity: VerbosityLevel,
63}
64
65impl<T: ServiceStateActions + Send> ServiceManager<T> {
66 pub fn new(
67 service: T,
68 service_control: Box<dyn ServiceControl + Send>,
69 verbosity: VerbosityLevel,
70 ) -> Self {
71 ServiceManager {
72 service,
73 service_control,
74 verbosity,
75 }
76 }
77
78 pub async fn start(&mut self) -> Result<()> {
79 info!("Starting the {} service", self.service.name());
80 if ServiceStatus::Running == self.service.status() {
81 if self
87 .service_control
88 .get_process_pid(&self.service.bin_path())
89 .is_ok()
90 {
91 debug!("The {} service is already running", self.service.name());
92 if self.verbosity != VerbosityLevel::Minimal {
93 println!("The {} service is already running", self.service.name());
94 }
95 return Ok(());
96 }
97 }
98
99 if self.verbosity != VerbosityLevel::Minimal {
102 println!("Attempting to start {}...", self.service.name());
103 }
104 self.service_control
105 .start(&self.service.name(), self.service.is_user_mode())?;
106 self.service_control.wait(RPC_START_UP_DELAY_MS);
107
108 match self
114 .service_control
115 .get_process_pid(&self.service.bin_path())
116 {
117 Ok(pid) => {
118 debug!(
119 "Service process started for {} with PID {}",
120 self.service.name(),
121 pid
122 );
123 self.service.on_start(Some(pid), true).await?;
124
125 info!(
126 "Service {} has been started successfully",
127 self.service.name()
128 );
129 }
130 Err(sn_service_management::error::Error::ServiceProcessNotFound(_)) => {
131 error!("The '{}' service has failed to start because ServiceProcessNotFound when fetching PID", self.service.name());
132 return Err(Error::PidNotFoundAfterStarting);
133 }
134 Err(err) => {
135 error!("Failed to start service, because PID could not be obtained: {err}");
136 return Err(err.into());
137 }
138 };
139
140 if self.verbosity != VerbosityLevel::Minimal {
141 println!("{} Started {} service", "✓".green(), self.service.name());
142 println!(
143 " - PID: {}",
144 self.service
145 .pid()
146 .map_or("-".to_string(), |p| p.to_string())
147 );
148 println!(
149 " - Bin path: {}",
150 self.service.bin_path().to_string_lossy()
151 );
152 println!(
153 " - Data path: {}",
154 self.service.data_dir_path().to_string_lossy()
155 );
156 println!(
157 " - Logs path: {}",
158 self.service.log_dir_path().to_string_lossy()
159 );
160 }
161 Ok(())
162 }
163
164 pub async fn stop(&mut self) -> Result<()> {
165 info!("Stopping the {} service", self.service.name());
166 match self.service.status() {
167 ServiceStatus::Added => {
168 debug!(
169 "The {} service has not been started since it was installed",
170 self.service.name()
171 );
172 if self.verbosity != VerbosityLevel::Minimal {
173 println!(
174 "Service {} has not been started since it was installed",
175 self.service.name()
176 );
177 }
178 Ok(())
179 }
180 ServiceStatus::Removed => {
181 debug!("The {} service has been removed", self.service.name());
182 if self.verbosity != VerbosityLevel::Minimal {
183 println!("Service {} has been removed", self.service.name());
184 }
185 Ok(())
186 }
187 ServiceStatus::Running => {
188 let pid = self.service.pid().ok_or(Error::PidNotSet)?;
189 let name = self.service.name();
190
191 if self
192 .service_control
193 .get_process_pid(&self.service.bin_path())
194 .is_ok()
195 {
196 if self.verbosity != VerbosityLevel::Minimal {
197 println!("Attempting to stop {}...", name);
198 }
199 self.service_control
200 .stop(&name, self.service.is_user_mode())?;
201 if self.verbosity != VerbosityLevel::Minimal {
202 println!(
203 "{} Service {} with PID {} was stopped",
204 "✓".green(),
205 name,
206 pid
207 );
208 }
209 } else if self.verbosity != VerbosityLevel::Minimal {
210 debug!("Service {name} was already stopped");
211 println!("{} Service {} was already stopped", "✓".green(), name);
212 }
213
214 self.service.on_stop().await?;
215 info!("Service {name} has been stopped successfully.");
216 Ok(())
217 }
218 ServiceStatus::Stopped => {
219 debug!("Service {} was already stopped", self.service.name());
220 if self.verbosity != VerbosityLevel::Minimal {
221 println!(
222 "{} Service {} was already stopped",
223 "✓".green(),
224 self.service.name()
225 );
226 }
227 Ok(())
228 }
229 }
230 }
231
232 pub async fn remove(&mut self, keep_directories: bool) -> Result<()> {
233 if let ServiceStatus::Running = self.service.status() {
234 if self
235 .service_control
236 .get_process_pid(&self.service.bin_path())
237 .is_ok()
238 {
239 error!(
240 "Service {} is already running. Stop it before removing it",
241 self.service.name()
242 );
243 return Err(Error::ServiceAlreadyRunning(vec![self.service.name()]));
244 } else {
245 self.service.on_stop().await?;
248 error!(
249 "The service: {} was marked as running but it had actually stopped. You may want to check the logs for errors before removing it. To remove the service, run the command again.",
250 self.service.name()
251 );
252 return Err(Error::ServiceStatusMismatch {
253 expected: ServiceStatus::Running,
254 });
255 }
256 }
257
258 match self
259 .service_control
260 .uninstall(&self.service.name(), self.service.is_user_mode())
261 {
262 Ok(()) => {
263 debug!("Service {} has been uninstalled", self.service.name());
264 }
265 Err(err) => match err {
266 ServiceError::ServiceRemovedManually(name) => {
267 warn!("The user appears to have removed the {name} service manually. Skipping the error.",);
268 println!("The user appears to have removed the {name} service manually");
273 }
274 ServiceError::ServiceDoesNotExists(name) => {
275 warn!("The service {name} has most probably been removed already, it does not exists. Skipping the error.");
276 }
277 _ => {
278 error!("Error uninstalling the service: {err}");
279 return Err(err.into());
280 }
281 },
282 }
283
284 if !keep_directories {
285 debug!(
286 "Removing data and log directories for {}",
287 self.service.name()
288 );
289 if self.service.data_dir_path().exists() {
292 debug!("Removing data directory {:?}", self.service.data_dir_path());
293 std::fs::remove_dir_all(self.service.data_dir_path())?;
294 }
295 if self.service.log_dir_path().exists() {
296 debug!("Removing log directory {:?}", self.service.log_dir_path());
297 std::fs::remove_dir_all(self.service.log_dir_path())?;
298 }
299 }
300
301 self.service.on_remove();
302 info!(
303 "Service {} has been removed successfully.",
304 self.service.name()
305 );
306
307 if self.verbosity != VerbosityLevel::Minimal {
308 println!(
309 "{} Service {} was removed",
310 "✓".green(),
311 self.service.name()
312 );
313 }
314
315 Ok(())
316 }
317
318 pub async fn upgrade(&mut self, options: UpgradeOptions) -> Result<UpgradeResult> {
319 let current_version = Version::parse(&self.service.version())?;
320 if !options.force
321 && (current_version == options.target_version
322 || options.target_version < current_version)
323 {
324 info!(
325 "The service {} is already at the latest version. No upgrade is required.",
326 self.service.name()
327 );
328 return Ok(UpgradeResult::NotRequired);
329 }
330
331 debug!("Stopping the service and copying the binary");
332 self.stop().await?;
333 std::fs::copy(options.clone().target_bin_path, self.service.bin_path())?;
334
335 self.service_control
336 .uninstall(&self.service.name(), self.service.is_user_mode())?;
337 self.service_control.install(
338 self.service
339 .build_upgrade_install_context(options.clone())?,
340 self.service.is_user_mode(),
341 )?;
342
343 if options.start_service {
344 match self.start().await {
345 Ok(start_duration) => start_duration,
346 Err(err) => {
347 self.service
348 .set_version(&options.target_version.to_string());
349 info!("The service has been upgraded but could not be started: {err}");
350 return Ok(UpgradeResult::UpgradedButNotStarted(
351 current_version.to_string(),
352 options.target_version.to_string(),
353 err.to_string(),
354 ));
355 }
356 }
357 }
358 self.service
359 .set_version(&options.target_version.to_string());
360
361 if options.force {
362 Ok(UpgradeResult::Forced(
363 current_version.to_string(),
364 options.target_version.to_string(),
365 ))
366 } else {
367 Ok(UpgradeResult::Upgraded(
368 current_version.to_string(),
369 options.target_version.to_string(),
370 ))
371 }
372 }
373}
374
375pub async fn status_report(
376 node_registry: &mut NodeRegistry,
377 service_control: &dyn ServiceControl,
378 detailed_view: bool,
379 output_json: bool,
380 fail: bool,
381 is_local_network: bool,
382) -> Result<()> {
383 refresh_node_registry(
384 node_registry,
385 service_control,
386 !output_json,
387 true,
388 is_local_network,
389 )
390 .await?;
391
392 if output_json {
393 let json = serde_json::to_string_pretty(&node_registry.to_status_summary())?;
394 println!("{json}");
395 } else if detailed_view {
396 for node in &node_registry.nodes {
397 print_banner(&format!(
398 "{} - {}",
399 &node.service_name,
400 format_status_without_colour(&node.status)
401 ));
402 println!("Version: {}", node.version);
403 println!(
404 "Peer ID: {}",
405 node.peer_id.map_or("-".to_string(), |p| p.to_string())
406 );
407 println!("RPC Socket: {}", node.rpc_socket_addr);
408 println!("Listen Addresses: {:?}", node.listen_addr);
409 println!(
410 "PID: {}",
411 node.pid.map_or("-".to_string(), |p| p.to_string())
412 );
413 println!("Data path: {}", node.data_dir_path.to_string_lossy());
414 println!("Log path: {}", node.log_dir_path.to_string_lossy());
415 println!("Bin path: {}", node.safenode_path.to_string_lossy());
416 println!(
417 "Connected peers: {}",
418 node.connected_peers
419 .as_ref()
420 .map_or("-".to_string(), |p| p.len().to_string())
421 );
422 println!(
423 "Reward balance: {}",
424 node.reward_balance
425 .map_or("-".to_string(), |b| b.to_string())
426 );
427 println!("Rewards address: {}", node.rewards_address);
428 println!();
429 }
430
431 if let Some(daemon) = &node_registry.daemon {
432 print_banner(&format!(
433 "{} - {}",
434 &daemon.service_name,
435 format_status(&daemon.status)
436 ));
437 println!("Version: {}", daemon.version);
438 println!("Bin path: {}", daemon.daemon_path.to_string_lossy());
439 }
440
441 if let Some(faucet) = &node_registry.faucet {
442 print_banner(&format!(
443 "{} - {}",
444 &faucet.service_name,
445 format_status(&faucet.status)
446 ));
447 println!("Version: {}", faucet.version);
448 println!("Bin path: {}", faucet.faucet_path.to_string_lossy());
449 println!("Log path: {}", faucet.log_dir_path.to_string_lossy());
450 }
451 } else {
452 println!(
453 "{:<18} {:<52} {:<7} {:>15}",
454 "Service Name", "Peer ID", "Status", "Connected Peers"
455 );
456 let nodes = node_registry
457 .nodes
458 .iter()
459 .filter(|n| n.status != ServiceStatus::Removed)
460 .collect::<Vec<&NodeServiceData>>();
461 for node in nodes {
462 let peer_id = node.peer_id.map_or("-".to_string(), |p| p.to_string());
463 let connected_peers = node
464 .connected_peers
465 .clone()
466 .map_or("-".to_string(), |p| p.len().to_string());
467 println!(
468 "{:<18} {:<52} {:<7} {:>15}",
469 node.service_name,
470 peer_id,
471 format_status(&node.status),
472 connected_peers
473 );
474 }
475 if let Some(daemon) = &node_registry.daemon {
476 println!(
477 "{:<18} {:<52} {:<7} {:>15}",
478 daemon.service_name,
479 "-",
480 format_status(&daemon.status),
481 "-"
482 );
483 }
484 if let Some(faucet) = &node_registry.faucet {
485 println!(
486 "{:<18} {:<52} {:<7} {:>15}",
487 faucet.service_name,
488 "-",
489 format_status(&faucet.status),
490 "-"
491 );
492 }
493 }
494
495 if fail {
496 let non_running_services = node_registry
497 .nodes
498 .iter()
499 .filter_map(|n| {
500 if n.status != ServiceStatus::Running {
501 Some(n.service_name.clone())
502 } else {
503 None
504 }
505 })
506 .collect::<Vec<String>>();
507 if non_running_services.is_empty() {
508 info!("Fail is set to true, but all services are running.");
509 } else {
510 error!(
511 "One or more nodes are not in a running state: {non_running_services:?}
512 "
513 );
514
515 return Err(Error::ServiceNotRunning(non_running_services));
516 }
517 }
518
519 Ok(())
520}
521
522pub async fn refresh_node_registry(
536 node_registry: &mut NodeRegistry,
537 service_control: &dyn ServiceControl,
538 print_refresh_message: bool,
539 full_refresh: bool,
540 is_local_network: bool,
541) -> Result<()> {
542 if print_refresh_message {
545 println!("Refreshing the node registry...");
546 }
547 info!("Refreshing the node registry");
548
549 for node in &mut node_registry.nodes {
550 match HotWallet::try_load_from(&node.data_dir_path) {
553 Ok(wallet) => {
554 node.reward_balance = Some(AttoTokens::from_u64(wallet.balance().as_nano()));
555 trace!(
556 "Wallet balance for node {}: {}",
557 node.service_name,
558 wallet.balance()
559 );
560 }
561 Err(_) => node.reward_balance = None,
562 }
563
564 let mut rpc_client = RpcClient::from_socket_addr(node.rpc_socket_addr);
565 rpc_client.set_max_attempts(1);
566 let mut service = NodeService::new(node, Box::new(rpc_client.clone()));
567
568 if is_local_network {
569 match rpc_client.node_info().await {
574 Ok(info) => {
575 let pid = info.pid;
576 debug!(
577 "local node {} is running with PID {pid}",
578 service.service_data.service_name
579 );
580 service.on_start(Some(pid), full_refresh).await?;
581 }
582 Err(_) => {
583 debug!(
584 "Failed to retrieve PID for local node {}",
585 service.service_data.service_name
586 );
587 service.on_stop().await?;
588 }
589 }
590 } else {
591 match service_control.get_process_pid(&service.bin_path()) {
592 Ok(pid) => {
593 debug!(
594 "{} is running with PID {pid}",
595 service.service_data.service_name
596 );
597 service.on_start(Some(pid), full_refresh).await?;
598 }
599 Err(_) => {
600 match service.status() {
601 ServiceStatus::Added => {
602 debug!(
606 "{} has not been started since it was installed",
607 service.service_data.service_name
608 );
609 }
610 ServiceStatus::Removed => {
611 debug!("{} has been removed", service.service_data.service_name);
614 }
615 _ => {
616 debug!(
617 "Failed to retrieve PID for {}",
618 service.service_data.service_name
619 );
620 service.on_stop().await?;
621 }
622 }
623 }
624 }
625 }
626 }
627 Ok(())
628}
629
630pub fn print_banner(text: &str) {
631 let padding = 2;
632 let text_width = text.len() + padding * 2;
633 let border_chars = 2;
634 let total_width = text_width + border_chars;
635 let top_bottom = "═".repeat(total_width);
636
637 println!("╔{}╗", top_bottom);
638 println!("║ {:^width$} ║", text, width = text_width);
639 println!("╚{}╝", top_bottom);
640}
641
642fn format_status(status: &ServiceStatus) -> String {
643 match status {
644 ServiceStatus::Running => "RUNNING".green().to_string(),
645 ServiceStatus::Stopped => "STOPPED".red().to_string(),
646 ServiceStatus::Added => "ADDED".yellow().to_string(),
647 ServiceStatus::Removed => "REMOVED".red().to_string(),
648 }
649}
650
651fn format_status_without_colour(status: &ServiceStatus) -> String {
652 match status {
653 ServiceStatus::Running => "RUNNING".to_string(),
654 ServiceStatus::Stopped => "STOPPED".to_string(),
655 ServiceStatus::Added => "ADDED".to_string(),
656 ServiceStatus::Removed => "REMOVED".to_string(),
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663 use assert_fs::prelude::*;
664 use assert_matches::assert_matches;
665 use async_trait::async_trait;
666 use color_eyre::eyre::Result;
667 use libp2p_identity::PeerId;
668 use mockall::{mock, predicate::*};
669 use predicates::prelude::*;
670 use service_manager::ServiceInstallCtx;
671 use sn_evm::{AttoTokens, CustomNetwork, EvmNetwork, RewardsAddress};
672 use sn_logging::LogFormat;
673 use sn_service_management::{
674 error::{Error as ServiceControlError, Result as ServiceControlResult},
675 node::{NodeService, NodeServiceData},
676 rpc::{NetworkInfo, NodeInfo, RecordAddress, RpcActions},
677 UpgradeOptions, UpgradeResult,
678 };
679 use std::{
680 ffi::OsString,
681 net::{IpAddr, Ipv4Addr, SocketAddr},
682 path::{Path, PathBuf},
683 str::FromStr,
684 time::Duration,
685 };
686
687 mock! {
688 pub RpcClient {}
689 #[async_trait]
690 impl RpcActions for RpcClient {
691 async fn node_info(&self) -> ServiceControlResult<NodeInfo>;
692 async fn network_info(&self) -> ServiceControlResult<NetworkInfo>;
693 async fn record_addresses(&self) -> ServiceControlResult<Vec<RecordAddress>>;
694 async fn node_restart(&self, delay_millis: u64, retain_peer_id: bool) -> ServiceControlResult<()>;
695 async fn node_stop(&self, delay_millis: u64) -> ServiceControlResult<()>;
696 async fn node_update(&self, delay_millis: u64) -> ServiceControlResult<()>;
697 async fn is_node_connected_to_network(&self, timeout: std::time::Duration) -> ServiceControlResult<()>;
698 async fn update_log_level(&self, log_levels: String) -> ServiceControlResult<()>;
699 }
700 }
701
702 mock! {
703 pub ServiceControl {}
704 impl ServiceControl for ServiceControl {
705 fn create_service_user(&self, username: &str) -> ServiceControlResult<()>;
706 fn get_available_port(&self) -> ServiceControlResult<u16>;
707 fn install(&self, install_ctx: ServiceInstallCtx, user_mode: bool) -> ServiceControlResult<()>;
708 fn get_process_pid(&self, bin_path: &Path) -> ServiceControlResult<u32>;
709 fn start(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
710 fn stop(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
711 fn uninstall(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
712 fn wait(&self, delay: u64);
713 }
714 }
715
716 #[tokio::test]
717 async fn start_should_start_a_newly_installed_service() -> Result<()> {
718 let mut mock_service_control = MockServiceControl::new();
719 let mut mock_rpc_client = MockRpcClient::new();
720
721 mock_service_control
722 .expect_start()
723 .with(eq("safenode1"), eq(false))
724 .times(1)
725 .returning(|_, _| Ok(()));
726 mock_service_control
727 .expect_wait()
728 .with(eq(3000))
729 .times(1)
730 .returning(|_| ());
731 mock_service_control
732 .expect_get_process_pid()
733 .with(eq(PathBuf::from(
734 "/var/safenode-manager/services/safenode1/safenode",
735 )))
736 .times(1)
737 .returning(|_| Ok(1000));
738
739 mock_rpc_client.expect_node_info().times(1).returning(|| {
740 Ok(NodeInfo {
741 pid: 1000,
742 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
743 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
744 log_path: PathBuf::from("/var/log/safenode/safenode1"),
745 version: "0.98.1".to_string(),
746 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
748 })
749 });
750 mock_rpc_client
751 .expect_network_info()
752 .times(1)
753 .returning(|| {
754 Ok(NetworkInfo {
755 connected_peers: vec![PeerId::from_str(
756 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
757 )?],
758 listeners: Vec::new(),
759 })
760 });
761
762 let mut service_data = NodeServiceData {
763 auto_restart: false,
764 connected_peers: None,
765 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
766 evm_network: EvmNetwork::Custom(CustomNetwork {
767 rpc_url_http: "http://localhost:8545".parse()?,
768 payment_token_address: RewardsAddress::from_str(
769 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
770 )?,
771 data_payments_address: RewardsAddress::from_str(
772 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
773 )?,
774 }),
775 genesis: false,
776 home_network: false,
777 listen_addr: None,
778 local: false,
779 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
780 log_format: None,
781 max_archived_log_files: None,
782 max_log_files: None,
783 metrics_port: None,
784 node_ip: None,
785 node_port: None,
786 number: 1,
787 owner: None,
788 peer_id: None,
789 pid: None,
790 rewards_address: RewardsAddress::from_str(
791 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
792 )?,
793 reward_balance: Some(AttoTokens::zero()),
794 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
795 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
796 service_name: "safenode1".to_string(),
797 status: ServiceStatus::Added,
798 upnp: false,
799 user: Some("safe".to_string()),
800 user_mode: false,
801 version: "0.98.1".to_string(),
802 };
803 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
804
805 let mut service_manager = ServiceManager::new(
806 service,
807 Box::new(mock_service_control),
808 VerbosityLevel::Normal,
809 );
810
811 service_manager.start().await?;
812
813 assert_eq!(
814 service_manager.service.service_data.connected_peers,
815 Some(vec![PeerId::from_str(
816 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
817 )?,])
818 );
819 assert_eq!(service_manager.service.service_data.pid, Some(1000));
820 assert_eq!(
821 service_manager.service.service_data.peer_id,
822 Some(PeerId::from_str(
823 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
824 )?)
825 );
826 assert_matches!(
827 service_manager.service.service_data.status,
828 ServiceStatus::Running
829 );
830
831 Ok(())
832 }
833
834 #[tokio::test]
835 async fn start_should_start_a_stopped_service() -> Result<()> {
836 let mut mock_service_control = MockServiceControl::new();
837 let mut mock_rpc_client = MockRpcClient::new();
838
839 mock_service_control
840 .expect_start()
841 .with(eq("safenode1"), eq(false))
842 .times(1)
843 .returning(|_, _| Ok(()));
844 mock_service_control
845 .expect_wait()
846 .with(eq(3000))
847 .times(1)
848 .returning(|_| ());
849 mock_service_control
850 .expect_get_process_pid()
851 .with(eq(PathBuf::from(
852 "/var/safenode-manager/services/safenode1/safenode",
853 )))
854 .times(1)
855 .returning(|_| Ok(1000));
856
857 mock_rpc_client.expect_node_info().times(1).returning(|| {
858 Ok(NodeInfo {
859 pid: 1000,
860 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
861 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
862 log_path: PathBuf::from("/var/log/safenode/safenode1"),
863 version: "0.98.1".to_string(),
864 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
866 })
867 });
868 mock_rpc_client
869 .expect_network_info()
870 .times(1)
871 .returning(|| {
872 Ok(NetworkInfo {
873 connected_peers: Vec::new(),
874 listeners: Vec::new(),
875 })
876 });
877
878 let mut service_data = NodeServiceData {
879 auto_restart: false,
880 connected_peers: None,
881 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
882 evm_network: EvmNetwork::Custom(CustomNetwork {
883 rpc_url_http: "http://localhost:8545".parse()?,
884 payment_token_address: RewardsAddress::from_str(
885 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
886 )?,
887 data_payments_address: RewardsAddress::from_str(
888 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
889 )?,
890 }),
891 genesis: false,
892 home_network: false,
893 listen_addr: None,
894 local: false,
895 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
896 log_format: None,
897 max_archived_log_files: None,
898 max_log_files: None,
899 metrics_port: None,
900 node_ip: None,
901 node_port: None,
902 number: 1,
903 owner: None,
904 peer_id: Some(PeerId::from_str(
905 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
906 )?),
907 pid: None,
908 rewards_address: RewardsAddress::from_str(
909 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
910 )?,
911 reward_balance: Some(AttoTokens::zero()),
912 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
913 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
914 service_name: "safenode1".to_string(),
915 status: ServiceStatus::Stopped,
916 upnp: false,
917 user: Some("safe".to_string()),
918 user_mode: false,
919 version: "0.98.1".to_string(),
920 };
921 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
922
923 let mut service_manager = ServiceManager::new(
924 service,
925 Box::new(mock_service_control),
926 VerbosityLevel::Normal,
927 );
928
929 service_manager.start().await?;
930
931 assert_eq!(service_manager.service.service_data.pid, Some(1000));
932 assert_eq!(
933 service_manager.service.service_data.peer_id,
934 Some(PeerId::from_str(
935 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
936 )?)
937 );
938 assert_matches!(
939 service_manager.service.service_data.status,
940 ServiceStatus::Running
941 );
942
943 Ok(())
944 }
945
946 #[tokio::test]
947 async fn start_should_not_attempt_to_start_a_running_service() -> Result<()> {
948 let mut mock_service_control = MockServiceControl::new();
949 let mock_rpc_client = MockRpcClient::new();
950
951 mock_service_control
952 .expect_get_process_pid()
953 .with(eq(PathBuf::from(
954 "/var/safenode-manager/services/safenode1/safenode",
955 )))
956 .times(1)
957 .returning(|_| Ok(100));
958
959 let mut service_data = NodeServiceData {
960 auto_restart: false,
961 connected_peers: None,
962 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
963 evm_network: EvmNetwork::Custom(CustomNetwork {
964 rpc_url_http: "http://localhost:8545".parse()?,
965 payment_token_address: RewardsAddress::from_str(
966 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
967 )?,
968 data_payments_address: RewardsAddress::from_str(
969 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
970 )?,
971 }),
972 genesis: false,
973 home_network: false,
974 listen_addr: None,
975 local: false,
976 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
977 log_format: None,
978 max_archived_log_files: None,
979 max_log_files: None,
980 metrics_port: None,
981 node_ip: None,
982 node_port: None,
983 number: 1,
984 owner: None,
985 peer_id: Some(PeerId::from_str(
986 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
987 )?),
988 pid: Some(1000),
989 rewards_address: RewardsAddress::from_str(
990 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
991 )?,
992 reward_balance: Some(AttoTokens::zero()),
993 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
994 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
995 service_name: "safenode1".to_string(),
996 status: ServiceStatus::Running,
997 upnp: false,
998 user: Some("safe".to_string()),
999 user_mode: false,
1000 version: "0.98.1".to_string(),
1001 };
1002 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1003
1004 let mut service_manager = ServiceManager::new(
1005 service,
1006 Box::new(mock_service_control),
1007 VerbosityLevel::Normal,
1008 );
1009
1010 service_manager.start().await?;
1011
1012 assert_eq!(service_manager.service.service_data.pid, Some(1000));
1013 assert_eq!(
1014 service_manager.service.service_data.peer_id,
1015 Some(PeerId::from_str(
1016 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
1017 )?)
1018 );
1019 assert_matches!(
1020 service_manager.service.service_data.status,
1021 ServiceStatus::Running
1022 );
1023
1024 Ok(())
1025 }
1026
1027 #[tokio::test]
1028 async fn start_should_start_a_service_marked_as_running_but_had_since_stopped() -> Result<()> {
1029 let mut mock_service_control = MockServiceControl::new();
1030 let mut mock_rpc_client = MockRpcClient::new();
1031
1032 mock_service_control
1033 .expect_get_process_pid()
1034 .with(eq(PathBuf::from(
1035 "/var/safenode-manager/services/safenode1/safenode",
1036 )))
1037 .times(1)
1038 .returning(|_| {
1039 Err(ServiceError::ServiceProcessNotFound(
1040 "Could not find process at '/var/safenode-manager/services/safenode1/safenode'"
1041 .to_string(),
1042 ))
1043 });
1044 mock_service_control
1045 .expect_start()
1046 .with(eq("safenode1"), eq(false))
1047 .times(1)
1048 .returning(|_, _| Ok(()));
1049 mock_service_control
1050 .expect_wait()
1051 .with(eq(3000))
1052 .times(1)
1053 .returning(|_| ());
1054 mock_service_control
1055 .expect_get_process_pid()
1056 .with(eq(PathBuf::from(
1057 "/var/safenode-manager/services/safenode1/safenode",
1058 )))
1059 .times(1)
1060 .returning(|_| Ok(1000));
1061
1062 mock_rpc_client.expect_node_info().times(1).returning(|| {
1063 Ok(NodeInfo {
1064 pid: 1000,
1065 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1066 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1067 log_path: PathBuf::from("/var/log/safenode/safenode1"),
1068 version: "0.98.1".to_string(),
1069 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
1071 })
1072 });
1073 mock_rpc_client
1074 .expect_network_info()
1075 .times(1)
1076 .returning(|| {
1077 Ok(NetworkInfo {
1078 connected_peers: Vec::new(),
1079 listeners: Vec::new(),
1080 })
1081 });
1082
1083 let mut service_data = NodeServiceData {
1084 auto_restart: false,
1085 connected_peers: None,
1086 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1087 evm_network: EvmNetwork::Custom(CustomNetwork {
1088 rpc_url_http: "http://localhost:8545".parse()?,
1089 payment_token_address: RewardsAddress::from_str(
1090 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1091 )?,
1092 data_payments_address: RewardsAddress::from_str(
1093 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1094 )?,
1095 }),
1096 genesis: false,
1097 home_network: false,
1098 listen_addr: None,
1099 local: false,
1100 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1101 log_format: None,
1102 max_archived_log_files: None,
1103 max_log_files: None,
1104 metrics_port: None,
1105 node_ip: None,
1106 node_port: None,
1107 number: 1,
1108 owner: None,
1109 peer_id: Some(PeerId::from_str(
1110 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1111 )?),
1112 pid: Some(1000),
1113 rewards_address: RewardsAddress::from_str(
1114 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1115 )?,
1116 reward_balance: Some(AttoTokens::zero()),
1117 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1118 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1119 service_name: "safenode1".to_string(),
1120 status: ServiceStatus::Running,
1121 upnp: false,
1122 user: Some("safe".to_string()),
1123 user_mode: false,
1124 version: "0.98.1".to_string(),
1125 };
1126 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1127
1128 let mut service_manager = ServiceManager::new(
1129 service,
1130 Box::new(mock_service_control),
1131 VerbosityLevel::Normal,
1132 );
1133
1134 service_manager.start().await?;
1135
1136 assert_eq!(service_manager.service.service_data.pid, Some(1000));
1137 assert_eq!(
1138 service_manager.service.service_data.peer_id,
1139 Some(PeerId::from_str(
1140 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
1141 )?)
1142 );
1143 assert_matches!(
1144 service_manager.service.service_data.status,
1145 ServiceStatus::Running
1146 );
1147
1148 Ok(())
1149 }
1150
1151 #[tokio::test]
1152 async fn start_should_return_an_error_if_the_process_was_not_found() -> Result<()> {
1153 let mut mock_service_control = MockServiceControl::new();
1154
1155 mock_service_control
1156 .expect_start()
1157 .with(eq("safenode1"), eq(false))
1158 .times(1)
1159 .returning(|_, _| Ok(()));
1160 mock_service_control
1161 .expect_wait()
1162 .with(eq(3000))
1163 .times(1)
1164 .returning(|_| ());
1165 mock_service_control
1166 .expect_get_process_pid()
1167 .with(eq(PathBuf::from(
1168 "/var/safenode-manager/services/safenode1/safenode",
1169 )))
1170 .times(1)
1171 .returning(|_| {
1172 Err(ServiceControlError::ServiceProcessNotFound(
1173 "/var/safenode-manager/services/safenode1/safenode".to_string(),
1174 ))
1175 });
1176
1177 let mut service_data = NodeServiceData {
1178 auto_restart: false,
1179 connected_peers: None,
1180 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1181 evm_network: EvmNetwork::Custom(CustomNetwork {
1182 rpc_url_http: "http://localhost:8545".parse()?,
1183 payment_token_address: RewardsAddress::from_str(
1184 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1185 )?,
1186 data_payments_address: RewardsAddress::from_str(
1187 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1188 )?,
1189 }),
1190 genesis: false,
1191 home_network: false,
1192 listen_addr: None,
1193 local: false,
1194 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1195 log_format: None,
1196 max_archived_log_files: None,
1197 max_log_files: None,
1198 metrics_port: None,
1199 node_ip: None,
1200 node_port: None,
1201 number: 1,
1202 owner: None,
1203 peer_id: None,
1204 pid: None,
1205 rewards_address: RewardsAddress::from_str(
1206 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1207 )?,
1208 reward_balance: Some(AttoTokens::zero()),
1209 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1210 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1211 service_name: "safenode1".to_string(),
1212 status: ServiceStatus::Added,
1213 upnp: false,
1214 user: Some("safe".to_string()),
1215 user_mode: false,
1216 version: "0.98.1".to_string(),
1217 };
1218 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1219 let mut service_manager = ServiceManager::new(
1220 service,
1221 Box::new(mock_service_control),
1222 VerbosityLevel::Normal,
1223 );
1224
1225 let result = service_manager.start().await;
1226 match result {
1227 Ok(_) => panic!("This test should have resulted in an error"),
1228 Err(e) => assert_eq!(
1229 "The PID of the process was not found after starting it.",
1230 e.to_string()
1231 ),
1232 }
1233
1234 Ok(())
1235 }
1236
1237 #[tokio::test]
1238 async fn start_should_start_a_user_mode_service() -> Result<()> {
1239 let mut mock_service_control = MockServiceControl::new();
1240 let mut mock_rpc_client = MockRpcClient::new();
1241
1242 mock_service_control
1243 .expect_start()
1244 .with(eq("safenode1"), eq(true))
1245 .times(1)
1246 .returning(|_, _| Ok(()));
1247 mock_service_control
1248 .expect_wait()
1249 .with(eq(3000))
1250 .times(1)
1251 .returning(|_| ());
1252 mock_service_control
1253 .expect_get_process_pid()
1254 .with(eq(PathBuf::from(
1255 "/var/safenode-manager/services/safenode1/safenode",
1256 )))
1257 .times(1)
1258 .returning(|_| Ok(100));
1259
1260 mock_rpc_client.expect_node_info().times(1).returning(|| {
1261 Ok(NodeInfo {
1262 pid: 1000,
1263 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1264 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1265 log_path: PathBuf::from("/var/log/safenode/safenode1"),
1266 version: "0.98.1".to_string(),
1267 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
1269 })
1270 });
1271 mock_rpc_client
1272 .expect_network_info()
1273 .times(1)
1274 .returning(|| {
1275 Ok(NetworkInfo {
1276 connected_peers: Vec::new(),
1277 listeners: Vec::new(),
1278 })
1279 });
1280
1281 let mut service_data = NodeServiceData {
1282 auto_restart: false,
1283 connected_peers: None,
1284 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1285 evm_network: EvmNetwork::Custom(CustomNetwork {
1286 rpc_url_http: "http://localhost:8545".parse()?,
1287 payment_token_address: RewardsAddress::from_str(
1288 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1289 )?,
1290 data_payments_address: RewardsAddress::from_str(
1291 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1292 )?,
1293 }),
1294 genesis: false,
1295 home_network: false,
1296 listen_addr: None,
1297 local: false,
1298 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1299 log_format: None,
1300 max_archived_log_files: None,
1301 max_log_files: None,
1302 metrics_port: None,
1303 node_ip: None,
1304 node_port: None,
1305 number: 1,
1306 owner: None,
1307 peer_id: None,
1308 pid: None,
1309 rewards_address: RewardsAddress::from_str(
1310 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1311 )?,
1312 reward_balance: Some(AttoTokens::zero()),
1313 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1314 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1315 service_name: "safenode1".to_string(),
1316 status: ServiceStatus::Added,
1317 upnp: false,
1318 user: Some("safe".to_string()),
1319 user_mode: true,
1320 version: "0.98.1".to_string(),
1321 };
1322 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1323
1324 let mut service_manager = ServiceManager::new(
1325 service,
1326 Box::new(mock_service_control),
1327 VerbosityLevel::Normal,
1328 );
1329
1330 service_manager.start().await?;
1331
1332 Ok(())
1333 }
1334
1335 #[tokio::test]
1336 async fn start_should_use_dynamic_startup_delay_if_set() -> Result<()> {
1337 let mut mock_service_control = MockServiceControl::new();
1338 let mut mock_rpc_client = MockRpcClient::new();
1339
1340 mock_service_control
1341 .expect_start()
1342 .with(eq("safenode1"), eq(false))
1343 .times(1)
1344 .returning(|_, _| Ok(()));
1345 mock_service_control
1346 .expect_wait()
1347 .with(eq(3000))
1348 .times(1)
1349 .returning(|_| ());
1350 mock_service_control
1351 .expect_get_process_pid()
1352 .with(eq(PathBuf::from(
1353 "/var/safenode-manager/services/safenode1/safenode",
1354 )))
1355 .times(1)
1356 .returning(|_| Ok(1000));
1357 mock_rpc_client
1358 .expect_is_node_connected_to_network()
1359 .times(1)
1360 .returning(|_| Ok(()));
1361 mock_rpc_client.expect_node_info().times(1).returning(|| {
1362 Ok(NodeInfo {
1363 pid: 1000,
1364 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1365 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1366 log_path: PathBuf::from("/var/log/safenode/safenode1"),
1367 version: "0.98.1".to_string(),
1368 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
1370 })
1371 });
1372 mock_rpc_client
1373 .expect_network_info()
1374 .times(1)
1375 .returning(|| {
1376 Ok(NetworkInfo {
1377 connected_peers: vec![PeerId::from_str(
1378 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1379 )?],
1380 listeners: Vec::new(),
1381 })
1382 });
1383
1384 let mut service_data = NodeServiceData {
1385 auto_restart: false,
1386 connected_peers: None,
1387 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1388 evm_network: EvmNetwork::Custom(CustomNetwork {
1389 rpc_url_http: "http://localhost:8545".parse()?,
1390 payment_token_address: RewardsAddress::from_str(
1391 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1392 )?,
1393 data_payments_address: RewardsAddress::from_str(
1394 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1395 )?,
1396 }),
1397 genesis: false,
1398 home_network: false,
1399 listen_addr: None,
1400 local: false,
1401 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1402 log_format: None,
1403 max_archived_log_files: None,
1404 max_log_files: None,
1405 metrics_port: None,
1406 node_ip: None,
1407 node_port: None,
1408 number: 1,
1409 owner: None,
1410 peer_id: None,
1411 pid: None,
1412 rewards_address: RewardsAddress::from_str(
1413 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1414 )?,
1415 reward_balance: Some(AttoTokens::zero()),
1416 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1417 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1418 service_name: "safenode1".to_string(),
1419 status: ServiceStatus::Added,
1420 upnp: false,
1421 user: Some("safe".to_string()),
1422 user_mode: false,
1423 version: "0.98.1".to_string(),
1424 };
1425 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client))
1426 .with_connection_timeout(Duration::from_secs(
1427 DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S,
1428 ));
1429 let mut service_manager = ServiceManager::new(
1430 service,
1431 Box::new(mock_service_control),
1432 VerbosityLevel::Normal,
1433 );
1434
1435 service_manager.start().await?;
1436
1437 Ok(())
1438 }
1439
1440 #[tokio::test]
1441 async fn stop_should_stop_a_running_service() -> Result<()> {
1442 let mut mock_service_control = MockServiceControl::new();
1443
1444 mock_service_control
1445 .expect_stop()
1446 .with(eq("safenode1"), eq(false))
1447 .times(1)
1448 .returning(|_, _| Ok(()));
1449 mock_service_control
1450 .expect_get_process_pid()
1451 .with(eq(PathBuf::from(
1452 "/var/safenode-manager/services/safenode1/safenode",
1453 )))
1454 .times(1)
1455 .returning(|_| Ok(100));
1456
1457 let mut service_data = NodeServiceData {
1458 auto_restart: false,
1459 connected_peers: None,
1460 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1461 evm_network: EvmNetwork::Custom(CustomNetwork {
1462 rpc_url_http: "http://localhost:8545".parse()?,
1463 payment_token_address: RewardsAddress::from_str(
1464 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1465 )?,
1466 data_payments_address: RewardsAddress::from_str(
1467 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1468 )?,
1469 }),
1470 genesis: false,
1471 home_network: false,
1472 listen_addr: None,
1473 local: false,
1474 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1475 log_format: None,
1476 max_archived_log_files: None,
1477 max_log_files: None,
1478 metrics_port: None,
1479 node_ip: None,
1480 node_port: None,
1481 number: 1,
1482 owner: None,
1483 peer_id: Some(PeerId::from_str(
1484 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1485 )?),
1486 pid: Some(1000),
1487 rewards_address: RewardsAddress::from_str(
1488 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1489 )?,
1490 reward_balance: Some(AttoTokens::zero()),
1491 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1492 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1493 service_name: "safenode1".to_string(),
1494 status: ServiceStatus::Running,
1495 upnp: false,
1496 user: Some("safe".to_string()),
1497 user_mode: false,
1498 version: "0.98.1".to_string(),
1499 };
1500 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1501 let mut service_manager = ServiceManager::new(
1502 service,
1503 Box::new(mock_service_control),
1504 VerbosityLevel::Normal,
1505 );
1506
1507 service_manager.stop().await?;
1508
1509 assert_eq!(service_manager.service.service_data.pid, None);
1510 assert_eq!(service_manager.service.service_data.connected_peers, None);
1511 assert_matches!(
1512 service_manager.service.service_data.status,
1513 ServiceStatus::Stopped
1514 );
1515 Ok(())
1516 }
1517
1518 #[tokio::test]
1519 async fn stop_should_not_return_error_for_attempt_to_stop_installed_service() -> Result<()> {
1520 let mut service_data = NodeServiceData {
1521 auto_restart: false,
1522 connected_peers: None,
1523 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1524 evm_network: EvmNetwork::Custom(CustomNetwork {
1525 rpc_url_http: "http://localhost:8545".parse()?,
1526 payment_token_address: RewardsAddress::from_str(
1527 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1528 )?,
1529 data_payments_address: RewardsAddress::from_str(
1530 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1531 )?,
1532 }),
1533 genesis: false,
1534 home_network: false,
1535 listen_addr: None,
1536 local: false,
1537 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1538 log_format: None,
1539 max_archived_log_files: None,
1540 max_log_files: None,
1541 metrics_port: None,
1542 node_ip: None,
1543 node_port: None,
1544 number: 1,
1545 owner: None,
1546 peer_id: None,
1547 pid: None,
1548 rewards_address: RewardsAddress::from_str(
1549 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1550 )?,
1551 reward_balance: Some(AttoTokens::zero()),
1552 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1553 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1554 service_name: "safenode1".to_string(),
1555 status: ServiceStatus::Added,
1556 upnp: false,
1557 user: Some("safe".to_string()),
1558 user_mode: false,
1559 version: "0.98.1".to_string(),
1560 };
1561 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1562 let mut service_manager = ServiceManager::new(
1563 service,
1564 Box::new(MockServiceControl::new()),
1565 VerbosityLevel::Normal,
1566 );
1567
1568 let result = service_manager.stop().await;
1569
1570 match result {
1571 Ok(()) => Ok(()),
1572 Err(_) => {
1573 panic!("The stop command should be idempotent and do nothing for an added service");
1574 }
1575 }
1576 }
1577
1578 #[tokio::test]
1579 async fn stop_should_return_ok_when_attempting_to_stop_service_that_was_already_stopped(
1580 ) -> Result<()> {
1581 let mut service_data = NodeServiceData {
1582 auto_restart: false,
1583 connected_peers: None,
1584 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1585 evm_network: EvmNetwork::Custom(CustomNetwork {
1586 rpc_url_http: "http://localhost:8545".parse()?,
1587 payment_token_address: RewardsAddress::from_str(
1588 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1589 )?,
1590 data_payments_address: RewardsAddress::from_str(
1591 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1592 )?,
1593 }),
1594 genesis: false,
1595 home_network: false,
1596 listen_addr: None,
1597 local: false,
1598 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1599 log_format: None,
1600 max_archived_log_files: None,
1601 max_log_files: None,
1602 metrics_port: None,
1603 node_ip: None,
1604 node_port: None,
1605 number: 1,
1606 owner: None,
1607 peer_id: Some(PeerId::from_str(
1608 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1609 )?),
1610 pid: None,
1611 rewards_address: RewardsAddress::from_str(
1612 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1613 )?,
1614 reward_balance: Some(AttoTokens::zero()),
1615 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1616 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1617 service_name: "safenode1".to_string(),
1618 status: ServiceStatus::Stopped,
1619 upnp: false,
1620 user: Some("safe".to_string()),
1621 user_mode: false,
1622 version: "0.98.1".to_string(),
1623 };
1624 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1625 let mut service_manager = ServiceManager::new(
1626 service,
1627 Box::new(MockServiceControl::new()),
1628 VerbosityLevel::Normal,
1629 );
1630
1631 let result = service_manager.stop().await;
1632
1633 match result {
1634 Ok(()) => Ok(()),
1635 Err(_) => {
1636 panic!(
1637 "The stop command should be idempotent and do nothing for an stopped service"
1638 );
1639 }
1640 }
1641 }
1642
1643 #[tokio::test]
1644 async fn stop_should_return_ok_when_attempting_to_stop_a_removed_service() -> Result<()> {
1645 let mut service_data = NodeServiceData {
1646 auto_restart: false,
1647 connected_peers: None,
1648 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1649 evm_network: EvmNetwork::Custom(CustomNetwork {
1650 rpc_url_http: "http://localhost:8545".parse()?,
1651 payment_token_address: RewardsAddress::from_str(
1652 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1653 )?,
1654 data_payments_address: RewardsAddress::from_str(
1655 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1656 )?,
1657 }),
1658 genesis: false,
1659 home_network: false,
1660 listen_addr: None,
1661 local: false,
1662 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1663 log_format: None,
1664 max_archived_log_files: None,
1665 max_log_files: None,
1666 metrics_port: None,
1667 node_ip: None,
1668 node_port: None,
1669 number: 1,
1670 owner: None,
1671 peer_id: None,
1672 pid: None,
1673 rewards_address: RewardsAddress::from_str(
1674 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1675 )?,
1676 reward_balance: Some(AttoTokens::zero()),
1677 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1678 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1679 service_name: "safenode1".to_string(),
1680 status: ServiceStatus::Removed,
1681 upnp: false,
1682 user: Some("safe".to_string()),
1683 user_mode: false,
1684 version: "0.98.1".to_string(),
1685 };
1686 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1687 let mut service_manager = ServiceManager::new(
1688 service,
1689 Box::new(MockServiceControl::new()),
1690 VerbosityLevel::Normal,
1691 );
1692
1693 let result = service_manager.stop().await;
1694
1695 match result {
1696 Ok(()) => Ok(()),
1697 Err(_) => {
1698 panic!(
1699 "The stop command should be idempotent and do nothing for a removed service"
1700 );
1701 }
1702 }
1703 }
1704
1705 #[tokio::test]
1706 async fn stop_should_stop_a_user_mode_service() -> Result<()> {
1707 let mut mock_service_control = MockServiceControl::new();
1708
1709 mock_service_control
1710 .expect_stop()
1711 .with(eq("safenode1"), eq(true))
1712 .times(1)
1713 .returning(|_, _| Ok(()));
1714 mock_service_control
1715 .expect_get_process_pid()
1716 .with(eq(PathBuf::from(
1717 "/var/safenode-manager/services/safenode1/safenode",
1718 )))
1719 .times(1)
1720 .returning(|_| Ok(100));
1721
1722 let mut service_data = NodeServiceData {
1723 auto_restart: false,
1724 connected_peers: None,
1725 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1726 evm_network: EvmNetwork::Custom(CustomNetwork {
1727 rpc_url_http: "http://localhost:8545".parse()?,
1728 payment_token_address: RewardsAddress::from_str(
1729 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1730 )?,
1731 data_payments_address: RewardsAddress::from_str(
1732 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1733 )?,
1734 }),
1735 genesis: false,
1736 home_network: false,
1737 listen_addr: None,
1738 local: false,
1739 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1740 log_format: None,
1741 max_archived_log_files: None,
1742 max_log_files: None,
1743 metrics_port: None,
1744 node_ip: None,
1745 node_port: None,
1746 number: 1,
1747 owner: None,
1748 peer_id: Some(PeerId::from_str(
1749 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1750 )?),
1751 pid: Some(1000),
1752 rewards_address: RewardsAddress::from_str(
1753 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1754 )?,
1755 reward_balance: Some(AttoTokens::zero()),
1756 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1757 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
1758 service_name: "safenode1".to_string(),
1759 status: ServiceStatus::Running,
1760 upnp: false,
1761 user: None,
1762 user_mode: true,
1763 version: "0.98.1".to_string(),
1764 };
1765 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1766 let mut service_manager = ServiceManager::new(
1767 service,
1768 Box::new(mock_service_control),
1769 VerbosityLevel::Normal,
1770 );
1771
1772 service_manager.stop().await?;
1773
1774 assert_eq!(service_manager.service.service_data.pid, None);
1775 assert_eq!(service_manager.service.service_data.connected_peers, None);
1776 assert_matches!(
1777 service_manager.service.service_data.status,
1778 ServiceStatus::Stopped
1779 );
1780 Ok(())
1781 }
1782
1783 #[tokio::test]
1784 async fn upgrade_should_upgrade_a_service_to_a_new_version() -> Result<()> {
1785 let current_version = "0.1.0";
1786 let target_version = "0.2.0";
1787
1788 let tmp_data_dir = assert_fs::TempDir::new()?;
1789 let current_install_dir = tmp_data_dir.child("safenode_install");
1790 current_install_dir.create_dir_all()?;
1791
1792 let current_node_bin = current_install_dir.child("safenode");
1793 current_node_bin.write_binary(b"fake safenode binary")?;
1794 let target_node_bin = tmp_data_dir.child("safenode");
1795 target_node_bin.write_binary(b"fake safenode binary")?;
1796
1797 let mut mock_service_control = MockServiceControl::new();
1798 let mut mock_rpc_client = MockRpcClient::new();
1799
1800 mock_service_control
1802 .expect_get_process_pid()
1803 .with(eq(current_node_bin.to_path_buf().clone()))
1804 .times(1)
1805 .returning(|_| Ok(1000));
1806 mock_service_control
1807 .expect_stop()
1808 .with(eq("safenode1"), eq(false))
1809 .times(1)
1810 .returning(|_, _| Ok(()));
1811
1812 mock_service_control
1814 .expect_uninstall()
1815 .with(eq("safenode1"), eq(false))
1816 .times(1)
1817 .returning(|_, _| Ok(()));
1818 mock_service_control
1819 .expect_install()
1820 .with(always(), always())
1821 .times(1)
1822 .returning(|_, _| Ok(()));
1823
1824 mock_service_control
1826 .expect_start()
1827 .with(eq("safenode1"), eq(false))
1828 .times(1)
1829 .returning(|_, _| Ok(()));
1830 mock_service_control
1831 .expect_wait()
1832 .with(eq(3000))
1833 .times(1)
1834 .returning(|_| ());
1835 mock_service_control
1836 .expect_get_process_pid()
1837 .with(eq(current_node_bin.to_path_buf().clone()))
1838 .times(1)
1839 .returning(|_| Ok(2000));
1840
1841 mock_rpc_client.expect_node_info().times(1).returning(|| {
1842 Ok(NodeInfo {
1843 pid: 2000,
1844 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1845 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1846 log_path: PathBuf::from("/var/log/safenode/safenode1"),
1847 version: target_version.to_string(),
1848 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
1850 })
1851 });
1852 mock_rpc_client
1853 .expect_network_info()
1854 .times(1)
1855 .returning(|| {
1856 Ok(NetworkInfo {
1857 connected_peers: Vec::new(),
1858 listeners: Vec::new(),
1859 })
1860 });
1861
1862 let mut service_data = NodeServiceData {
1863 auto_restart: false,
1864 connected_peers: None,
1865 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1866 evm_network: EvmNetwork::Custom(CustomNetwork {
1867 rpc_url_http: "http://localhost:8545".parse()?,
1868 payment_token_address: RewardsAddress::from_str(
1869 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1870 )?,
1871 data_payments_address: RewardsAddress::from_str(
1872 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1873 )?,
1874 }),
1875 genesis: false,
1876 home_network: false,
1877 listen_addr: None,
1878 local: false,
1879 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1880 log_format: None,
1881 max_archived_log_files: None,
1882 max_log_files: None,
1883 metrics_port: None,
1884 node_ip: None,
1885 node_port: None,
1886 number: 1,
1887 owner: None,
1888 peer_id: Some(PeerId::from_str(
1889 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1890 )?),
1891 pid: Some(1000),
1892 rewards_address: RewardsAddress::from_str(
1893 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1894 )?,
1895 reward_balance: Some(AttoTokens::zero()),
1896 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1897 safenode_path: current_node_bin.to_path_buf(),
1898 service_name: "safenode1".to_string(),
1899 status: ServiceStatus::Running,
1900 upnp: false,
1901 user: Some("safe".to_string()),
1902 user_mode: false,
1903 version: current_version.to_string(),
1904 };
1905 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1906 let mut service_manager = ServiceManager::new(
1907 service,
1908 Box::new(mock_service_control),
1909 VerbosityLevel::Normal,
1910 );
1911
1912 let upgrade_result = service_manager
1913 .upgrade(UpgradeOptions {
1914 auto_restart: false,
1915 bootstrap_peers: Vec::new(),
1916 env_variables: None,
1917 force: false,
1918 start_service: true,
1919 target_bin_path: target_node_bin.to_path_buf(),
1920 target_version: Version::parse(target_version).unwrap(),
1921 })
1922 .await?;
1923
1924 match upgrade_result {
1925 UpgradeResult::Upgraded(old_version, new_version) => {
1926 assert_eq!(old_version, current_version);
1927 assert_eq!(new_version, target_version);
1928 }
1929 _ => panic!(
1930 "Expected UpgradeResult::Upgraded but was {:#?}",
1931 upgrade_result
1932 ),
1933 }
1934
1935 assert_eq!(service_manager.service.service_data.pid, Some(2000));
1936 assert_eq!(
1937 service_manager.service.service_data.peer_id,
1938 Some(PeerId::from_str(
1939 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1940 )?)
1941 );
1942 assert_eq!(service_manager.service.service_data.version, target_version);
1943
1944 Ok(())
1945 }
1946
1947 #[tokio::test]
1948 async fn upgrade_should_not_be_required_if_target_is_less_than_current_version() -> Result<()> {
1949 let current_version = "0.2.0";
1950 let target_version = "0.1.0";
1951
1952 let tmp_data_dir = assert_fs::TempDir::new()?;
1953 let current_install_dir = tmp_data_dir.child("safenode_install");
1954 current_install_dir.create_dir_all()?;
1955
1956 let current_node_bin = current_install_dir.child("safenode");
1957 current_node_bin.write_binary(b"fake safenode binary")?;
1958 let target_node_bin = tmp_data_dir.child("safenode");
1959 target_node_bin.write_binary(b"fake safenode binary")?;
1960
1961 let mock_service_control = MockServiceControl::new();
1962 let mock_rpc_client = MockRpcClient::new();
1963
1964 let mut service_data = NodeServiceData {
1965 auto_restart: false,
1966 connected_peers: None,
1967 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
1968 evm_network: EvmNetwork::Custom(CustomNetwork {
1969 rpc_url_http: "http://localhost:8545".parse()?,
1970 payment_token_address: RewardsAddress::from_str(
1971 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1972 )?,
1973 data_payments_address: RewardsAddress::from_str(
1974 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1975 )?,
1976 }),
1977 genesis: false,
1978 home_network: false,
1979 listen_addr: None,
1980 local: false,
1981 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
1982 log_format: None,
1983 max_archived_log_files: None,
1984 max_log_files: None,
1985 metrics_port: None,
1986 node_ip: None,
1987 node_port: None,
1988 number: 1,
1989 owner: None,
1990 peer_id: Some(PeerId::from_str(
1991 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1992 )?),
1993 pid: Some(1000),
1994 rewards_address: RewardsAddress::from_str(
1995 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1996 )?,
1997 reward_balance: Some(AttoTokens::zero()),
1998 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1999 safenode_path: current_node_bin.to_path_buf(),
2000 service_name: "safenode1".to_string(),
2001 status: ServiceStatus::Running,
2002 upnp: false,
2003 user: Some("safe".to_string()),
2004 user_mode: false,
2005 version: current_version.to_string(),
2006 };
2007 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2008
2009 let mut service_manager = ServiceManager::new(
2010 service,
2011 Box::new(mock_service_control),
2012 VerbosityLevel::Normal,
2013 );
2014
2015 let upgrade_result = service_manager
2016 .upgrade(UpgradeOptions {
2017 auto_restart: false,
2018 bootstrap_peers: Vec::new(),
2019 env_variables: None,
2020 force: false,
2021 start_service: true,
2022 target_bin_path: target_node_bin.to_path_buf(),
2023 target_version: Version::parse(target_version).unwrap(),
2024 })
2025 .await?;
2026
2027 assert_matches!(upgrade_result, UpgradeResult::NotRequired);
2028
2029 Ok(())
2030 }
2031
2032 #[tokio::test]
2033 async fn upgrade_should_downgrade_to_a_previous_version_if_force_is_used() -> Result<()> {
2034 let current_version = "0.1.0";
2035 let target_version = "0.2.0";
2036
2037 let tmp_data_dir = assert_fs::TempDir::new()?;
2038 let current_install_dir = tmp_data_dir.child("safenode_install");
2039 current_install_dir.create_dir_all()?;
2040
2041 let current_node_bin = current_install_dir.child("safenode");
2042 current_node_bin.write_binary(b"fake safenode binary")?;
2043 let target_node_bin = tmp_data_dir.child("safenode");
2044 target_node_bin.write_binary(b"fake safenode binary")?;
2045
2046 let mut mock_service_control = MockServiceControl::new();
2047 let mut mock_rpc_client = MockRpcClient::new();
2048
2049 mock_service_control
2051 .expect_get_process_pid()
2052 .with(eq(current_node_bin.to_path_buf().clone()))
2053 .times(1)
2054 .returning(|_| Ok(1000));
2055 mock_service_control
2056 .expect_stop()
2057 .with(eq("safenode1"), eq(false))
2058 .times(1)
2059 .returning(|_, _| Ok(()));
2060
2061 mock_service_control
2063 .expect_uninstall()
2064 .with(eq("safenode1"), eq(false))
2065 .times(1)
2066 .returning(|_, _| Ok(()));
2067 mock_service_control
2068 .expect_install()
2069 .with(always(), always())
2070 .times(1)
2071 .returning(|_, _| Ok(()));
2072
2073 mock_service_control
2075 .expect_start()
2076 .with(eq("safenode1"), eq(false))
2077 .times(1)
2078 .returning(|_, _| Ok(()));
2079 mock_service_control
2080 .expect_wait()
2081 .with(eq(3000))
2082 .times(1)
2083 .returning(|_| ());
2084 mock_service_control
2085 .expect_get_process_pid()
2086 .with(eq(current_node_bin.to_path_buf().clone()))
2087 .times(1)
2088 .returning(|_| Ok(2000));
2089
2090 mock_rpc_client.expect_node_info().times(1).returning(|| {
2091 Ok(NodeInfo {
2092 pid: 2000,
2093 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2094 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2095 log_path: PathBuf::from("/var/log/safenode/safenode1"),
2096 version: target_version.to_string(),
2097 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
2099 })
2100 });
2101 mock_rpc_client
2102 .expect_network_info()
2103 .times(1)
2104 .returning(|| {
2105 Ok(NetworkInfo {
2106 connected_peers: Vec::new(),
2107 listeners: Vec::new(),
2108 })
2109 });
2110
2111 let mut service_data = NodeServiceData {
2112 auto_restart: false,
2113 connected_peers: None,
2114 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2115 evm_network: EvmNetwork::Custom(CustomNetwork {
2116 rpc_url_http: "http://localhost:8545".parse()?,
2117 payment_token_address: RewardsAddress::from_str(
2118 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2119 )?,
2120 data_payments_address: RewardsAddress::from_str(
2121 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2122 )?,
2123 }),
2124 genesis: false,
2125 home_network: false,
2126 listen_addr: None,
2127 local: false,
2128 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2129 log_format: None,
2130 max_archived_log_files: None,
2131 max_log_files: None,
2132 metrics_port: None,
2133 node_ip: None,
2134 node_port: None,
2135 number: 1,
2136 owner: None,
2137 peer_id: Some(PeerId::from_str(
2138 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2139 )?),
2140 pid: Some(1000),
2141 rewards_address: RewardsAddress::from_str(
2142 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2143 )?,
2144 reward_balance: Some(AttoTokens::zero()),
2145 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2146 safenode_path: current_node_bin.to_path_buf(),
2147 service_name: "safenode1".to_string(),
2148 status: ServiceStatus::Running,
2149 upnp: false,
2150 user: Some("safe".to_string()),
2151 user_mode: false,
2152 version: current_version.to_string(),
2153 };
2154 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2155
2156 let mut service_manager = ServiceManager::new(
2157 service,
2158 Box::new(mock_service_control),
2159 VerbosityLevel::Normal,
2160 );
2161
2162 let upgrade_result = service_manager
2163 .upgrade(UpgradeOptions {
2164 auto_restart: false,
2165 bootstrap_peers: Vec::new(),
2166 env_variables: None,
2167 force: true,
2168 start_service: true,
2169 target_bin_path: target_node_bin.to_path_buf(),
2170 target_version: Version::parse(target_version).unwrap(),
2171 })
2172 .await?;
2173
2174 match upgrade_result {
2175 UpgradeResult::Forced(old_version, new_version) => {
2176 assert_eq!(old_version, current_version);
2177 assert_eq!(new_version, target_version);
2178 }
2179 _ => panic!(
2180 "Expected UpgradeResult::Forced but was {:#?}",
2181 upgrade_result
2182 ),
2183 }
2184
2185 assert_eq!(service_manager.service.service_data.pid, Some(2000));
2186 assert_eq!(
2187 service_manager.service.service_data.peer_id,
2188 Some(PeerId::from_str(
2189 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2190 )?)
2191 );
2192 assert_eq!(service_manager.service.service_data.version, target_version);
2193
2194 Ok(())
2195 }
2196
2197 #[tokio::test]
2198 async fn upgrade_should_upgrade_and_not_start_the_service() -> Result<()> {
2199 let current_version = "0.1.0";
2200 let target_version = "0.2.0";
2201
2202 let tmp_data_dir = assert_fs::TempDir::new()?;
2203 let current_install_dir = tmp_data_dir.child("safenode_install");
2204 current_install_dir.create_dir_all()?;
2205
2206 let current_node_bin = current_install_dir.child("safenode");
2207 current_node_bin.write_binary(b"fake safenode binary")?;
2208 let target_node_bin = tmp_data_dir.child("safenode");
2209 target_node_bin.write_binary(b"fake safenode binary")?;
2210
2211 let mut mock_service_control = MockServiceControl::new();
2212 let mut mock_rpc_client = MockRpcClient::new();
2213
2214 mock_service_control
2216 .expect_get_process_pid()
2217 .with(eq(current_node_bin.to_path_buf().clone()))
2218 .times(1)
2219 .returning(|_| Ok(1000));
2220 mock_service_control
2221 .expect_stop()
2222 .with(eq("safenode1"), eq(false))
2223 .times(1)
2224 .returning(|_, _| Ok(()));
2225
2226 mock_service_control
2228 .expect_uninstall()
2229 .with(eq("safenode1"), eq(false))
2230 .times(1)
2231 .returning(|_, _| Ok(()));
2232 mock_service_control
2233 .expect_install()
2234 .with(always(), always())
2235 .times(1)
2236 .returning(|_, _| Ok(()));
2237
2238 mock_service_control
2240 .expect_start()
2241 .with(eq("safenode1"), eq(false))
2242 .times(0)
2243 .returning(|_, _| Ok(()));
2244 mock_service_control
2245 .expect_wait()
2246 .with(eq(3000))
2247 .times(0)
2248 .returning(|_| ());
2249 mock_rpc_client.expect_node_info().times(0).returning(|| {
2250 Ok(NodeInfo {
2251 pid: 2000,
2252 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2253 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2254 log_path: PathBuf::from("/var/log/safenode/safenode1"),
2255 version: target_version.to_string(),
2256 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
2258 })
2259 });
2260 mock_rpc_client
2261 .expect_network_info()
2262 .times(0)
2263 .returning(|| {
2264 Ok(NetworkInfo {
2265 connected_peers: Vec::new(),
2266 listeners: Vec::new(),
2267 })
2268 });
2269
2270 let mut service_data = NodeServiceData {
2271 auto_restart: false,
2272 connected_peers: None,
2273 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2274 evm_network: EvmNetwork::Custom(CustomNetwork {
2275 rpc_url_http: "http://localhost:8545".parse()?,
2276 payment_token_address: RewardsAddress::from_str(
2277 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2278 )?,
2279 data_payments_address: RewardsAddress::from_str(
2280 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2281 )?,
2282 }),
2283 genesis: false,
2284 home_network: false,
2285 listen_addr: None,
2286 local: false,
2287 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2288 log_format: None,
2289 max_archived_log_files: None,
2290 max_log_files: None,
2291 metrics_port: None,
2292 node_ip: None,
2293 node_port: None,
2294 number: 1,
2295 owner: None,
2296 peer_id: Some(PeerId::from_str(
2297 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2298 )?),
2299 pid: Some(1000),
2300 rewards_address: RewardsAddress::from_str(
2301 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2302 )?,
2303 reward_balance: Some(AttoTokens::zero()),
2304 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2305 safenode_path: current_node_bin.to_path_buf(),
2306 service_name: "safenode1".to_string(),
2307 status: ServiceStatus::Running,
2308 upnp: false,
2309 user: Some("safe".to_string()),
2310 user_mode: false,
2311 version: current_version.to_string(),
2312 };
2313 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2314
2315 let mut service_manager = ServiceManager::new(
2316 service,
2317 Box::new(mock_service_control),
2318 VerbosityLevel::Normal,
2319 );
2320
2321 let upgrade_result = service_manager
2322 .upgrade(UpgradeOptions {
2323 auto_restart: false,
2324 bootstrap_peers: Vec::new(),
2325 env_variables: None,
2326 force: false,
2327 start_service: false,
2328 target_bin_path: target_node_bin.to_path_buf(),
2329 target_version: Version::parse(target_version).unwrap(),
2330 })
2331 .await?;
2332
2333 match upgrade_result {
2334 UpgradeResult::Upgraded(old_version, new_version) => {
2335 assert_eq!(old_version, current_version);
2336 assert_eq!(new_version, target_version);
2337 }
2338 _ => panic!(
2339 "Expected UpgradeResult::Upgraded but was {:#?}",
2340 upgrade_result
2341 ),
2342 }
2343
2344 assert_eq!(service_manager.service.service_data.pid, None);
2345 assert_eq!(
2346 service_manager.service.service_data.peer_id,
2347 Some(PeerId::from_str(
2348 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2349 )?)
2350 );
2351 assert_eq!(service_manager.service.service_data.version, target_version);
2352 assert_matches!(
2353 service_manager.service.service_data.status,
2354 ServiceStatus::Stopped
2355 );
2356
2357 Ok(())
2358 }
2359
2360 #[tokio::test]
2361 async fn upgrade_should_return_upgraded_but_not_started_if_service_did_not_start() -> Result<()>
2362 {
2363 let current_version = "0.1.0";
2364 let target_version = "0.2.0";
2365
2366 let tmp_data_dir = assert_fs::TempDir::new()?;
2367 let current_install_dir = tmp_data_dir.child("safenode_install");
2368 current_install_dir.create_dir_all()?;
2369
2370 let current_node_bin = current_install_dir.child("safenode");
2371 current_node_bin.write_binary(b"fake safenode binary")?;
2372 let target_node_bin = tmp_data_dir.child("safenode");
2373 target_node_bin.write_binary(b"fake safenode binary")?;
2374
2375 let current_node_bin_str = current_node_bin.to_path_buf().to_string_lossy().to_string();
2376
2377 let mut mock_service_control = MockServiceControl::new();
2378
2379 mock_service_control
2381 .expect_get_process_pid()
2382 .with(eq(current_node_bin.to_path_buf().clone()))
2383 .times(1)
2384 .returning(|_| Ok(1000));
2385 mock_service_control
2386 .expect_stop()
2387 .with(eq("safenode1"), eq(false))
2388 .times(1)
2389 .returning(|_, _| Ok(()));
2390
2391 mock_service_control
2393 .expect_uninstall()
2394 .with(eq("safenode1"), eq(false))
2395 .times(1)
2396 .returning(|_, _| Ok(()));
2397 mock_service_control
2398 .expect_install()
2399 .with(always(), always())
2400 .times(1)
2401 .returning(|_, _| Ok(()));
2402
2403 mock_service_control
2405 .expect_start()
2406 .with(eq("safenode1"), eq(false))
2407 .times(1)
2408 .returning(|_, _| Ok(()));
2409 mock_service_control
2410 .expect_wait()
2411 .with(eq(3000))
2412 .times(1)
2413 .returning(|_| ());
2414 mock_service_control
2415 .expect_get_process_pid()
2416 .with(eq(current_node_bin.to_path_buf().clone()))
2417 .times(1)
2418 .returning(move |_| {
2419 Err(ServiceControlError::ServiceProcessNotFound(
2420 current_node_bin_str.clone(),
2421 ))
2422 });
2423
2424 let mut service_data = NodeServiceData {
2425 auto_restart: false,
2426 connected_peers: None,
2427 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2428 evm_network: EvmNetwork::Custom(CustomNetwork {
2429 rpc_url_http: "http://localhost:8545".parse()?,
2430 payment_token_address: RewardsAddress::from_str(
2431 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2432 )?,
2433 data_payments_address: RewardsAddress::from_str(
2434 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2435 )?,
2436 }),
2437 genesis: false,
2438 home_network: false,
2439 listen_addr: None,
2440 local: false,
2441 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2442 log_format: None,
2443 max_archived_log_files: None,
2444 max_log_files: None,
2445 metrics_port: None,
2446 node_ip: None,
2447 node_port: None,
2448 number: 1,
2449 owner: None,
2450 peer_id: Some(PeerId::from_str(
2451 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2452 )?),
2453 pid: Some(1000),
2454 rewards_address: RewardsAddress::from_str(
2455 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2456 )?,
2457 reward_balance: Some(AttoTokens::zero()),
2458 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2459 safenode_path: current_node_bin.to_path_buf(),
2460 service_name: "safenode1".to_string(),
2461 status: ServiceStatus::Running,
2462 upnp: false,
2463 user: Some("safe".to_string()),
2464 user_mode: false,
2465 version: current_version.to_string(),
2466 };
2467 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
2468 let mut service_manager = ServiceManager::new(
2469 service,
2470 Box::new(mock_service_control),
2471 VerbosityLevel::Normal,
2472 );
2473
2474 let upgrade_result = service_manager
2475 .upgrade(UpgradeOptions {
2476 auto_restart: false,
2477 bootstrap_peers: Vec::new(),
2478 env_variables: None,
2479 force: false,
2480 start_service: true,
2481 target_bin_path: target_node_bin.to_path_buf(),
2482 target_version: Version::parse(target_version).unwrap(),
2483 })
2484 .await?;
2485
2486 match upgrade_result {
2487 UpgradeResult::UpgradedButNotStarted(old_version, new_version, _) => {
2488 assert_eq!(old_version, current_version);
2489 assert_eq!(new_version, target_version);
2490 }
2491 _ => panic!(
2492 "Expected UpgradeResult::UpgradedButNotStarted but was {:#?}",
2493 upgrade_result
2494 ),
2495 }
2496
2497 Ok(())
2498 }
2499
2500 #[tokio::test]
2501 async fn upgrade_should_upgrade_a_service_in_user_mode() -> Result<()> {
2502 let current_version = "0.1.0";
2503 let target_version = "0.2.0";
2504
2505 let tmp_data_dir = assert_fs::TempDir::new()?;
2506 let current_install_dir = tmp_data_dir.child("safenode_install");
2507 current_install_dir.create_dir_all()?;
2508
2509 let current_node_bin = current_install_dir.child("safenode");
2510 current_node_bin.write_binary(b"fake safenode binary")?;
2511 let target_node_bin = tmp_data_dir.child("safenode");
2512 target_node_bin.write_binary(b"fake safenode binary")?;
2513
2514 let mut mock_service_control = MockServiceControl::new();
2515 let mut mock_rpc_client = MockRpcClient::new();
2516
2517 mock_service_control
2519 .expect_get_process_pid()
2520 .with(eq(current_node_bin.to_path_buf().clone()))
2521 .times(1)
2522 .returning(|_| Ok(1000));
2523 mock_service_control
2524 .expect_stop()
2525 .with(eq("safenode1"), eq(true))
2526 .times(1)
2527 .returning(|_, _| Ok(()));
2528
2529 mock_service_control
2531 .expect_uninstall()
2532 .with(eq("safenode1"), eq(true))
2533 .times(1)
2534 .returning(|_, _| Ok(()));
2535 mock_service_control
2536 .expect_install()
2537 .with(always(), eq(true))
2538 .times(1)
2539 .returning(|_, _| Ok(()));
2540
2541 mock_service_control
2543 .expect_start()
2544 .with(eq("safenode1"), eq(true))
2545 .times(1)
2546 .returning(|_, _| Ok(()));
2547 mock_service_control
2548 .expect_wait()
2549 .with(eq(3000))
2550 .times(1)
2551 .returning(|_| ());
2552 mock_service_control
2553 .expect_get_process_pid()
2554 .with(eq(current_node_bin.to_path_buf().clone()))
2555 .times(1)
2556 .returning(|_| Ok(2000));
2557
2558 mock_rpc_client.expect_node_info().times(1).returning(|| {
2559 Ok(NodeInfo {
2560 pid: 2000,
2561 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2562 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2563 log_path: PathBuf::from("/var/log/safenode/safenode1"),
2564 version: target_version.to_string(),
2565 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
2567 })
2568 });
2569 mock_rpc_client
2570 .expect_network_info()
2571 .times(1)
2572 .returning(|| {
2573 Ok(NetworkInfo {
2574 connected_peers: Vec::new(),
2575 listeners: Vec::new(),
2576 })
2577 });
2578
2579 let mut service_data = NodeServiceData {
2580 auto_restart: false,
2581 connected_peers: None,
2582 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2583 evm_network: EvmNetwork::Custom(CustomNetwork {
2584 rpc_url_http: "http://localhost:8545".parse()?,
2585 payment_token_address: RewardsAddress::from_str(
2586 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2587 )?,
2588 data_payments_address: RewardsAddress::from_str(
2589 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2590 )?,
2591 }),
2592 genesis: false,
2593 home_network: false,
2594 listen_addr: None,
2595 local: false,
2596 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2597 log_format: None,
2598 max_archived_log_files: None,
2599 max_log_files: None,
2600 metrics_port: None,
2601 node_ip: None,
2602 node_port: None,
2603 number: 1,
2604 owner: None,
2605 peer_id: Some(PeerId::from_str(
2606 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2607 )?),
2608 pid: Some(1000),
2609 rewards_address: RewardsAddress::from_str(
2610 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2611 )?,
2612 reward_balance: Some(AttoTokens::zero()),
2613 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2614 safenode_path: current_node_bin.to_path_buf(),
2615 service_name: "safenode1".to_string(),
2616 status: ServiceStatus::Running,
2617 upnp: false,
2618 user: None,
2619 user_mode: true,
2620 version: current_version.to_string(),
2621 };
2622 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2623
2624 let mut service_manager = ServiceManager::new(
2625 service,
2626 Box::new(mock_service_control),
2627 VerbosityLevel::Normal,
2628 );
2629
2630 let upgrade_result = service_manager
2631 .upgrade(UpgradeOptions {
2632 auto_restart: false,
2633 bootstrap_peers: Vec::new(),
2634 env_variables: None,
2635 force: false,
2636 start_service: true,
2637 target_bin_path: target_node_bin.to_path_buf(),
2638 target_version: Version::parse(target_version).unwrap(),
2639 })
2640 .await?;
2641
2642 match upgrade_result {
2643 UpgradeResult::Upgraded(old_version, new_version) => {
2644 assert_eq!(old_version, current_version);
2645 assert_eq!(new_version, target_version);
2646 }
2647 _ => panic!(
2648 "Expected UpgradeResult::Upgraded but was {:#?}",
2649 upgrade_result
2650 ),
2651 }
2652
2653 assert_eq!(service_manager.service.service_data.pid, Some(2000));
2654 assert_eq!(
2655 service_manager.service.service_data.peer_id,
2656 Some(PeerId::from_str(
2657 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2658 )?)
2659 );
2660 assert_eq!(service_manager.service.service_data.version, target_version);
2661
2662 Ok(())
2663 }
2664
2665 #[tokio::test]
2666 async fn upgrade_should_retain_the_upnp_flag() -> Result<()> {
2667 let current_version = "0.1.0";
2668 let target_version = "0.2.0";
2669
2670 let tmp_data_dir = assert_fs::TempDir::new()?;
2671 let current_install_dir = tmp_data_dir.child("safenode_install");
2672 current_install_dir.create_dir_all()?;
2673
2674 let current_node_bin = current_install_dir.child("safenode");
2675 current_node_bin.write_binary(b"fake safenode binary")?;
2676 let target_node_bin = tmp_data_dir.child("safenode");
2677 target_node_bin.write_binary(b"fake safenode binary")?;
2678
2679 let mut mock_service_control = MockServiceControl::new();
2680 let mut mock_rpc_client = MockRpcClient::new();
2681
2682 mock_service_control
2684 .expect_get_process_pid()
2685 .with(eq(current_node_bin.to_path_buf().clone()))
2686 .times(1)
2687 .returning(|_| Ok(1000));
2688 mock_service_control
2689 .expect_stop()
2690 .with(eq("safenode1"), eq(false))
2691 .times(1)
2692 .returning(|_, _| Ok(()));
2693
2694 mock_service_control
2696 .expect_uninstall()
2697 .with(eq("safenode1"), eq(false))
2698 .times(1)
2699 .returning(|_, _| Ok(()));
2700 mock_service_control
2701 .expect_install()
2702 .with(
2703 eq(ServiceInstallCtx {
2704 args: vec![
2705 OsString::from("--rpc"),
2706 OsString::from("127.0.0.1:8081"),
2707 OsString::from("--root-dir"),
2708 OsString::from("/var/safenode-manager/services/safenode1"),
2709 OsString::from("--log-output-dest"),
2710 OsString::from("/var/log/safenode/safenode1"),
2711 OsString::from("--upnp"),
2712 OsString::from("--rewards-address"),
2713 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
2714 OsString::from("evm-arbitrum-one"),
2715 ],
2716 autostart: false,
2717 contents: None,
2718 environment: None,
2719 label: "safenode1".parse()?,
2720 program: current_node_bin.to_path_buf(),
2721 username: Some("safe".to_string()),
2722 working_directory: None,
2723 }),
2724 eq(false),
2725 )
2726 .times(1)
2727 .returning(|_, _| Ok(()));
2728
2729 mock_service_control
2731 .expect_start()
2732 .with(eq("safenode1"), eq(false))
2733 .times(1)
2734 .returning(|_, _| Ok(()));
2735 mock_service_control
2736 .expect_wait()
2737 .with(eq(3000))
2738 .times(1)
2739 .returning(|_| ());
2740 mock_service_control
2741 .expect_get_process_pid()
2742 .with(eq(current_node_bin.to_path_buf().clone()))
2743 .times(1)
2744 .returning(|_| Ok(100));
2745
2746 mock_rpc_client.expect_node_info().times(1).returning(|| {
2747 Ok(NodeInfo {
2748 pid: 2000,
2749 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2750 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2751 log_path: PathBuf::from("/var/log/safenode/safenode1"),
2752 version: target_version.to_string(),
2753 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
2755 })
2756 });
2757 mock_rpc_client
2758 .expect_network_info()
2759 .times(1)
2760 .returning(|| {
2761 Ok(NetworkInfo {
2762 connected_peers: Vec::new(),
2763 listeners: Vec::new(),
2764 })
2765 });
2766
2767 let mut service_data = NodeServiceData {
2768 auto_restart: false,
2769 connected_peers: None,
2770 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2771 evm_network: EvmNetwork::ArbitrumOne,
2772 genesis: false,
2773 home_network: false,
2774 listen_addr: None,
2775 local: false,
2776 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2777 log_format: None,
2778 max_archived_log_files: None,
2779 max_log_files: None,
2780 metrics_port: None,
2781 node_ip: None,
2782 node_port: None,
2783 number: 1,
2784 owner: None,
2785 peer_id: Some(PeerId::from_str(
2786 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2787 )?),
2788 pid: Some(1000),
2789 rewards_address: RewardsAddress::from_str(
2790 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2791 )?,
2792 reward_balance: Some(AttoTokens::zero()),
2793 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2794 safenode_path: current_node_bin.to_path_buf(),
2795 service_name: "safenode1".to_string(),
2796 status: ServiceStatus::Running,
2797 upnp: true,
2798 user: Some("safe".to_string()),
2799 user_mode: false,
2800 version: current_version.to_string(),
2801 };
2802 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2803
2804 let mut service_manager = ServiceManager::new(
2805 service,
2806 Box::new(mock_service_control),
2807 VerbosityLevel::Normal,
2808 );
2809
2810 service_manager
2811 .upgrade(UpgradeOptions {
2812 auto_restart: false,
2813 bootstrap_peers: Vec::new(),
2814 env_variables: None,
2815 force: false,
2816 start_service: true,
2817 target_bin_path: target_node_bin.to_path_buf(),
2818 target_version: Version::parse(target_version).unwrap(),
2819 })
2820 .await?;
2821
2822 assert!(service_manager.service.service_data.upnp);
2823
2824 Ok(())
2825 }
2826
2827 #[tokio::test]
2828 async fn upgrade_should_retain_the_log_format_flag() -> Result<()> {
2829 let current_version = "0.1.0";
2830 let target_version = "0.2.0";
2831
2832 let tmp_data_dir = assert_fs::TempDir::new()?;
2833 let current_install_dir = tmp_data_dir.child("safenode_install");
2834 current_install_dir.create_dir_all()?;
2835
2836 let current_node_bin = current_install_dir.child("safenode");
2837 current_node_bin.write_binary(b"fake safenode binary")?;
2838 let target_node_bin = tmp_data_dir.child("safenode");
2839 target_node_bin.write_binary(b"fake safenode binary")?;
2840
2841 let mut mock_service_control = MockServiceControl::new();
2842 let mut mock_rpc_client = MockRpcClient::new();
2843
2844 mock_service_control
2846 .expect_get_process_pid()
2847 .with(eq(current_node_bin.to_path_buf().clone()))
2848 .times(1)
2849 .returning(|_| Ok(1000));
2850 mock_service_control
2851 .expect_stop()
2852 .with(eq("safenode1"), eq(false))
2853 .times(1)
2854 .returning(|_, _| Ok(()));
2855
2856 mock_service_control
2858 .expect_uninstall()
2859 .with(eq("safenode1"), eq(false))
2860 .times(1)
2861 .returning(|_, _| Ok(()));
2862 mock_service_control
2863 .expect_install()
2864 .with(
2865 eq(ServiceInstallCtx {
2866 args: vec![
2867 OsString::from("--rpc"),
2868 OsString::from("127.0.0.1:8081"),
2869 OsString::from("--root-dir"),
2870 OsString::from("/var/safenode-manager/services/safenode1"),
2871 OsString::from("--log-output-dest"),
2872 OsString::from("/var/log/safenode/safenode1"),
2873 OsString::from("--log-format"),
2874 OsString::from("json"),
2875 OsString::from("--rewards-address"),
2876 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
2877 OsString::from("evm-arbitrum-one"),
2878 ],
2879 autostart: false,
2880 contents: None,
2881 environment: None,
2882 label: "safenode1".parse()?,
2883 program: current_node_bin.to_path_buf(),
2884 username: Some("safe".to_string()),
2885 working_directory: None,
2886 }),
2887 eq(false),
2888 )
2889 .times(1)
2890 .returning(|_, _| Ok(()));
2891
2892 mock_service_control
2894 .expect_start()
2895 .with(eq("safenode1"), eq(false))
2896 .times(1)
2897 .returning(|_, _| Ok(()));
2898 mock_service_control
2899 .expect_wait()
2900 .with(eq(3000))
2901 .times(1)
2902 .returning(|_| ());
2903 mock_service_control
2904 .expect_get_process_pid()
2905 .with(eq(current_node_bin.to_path_buf().clone()))
2906 .times(1)
2907 .returning(|_| Ok(100));
2908
2909 mock_rpc_client.expect_node_info().times(1).returning(|| {
2910 Ok(NodeInfo {
2911 pid: 2000,
2912 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2913 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2914 log_path: PathBuf::from("/var/log/safenode/safenode1"),
2915 version: target_version.to_string(),
2916 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
2918 })
2919 });
2920 mock_rpc_client
2921 .expect_network_info()
2922 .times(1)
2923 .returning(|| {
2924 Ok(NetworkInfo {
2925 connected_peers: Vec::new(),
2926 listeners: Vec::new(),
2927 })
2928 });
2929
2930 let mut service_data = NodeServiceData {
2931 auto_restart: false,
2932 connected_peers: None,
2933 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
2934 evm_network: EvmNetwork::ArbitrumOne,
2935 genesis: false,
2936 home_network: false,
2937 listen_addr: None,
2938 local: false,
2939 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
2940 log_format: Some(LogFormat::Json),
2941 max_archived_log_files: None,
2942 max_log_files: None,
2943 metrics_port: None,
2944 node_ip: None,
2945 node_port: None,
2946 owner: None,
2947 number: 1,
2948 peer_id: Some(PeerId::from_str(
2949 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2950 )?),
2951 pid: Some(1000),
2952 rewards_address: RewardsAddress::from_str(
2953 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2954 )?,
2955 reward_balance: Some(AttoTokens::zero()),
2956 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2957 safenode_path: current_node_bin.to_path_buf(),
2958 service_name: "safenode1".to_string(),
2959 status: ServiceStatus::Running,
2960 upnp: false,
2961 user: Some("safe".to_string()),
2962 user_mode: false,
2963 version: current_version.to_string(),
2964 };
2965 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2966
2967 let mut service_manager = ServiceManager::new(
2968 service,
2969 Box::new(mock_service_control),
2970 VerbosityLevel::Normal,
2971 );
2972
2973 service_manager
2974 .upgrade(UpgradeOptions {
2975 auto_restart: false,
2976 bootstrap_peers: Vec::new(),
2977 env_variables: None,
2978 force: false,
2979 start_service: true,
2980 target_bin_path: target_node_bin.to_path_buf(),
2981 target_version: Version::parse(target_version).unwrap(),
2982 })
2983 .await?;
2984
2985 assert!(service_manager.service.service_data.log_format.is_some());
2986 assert_eq!(
2987 service_manager.service.service_data.log_format,
2988 Some(LogFormat::Json)
2989 );
2990
2991 Ok(())
2992 }
2993
2994 #[tokio::test]
2995 async fn upgrade_should_retain_the_home_network_flag() -> Result<()> {
2996 let current_version = "0.1.0";
2997 let target_version = "0.2.0";
2998
2999 let tmp_data_dir = assert_fs::TempDir::new()?;
3000 let current_install_dir = tmp_data_dir.child("safenode_install");
3001 current_install_dir.create_dir_all()?;
3002
3003 let current_node_bin = current_install_dir.child("safenode");
3004 current_node_bin.write_binary(b"fake safenode binary")?;
3005 let target_node_bin = tmp_data_dir.child("safenode");
3006 target_node_bin.write_binary(b"fake safenode binary")?;
3007
3008 let mut mock_service_control = MockServiceControl::new();
3009 let mut mock_rpc_client = MockRpcClient::new();
3010
3011 mock_service_control
3013 .expect_get_process_pid()
3014 .with(eq(current_node_bin.to_path_buf().clone()))
3015 .times(1)
3016 .returning(|_| Ok(1000));
3017 mock_service_control
3018 .expect_stop()
3019 .with(eq("safenode1"), eq(false))
3020 .times(1)
3021 .returning(|_, _| Ok(()));
3022
3023 mock_service_control
3025 .expect_uninstall()
3026 .with(eq("safenode1"), eq(false))
3027 .times(1)
3028 .returning(|_, _| Ok(()));
3029 mock_service_control
3030 .expect_install()
3031 .with(
3032 eq(ServiceInstallCtx {
3033 args: vec![
3034 OsString::from("--rpc"),
3035 OsString::from("127.0.0.1:8081"),
3036 OsString::from("--root-dir"),
3037 OsString::from("/var/safenode-manager/services/safenode1"),
3038 OsString::from("--log-output-dest"),
3039 OsString::from("/var/log/safenode/safenode1"),
3040 OsString::from("--home-network"),
3041 OsString::from("--rewards-address"),
3042 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3043 OsString::from("evm-arbitrum-one"),
3044 ],
3045 autostart: false,
3046 contents: None,
3047 environment: None,
3048 label: "safenode1".parse()?,
3049 program: current_node_bin.to_path_buf(),
3050 username: Some("safe".to_string()),
3051 working_directory: None,
3052 }),
3053 eq(false),
3054 )
3055 .times(1)
3056 .returning(|_, _| Ok(()));
3057
3058 mock_service_control
3060 .expect_start()
3061 .with(eq("safenode1"), eq(false))
3062 .times(1)
3063 .returning(|_, _| Ok(()));
3064 mock_service_control
3065 .expect_wait()
3066 .with(eq(3000))
3067 .times(1)
3068 .returning(|_| ());
3069 mock_service_control
3070 .expect_get_process_pid()
3071 .with(eq(current_node_bin.to_path_buf().clone()))
3072 .times(1)
3073 .returning(|_| Ok(100));
3074
3075 mock_rpc_client.expect_node_info().times(1).returning(|| {
3076 Ok(NodeInfo {
3077 pid: 2000,
3078 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3079 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3080 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3081 version: target_version.to_string(),
3082 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3084 })
3085 });
3086 mock_rpc_client
3087 .expect_network_info()
3088 .times(1)
3089 .returning(|| {
3090 Ok(NetworkInfo {
3091 connected_peers: Vec::new(),
3092 listeners: Vec::new(),
3093 })
3094 });
3095
3096 let mut service_data = NodeServiceData {
3097 auto_restart: false,
3098 connected_peers: None,
3099 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3100 evm_network: EvmNetwork::ArbitrumOne,
3101 genesis: false,
3102 home_network: true,
3103 listen_addr: None,
3104 local: false,
3105 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3106 log_format: None,
3107 max_archived_log_files: None,
3108 max_log_files: None,
3109 metrics_port: None,
3110 node_ip: None,
3111 node_port: None,
3112 number: 1,
3113 owner: None,
3114 peer_id: Some(PeerId::from_str(
3115 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3116 )?),
3117 pid: Some(1000),
3118 rewards_address: RewardsAddress::from_str(
3119 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3120 )?,
3121 reward_balance: Some(AttoTokens::zero()),
3122 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3123 safenode_path: current_node_bin.to_path_buf(),
3124 service_name: "safenode1".to_string(),
3125 status: ServiceStatus::Running,
3126 upnp: false,
3127 user: Some("safe".to_string()),
3128 user_mode: false,
3129 version: current_version.to_string(),
3130 };
3131 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3132
3133 let mut service_manager = ServiceManager::new(
3134 service,
3135 Box::new(mock_service_control),
3136 VerbosityLevel::Normal,
3137 );
3138
3139 service_manager
3140 .upgrade(UpgradeOptions {
3141 auto_restart: false,
3142 bootstrap_peers: Vec::new(),
3143 env_variables: None,
3144 force: false,
3145 start_service: true,
3146 target_bin_path: target_node_bin.to_path_buf(),
3147 target_version: Version::parse(target_version).unwrap(),
3148 })
3149 .await?;
3150
3151 assert!(service_manager.service.service_data.home_network);
3152
3153 Ok(())
3154 }
3155
3156 #[tokio::test]
3157 async fn upgrade_should_retain_custom_node_ip() -> Result<()> {
3158 let current_version = "0.1.0";
3159 let target_version = "0.2.0";
3160
3161 let tmp_data_dir = assert_fs::TempDir::new()?;
3162 let current_install_dir = tmp_data_dir.child("safenode_install");
3163 current_install_dir.create_dir_all()?;
3164
3165 let current_node_bin = current_install_dir.child("safenode");
3166 current_node_bin.write_binary(b"fake safenode binary")?;
3167 let target_node_bin = tmp_data_dir.child("safenode");
3168 target_node_bin.write_binary(b"fake safenode binary")?;
3169
3170 let mut mock_service_control = MockServiceControl::new();
3171 let mut mock_rpc_client = MockRpcClient::new();
3172
3173 mock_service_control
3175 .expect_get_process_pid()
3176 .with(eq(current_node_bin.to_path_buf().clone()))
3177 .times(1)
3178 .returning(|_| Ok(1000));
3179 mock_service_control
3180 .expect_stop()
3181 .with(eq("safenode1"), eq(false))
3182 .times(1)
3183 .returning(|_, _| Ok(()));
3184
3185 mock_service_control
3187 .expect_uninstall()
3188 .with(eq("safenode1"), eq(false))
3189 .times(1)
3190 .returning(|_, _| Ok(()));
3191 mock_service_control
3192 .expect_install()
3193 .with(
3194 eq(ServiceInstallCtx {
3195 args: vec![
3196 OsString::from("--rpc"),
3197 OsString::from("127.0.0.1:8081"),
3198 OsString::from("--root-dir"),
3199 OsString::from("/var/safenode-manager/services/safenode1"),
3200 OsString::from("--log-output-dest"),
3201 OsString::from("/var/log/safenode/safenode1"),
3202 OsString::from("--ip"),
3203 OsString::from("192.168.1.1"),
3204 OsString::from("--rewards-address"),
3205 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3206 OsString::from("evm-arbitrum-one"),
3207 ],
3208 autostart: false,
3209 contents: None,
3210 environment: None,
3211 label: "safenode1".parse()?,
3212 program: current_node_bin.to_path_buf(),
3213 username: Some("safe".to_string()),
3214 working_directory: None,
3215 }),
3216 eq(false),
3217 )
3218 .times(1)
3219 .returning(|_, _| Ok(()));
3220
3221 mock_service_control
3223 .expect_start()
3224 .with(eq("safenode1"), eq(false))
3225 .times(1)
3226 .returning(|_, _| Ok(()));
3227 mock_service_control
3228 .expect_wait()
3229 .with(eq(3000))
3230 .times(1)
3231 .returning(|_| ());
3232 mock_service_control
3233 .expect_get_process_pid()
3234 .with(eq(current_node_bin.to_path_buf().clone()))
3235 .times(1)
3236 .returning(|_| Ok(100));
3237
3238 mock_rpc_client.expect_node_info().times(1).returning(|| {
3239 Ok(NodeInfo {
3240 pid: 2000,
3241 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3242 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3243 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3244 version: target_version.to_string(),
3245 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3247 })
3248 });
3249 mock_rpc_client
3250 .expect_network_info()
3251 .times(1)
3252 .returning(|| {
3253 Ok(NetworkInfo {
3254 connected_peers: Vec::new(),
3255 listeners: Vec::new(),
3256 })
3257 });
3258
3259 let mut service_data = NodeServiceData {
3260 auto_restart: false,
3261 connected_peers: None,
3262 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3263 evm_network: EvmNetwork::ArbitrumOne,
3264 genesis: false,
3265 home_network: false,
3266 listen_addr: None,
3267 local: false,
3268 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3269 log_format: None,
3270 max_archived_log_files: None,
3271 max_log_files: None,
3272 metrics_port: None,
3273 number: 1,
3274 node_ip: Some(Ipv4Addr::new(192, 168, 1, 1)),
3275 node_port: None,
3276 owner: None,
3277 peer_id: Some(PeerId::from_str(
3278 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3279 )?),
3280 pid: Some(1000),
3281 rewards_address: RewardsAddress::from_str(
3282 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3283 )?,
3284 reward_balance: Some(AttoTokens::zero()),
3285 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3286 safenode_path: current_node_bin.to_path_buf(),
3287 service_name: "safenode1".to_string(),
3288 status: ServiceStatus::Running,
3289 upnp: false,
3290 user: Some("safe".to_string()),
3291 user_mode: false,
3292 version: current_version.to_string(),
3293 };
3294 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3295
3296 let mut service_manager = ServiceManager::new(
3297 service,
3298 Box::new(mock_service_control),
3299 VerbosityLevel::Normal,
3300 );
3301
3302 service_manager
3303 .upgrade(UpgradeOptions {
3304 auto_restart: false,
3305 bootstrap_peers: Vec::new(),
3306 env_variables: None,
3307 force: false,
3308 start_service: true,
3309 target_bin_path: target_node_bin.to_path_buf(),
3310 target_version: Version::parse(target_version).unwrap(),
3311 })
3312 .await?;
3313
3314 assert_eq!(
3315 service_manager.service.service_data.node_ip,
3316 Some(Ipv4Addr::new(192, 168, 1, 1))
3317 );
3318
3319 Ok(())
3320 }
3321
3322 #[tokio::test]
3323 async fn upgrade_should_retain_custom_node_ports() -> Result<()> {
3324 let current_version = "0.1.0";
3325 let target_version = "0.2.0";
3326
3327 let tmp_data_dir = assert_fs::TempDir::new()?;
3328 let current_install_dir = tmp_data_dir.child("safenode_install");
3329 current_install_dir.create_dir_all()?;
3330
3331 let current_node_bin = current_install_dir.child("safenode");
3332 current_node_bin.write_binary(b"fake safenode binary")?;
3333 let target_node_bin = tmp_data_dir.child("safenode");
3334 target_node_bin.write_binary(b"fake safenode binary")?;
3335
3336 let mut mock_service_control = MockServiceControl::new();
3337 let mut mock_rpc_client = MockRpcClient::new();
3338
3339 mock_service_control
3341 .expect_get_process_pid()
3342 .with(eq(current_node_bin.to_path_buf().clone()))
3343 .times(1)
3344 .returning(|_| Ok(1000));
3345 mock_service_control
3346 .expect_stop()
3347 .with(eq("safenode1"), eq(false))
3348 .times(1)
3349 .returning(|_, _| Ok(()));
3350
3351 mock_service_control
3353 .expect_uninstall()
3354 .with(eq("safenode1"), eq(false))
3355 .times(1)
3356 .returning(|_, _| Ok(()));
3357 mock_service_control
3358 .expect_install()
3359 .with(
3360 eq(ServiceInstallCtx {
3361 args: vec![
3362 OsString::from("--rpc"),
3363 OsString::from("127.0.0.1:8081"),
3364 OsString::from("--root-dir"),
3365 OsString::from("/var/safenode-manager/services/safenode1"),
3366 OsString::from("--log-output-dest"),
3367 OsString::from("/var/log/safenode/safenode1"),
3368 OsString::from("--port"),
3369 OsString::from("12000"),
3370 OsString::from("--rewards-address"),
3371 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3372 OsString::from("evm-arbitrum-one"),
3373 ],
3374 autostart: false,
3375 contents: None,
3376 environment: None,
3377 label: "safenode1".parse()?,
3378 program: current_node_bin.to_path_buf(),
3379 username: Some("safe".to_string()),
3380 working_directory: None,
3381 }),
3382 eq(false),
3383 )
3384 .times(1)
3385 .returning(|_, _| Ok(()));
3386
3387 mock_service_control
3389 .expect_start()
3390 .with(eq("safenode1"), eq(false))
3391 .times(1)
3392 .returning(|_, _| Ok(()));
3393 mock_service_control
3394 .expect_wait()
3395 .with(eq(3000))
3396 .times(1)
3397 .returning(|_| ());
3398 mock_service_control
3399 .expect_get_process_pid()
3400 .with(eq(current_node_bin.to_path_buf().clone()))
3401 .times(1)
3402 .returning(|_| Ok(100));
3403
3404 mock_rpc_client.expect_node_info().times(1).returning(|| {
3405 Ok(NodeInfo {
3406 pid: 2000,
3407 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3408 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3409 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3410 version: target_version.to_string(),
3411 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3413 })
3414 });
3415 mock_rpc_client
3416 .expect_network_info()
3417 .times(1)
3418 .returning(|| {
3419 Ok(NetworkInfo {
3420 connected_peers: Vec::new(),
3421 listeners: Vec::new(),
3422 })
3423 });
3424
3425 let mut service_data = NodeServiceData {
3426 auto_restart: false,
3427 connected_peers: None,
3428 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3429 evm_network: EvmNetwork::ArbitrumOne,
3430 genesis: false,
3431 home_network: false,
3432 listen_addr: None,
3433 local: false,
3434 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3435 log_format: None,
3436 max_archived_log_files: None,
3437 max_log_files: None,
3438 metrics_port: None,
3439 number: 1,
3440 node_ip: None,
3441 node_port: Some(12000),
3442 owner: None,
3443 peer_id: Some(PeerId::from_str(
3444 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3445 )?),
3446 pid: Some(1000),
3447 rewards_address: RewardsAddress::from_str(
3448 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3449 )?,
3450 reward_balance: Some(AttoTokens::zero()),
3451 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3452 safenode_path: current_node_bin.to_path_buf(),
3453 service_name: "safenode1".to_string(),
3454 status: ServiceStatus::Running,
3455 upnp: false,
3456 user: Some("safe".to_string()),
3457 user_mode: false,
3458 version: current_version.to_string(),
3459 };
3460 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3461
3462 let mut service_manager = ServiceManager::new(
3463 service,
3464 Box::new(mock_service_control),
3465 VerbosityLevel::Normal,
3466 );
3467
3468 service_manager
3469 .upgrade(UpgradeOptions {
3470 auto_restart: false,
3471 bootstrap_peers: Vec::new(),
3472 env_variables: None,
3473 force: false,
3474 start_service: true,
3475 target_bin_path: target_node_bin.to_path_buf(),
3476 target_version: Version::parse(target_version).unwrap(),
3477 })
3478 .await?;
3479
3480 assert_eq!(service_manager.service.service_data.node_port, Some(12000));
3481
3482 Ok(())
3483 }
3484
3485 #[tokio::test]
3486 async fn upgrade_should_retain_max_archived_log_files() -> Result<()> {
3487 let current_version = "0.1.0";
3488 let target_version = "0.2.0";
3489
3490 let tmp_data_dir = assert_fs::TempDir::new()?;
3491 let current_install_dir = tmp_data_dir.child("safenode_install");
3492 current_install_dir.create_dir_all()?;
3493
3494 let current_node_bin = current_install_dir.child("safenode");
3495 current_node_bin.write_binary(b"fake safenode binary")?;
3496 let target_node_bin = tmp_data_dir.child("safenode");
3497 target_node_bin.write_binary(b"fake safenode binary")?;
3498
3499 let mut mock_service_control = MockServiceControl::new();
3500 let mut mock_rpc_client = MockRpcClient::new();
3501
3502 mock_service_control
3504 .expect_get_process_pid()
3505 .with(eq(current_node_bin.to_path_buf().clone()))
3506 .times(1)
3507 .returning(|_| Ok(1000));
3508 mock_service_control
3509 .expect_stop()
3510 .with(eq("safenode1"), eq(false))
3511 .times(1)
3512 .returning(|_, _| Ok(()));
3513
3514 mock_service_control
3516 .expect_uninstall()
3517 .with(eq("safenode1"), eq(false))
3518 .times(1)
3519 .returning(|_, _| Ok(()));
3520 mock_service_control
3521 .expect_install()
3522 .with(
3523 eq(ServiceInstallCtx {
3524 args: vec![
3525 OsString::from("--rpc"),
3526 OsString::from("127.0.0.1:8081"),
3527 OsString::from("--root-dir"),
3528 OsString::from("/var/safenode-manager/services/safenode1"),
3529 OsString::from("--log-output-dest"),
3530 OsString::from("/var/log/safenode/safenode1"),
3531 OsString::from("--max-archived-log-files"),
3532 OsString::from("20"),
3533 OsString::from("--rewards-address"),
3534 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3535 OsString::from("evm-arbitrum-one"),
3536 ],
3537 autostart: false,
3538 contents: None,
3539 environment: None,
3540 label: "safenode1".parse()?,
3541 program: current_node_bin.to_path_buf(),
3542 username: Some("safe".to_string()),
3543 working_directory: None,
3544 }),
3545 eq(false),
3546 )
3547 .times(1)
3548 .returning(|_, _| Ok(()));
3549
3550 mock_service_control
3552 .expect_start()
3553 .with(eq("safenode1"), eq(false))
3554 .times(1)
3555 .returning(|_, _| Ok(()));
3556 mock_service_control
3557 .expect_wait()
3558 .with(eq(3000))
3559 .times(1)
3560 .returning(|_| ());
3561 mock_service_control
3562 .expect_get_process_pid()
3563 .with(eq(current_node_bin.to_path_buf().clone()))
3564 .times(1)
3565 .returning(|_| Ok(100));
3566
3567 mock_rpc_client.expect_node_info().times(1).returning(|| {
3568 Ok(NodeInfo {
3569 pid: 2000,
3570 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3571 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3572 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3573 version: target_version.to_string(),
3574 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3576 })
3577 });
3578 mock_rpc_client
3579 .expect_network_info()
3580 .times(1)
3581 .returning(|| {
3582 Ok(NetworkInfo {
3583 connected_peers: Vec::new(),
3584 listeners: Vec::new(),
3585 })
3586 });
3587
3588 let mut service_data = NodeServiceData {
3589 auto_restart: false,
3590 connected_peers: None,
3591 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3592 genesis: false,
3593 home_network: false,
3594 listen_addr: None,
3595 local: false,
3596 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3597 log_format: None,
3598 max_archived_log_files: Some(20),
3599 max_log_files: None,
3600 metrics_port: None,
3601 node_ip: None,
3602 node_port: None,
3603 number: 1,
3604 owner: None,
3605 peer_id: Some(PeerId::from_str(
3606 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3607 )?),
3608 pid: Some(1000),
3609 reward_balance: Some(AttoTokens::zero()),
3610 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3611 safenode_path: current_node_bin.to_path_buf(),
3612 service_name: "safenode1".to_string(),
3613 status: ServiceStatus::Running,
3614 upnp: false,
3615 user: Some("safe".to_string()),
3616 user_mode: false,
3617 version: current_version.to_string(),
3618 evm_network: EvmNetwork::ArbitrumOne,
3619 rewards_address: RewardsAddress::from_str(
3620 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3621 )?,
3622 };
3623 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3624
3625 let mut service_manager = ServiceManager::new(
3626 service,
3627 Box::new(mock_service_control),
3628 VerbosityLevel::Normal,
3629 );
3630
3631 service_manager
3632 .upgrade(UpgradeOptions {
3633 auto_restart: false,
3634 bootstrap_peers: Vec::new(),
3635 env_variables: None,
3636 force: false,
3637 start_service: true,
3638 target_bin_path: target_node_bin.to_path_buf(),
3639 target_version: Version::parse(target_version).unwrap(),
3640 })
3641 .await?;
3642
3643 assert_matches!(
3644 service_manager.service.service_data.max_archived_log_files,
3645 Some(20)
3646 );
3647
3648 Ok(())
3649 }
3650
3651 #[tokio::test]
3652 async fn upgrade_should_retain_max_log_files() -> Result<()> {
3653 let current_version = "0.1.0";
3654 let target_version = "0.2.0";
3655
3656 let tmp_data_dir = assert_fs::TempDir::new()?;
3657 let current_install_dir = tmp_data_dir.child("safenode_install");
3658 current_install_dir.create_dir_all()?;
3659
3660 let current_node_bin = current_install_dir.child("safenode");
3661 current_node_bin.write_binary(b"fake safenode binary")?;
3662 let target_node_bin = tmp_data_dir.child("safenode");
3663 target_node_bin.write_binary(b"fake safenode binary")?;
3664
3665 let mut mock_service_control = MockServiceControl::new();
3666 let mut mock_rpc_client = MockRpcClient::new();
3667
3668 mock_service_control
3670 .expect_get_process_pid()
3671 .with(eq(current_node_bin.to_path_buf().clone()))
3672 .times(1)
3673 .returning(|_| Ok(1000));
3674 mock_service_control
3675 .expect_stop()
3676 .with(eq("safenode1"), eq(false))
3677 .times(1)
3678 .returning(|_, _| Ok(()));
3679
3680 mock_service_control
3682 .expect_uninstall()
3683 .with(eq("safenode1"), eq(false))
3684 .times(1)
3685 .returning(|_, _| Ok(()));
3686 mock_service_control
3687 .expect_install()
3688 .with(
3689 eq(ServiceInstallCtx {
3690 args: vec![
3691 OsString::from("--rpc"),
3692 OsString::from("127.0.0.1:8081"),
3693 OsString::from("--root-dir"),
3694 OsString::from("/var/safenode-manager/services/safenode1"),
3695 OsString::from("--log-output-dest"),
3696 OsString::from("/var/log/safenode/safenode1"),
3697 OsString::from("--max-log-files"),
3698 OsString::from("20"),
3699 OsString::from("--rewards-address"),
3700 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3701 OsString::from("evm-arbitrum-one"),
3702 ],
3703 autostart: false,
3704 contents: None,
3705 environment: None,
3706 label: "safenode1".parse()?,
3707 program: current_node_bin.to_path_buf(),
3708 username: Some("safe".to_string()),
3709 working_directory: None,
3710 }),
3711 eq(false),
3712 )
3713 .times(1)
3714 .returning(|_, _| Ok(()));
3715
3716 mock_service_control
3718 .expect_start()
3719 .with(eq("safenode1"), eq(false))
3720 .times(1)
3721 .returning(|_, _| Ok(()));
3722 mock_service_control
3723 .expect_wait()
3724 .with(eq(3000))
3725 .times(1)
3726 .returning(|_| ());
3727 mock_service_control
3728 .expect_get_process_pid()
3729 .with(eq(current_node_bin.to_path_buf().clone()))
3730 .times(1)
3731 .returning(|_| Ok(100));
3732
3733 mock_rpc_client.expect_node_info().times(1).returning(|| {
3734 Ok(NodeInfo {
3735 pid: 2000,
3736 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3737 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3738 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3739 version: target_version.to_string(),
3740 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3742 })
3743 });
3744 mock_rpc_client
3745 .expect_network_info()
3746 .times(1)
3747 .returning(|| {
3748 Ok(NetworkInfo {
3749 connected_peers: Vec::new(),
3750 listeners: Vec::new(),
3751 })
3752 });
3753
3754 let mut service_data = NodeServiceData {
3755 auto_restart: false,
3756 connected_peers: None,
3757 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3758 genesis: false,
3759 home_network: false,
3760 listen_addr: None,
3761 local: false,
3762 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3763 log_format: None,
3764 max_archived_log_files: None,
3765 max_log_files: Some(20),
3766 metrics_port: None,
3767 node_ip: None,
3768 node_port: None,
3769 number: 1,
3770 owner: None,
3771 peer_id: Some(PeerId::from_str(
3772 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3773 )?),
3774 pid: Some(1000),
3775 reward_balance: Some(AttoTokens::zero()),
3776 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3777 safenode_path: current_node_bin.to_path_buf(),
3778 service_name: "safenode1".to_string(),
3779 status: ServiceStatus::Running,
3780 upnp: false,
3781 user: Some("safe".to_string()),
3782 user_mode: false,
3783 version: current_version.to_string(),
3784 evm_network: EvmNetwork::ArbitrumOne,
3785 rewards_address: RewardsAddress::from_str(
3786 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3787 )?,
3788 };
3789 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3790
3791 let mut service_manager = ServiceManager::new(
3792 service,
3793 Box::new(mock_service_control),
3794 VerbosityLevel::Normal,
3795 );
3796
3797 service_manager
3798 .upgrade(UpgradeOptions {
3799 auto_restart: false,
3800 bootstrap_peers: Vec::new(),
3801 env_variables: None,
3802 force: false,
3803 start_service: true,
3804 target_bin_path: target_node_bin.to_path_buf(),
3805 target_version: Version::parse(target_version).unwrap(),
3806 })
3807 .await?;
3808
3809 assert_matches!(service_manager.service.service_data.max_log_files, Some(20));
3810
3811 Ok(())
3812 }
3813
3814 #[tokio::test]
3815 async fn upgrade_should_retain_custom_metrics_ports() -> Result<()> {
3816 let current_version = "0.1.0";
3817 let target_version = "0.2.0";
3818
3819 let tmp_data_dir = assert_fs::TempDir::new()?;
3820 let current_install_dir = tmp_data_dir.child("safenode_install");
3821 current_install_dir.create_dir_all()?;
3822
3823 let current_node_bin = current_install_dir.child("safenode");
3824 current_node_bin.write_binary(b"fake safenode binary")?;
3825 let target_node_bin = tmp_data_dir.child("safenode");
3826 target_node_bin.write_binary(b"fake safenode binary")?;
3827
3828 let mut mock_service_control = MockServiceControl::new();
3829 let mut mock_rpc_client = MockRpcClient::new();
3830
3831 mock_service_control
3833 .expect_get_process_pid()
3834 .with(eq(current_node_bin.to_path_buf().clone()))
3835 .times(1)
3836 .returning(|_| Ok(1000));
3837 mock_service_control
3838 .expect_stop()
3839 .with(eq("safenode1"), eq(false))
3840 .times(1)
3841 .returning(|_, _| Ok(()));
3842
3843 mock_service_control
3845 .expect_uninstall()
3846 .with(eq("safenode1"), eq(false))
3847 .times(1)
3848 .returning(|_, _| Ok(()));
3849 mock_service_control
3850 .expect_install()
3851 .with(
3852 eq(ServiceInstallCtx {
3853 args: vec![
3854 OsString::from("--rpc"),
3855 OsString::from("127.0.0.1:8081"),
3856 OsString::from("--root-dir"),
3857 OsString::from("/var/safenode-manager/services/safenode1"),
3858 OsString::from("--log-output-dest"),
3859 OsString::from("/var/log/safenode/safenode1"),
3860 OsString::from("--metrics-server-port"),
3861 OsString::from("12000"),
3862 OsString::from("--rewards-address"),
3863 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3864 OsString::from("evm-arbitrum-one"),
3865 ],
3866 autostart: false,
3867 contents: None,
3868 environment: None,
3869 label: "safenode1".parse()?,
3870 program: current_node_bin.to_path_buf(),
3871 username: Some("safe".to_string()),
3872 working_directory: None,
3873 }),
3874 eq(false),
3875 )
3876 .times(1)
3877 .returning(|_, _| Ok(()));
3878
3879 mock_service_control
3881 .expect_start()
3882 .with(eq("safenode1"), eq(false))
3883 .times(1)
3884 .returning(|_, _| Ok(()));
3885 mock_service_control
3886 .expect_wait()
3887 .with(eq(3000))
3888 .times(1)
3889 .returning(|_| ());
3890 mock_service_control
3891 .expect_get_process_pid()
3892 .with(eq(current_node_bin.to_path_buf().clone()))
3893 .times(1)
3894 .returning(|_| Ok(100));
3895
3896 mock_rpc_client.expect_node_info().times(1).returning(|| {
3897 Ok(NodeInfo {
3898 pid: 2000,
3899 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3900 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3901 log_path: PathBuf::from("/var/log/safenode/safenode1"),
3902 version: target_version.to_string(),
3903 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
3905 })
3906 });
3907 mock_rpc_client
3908 .expect_network_info()
3909 .times(1)
3910 .returning(|| {
3911 Ok(NetworkInfo {
3912 connected_peers: Vec::new(),
3913 listeners: Vec::new(),
3914 })
3915 });
3916
3917 let mut service_data = NodeServiceData {
3918 auto_restart: false,
3919 connected_peers: None,
3920 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
3921 evm_network: EvmNetwork::ArbitrumOne,
3922 genesis: false,
3923 home_network: false,
3924 listen_addr: None,
3925 local: false,
3926 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
3927 log_format: None,
3928 max_archived_log_files: None,
3929 max_log_files: None,
3930 metrics_port: Some(12000),
3931 node_ip: None,
3932 node_port: None,
3933 number: 1,
3934 owner: None,
3935 peer_id: Some(PeerId::from_str(
3936 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3937 )?),
3938 pid: Some(1000),
3939 rewards_address: RewardsAddress::from_str(
3940 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3941 )?,
3942 reward_balance: Some(AttoTokens::zero()),
3943 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3944 safenode_path: current_node_bin.to_path_buf(),
3945 service_name: "safenode1".to_string(),
3946 status: ServiceStatus::Running,
3947 upnp: false,
3948 user: Some("safe".to_string()),
3949 user_mode: false,
3950 version: current_version.to_string(),
3951 };
3952 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3953
3954 let mut service_manager = ServiceManager::new(
3955 service,
3956 Box::new(mock_service_control),
3957 VerbosityLevel::Normal,
3958 );
3959
3960 service_manager
3961 .upgrade(UpgradeOptions {
3962 auto_restart: false,
3963 bootstrap_peers: Vec::new(),
3964 env_variables: None,
3965 force: false,
3966 start_service: true,
3967 target_bin_path: target_node_bin.to_path_buf(),
3968 target_version: Version::parse(target_version).unwrap(),
3969 })
3970 .await?;
3971
3972 assert_eq!(
3973 service_manager.service.service_data.metrics_port,
3974 Some(12000)
3975 );
3976
3977 Ok(())
3978 }
3979
3980 #[tokio::test]
3981 async fn upgrade_should_retain_custom_rpc_ports() -> Result<()> {
3982 let current_version = "0.1.0";
3983 let target_version = "0.2.0";
3984
3985 let tmp_data_dir = assert_fs::TempDir::new()?;
3986 let current_install_dir = tmp_data_dir.child("safenode_install");
3987 current_install_dir.create_dir_all()?;
3988
3989 let current_node_bin = current_install_dir.child("safenode");
3990 current_node_bin.write_binary(b"fake safenode binary")?;
3991 let target_node_bin = tmp_data_dir.child("safenode");
3992 target_node_bin.write_binary(b"fake safenode binary")?;
3993
3994 let mut mock_service_control = MockServiceControl::new();
3995 let mut mock_rpc_client = MockRpcClient::new();
3996
3997 mock_service_control
3999 .expect_get_process_pid()
4000 .with(eq(current_node_bin.to_path_buf().clone()))
4001 .times(1)
4002 .returning(|_| Ok(1000));
4003 mock_service_control
4004 .expect_stop()
4005 .with(eq("safenode1"), eq(false))
4006 .times(1)
4007 .returning(|_, _| Ok(()));
4008
4009 mock_service_control
4011 .expect_uninstall()
4012 .with(eq("safenode1"), eq(false))
4013 .times(1)
4014 .returning(|_, _| Ok(()));
4015 mock_service_control
4016 .expect_install()
4017 .with(
4018 eq(ServiceInstallCtx {
4019 args: vec![
4020 OsString::from("--rpc"),
4021 OsString::from("127.0.0.1:8081"),
4022 OsString::from("--root-dir"),
4023 OsString::from("/var/safenode-manager/services/safenode1"),
4024 OsString::from("--log-output-dest"),
4025 OsString::from("/var/log/safenode/safenode1"),
4026 OsString::from("--metrics-server-port"),
4027 OsString::from("12000"),
4028 OsString::from("--rewards-address"),
4029 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4030 OsString::from("evm-arbitrum-one"),
4031 ],
4032 autostart: false,
4033 contents: None,
4034 environment: None,
4035 label: "safenode1".parse()?,
4036 program: current_node_bin.to_path_buf(),
4037 username: Some("safe".to_string()),
4038 working_directory: None,
4039 }),
4040 eq(false),
4041 )
4042 .times(1)
4043 .returning(|_, _| Ok(()));
4044
4045 mock_service_control
4047 .expect_start()
4048 .with(eq("safenode1"), eq(false))
4049 .times(1)
4050 .returning(|_, _| Ok(()));
4051 mock_service_control
4052 .expect_wait()
4053 .with(eq(3000))
4054 .times(1)
4055 .returning(|_| ());
4056 mock_service_control
4057 .expect_get_process_pid()
4058 .with(eq(current_node_bin.to_path_buf().clone()))
4059 .times(1)
4060 .returning(|_| Ok(100));
4061
4062 mock_rpc_client.expect_node_info().times(1).returning(|| {
4063 Ok(NodeInfo {
4064 pid: 2000,
4065 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4066 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4067 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4068 version: target_version.to_string(),
4069 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4071 })
4072 });
4073 mock_rpc_client
4074 .expect_network_info()
4075 .times(1)
4076 .returning(|| {
4077 Ok(NetworkInfo {
4078 connected_peers: Vec::new(),
4079 listeners: Vec::new(),
4080 })
4081 });
4082
4083 let mut service_data = NodeServiceData {
4084 auto_restart: false,
4085 connected_peers: None,
4086 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4087 evm_network: EvmNetwork::ArbitrumOne,
4088 genesis: false,
4089 home_network: false,
4090 listen_addr: None,
4091 local: false,
4092 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4093 log_format: None,
4094 max_archived_log_files: None,
4095 max_log_files: None,
4096 metrics_port: Some(12000),
4097 node_ip: None,
4098 node_port: None,
4099 number: 1,
4100 owner: None,
4101 peer_id: Some(PeerId::from_str(
4102 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4103 )?),
4104 pid: Some(1000),
4105 rewards_address: RewardsAddress::from_str(
4106 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4107 )?,
4108 reward_balance: Some(AttoTokens::zero()),
4109 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4110 safenode_path: current_node_bin.to_path_buf(),
4111 service_name: "safenode1".to_string(),
4112 status: ServiceStatus::Running,
4113 upnp: false,
4114 user: Some("safe".to_string()),
4115 user_mode: false,
4116 version: current_version.to_string(),
4117 };
4118 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4119
4120 let mut service_manager = ServiceManager::new(
4121 service,
4122 Box::new(mock_service_control),
4123 VerbosityLevel::Normal,
4124 );
4125
4126 service_manager
4127 .upgrade(UpgradeOptions {
4128 auto_restart: false,
4129 bootstrap_peers: Vec::new(),
4130 env_variables: None,
4131 force: false,
4132 start_service: true,
4133 target_bin_path: target_node_bin.to_path_buf(),
4134 target_version: Version::parse(target_version).unwrap(),
4135 })
4136 .await?;
4137
4138 assert_eq!(
4139 service_manager.service.service_data.rpc_socket_addr,
4140 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081)
4141 );
4142
4143 Ok(())
4144 }
4145
4146 #[tokio::test]
4147 async fn upgrade_should_retain_owner() -> Result<()> {
4148 let current_version = "0.1.0";
4149 let target_version = "0.2.0";
4150
4151 let tmp_data_dir = assert_fs::TempDir::new()?;
4152 let current_install_dir = tmp_data_dir.child("safenode_install");
4153 current_install_dir.create_dir_all()?;
4154
4155 let current_node_bin = current_install_dir.child("safenode");
4156 current_node_bin.write_binary(b"fake safenode binary")?;
4157 let target_node_bin = tmp_data_dir.child("safenode");
4158 target_node_bin.write_binary(b"fake safenode binary")?;
4159
4160 let mut mock_service_control = MockServiceControl::new();
4161 let mut mock_rpc_client = MockRpcClient::new();
4162
4163 mock_service_control
4165 .expect_get_process_pid()
4166 .with(eq(current_node_bin.to_path_buf().clone()))
4167 .times(1)
4168 .returning(|_| Ok(1000));
4169 mock_service_control
4170 .expect_stop()
4171 .with(eq("safenode1"), eq(false))
4172 .times(1)
4173 .returning(|_, _| Ok(()));
4174
4175 mock_service_control
4177 .expect_uninstall()
4178 .with(eq("safenode1"), eq(false))
4179 .times(1)
4180 .returning(|_, _| Ok(()));
4181 mock_service_control
4182 .expect_install()
4183 .with(
4184 eq(ServiceInstallCtx {
4185 args: vec![
4186 OsString::from("--rpc"),
4187 OsString::from("127.0.0.1:8081"),
4188 OsString::from("--root-dir"),
4189 OsString::from("/var/safenode-manager/services/safenode1"),
4190 OsString::from("--log-output-dest"),
4191 OsString::from("/var/log/safenode/safenode1"),
4192 OsString::from("--owner"),
4193 OsString::from("discord_username"),
4194 OsString::from("--rewards-address"),
4195 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4196 OsString::from("evm-arbitrum-one"),
4197 ],
4198 autostart: false,
4199 contents: None,
4200 environment: None,
4201 label: "safenode1".parse()?,
4202 program: current_node_bin.to_path_buf(),
4203 username: Some("safe".to_string()),
4204 working_directory: None,
4205 }),
4206 eq(false),
4207 )
4208 .times(1)
4209 .returning(|_, _| Ok(()));
4210
4211 mock_service_control
4213 .expect_start()
4214 .with(eq("safenode1"), eq(false))
4215 .times(1)
4216 .returning(|_, _| Ok(()));
4217 mock_service_control
4218 .expect_wait()
4219 .with(eq(3000))
4220 .times(1)
4221 .returning(|_| ());
4222 mock_service_control
4223 .expect_get_process_pid()
4224 .with(eq(current_node_bin.to_path_buf().clone()))
4225 .times(1)
4226 .returning(|_| Ok(100));
4227
4228 mock_rpc_client.expect_node_info().times(1).returning(|| {
4229 Ok(NodeInfo {
4230 pid: 2000,
4231 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4232 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4233 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4234 version: target_version.to_string(),
4235 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4237 })
4238 });
4239 mock_rpc_client
4240 .expect_network_info()
4241 .times(1)
4242 .returning(|| {
4243 Ok(NetworkInfo {
4244 connected_peers: Vec::new(),
4245 listeners: Vec::new(),
4246 })
4247 });
4248
4249 let mut service_data = NodeServiceData {
4250 auto_restart: false,
4251 connected_peers: None,
4252 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4253 evm_network: EvmNetwork::ArbitrumOne,
4254 genesis: false,
4255 home_network: false,
4256 listen_addr: None,
4257 local: false,
4258 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4259 log_format: None,
4260 max_archived_log_files: None,
4261 max_log_files: None,
4262 metrics_port: None,
4263 node_ip: None,
4264 node_port: None,
4265 number: 1,
4266 owner: Some("discord_username".to_string()),
4267 peer_id: Some(PeerId::from_str(
4268 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4269 )?),
4270 pid: Some(1000),
4271 rewards_address: RewardsAddress::from_str(
4272 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4273 )?,
4274 reward_balance: Some(AttoTokens::zero()),
4275 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4276 safenode_path: current_node_bin.to_path_buf(),
4277 service_name: "safenode1".to_string(),
4278 status: ServiceStatus::Running,
4279 upnp: false,
4280 user: Some("safe".to_string()),
4281 user_mode: false,
4282 version: current_version.to_string(),
4283 };
4284 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4285
4286 let mut service_manager = ServiceManager::new(
4287 service,
4288 Box::new(mock_service_control),
4289 VerbosityLevel::Normal,
4290 );
4291
4292 service_manager
4293 .upgrade(UpgradeOptions {
4294 auto_restart: false,
4295 bootstrap_peers: Vec::new(),
4296 env_variables: None,
4297 force: false,
4298 start_service: true,
4299 target_bin_path: target_node_bin.to_path_buf(),
4300 target_version: Version::parse(target_version).unwrap(),
4301 })
4302 .await?;
4303
4304 assert_eq!(
4305 service_manager.service.service_data.owner,
4306 Some("discord_username".to_string())
4307 );
4308
4309 Ok(())
4310 }
4311
4312 #[tokio::test]
4313 async fn upgrade_should_retain_auto_restart() -> Result<()> {
4314 let current_version = "0.1.0";
4315 let target_version = "0.2.0";
4316
4317 let tmp_data_dir = assert_fs::TempDir::new()?;
4318 let current_install_dir = tmp_data_dir.child("safenode_install");
4319 current_install_dir.create_dir_all()?;
4320
4321 let current_node_bin = current_install_dir.child("safenode");
4322 current_node_bin.write_binary(b"fake safenode binary")?;
4323 let target_node_bin = tmp_data_dir.child("safenode");
4324 target_node_bin.write_binary(b"fake safenode binary")?;
4325
4326 let mut mock_service_control = MockServiceControl::new();
4327 let mut mock_rpc_client = MockRpcClient::new();
4328
4329 mock_service_control
4331 .expect_get_process_pid()
4332 .with(eq(current_node_bin.to_path_buf().clone()))
4333 .times(1)
4334 .returning(|_| Ok(1000));
4335 mock_service_control
4336 .expect_stop()
4337 .with(eq("safenode1"), eq(false))
4338 .times(1)
4339 .returning(|_, _| Ok(()));
4340
4341 mock_service_control
4343 .expect_uninstall()
4344 .with(eq("safenode1"), eq(false))
4345 .times(1)
4346 .returning(|_, _| Ok(()));
4347 mock_service_control
4348 .expect_install()
4349 .with(
4350 eq(ServiceInstallCtx {
4351 args: vec![
4352 OsString::from("--rpc"),
4353 OsString::from("127.0.0.1:8081"),
4354 OsString::from("--root-dir"),
4355 OsString::from("/var/safenode-manager/services/safenode1"),
4356 OsString::from("--log-output-dest"),
4357 OsString::from("/var/log/safenode/safenode1"),
4358 OsString::from("--owner"),
4359 OsString::from("discord_username"),
4360 OsString::from("--rewards-address"),
4361 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4362 OsString::from("evm-arbitrum-one"),
4363 ],
4364 autostart: true,
4365 contents: None,
4366 environment: None,
4367 label: "safenode1".parse()?,
4368 program: current_node_bin.to_path_buf(),
4369 username: Some("safe".to_string()),
4370 working_directory: None,
4371 }),
4372 eq(false),
4373 )
4374 .times(1)
4375 .returning(|_, _| Ok(()));
4376
4377 mock_service_control
4379 .expect_start()
4380 .with(eq("safenode1"), eq(false))
4381 .times(1)
4382 .returning(|_, _| Ok(()));
4383 mock_service_control
4384 .expect_wait()
4385 .with(eq(3000))
4386 .times(1)
4387 .returning(|_| ());
4388 mock_service_control
4389 .expect_get_process_pid()
4390 .with(eq(current_node_bin.to_path_buf().clone()))
4391 .times(1)
4392 .returning(|_| Ok(100));
4393
4394 mock_rpc_client.expect_node_info().times(1).returning(|| {
4395 Ok(NodeInfo {
4396 pid: 2000,
4397 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4398 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4399 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4400 version: target_version.to_string(),
4401 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4403 })
4404 });
4405 mock_rpc_client
4406 .expect_network_info()
4407 .times(1)
4408 .returning(|| {
4409 Ok(NetworkInfo {
4410 connected_peers: Vec::new(),
4411 listeners: Vec::new(),
4412 })
4413 });
4414
4415 let mut service_data = NodeServiceData {
4416 auto_restart: true,
4417 connected_peers: None,
4418 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4419 evm_network: EvmNetwork::ArbitrumOne,
4420 genesis: false,
4421 home_network: false,
4422 listen_addr: None,
4423 local: false,
4424 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4425 log_format: None,
4426 max_archived_log_files: None,
4427 max_log_files: None,
4428 metrics_port: None,
4429 node_ip: None,
4430 node_port: None,
4431 number: 1,
4432 owner: Some("discord_username".to_string()),
4433 peer_id: Some(PeerId::from_str(
4434 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4435 )?),
4436 pid: Some(1000),
4437 rewards_address: RewardsAddress::from_str(
4438 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4439 )?,
4440 reward_balance: Some(AttoTokens::zero()),
4441 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4442 safenode_path: current_node_bin.to_path_buf(),
4443 service_name: "safenode1".to_string(),
4444 status: ServiceStatus::Running,
4445 upnp: false,
4446 user: Some("safe".to_string()),
4447 user_mode: false,
4448 version: current_version.to_string(),
4449 };
4450 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4451
4452 let mut service_manager = ServiceManager::new(
4453 service,
4454 Box::new(mock_service_control),
4455 VerbosityLevel::Normal,
4456 );
4457
4458 service_manager
4459 .upgrade(UpgradeOptions {
4460 auto_restart: true,
4461 bootstrap_peers: Vec::new(),
4462 env_variables: None,
4463 force: false,
4464 start_service: true,
4465 target_bin_path: target_node_bin.to_path_buf(),
4466 target_version: Version::parse(target_version).unwrap(),
4467 })
4468 .await?;
4469
4470 assert!(service_manager.service.service_data.auto_restart,);
4471
4472 Ok(())
4473 }
4474
4475 #[tokio::test]
4476 async fn upgrade_should_retain_evm_network_settings() -> Result<()> {
4477 let current_version = "0.1.0";
4478 let target_version = "0.2.0";
4479
4480 let tmp_data_dir = assert_fs::TempDir::new()?;
4481 let current_install_dir = tmp_data_dir.child("safenode_install");
4482 current_install_dir.create_dir_all()?;
4483
4484 let current_node_bin = current_install_dir.child("safenode");
4485 current_node_bin.write_binary(b"fake safenode binary")?;
4486 let target_node_bin = tmp_data_dir.child("safenode");
4487 target_node_bin.write_binary(b"fake safenode binary")?;
4488
4489 let mut mock_service_control = MockServiceControl::new();
4490 let mut mock_rpc_client = MockRpcClient::new();
4491
4492 mock_service_control
4494 .expect_get_process_pid()
4495 .with(eq(current_node_bin.to_path_buf().clone()))
4496 .times(1)
4497 .returning(|_| Ok(1000));
4498 mock_service_control
4499 .expect_stop()
4500 .with(eq("safenode1"), eq(false))
4501 .times(1)
4502 .returning(|_, _| Ok(()));
4503
4504 mock_service_control
4506 .expect_uninstall()
4507 .with(eq("safenode1"), eq(false))
4508 .times(1)
4509 .returning(|_, _| Ok(()));
4510 mock_service_control
4511 .expect_install()
4512 .with(
4513 eq(ServiceInstallCtx {
4514 args: vec![
4515 OsString::from("--rpc"),
4516 OsString::from("127.0.0.1:8081"),
4517 OsString::from("--root-dir"),
4518 OsString::from("/var/safenode-manager/services/safenode1"),
4519 OsString::from("--log-output-dest"),
4520 OsString::from("/var/log/safenode/safenode1"),
4521 OsString::from("--owner"),
4522 OsString::from("discord_username"),
4523 OsString::from("--rewards-address"),
4524 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4525 OsString::from("evm-custom"),
4526 OsString::from("--rpc-url"),
4527 OsString::from("http://localhost:8545/"),
4528 OsString::from("--payment-token-address"),
4529 OsString::from("0x5FbDB2315678afecb367f032d93F642f64180aa3"),
4530 OsString::from("--data-payments-address"),
4531 OsString::from("0x8464135c8F25Da09e49BC8782676a84730C318bC"),
4532 ],
4533 autostart: true,
4534 contents: None,
4535 environment: None,
4536 label: "safenode1".parse()?,
4537 program: current_node_bin.to_path_buf(),
4538 username: Some("safe".to_string()),
4539 working_directory: None,
4540 }),
4541 eq(false),
4542 )
4543 .times(1)
4544 .returning(|_, _| Ok(()));
4545
4546 mock_service_control
4548 .expect_start()
4549 .with(eq("safenode1"), eq(false))
4550 .times(1)
4551 .returning(|_, _| Ok(()));
4552 mock_service_control
4553 .expect_wait()
4554 .with(eq(3000))
4555 .times(1)
4556 .returning(|_| ());
4557 mock_service_control
4558 .expect_get_process_pid()
4559 .with(eq(current_node_bin.to_path_buf().clone()))
4560 .times(1)
4561 .returning(|_| Ok(100));
4562
4563 mock_rpc_client.expect_node_info().times(1).returning(|| {
4564 Ok(NodeInfo {
4565 pid: 2000,
4566 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4567 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4568 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4569 version: target_version.to_string(),
4570 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4572 })
4573 });
4574 mock_rpc_client
4575 .expect_network_info()
4576 .times(1)
4577 .returning(|| {
4578 Ok(NetworkInfo {
4579 connected_peers: Vec::new(),
4580 listeners: Vec::new(),
4581 })
4582 });
4583
4584 let mut service_data = NodeServiceData {
4585 auto_restart: true,
4586 connected_peers: None,
4587 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4588 evm_network: EvmNetwork::Custom(CustomNetwork {
4589 rpc_url_http: "http://localhost:8545".parse()?,
4590 payment_token_address: RewardsAddress::from_str(
4591 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
4592 )?,
4593 data_payments_address: RewardsAddress::from_str(
4594 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
4595 )?,
4596 }),
4597 genesis: false,
4598 home_network: false,
4599 listen_addr: None,
4600 local: false,
4601 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4602 log_format: None,
4603 max_archived_log_files: None,
4604 max_log_files: None,
4605 metrics_port: None,
4606 node_ip: None,
4607 node_port: None,
4608 number: 1,
4609 owner: Some("discord_username".to_string()),
4610 peer_id: Some(PeerId::from_str(
4611 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4612 )?),
4613 pid: Some(1000),
4614 rewards_address: RewardsAddress::from_str(
4615 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4616 )?,
4617 reward_balance: Some(AttoTokens::zero()),
4618
4619 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4620 safenode_path: current_node_bin.to_path_buf(),
4621 service_name: "safenode1".to_string(),
4622 status: ServiceStatus::Running,
4623 upnp: false,
4624 user: Some("safe".to_string()),
4625 user_mode: false,
4626 version: current_version.to_string(),
4627 };
4628 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4629
4630 let mut service_manager = ServiceManager::new(
4631 service,
4632 Box::new(mock_service_control),
4633 VerbosityLevel::Normal,
4634 );
4635
4636 service_manager
4637 .upgrade(UpgradeOptions {
4638 auto_restart: true,
4639 bootstrap_peers: Vec::new(),
4640 env_variables: None,
4641 force: false,
4642 start_service: true,
4643 target_bin_path: target_node_bin.to_path_buf(),
4644 target_version: Version::parse(target_version).unwrap(),
4645 })
4646 .await?;
4647
4648 assert!(service_manager.service.service_data.auto_restart,);
4649
4650 Ok(())
4651 }
4652
4653 #[tokio::test]
4654 async fn upgrade_should_retain_the_rewards_address() -> Result<()> {
4655 let current_version = "0.1.0";
4656 let target_version = "0.2.0";
4657
4658 let tmp_data_dir = assert_fs::TempDir::new()?;
4659 let current_install_dir = tmp_data_dir.child("safenode_install");
4660 current_install_dir.create_dir_all()?;
4661
4662 let current_node_bin = current_install_dir.child("safenode");
4663 current_node_bin.write_binary(b"fake safenode binary")?;
4664 let target_node_bin = tmp_data_dir.child("safenode");
4665 target_node_bin.write_binary(b"fake safenode binary")?;
4666
4667 let mut mock_service_control = MockServiceControl::new();
4668 let mut mock_rpc_client = MockRpcClient::new();
4669
4670 mock_service_control
4672 .expect_get_process_pid()
4673 .with(eq(current_node_bin.to_path_buf().clone()))
4674 .times(1)
4675 .returning(|_| Ok(1000));
4676 mock_service_control
4677 .expect_stop()
4678 .with(eq("safenode1"), eq(false))
4679 .times(1)
4680 .returning(|_, _| Ok(()));
4681
4682 mock_service_control
4684 .expect_uninstall()
4685 .with(eq("safenode1"), eq(false))
4686 .times(1)
4687 .returning(|_, _| Ok(()));
4688 mock_service_control
4689 .expect_install()
4690 .with(
4691 eq(ServiceInstallCtx {
4692 args: vec![
4693 OsString::from("--rpc"),
4694 OsString::from("127.0.0.1:8081"),
4695 OsString::from("--root-dir"),
4696 OsString::from("/var/safenode-manager/services/safenode1"),
4697 OsString::from("--log-output-dest"),
4698 OsString::from("/var/log/safenode/safenode1"),
4699 OsString::from("--owner"),
4700 OsString::from("discord_username"),
4701 OsString::from("--rewards-address"),
4702 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4703 OsString::from("evm-custom"),
4704 OsString::from("--rpc-url"),
4705 OsString::from("http://localhost:8545/"),
4706 OsString::from("--payment-token-address"),
4707 OsString::from("0x5FbDB2315678afecb367f032d93F642f64180aa3"),
4708 OsString::from("--data-payments-address"),
4709 OsString::from("0x8464135c8F25Da09e49BC8782676a84730C318bC"),
4710 ],
4711 autostart: true,
4712 contents: None,
4713 environment: None,
4714 label: "safenode1".parse()?,
4715 program: current_node_bin.to_path_buf(),
4716 username: Some("safe".to_string()),
4717 working_directory: None,
4718 }),
4719 eq(false),
4720 )
4721 .times(1)
4722 .returning(|_, _| Ok(()));
4723
4724 mock_service_control
4726 .expect_start()
4727 .with(eq("safenode1"), eq(false))
4728 .times(1)
4729 .returning(|_, _| Ok(()));
4730 mock_service_control
4731 .expect_wait()
4732 .with(eq(3000))
4733 .times(1)
4734 .returning(|_| ());
4735 mock_service_control
4736 .expect_get_process_pid()
4737 .with(eq(current_node_bin.to_path_buf().clone()))
4738 .times(1)
4739 .returning(|_| Ok(100));
4740
4741 mock_rpc_client.expect_node_info().times(1).returning(|| {
4742 Ok(NodeInfo {
4743 pid: 2000,
4744 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4745 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4746 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4747 version: target_version.to_string(),
4748 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4750 })
4751 });
4752 mock_rpc_client
4753 .expect_network_info()
4754 .times(1)
4755 .returning(|| {
4756 Ok(NetworkInfo {
4757 connected_peers: Vec::new(),
4758 listeners: Vec::new(),
4759 })
4760 });
4761
4762 let mut service_data = NodeServiceData {
4763 auto_restart: true,
4764 connected_peers: None,
4765 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4766 evm_network: EvmNetwork::Custom(CustomNetwork {
4767 rpc_url_http: "http://localhost:8545".parse()?,
4768 payment_token_address: RewardsAddress::from_str(
4769 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
4770 )?,
4771 data_payments_address: RewardsAddress::from_str(
4772 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
4773 )?,
4774 }),
4775 genesis: false,
4776 home_network: false,
4777 listen_addr: None,
4778 local: false,
4779 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4780 log_format: None,
4781 max_archived_log_files: None,
4782 max_log_files: None,
4783 metrics_port: None,
4784 node_ip: None,
4785 node_port: None,
4786 number: 1,
4787 owner: Some("discord_username".to_string()),
4788 peer_id: Some(PeerId::from_str(
4789 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4790 )?),
4791 pid: Some(1000),
4792 rewards_address: RewardsAddress::from_str(
4793 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4794 )?,
4795 reward_balance: Some(AttoTokens::zero()),
4796
4797 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4798 safenode_path: current_node_bin.to_path_buf(),
4799 service_name: "safenode1".to_string(),
4800 status: ServiceStatus::Running,
4801 upnp: false,
4802 user: Some("safe".to_string()),
4803 user_mode: false,
4804 version: current_version.to_string(),
4805 };
4806 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4807
4808 let mut service_manager = ServiceManager::new(
4809 service,
4810 Box::new(mock_service_control),
4811 VerbosityLevel::Normal,
4812 );
4813
4814 service_manager
4815 .upgrade(UpgradeOptions {
4816 auto_restart: true,
4817 bootstrap_peers: Vec::new(),
4818 env_variables: None,
4819 force: false,
4820 start_service: true,
4821 target_bin_path: target_node_bin.to_path_buf(),
4822 target_version: Version::parse(target_version).unwrap(),
4823 })
4824 .await?;
4825
4826 assert!(service_manager.service.service_data.auto_restart,);
4827
4828 Ok(())
4829 }
4830
4831 #[tokio::test]
4832 async fn upgrade_should_use_dynamic_startup_delay_if_set() -> Result<()> {
4833 let current_version = "0.1.0";
4834 let target_version = "0.2.0";
4835
4836 let tmp_data_dir = assert_fs::TempDir::new()?;
4837 let current_install_dir = tmp_data_dir.child("safenode_install");
4838 current_install_dir.create_dir_all()?;
4839
4840 let current_node_bin = current_install_dir.child("safenode");
4841 current_node_bin.write_binary(b"fake safenode binary")?;
4842 let target_node_bin = tmp_data_dir.child("safenode");
4843 target_node_bin.write_binary(b"fake safenode binary")?;
4844
4845 let mut mock_service_control = MockServiceControl::new();
4846 let mut mock_rpc_client = MockRpcClient::new();
4847
4848 mock_service_control
4850 .expect_get_process_pid()
4851 .with(eq(current_node_bin.to_path_buf().clone()))
4852 .times(1)
4853 .returning(|_| Ok(1000));
4854 mock_service_control
4855 .expect_stop()
4856 .with(eq("safenode1"), eq(false))
4857 .times(1)
4858 .returning(|_, _| Ok(()));
4859
4860 mock_service_control
4862 .expect_uninstall()
4863 .with(eq("safenode1"), eq(false))
4864 .times(1)
4865 .returning(|_, _| Ok(()));
4866 mock_service_control
4867 .expect_install()
4868 .with(
4869 eq(ServiceInstallCtx {
4870 args: vec![
4871 OsString::from("--rpc"),
4872 OsString::from("127.0.0.1:8081"),
4873 OsString::from("--root-dir"),
4874 OsString::from("/var/safenode-manager/services/safenode1"),
4875 OsString::from("--log-output-dest"),
4876 OsString::from("/var/log/safenode/safenode1"),
4877 OsString::from("--upnp"),
4878 OsString::from("--rewards-address"),
4879 OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4880 OsString::from("evm-arbitrum-one"),
4881 ],
4882 autostart: false,
4883 contents: None,
4884 environment: None,
4885 label: "safenode1".parse()?,
4886 program: current_node_bin.to_path_buf(),
4887 username: Some("safe".to_string()),
4888 working_directory: None,
4889 }),
4890 eq(false),
4891 )
4892 .times(1)
4893 .returning(|_, _| Ok(()));
4894
4895 mock_service_control
4897 .expect_start()
4898 .with(eq("safenode1"), eq(false))
4899 .times(1)
4900 .returning(|_, _| Ok(()));
4901 mock_service_control
4902 .expect_wait()
4903 .with(eq(3000))
4904 .times(1)
4905 .returning(|_| ());
4906 mock_service_control
4907 .expect_get_process_pid()
4908 .with(eq(current_node_bin.to_path_buf().clone()))
4909 .times(1)
4910 .returning(|_| Ok(100));
4911 mock_rpc_client
4912 .expect_is_node_connected_to_network()
4913 .times(1)
4914 .returning(|_| Ok(()));
4915 mock_rpc_client.expect_node_info().times(1).returning(|| {
4916 Ok(NodeInfo {
4917 pid: 2000,
4918 peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4919 data_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4920 log_path: PathBuf::from("/var/log/safenode/safenode1"),
4921 version: target_version.to_string(),
4922 uptime: std::time::Duration::from_secs(1), wallet_balance: 0,
4924 })
4925 });
4926 mock_rpc_client
4927 .expect_network_info()
4928 .times(1)
4929 .returning(|| {
4930 Ok(NetworkInfo {
4931 connected_peers: Vec::new(),
4932 listeners: Vec::new(),
4933 })
4934 });
4935
4936 let mut service_data = NodeServiceData {
4937 auto_restart: false,
4938 connected_peers: None,
4939 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
4940 evm_network: EvmNetwork::ArbitrumOne,
4941 genesis: false,
4942 home_network: false,
4943 listen_addr: None,
4944 local: false,
4945 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
4946 log_format: None,
4947 max_archived_log_files: None,
4948 max_log_files: None,
4949 metrics_port: None,
4950 node_ip: None,
4951 node_port: None,
4952 number: 1,
4953 owner: None,
4954 peer_id: Some(PeerId::from_str(
4955 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4956 )?),
4957 pid: Some(1000),
4958 rewards_address: RewardsAddress::from_str(
4959 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4960 )?,
4961 reward_balance: Some(AttoTokens::zero()),
4962 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4963 safenode_path: current_node_bin.to_path_buf(),
4964 service_name: "safenode1".to_string(),
4965 status: ServiceStatus::Running,
4966 upnp: true,
4967 user: Some("safe".to_string()),
4968 user_mode: false,
4969 version: current_version.to_string(),
4970 };
4971 let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client))
4972 .with_connection_timeout(Duration::from_secs(
4973 DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S,
4974 ));
4975
4976 let mut service_manager = ServiceManager::new(
4977 service,
4978 Box::new(mock_service_control),
4979 VerbosityLevel::Normal,
4980 );
4981
4982 service_manager
4983 .upgrade(UpgradeOptions {
4984 auto_restart: false,
4985 bootstrap_peers: Vec::new(),
4986 env_variables: None,
4987 force: false,
4988 start_service: true,
4989 target_bin_path: target_node_bin.to_path_buf(),
4990 target_version: Version::parse(target_version).unwrap(),
4991 })
4992 .await?;
4993
4994 Ok(())
4995 }
4996
4997 #[tokio::test]
4998 async fn remove_should_remove_an_added_node() -> Result<()> {
4999 let temp_dir = assert_fs::TempDir::new()?;
5000 let log_dir = temp_dir.child("safenode1-logs");
5001 log_dir.create_dir_all()?;
5002 let data_dir = temp_dir.child("safenode1-data");
5003 data_dir.create_dir_all()?;
5004 let safenode_bin = data_dir.child("safenode");
5005 safenode_bin.write_binary(b"fake safenode binary")?;
5006
5007 let mut mock_service_control = MockServiceControl::new();
5008 mock_service_control
5009 .expect_uninstall()
5010 .with(eq("safenode1"), eq(false))
5011 .times(1)
5012 .returning(|_, _| Ok(()));
5013
5014 let mut service_data = NodeServiceData {
5015 auto_restart: false,
5016 connected_peers: None,
5017 data_dir_path: data_dir.to_path_buf(),
5018 evm_network: EvmNetwork::Custom(CustomNetwork {
5019 rpc_url_http: "http://localhost:8545".parse()?,
5020 payment_token_address: RewardsAddress::from_str(
5021 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5022 )?,
5023 data_payments_address: RewardsAddress::from_str(
5024 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5025 )?,
5026 }),
5027 genesis: false,
5028 home_network: false,
5029 listen_addr: None,
5030 local: false,
5031 log_dir_path: log_dir.to_path_buf(),
5032 log_format: None,
5033 max_archived_log_files: None,
5034 max_log_files: None,
5035 metrics_port: None,
5036 node_ip: None,
5037 node_port: None,
5038 number: 1,
5039 owner: None,
5040 pid: None,
5041 peer_id: None,
5042 rewards_address: RewardsAddress::from_str(
5043 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5044 )?,
5045 reward_balance: Some(AttoTokens::zero()),
5046 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5047 safenode_path: safenode_bin.to_path_buf(),
5048 status: ServiceStatus::Stopped,
5049 service_name: "safenode1".to_string(),
5050 version: "0.98.1".to_string(),
5051 upnp: false,
5052 user: Some("safe".to_string()),
5053 user_mode: false,
5054 };
5055 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
5056 let mut service_manager = ServiceManager::new(
5057 service,
5058 Box::new(mock_service_control),
5059 VerbosityLevel::Normal,
5060 );
5061
5062 service_manager.remove(false).await?;
5063
5064 assert_matches!(
5065 service_manager.service.service_data.status,
5066 ServiceStatus::Removed
5067 );
5068 log_dir.assert(predicate::path::missing());
5069 data_dir.assert(predicate::path::missing());
5070
5071 Ok(())
5072 }
5073
5074 #[tokio::test]
5075 async fn remove_should_return_an_error_if_attempting_to_remove_a_running_node() -> Result<()> {
5076 let mut mock_service_control = MockServiceControl::new();
5077 mock_service_control
5078 .expect_get_process_pid()
5079 .with(eq(PathBuf::from(
5080 "/var/safenode-manager/services/safenode1/safenode",
5081 )))
5082 .times(1)
5083 .returning(|_| Ok(1000));
5084
5085 let mut service_data = NodeServiceData {
5086 auto_restart: false,
5087 connected_peers: None,
5088 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
5089 evm_network: EvmNetwork::Custom(CustomNetwork {
5090 rpc_url_http: "http://localhost:8545".parse()?,
5091 payment_token_address: RewardsAddress::from_str(
5092 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5093 )?,
5094 data_payments_address: RewardsAddress::from_str(
5095 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5096 )?,
5097 }),
5098 genesis: false,
5099 home_network: false,
5100 listen_addr: None,
5101 local: false,
5102 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
5103 log_format: None,
5104 max_archived_log_files: None,
5105 max_log_files: None,
5106 metrics_port: None,
5107 node_ip: None,
5108 node_port: None,
5109 number: 1,
5110 owner: None,
5111 pid: Some(1000),
5112 peer_id: Some(PeerId::from_str(
5113 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5114 )?),
5115 rewards_address: RewardsAddress::from_str(
5116 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5117 )?,
5118 reward_balance: Some(AttoTokens::zero()),
5119 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5120 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
5121 service_name: "safenode1".to_string(),
5122 status: ServiceStatus::Running,
5123 upnp: false,
5124 user: Some("safe".to_string()),
5125 user_mode: false,
5126 version: "0.98.1".to_string(),
5127 };
5128 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
5129 let mut service_manager = ServiceManager::new(
5130 service,
5131 Box::new(mock_service_control),
5132 VerbosityLevel::Normal,
5133 );
5134
5135 let result = service_manager.remove(false).await;
5136 match result {
5137 Ok(_) => panic!("This test should result in an error"),
5138 Err(e) => assert_eq!(
5139 "The service(s) is already running: [\"safenode1\"]",
5140 e.to_string()
5141 ),
5142 }
5143
5144 Ok(())
5145 }
5146
5147 #[tokio::test]
5148 async fn remove_should_return_an_error_for_a_node_that_was_marked_running_but_was_not_actually_running(
5149 ) -> Result<()> {
5150 let temp_dir = assert_fs::TempDir::new()?;
5151 let log_dir = temp_dir.child("safenode1-logs");
5152 log_dir.create_dir_all()?;
5153 let data_dir = temp_dir.child("safenode1-data");
5154 data_dir.create_dir_all()?;
5155 let safenode_bin = data_dir.child("safenode");
5156 safenode_bin.write_binary(b"fake safenode binary")?;
5157
5158 let mut mock_service_control = MockServiceControl::new();
5159 mock_service_control
5160 .expect_get_process_pid()
5161 .with(eq(PathBuf::from(
5162 "/var/safenode-manager/services/safenode1/safenode",
5163 )))
5164 .times(1)
5165 .returning(|_| {
5166 Err(ServiceError::ServiceProcessNotFound(
5167 "Could not find process at '/var/safenode-manager/services/safenode1/safenode'"
5168 .to_string(),
5169 ))
5170 });
5171
5172 let mut service_data = NodeServiceData {
5173 auto_restart: false,
5174 connected_peers: None,
5175 data_dir_path: PathBuf::from("/var/safenode-manager/services/safenode1"),
5176 evm_network: EvmNetwork::Custom(CustomNetwork {
5177 rpc_url_http: "http://localhost:8545".parse()?,
5178 payment_token_address: RewardsAddress::from_str(
5179 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5180 )?,
5181 data_payments_address: RewardsAddress::from_str(
5182 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5183 )?,
5184 }),
5185 genesis: false,
5186 home_network: false,
5187 listen_addr: None,
5188 local: false,
5189 log_dir_path: PathBuf::from("/var/log/safenode/safenode1"),
5190 log_format: None,
5191 max_archived_log_files: None,
5192 max_log_files: None,
5193 metrics_port: None,
5194 node_ip: None,
5195 node_port: None,
5196 number: 1,
5197 owner: None,
5198 pid: Some(1000),
5199 peer_id: Some(PeerId::from_str(
5200 "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5201 )?),
5202 rewards_address: RewardsAddress::from_str(
5203 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5204 )?,
5205 reward_balance: Some(AttoTokens::zero()),
5206 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5207 safenode_path: PathBuf::from("/var/safenode-manager/services/safenode1/safenode"),
5208 service_name: "safenode1".to_string(),
5209 status: ServiceStatus::Running,
5210 upnp: false,
5211 user: Some("safe".to_string()),
5212 user_mode: false,
5213 version: "0.98.1".to_string(),
5214 };
5215 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
5216 let mut service_manager = ServiceManager::new(
5217 service,
5218 Box::new(mock_service_control),
5219 VerbosityLevel::Normal,
5220 );
5221
5222 let result = service_manager.remove(false).await;
5223 match result {
5224 Ok(_) => panic!("This test should result in an error"),
5225 Err(e) => assert_eq!(
5226 "The service status is not as expected. Expected: Running",
5227 e.to_string()
5228 ),
5229 }
5230
5231 Ok(())
5232 }
5233
5234 #[tokio::test]
5235 async fn remove_should_remove_an_added_node_and_keep_directories() -> Result<()> {
5236 let temp_dir = assert_fs::TempDir::new()?;
5237 let log_dir = temp_dir.child("safenode1-logs");
5238 log_dir.create_dir_all()?;
5239 let data_dir = temp_dir.child("safenode1-data");
5240 data_dir.create_dir_all()?;
5241 let safenode_bin = data_dir.child("safenode");
5242 safenode_bin.write_binary(b"fake safenode binary")?;
5243
5244 let mut mock_service_control = MockServiceControl::new();
5245 mock_service_control
5246 .expect_uninstall()
5247 .with(eq("safenode1"), eq(false))
5248 .times(1)
5249 .returning(|_, _| Ok(()));
5250
5251 let mut service_data = NodeServiceData {
5252 auto_restart: false,
5253 connected_peers: None,
5254 data_dir_path: data_dir.to_path_buf(),
5255 evm_network: EvmNetwork::Custom(CustomNetwork {
5256 rpc_url_http: "http://localhost:8545".parse()?,
5257 payment_token_address: RewardsAddress::from_str(
5258 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5259 )?,
5260 data_payments_address: RewardsAddress::from_str(
5261 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5262 )?,
5263 }),
5264 genesis: false,
5265 home_network: false,
5266 listen_addr: None,
5267 local: false,
5268 log_dir_path: log_dir.to_path_buf(),
5269 log_format: None,
5270 max_archived_log_files: None,
5271 max_log_files: None,
5272 metrics_port: None,
5273 node_ip: None,
5274 node_port: None,
5275 number: 1,
5276 owner: None,
5277 pid: None,
5278 peer_id: None,
5279 rewards_address: RewardsAddress::from_str(
5280 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5281 )?,
5282 reward_balance: Some(AttoTokens::zero()),
5283 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5284 safenode_path: safenode_bin.to_path_buf(),
5285 service_name: "safenode1".to_string(),
5286 status: ServiceStatus::Stopped,
5287 upnp: false,
5288 user: Some("safe".to_string()),
5289 user_mode: false,
5290 version: "0.98.1".to_string(),
5291 };
5292 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
5293 let mut service_manager = ServiceManager::new(
5294 service,
5295 Box::new(mock_service_control),
5296 VerbosityLevel::Normal,
5297 );
5298
5299 service_manager.remove(true).await?;
5300
5301 assert_matches!(
5302 service_manager.service.service_data.status,
5303 ServiceStatus::Removed
5304 );
5305 log_dir.assert(predicate::path::is_dir());
5306 data_dir.assert(predicate::path::is_dir());
5307
5308 Ok(())
5309 }
5310
5311 #[tokio::test]
5312 async fn remove_should_remove_a_user_mode_service() -> Result<()> {
5313 let temp_dir = assert_fs::TempDir::new()?;
5314 let log_dir = temp_dir.child("safenode1-logs");
5315 log_dir.create_dir_all()?;
5316 let data_dir = temp_dir.child("safenode1-data");
5317 data_dir.create_dir_all()?;
5318 let safenode_bin = data_dir.child("safenode");
5319 safenode_bin.write_binary(b"fake safenode binary")?;
5320
5321 let mut mock_service_control = MockServiceControl::new();
5322 mock_service_control
5323 .expect_uninstall()
5324 .with(eq("safenode1"), eq(true))
5325 .times(1)
5326 .returning(|_, _| Ok(()));
5327
5328 let mut service_data = NodeServiceData {
5329 auto_restart: false,
5330 connected_peers: None,
5331 data_dir_path: data_dir.to_path_buf(),
5332 evm_network: EvmNetwork::Custom(CustomNetwork {
5333 rpc_url_http: "http://localhost:8545".parse()?,
5334 payment_token_address: RewardsAddress::from_str(
5335 "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5336 )?,
5337 data_payments_address: RewardsAddress::from_str(
5338 "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5339 )?,
5340 }),
5341 genesis: false,
5342 home_network: false,
5343 listen_addr: None,
5344 local: false,
5345 log_dir_path: log_dir.to_path_buf(),
5346 log_format: None,
5347 max_archived_log_files: None,
5348 max_log_files: None,
5349 metrics_port: None,
5350 node_ip: None,
5351 node_port: None,
5352 number: 1,
5353 owner: None,
5354 pid: None,
5355 peer_id: None,
5356 rewards_address: RewardsAddress::from_str(
5357 "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5358 )?,
5359 reward_balance: Some(AttoTokens::zero()),
5360 rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5361 safenode_path: safenode_bin.to_path_buf(),
5362 status: ServiceStatus::Stopped,
5363 service_name: "safenode1".to_string(),
5364 upnp: false,
5365 user: None,
5366 user_mode: true,
5367 version: "0.98.1".to_string(),
5368 };
5369 let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
5370 let mut service_manager = ServiceManager::new(
5371 service,
5372 Box::new(mock_service_control),
5373 VerbosityLevel::Normal,
5374 );
5375
5376 service_manager.remove(false).await?;
5377
5378 assert_matches!(
5379 service_manager.service.service_data.status,
5380 ServiceStatus::Removed
5381 );
5382 log_dir.assert(predicate::path::missing());
5383 data_dir.assert(predicate::path::missing());
5384
5385 Ok(())
5386 }
5387}