Skip to content

Commit

Permalink
test: use node registry for status
Browse files Browse the repository at this point in the history
Rather than parsing the text-based output of the `status` command, we now switch to using `status
--json`, which we can then serialize into the `NodeRegistry`, and any status can be obtained from
there.

This prevents breakage when the text output changes.
  • Loading branch information
jacderida committed Apr 30, 2024
1 parent 8a0b296 commit 1f8c456
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 145 deletions.
232 changes: 88 additions & 144 deletions sn_node_manager/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

use assert_cmd::Command;
use libp2p_identity::PeerId;
use std::str::FromStr;
use sn_service_management::{NodeRegistry, ServiceStatus};

/// These tests need to execute as the root user.
///
Expand All @@ -19,13 +19,6 @@ use std::str::FromStr;
const CI_USER: &str = "runner";

#[derive(Debug)]
struct ServiceStatus {
name: String,
peer_id: String,
status: String,
}

/// The default behaviour is for the service to run as the `safe` user, which gets created during
/// the process. However, there seems to be some sort of issue with adding user accounts on the GHA
/// build agent, so we will just tell it to use the `runner` user, which is the account for the
Expand All @@ -47,198 +40,149 @@ fn cross_platform_service_install_and_control() {
.assert()
.success();

let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let service_status = parse_service_status(&output.stdout);

assert_eq!(service_status[0].name, "safenode1");
assert_eq!(service_status[0].peer_id, "-");
assert_eq!(service_status[0].status, "ADDED");
assert_eq!(service_status[1].name, "safenode2");
assert_eq!(service_status[1].peer_id, "-");
assert_eq!(service_status[1].status, "ADDED");
assert_eq!(service_status[2].name, "safenode3");
assert_eq!(service_status[2].peer_id, "-");
assert_eq!(service_status[2].status, "ADDED");
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].peer_id, None);
assert_eq!(registry.nodes[0].status, ServiceStatus::Added);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].peer_id, None);
assert_eq!(registry.nodes[1].status, ServiceStatus::Added);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].peer_id, None);
assert_eq!(registry.nodes[2].status, ServiceStatus::Added);

// Start each of the three services.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("start").assert().success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let start_status = parse_service_status(&output.stdout);

// After `start`, all services should be running with valid peer IDs assigned.
assert_eq!(start_status[0].name, "safenode1");
assert_eq!(start_status[0].status, "RUNNING");
assert_eq!(start_status[1].name, "safenode2");
assert_eq!(start_status[1].status, "RUNNING");
assert_eq!(start_status[2].name, "safenode3");
assert_eq!(start_status[2].status, "RUNNING");
for status in start_status.iter() {
assert!(PeerId::from_str(&status.peer_id).is_ok());
}
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Running);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Running);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Running);

// The three peer IDs should persist throughout the rest of the test.
let peer_ids = start_status
let peer_ids = registry
.nodes
.iter()
.map(|s| s.peer_id.clone())
.collect::<Vec<String>>();
.map(|n| n.peer_id)
.collect::<Vec<Option<PeerId>>>();

// Stop each of the three services.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("stop").assert().success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let stop_status = parse_service_status(&output.stdout);

// After `stop`, all services should be stopped with peer IDs retained.
assert_eq!(stop_status[0].name, "safenode1");
assert_eq!(stop_status[0].status, "STOPPED");
assert_eq!(stop_status[0].peer_id, peer_ids[0]);
assert_eq!(stop_status[1].name, "safenode2");
assert_eq!(stop_status[1].status, "STOPPED");
assert_eq!(stop_status[1].peer_id, peer_ids[1]);
assert_eq!(stop_status[2].name, "safenode3");
assert_eq!(stop_status[2].status, "STOPPED");
assert_eq!(stop_status[2].peer_id, peer_ids[2]);
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[0].peer_id, peer_ids[0]);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[1].peer_id, peer_ids[1]);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[2].peer_id, peer_ids[2]);

// Start each of the three services again.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("start").assert().success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let start_status = parse_service_status(&output.stdout);

// Peer IDs again should be retained after restart.
assert_eq!(start_status[0].name, "safenode1");
assert_eq!(start_status[0].status, "RUNNING");
assert_eq!(start_status[0].peer_id, peer_ids[0]);
assert_eq!(start_status[1].name, "safenode2");
assert_eq!(start_status[1].status, "RUNNING");
assert_eq!(start_status[1].peer_id, peer_ids[1]);
assert_eq!(start_status[2].name, "safenode3");
assert_eq!(start_status[2].status, "RUNNING");
assert_eq!(start_status[2].peer_id, peer_ids[2]);
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Running);
assert_eq!(registry.nodes[0].peer_id, peer_ids[0]);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Running);
assert_eq!(registry.nodes[1].peer_id, peer_ids[1]);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Running);
assert_eq!(registry.nodes[2].peer_id, peer_ids[2]);

// Stop two nodes by peer ID.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("stop")
.arg("--peer-id")
.arg(start_status[0].peer_id.clone())
.arg(registry.nodes[0].peer_id.unwrap().to_string())
.arg("--peer-id")
.arg(start_status[2].peer_id.clone())
.arg(registry.nodes[2].peer_id.unwrap().to_string())
.assert()
.success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let stop_status = parse_service_status(&output.stdout);

// Peer IDs again should be retained after restart.
assert_eq!(stop_status[0].name, "safenode1");
assert_eq!(stop_status[0].status, "STOPPED");
assert_eq!(stop_status[0].peer_id, peer_ids[0]);
assert_eq!(stop_status[1].name, "safenode2");
assert_eq!(stop_status[1].status, "RUNNING");
assert_eq!(stop_status[1].peer_id, peer_ids[1]);
assert_eq!(stop_status[2].name, "safenode3");
assert_eq!(stop_status[2].status, "STOPPED");
assert_eq!(stop_status[2].peer_id, peer_ids[2]);
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[0].peer_id, peer_ids[0]);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Running);
assert_eq!(registry.nodes[1].peer_id, peer_ids[1]);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[2].peer_id, peer_ids[2]);

// Now restart the stopped nodes by service name.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("start")
.arg("--service-name")
.arg(stop_status[0].name.clone())
.arg(registry.nodes[0].service_name.clone())
.arg("--service-name")
.arg(stop_status[2].name.clone())
.arg(registry.nodes[2].service_name.clone())
.assert()
.success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let single_node_start_status = parse_service_status(&output.stdout);

// The stopped nodes should now be running again.
assert_eq!(single_node_start_status[0].name, "safenode1");
assert_eq!(single_node_start_status[0].status, "RUNNING");
assert_eq!(single_node_start_status[0].peer_id, peer_ids[0]);
assert_eq!(single_node_start_status[1].name, "safenode2");
assert_eq!(single_node_start_status[1].status, "RUNNING");
assert_eq!(single_node_start_status[1].peer_id, peer_ids[1]);
assert_eq!(single_node_start_status[2].name, "safenode3");
assert_eq!(single_node_start_status[2].status, "RUNNING");
assert_eq!(single_node_start_status[2].peer_id, peer_ids[2]);
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Running);
assert_eq!(registry.nodes[0].peer_id, peer_ids[0]);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Running);
assert_eq!(registry.nodes[1].peer_id, peer_ids[1]);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Running);
assert_eq!(registry.nodes[2].peer_id, peer_ids[2]);

// Finally, stop each of the three services.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("stop").assert().success();
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.output()
.expect("Could not retrieve service status");
let stop_status = parse_service_status(&output.stdout);

// After `stop`, all services should be stopped with peer IDs retained.
assert_eq!(stop_status[0].name, "safenode1");
assert_eq!(stop_status[0].status, "STOPPED");
assert_eq!(stop_status[0].peer_id, peer_ids[0]);
assert_eq!(stop_status[1].name, "safenode2");
assert_eq!(stop_status[1].status, "STOPPED");
assert_eq!(stop_status[1].peer_id, peer_ids[1]);
assert_eq!(stop_status[2].name, "safenode3");
assert_eq!(stop_status[2].status, "STOPPED");
assert_eq!(stop_status[2].peer_id, peer_ids[2]);

// Remove a two nodes.
let registry = get_status();
assert_eq!(registry.nodes[0].service_name, "safenode1");
assert_eq!(registry.nodes[0].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[0].peer_id, peer_ids[0]);
assert_eq!(registry.nodes[1].service_name, "safenode2");
assert_eq!(registry.nodes[1].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[1].peer_id, peer_ids[1]);
assert_eq!(registry.nodes[2].service_name, "safenode3");
assert_eq!(registry.nodes[2].status, ServiceStatus::Stopped);
assert_eq!(registry.nodes[2].peer_id, peer_ids[2]);

// Remove two nodes.
let mut cmd = Command::cargo_bin("safenode-manager").unwrap();
cmd.arg("remove")
.arg("--service-name")
.arg(stop_status[0].name.clone())
.arg(registry.nodes[0].service_name.clone())
.arg("--service-name")
.arg(stop_status[1].name.clone())
.arg(registry.nodes[1].service_name.clone())
.assert()
.success();
let registry = get_status();
assert_eq!(registry.nodes.len(), 1);
}

fn get_status() -> NodeRegistry {
let output = Command::cargo_bin("safenode-manager")
.unwrap()
.arg("status")
.arg("--json")
.output()
.expect("Could not retrieve service status");
let remove_status = parse_service_status(&output.stdout);
assert_eq!(remove_status.len(), 1);
}

fn parse_service_status(output: &[u8]) -> Vec<ServiceStatus> {
let output_str = String::from_utf8_lossy(output);
output_str
.split('\n')
.skip(5) // Skip header lines
.filter(|line| !line.is_empty())
.map(|line| {
let columns: Vec<&str> = line.split_whitespace().collect();
ServiceStatus {
name: columns[0].to_string(),
peer_id: columns[1].to_string(),
status: columns[2].to_string(),
}
})
.collect()
let output = String::from_utf8_lossy(&output.stdout).to_string();
NodeRegistry::from_json(&output).unwrap()
}
6 changes: 5 additions & 1 deletion sn_service_management/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ impl NodeRegistry {
});
}

let registry = serde_json::from_str(&contents)?;
Self::from_json(&contents)
}

pub fn from_json(json: &str) -> Result<Self> {
let registry = serde_json::from_str(json)?;
Ok(registry)
}

Expand Down

0 comments on commit 1f8c456

Please sign in to comment.