add unittest for shell cmd executor

This commit is contained in:
Robin Müller 2024-04-25 16:45:00 +02:00
parent 1e57b1f978
commit c5eddcb292
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 134 additions and 13 deletions

58
Cargo.lock generated
View File

@ -113,6 +113,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.16.0" version = "3.16.0"
@ -295,6 +301,22 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
[[package]] [[package]]
name = "fern" name = "fern"
version = "0.6.2" version = "0.6.2"
@ -495,6 +517,12 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.21"
@ -540,7 +568,7 @@ version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"libc", "libc",
"memoffset", "memoffset",
@ -619,6 +647,7 @@ dependencies = [
"serde_json", "serde_json",
"socket2", "socket2",
"strum", "strum",
"tempfile",
"thiserror", "thiserror",
"toml", "toml",
] ]
@ -687,7 +716,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
] ]
[[package]] [[package]]
@ -719,6 +748,19 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.15" version = "1.0.15"
@ -936,6 +978,18 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.59" version = "1.0.59"

View File

@ -33,6 +33,7 @@ version = ">=0.1.2, <0.2"
[dev-dependencies] [dev-dependencies]
env_logger = "0.11" env_logger = "0.11"
tempfile = "3"
# I don't think we need insane performance. If anything, a small binary is easier to upload # I don't think we need insane performance. If anything, a small binary is easier to upload
# to the satellite. # to the satellite.

View File

@ -6,6 +6,7 @@ use satrs::{
request::{GenericMessage, MessageMetadata}, request::{GenericMessage, MessageMetadata},
res_code::ResultU16, res_code::ResultU16,
}; };
use serde::{Deserialize, Serialize};
use std::{ use std::{
env::temp_dir, env::temp_dir,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -21,6 +22,12 @@ use ops_sat_rs::config::{
use crate::requests::CompositeRequest; use crate::requests::CompositeRequest;
#[derive(Serialize, Deserialize, Debug)]
pub struct ShellCmd {
cmd: String,
args: Vec<String>,
}
#[derive(Debug, Clone, Copy, TryFromPrimitive)] #[derive(Debug, Clone, Copy, TryFromPrimitive)]
#[repr(u32)] #[repr(u32)]
pub enum ActionId { pub enum ActionId {
@ -139,14 +146,18 @@ impl ExperimentController {
action_req: &ActionRequest, action_req: &ActionRequest,
) { ) {
if let ActionRequestVariant::VecData(data) = &action_req.variant { if let ActionRequestVariant::VecData(data) = &action_req.variant {
match String::from_utf8(data.to_vec()) { let shell_cmd_result: serde_json::Result<ShellCmd> = serde_json::from_slice(data);
Ok(cmd_str) => { match shell_cmd_result {
log::info!("executing command: {}", cmd_str); Ok(shell_cmd) => {
match Command::new(cmd_str).status() { log::info!("executing shell cmd {:?}", shell_cmd);
println!("executing shell cmd {:?}", shell_cmd);
match Command::new(shell_cmd.cmd).args(shell_cmd.args).status() {
Ok(status) => { Ok(status) => {
if status.success() { if status.success() {
println!("cmd exec success");
self.send_completion_success(requestor, action_req); self.send_completion_success(requestor, action_req);
} else { } else {
println!("cmd exec failed with {}", status);
log::warn!("execution of command failed: {}", status); log::warn!("execution of command failed: {}", status);
self.send_completion_failure( self.send_completion_failure(
requestor, requestor,
@ -158,6 +169,7 @@ impl ExperimentController {
} }
Err(e) => { Err(e) => {
log::warn!("execution of command failed with IO error: {}", e); log::warn!("execution of command failed with IO error: {}", e);
println!("cmd exec failed with {}", e);
self.send_completion_failure( self.send_completion_failure(
requestor, requestor,
action_req, action_req,
@ -168,15 +180,12 @@ impl ExperimentController {
} }
} }
Err(e) => { Err(e) => {
log::warn!("invalid utf-8 data in action request: {}", e); log::warn!("failed to deserialize shell command: {}", e);
self.send_completion_failure(
requestor,
action_req,
SHELL_CMD_INVALID_FORMAT,
Some(e.to_string().into()),
);
} }
} }
} else {
log::warn!("no shell command was supplied for shell command action command");
self.send_completion_failure(requestor, action_req, SHELL_CMD_INVALID_FORMAT, None);
} }
} }
@ -204,3 +213,60 @@ impl ExperimentController {
check_at_path(self.home_path_stop_file.as_path()); check_at_path(self.home_path_stop_file.as_path());
} }
} }
#[cfg(test)]
mod tests {
use std::sync::{mpsc, Arc};
use tempfile::NamedTempFile;
use super::*;
fn init() {
env_logger::builder().is_test(true).init();
}
#[test]
fn test_shell_cmd_exection() {
init();
let (composite_req_tx, composite_req_rx) = mpsc::channel();
let (action_reply_tx, action_reply_rx) = mpsc::channel();
let stop_signal = Arc::default();
let mut exp_ctrl =
ExperimentController::new(composite_req_rx, action_reply_tx, stop_signal);
let named_temp_file = NamedTempFile::new().expect("creating temp file failed");
let cmd = ShellCmd {
cmd: "rm".to_string(),
args: vec![named_temp_file.path().to_string_lossy().to_string()],
};
let cmd_serialized = serde_json::to_string(&cmd).expect("serialization failed");
let action_req = satrs::action::ActionRequest {
action_id: ActionId::ExecuteShellCommandBlocking as u32,
variant: satrs::action::ActionRequestVariant::VecData(cmd_serialized.into_bytes()),
};
composite_req_tx
.send(GenericMessage::new(
MessageMetadata::new(1, 2),
CompositeRequest::Action(action_req),
))
.expect("sending action request failed");
exp_ctrl.perform_operation();
assert!(!named_temp_file.path().exists());
let action_reply = action_reply_rx
.try_recv()
.expect("receiving action reply failed");
assert_eq!(
action_reply.message.action_id,
ActionId::ExecuteShellCommandBlocking as u32
);
match action_reply.message.variant {
ActionReplyVariant::Completed => (),
_ => {
panic!(
"unexecpted action reply variant {:?}",
action_reply.message.variant
)
}
}
}
}