diff --git a/KubeOS-Rust/agent/src/rpc/agent.rs b/KubeOS-Rust/agent/src/rpc/agent.rs index 13775afbde0a588cac269e0d27f6827d92a78087..2496bfb4b807eba584491830d5f0c11b015a6afe 100644 --- a/KubeOS-Rust/agent/src/rpc/agent.rs +++ b/KubeOS-Rust/agent/src/rpc/agent.rs @@ -22,9 +22,6 @@ pub trait Agent { #[rpc(name = "upgrade")] fn upgrade(&self) -> RpcResult; - #[rpc(name = "cleanup")] - fn cleanup(&self) -> RpcResult; - #[rpc(name = "configure")] fn configure(&self, req: ConfigureRequest) -> RpcResult; diff --git a/KubeOS-Rust/agent/src/rpc/agent_impl.rs b/KubeOS-Rust/agent/src/rpc/agent_impl.rs index 7101d0db6572315fd890c364ba9188821e8acb62..8aef4140e99a5844b67ff4cadc6aa4d5a286810d 100644 --- a/KubeOS-Rust/agent/src/rpc/agent_impl.rs +++ b/KubeOS-Rust/agent/src/rpc/agent_impl.rs @@ -13,11 +13,11 @@ use std::{sync::Mutex, thread, time::Duration}; use anyhow::{bail, Result}; -use log::{debug, error, info}; +use log::{debug, info}; use manager::{ api::{AgentStatus, ConfigureRequest, ImageType, Response, UpgradeRequest}, sys_mgmt::{CtrImageHandler, DiskImageHandler, DockerImageHandler, CONFIG_TEMPLATE, DEFAULT_GRUBENV_PATH}, - utils::{clean_env, get_partition_info, switch_boot_menuentry, PreparePath, RealCommandExecutor}, + utils::{get_partition_info, switch_boot_menuentry, RealCommandExecutor}, }; use nix::{sys::reboot::RebootMode, unistd::sync}; @@ -40,10 +40,6 @@ impl Agent for AgentImpl { RpcFunction::call(|| self.upgrade_impl()) } - fn cleanup(&self) -> RpcResult { - RpcFunction::call(|| self.cleanup_impl()) - } - fn configure(&self, req: ConfigureRequest) -> RpcResult { RpcFunction::call(|| self.configure_impl(req)) } @@ -94,14 +90,6 @@ impl AgentImpl { Ok(Response { status: AgentStatus::Upgraded }) } - pub fn cleanup_impl(&self) -> Result { - let _lock = self.mutex.lock().unwrap(); - info!("Start to cleanup"); - let paths = PreparePath::default(); - clean_env(paths.update_path, paths.mount_path, paths.image_path)?; - Ok(Response { status: AgentStatus::CleanedUp }) - } - pub fn configure_impl(&self, mut req: ConfigureRequest) -> Result { let _lock = self.mutex.lock().unwrap(); debug!("Received a 'configure' request: {:?}", req); @@ -113,7 +101,6 @@ impl AgentImpl { debug!("Found configuration type: \"{}\"", config_type); configuration.set_config(config)?; } else { - error!("Unknown configuration type: \"{}\"", config_type); bail!("Unknown configuration type: \"{}\"", config_type); } } @@ -135,7 +122,7 @@ impl AgentImpl { Ok(Response { status: AgentStatus::Rollbacked }) } - pub fn reboot(&self) -> Result<()> { + fn reboot(&self) -> Result<()> { info!("Wait to reboot"); thread::sleep(Duration::from_secs(1)); sync(); @@ -156,7 +143,15 @@ mod test { use super::*; #[test] - fn configure_impl_tests() { + fn test_reboot() { + let mut agent = AgentImpl::default(); + agent.disable_reboot = true; + let res = agent.reboot(); + assert!(res.is_ok()); + } + + #[test] + fn test_configure() { let agent = AgentImpl::default(); let req = ConfigureRequest { configs: vec![Sysconfig { @@ -165,7 +160,7 @@ mod test { contents: HashMap::new(), }], }; - let res = agent.configure_impl(req).unwrap(); + let res = agent.configure(req).unwrap(); assert_eq!(res, Response { status: AgentStatus::Configured }); let req = ConfigureRequest { @@ -175,17 +170,12 @@ mod test { contents: HashMap::new(), }], }; - let res = agent.configure_impl(req); + let res = agent.configure(req); assert!(res.is_err()); } #[test] - fn upgrade_impl_tests() { - let _ = env_logger::builder() - .target(env_logger::Target::Stdout) - .filter_level(log::LevelFilter::Trace) - .is_test(true) - .try_init(); + fn test_prepare_upgrade() { let agent = AgentImpl::default(); let req = UpgradeRequest { version: "v2".into(), @@ -197,7 +187,7 @@ mod test { mtls: false, certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, }; - let res = agent.prepare_upgrade_impl(req); + let res = agent.prepare_upgrade(req); assert!(res.is_err()); } } diff --git a/KubeOS-Rust/cli/src/client.rs b/KubeOS-Rust/cli/src/client.rs index ce45cdd7143bc34ee11f3add2a8eddffa088a283..9765a42545661973c1c0c4c4dc8285ad08e3e9aa 100644 --- a/KubeOS-Rust/cli/src/client.rs +++ b/KubeOS-Rust/cli/src/client.rs @@ -43,27 +43,14 @@ impl Client { #[cfg(test)] mod test { - use kubeos_manager::api; - use super::*; - use crate::method::{callable_method::RpcMethod, configure::ConfigureMethod}; - #[test] - #[ignore] fn test_client() { - let socket_path = "/home/yuhang/os-agent-rust.sock"; + let socket_path = "/tmp/KubeOS-test.sock"; let cli = Client::new(socket_path); - - let configured = api::AgentStatus::Configured; - let resp = api::Response { status: configured }; - let config_request = api::ConfigureRequest { - configs: vec![api::Sysconfig { - model: "kernel.sysctl".into(), - config_path: "".into(), - contents: std::collections::hash_map::HashMap::new(), - }], - }; - let config_resp = ConfigureMethod::new(config_request).call(&cli).unwrap(); - assert_eq!(resp, config_resp); + let command = "example_command"; + let params = vec![]; + let request = cli.send_request(cli.build_request(command, ¶ms)); + assert!(request.is_err()); } } diff --git a/KubeOS-Rust/cli/src/method/callable_method.rs b/KubeOS-Rust/cli/src/method/callable_method.rs index c46614b4f5d50c3f7c1afa3f6452363d95568bf1..a174b5b8fac8db9d4388ff960fe9d9ee6ee336c1 100644 --- a/KubeOS-Rust/cli/src/method/callable_method.rs +++ b/KubeOS-Rust/cli/src/method/callable_method.rs @@ -24,3 +24,31 @@ pub trait RpcMethod { response.result().map_err(parse_error) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::client; + + #[derive(Default)] + struct DummyMethod; + + impl RpcMethod for DummyMethod { + type Response = String; + + fn command_name(&self) -> &'static str { + "dummy_command" + } + + fn command_params(&self) -> Vec> { + vec![] + } + } + + #[test] + fn test_call() { + let client = client::Client::new("/tmp/KubeOS-test.sock"); + let result = DummyMethod::default().call(&client); + assert!(result.is_err()); + } +} diff --git a/KubeOS-Rust/cli/src/method/cleanup.rs b/KubeOS-Rust/cli/src/method/cleanup.rs deleted file mode 100644 index d1d7dbe2c5051ce995f06a282db13f915ebcd7b0..0000000000000000000000000000000000000000 --- a/KubeOS-Rust/cli/src/method/cleanup.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. - * KubeOS is licensed under the Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR - * PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use kubeos_manager::api; -use serde_json::value::RawValue; - -use crate::method::callable_method::RpcMethod; - -#[derive(Default)] -pub struct CleanupMethod {} - -impl RpcMethod for CleanupMethod { - type Response = api::Response; - fn command_name(&self) -> &'static str { - "cleanup" - } - fn command_params(&self) -> Vec> { - vec![] - } -} diff --git a/KubeOS-Rust/cli/src/method/configure.rs b/KubeOS-Rust/cli/src/method/configure.rs index d1371068f5a6bb272e3ecf6b7efa3a3f1c58b2f8..cca752d0a39b7c5073d9de57fdee5fbd3be04e75 100644 --- a/KubeOS-Rust/cli/src/method/configure.rs +++ b/KubeOS-Rust/cli/src/method/configure.rs @@ -39,3 +39,34 @@ impl RpcMethod for ConfigureMethod { vec![to_raw_value(&self.req).unwrap()] } } +#[cfg(test)] +mod tests { + use kubeos_manager::api::{ConfigureRequest, Sysconfig}; + + use super::*; + + #[test] + fn test_configure_method() { + let req = ConfigureRequest { configs: vec![] }; + let mut method = ConfigureMethod::new(req); + + // Test set_configure_request method + let new_req = ConfigureRequest { + configs: vec![Sysconfig { + model: "model".to_string(), + config_path: "config_path".to_string(), + contents: Default::default(), + }], + }; + method.set_configure_request(new_req); + + // Test command_name method + assert_eq!(method.command_name(), "configure"); + + // Test command_params method + let expected_params = + "RawValue({\"configs\":[{\"model\":\"model\",\"config_path\":\"config_path\",\"contents\":{}}]})"; + let actual_params = format!("{:?}", method.command_params()[0]); + assert_eq!(actual_params, expected_params); + } +} diff --git a/KubeOS-Rust/cli/src/method/mod.rs b/KubeOS-Rust/cli/src/method/mod.rs index b04b0fd887743cca0b39dff0909c1352a181003c..e1f38bcd24d860ab68fba0ef7a6b09846aa4d8f7 100644 --- a/KubeOS-Rust/cli/src/method/mod.rs +++ b/KubeOS-Rust/cli/src/method/mod.rs @@ -11,7 +11,6 @@ */ pub mod callable_method; -pub mod cleanup; pub mod configure; pub mod prepare_upgrade; pub mod request; diff --git a/KubeOS-Rust/cli/src/method/prepare_upgrade.rs b/KubeOS-Rust/cli/src/method/prepare_upgrade.rs index 91dae7937ec9d7fdab3812aaf6507dec07e4d8d1..f2034f6b7cd2d3d9bf2779e749bac6f5e7e5cbfd 100644 --- a/KubeOS-Rust/cli/src/method/prepare_upgrade.rs +++ b/KubeOS-Rust/cli/src/method/prepare_upgrade.rs @@ -39,3 +39,40 @@ impl RpcMethod for PrepareUpgradeMethod { vec![to_raw_value(&self.req).unwrap()] } } +#[cfg(test)] +mod tests { + use kubeos_manager::api::{CertsInfo, UpgradeRequest}; + + use super::*; + + #[test] + fn test_prepare_upgrade_method() { + let req = UpgradeRequest { + version: "v1".into(), + check_sum: "".into(), + image_type: "".into(), + container_image: "".into(), + image_url: "".to_string(), + flag_safe: false, + mtls: false, + certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, + }; + let mut method = PrepareUpgradeMethod::new(req); + let new_req = UpgradeRequest { + version: "v2".into(), + check_sum: "xxx".into(), + image_type: "xxx".into(), + container_image: "xxx".into(), + image_url: "".to_string(), + flag_safe: false, + mtls: false, + certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, + }; + method.set_prepare_upgrade_request(new_req); + assert_eq!(method.command_name(), "prepare_upgrade"); + + let expected_params = "RawValue({\"version\":\"v2\",\"check_sum\":\"xxx\",\"image_type\":\"xxx\",\"container_image\":\"xxx\",\"image_url\":\"\",\"flag_safe\":false,\"mtls\":false,\"certs\":{\"ca_cert\":\"\",\"client_cert\":\"\",\"client_key\":\"\"}})"; + let actual_params = format!("{:?}", method.command_params()[0]); + assert_eq!(actual_params, expected_params); + } +} diff --git a/KubeOS-Rust/cli/src/method/request.rs b/KubeOS-Rust/cli/src/method/request.rs index 2dc1ffba19485d202c11794c9fdbd1566dc8e65c..ff75afd009882e1f95e108eb442069f322c2c4fc 100644 --- a/KubeOS-Rust/cli/src/method/request.rs +++ b/KubeOS-Rust/cli/src/method/request.rs @@ -50,3 +50,40 @@ pub fn parse_error(error: Error) -> anyhow::Error { }, } } + +#[cfg(test)] +mod tests { + use jsonrpc::error::RpcError; + use serde::de::Error as DeError; + + use super::*; + + #[test] + fn test_parse_error() { + // Test Error::Transport + let transport_error = + Error::Transport(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "Connection timeout"))); + let result = parse_error(transport_error); + assert_eq!(result.to_string(), "Cannot connect to KubeOS os-agent unix socket, Connection timeout"); + + // Test Error::Json + let json_error = Error::Json(serde_json::Error::custom("Failed to parse response")); + let result = parse_error(json_error); + assert_eq!(result.to_string(), "Failed to parse response"); + + // Test Error::Rpc with "Method not found" message + let rpc_error = Error::Rpc(RpcError { code: -32601, message: "Method not found".to_string(), data: None }); + let result = parse_error(rpc_error); + assert_eq!(result.to_string(), "Method is unimplemented"); + + // Test Error::Rpc with other message + let rpc_error = Error::Rpc(RpcError { code: -32603, message: "Internal server error".to_string(), data: None }); + let result = parse_error(rpc_error); + assert_eq!(result.to_string(), "Internal server error"); + + // Test other Error variant + let other_error = Error::VersionMismatch; + let result = parse_error(other_error); + assert_eq!(result.to_string(), "Response is invalid"); + } +} diff --git a/KubeOS-Rust/cli/src/method/rollback.rs b/KubeOS-Rust/cli/src/method/rollback.rs index 55aa75116ae462a1e5cc59d02280578aea4e4e75..7945f4b16c60b14ecbd4d478e6ac3dab4196980b 100644 --- a/KubeOS-Rust/cli/src/method/rollback.rs +++ b/KubeOS-Rust/cli/src/method/rollback.rs @@ -27,3 +27,16 @@ impl RpcMethod for RollbackMethod { vec![] } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_rollback_method() { + let method = RollbackMethod::default(); + assert_eq!(method.command_name(), "rollback"); + let expected_params = "[]"; + let actual_params = format!("{:?}", method.command_params()); + assert_eq!(actual_params, expected_params); + } +} diff --git a/KubeOS-Rust/cli/src/method/upgrade.rs b/KubeOS-Rust/cli/src/method/upgrade.rs index a9692ca182a49de3f8e26cd01460bc569f3bb8cc..f2f94cd5ead367a64f0a1461d11d47b2a1e1c410 100644 --- a/KubeOS-Rust/cli/src/method/upgrade.rs +++ b/KubeOS-Rust/cli/src/method/upgrade.rs @@ -27,3 +27,16 @@ impl RpcMethod for UpgradeMethod { vec![] } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_upgrade_method() { + let method = UpgradeMethod::default(); + assert_eq!(method.command_name(), "upgrade"); + let expected_params = "[]"; + let actual_params = format!("{:?}", method.command_params()); + assert_eq!(actual_params, expected_params); + } +} diff --git a/KubeOS-Rust/manager/src/api/agent_status.rs b/KubeOS-Rust/manager/src/api/agent_status.rs index e466a50cd8862136ef45a951356b833bd5dfdc83..bb16e6bc7ee61113eac48f9f73300ab85234aaf4 100644 --- a/KubeOS-Rust/manager/src/api/agent_status.rs +++ b/KubeOS-Rust/manager/src/api/agent_status.rs @@ -12,19 +12,10 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)] pub enum AgentStatus { - Unknown, - NotApplied, UpgradeReady, Upgraded, Rollbacked, Configured, - CleanedUp, -} - -impl Default for AgentStatus { - fn default() -> Self { - Self::Unknown - } } diff --git a/KubeOS-Rust/manager/src/api/types.rs b/KubeOS-Rust/manager/src/api/types.rs index 28ee97dbf8038800707464d3f33da63ab02ddf5f..98aeaa3342e94ac887248ce85c858c99bc43649b 100644 --- a/KubeOS-Rust/manager/src/api/types.rs +++ b/KubeOS-Rust/manager/src/api/types.rs @@ -80,3 +80,61 @@ impl ImageType { pub trait ImageHandler { fn download_image(&self, req: &UpgradeRequest) -> anyhow::Result>; } + +#[cfg(test)] +mod tests { + use anyhow::Result; + use mockall::mock; + + use super::*; + use crate::utils::PreparePath; + + mock! { + pub CommandExec{} + impl CommandExecutor for CommandExec { + fn run_command<'a>(&self, name: &'a str, args: &[&'a str]) -> Result<()>; + fn run_command_with_output<'a>(&self, name: &'a str, args: &[&'a str]) -> Result; + } + impl Clone for CommandExec { + fn clone(&self) -> Self; + } + } + + #[test] + fn test_download_image() { + let req = UpgradeRequest { + version: "KubeOS v2".to_string(), + image_type: "containerd".to_string(), + container_image: "kubeos-temp".to_string(), + check_sum: "22222".to_string(), + image_url: "".to_string(), + flag_safe: false, + mtls: false, + certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, + }; + + let mut mock_executor1 = MockCommandExec::new(); + mock_executor1.expect_run_command().returning(|_, _| Ok(())); + mock_executor1.expect_run_command_with_output().returning(|_, _| Ok(String::new())); + let c_handler = CtrImageHandler::new(PreparePath::default(), mock_executor1); + let image_type = ImageType::Containerd(c_handler); + let result = image_type.download_image(&req); + assert!(result.is_err()); + + let mut mock_executor2 = MockCommandExec::new(); + mock_executor2.expect_run_command().returning(|_, _| Ok(())); + mock_executor2.expect_run_command_with_output().returning(|_, _| Ok(String::new())); + let docker_handler = DockerImageHandler::new(PreparePath::default(), "test".into(), mock_executor2); + let image_type = ImageType::Docker(docker_handler); + let result = image_type.download_image(&req); + assert!(result.is_err()); + + let mut mock_executor3 = MockCommandExec::new(); + mock_executor3.expect_run_command().returning(|_, _| Ok(())); + mock_executor3.expect_run_command_with_output().returning(|_, _| Ok(String::new())); + let disk_handler = DiskImageHandler::new(PreparePath::default(), mock_executor3, "test".into()); + let image_type = ImageType::Disk(disk_handler); + let result = image_type.download_image(&req); + assert!(result.is_err()); + } +} diff --git a/KubeOS-Rust/manager/src/sys_mgmt/config.rs b/KubeOS-Rust/manager/src/sys_mgmt/config.rs index cb5fad1f070b1afa6efe37f155093af7631637bd..48517b4fc10feda1ced2b4ff3e955c5be42ac437 100644 --- a/KubeOS-Rust/manager/src/sys_mgmt/config.rs +++ b/KubeOS-Rust/manager/src/sys_mgmt/config.rs @@ -186,7 +186,7 @@ fn handle_delete_key(config_kv: &Vec<&str>, new_config_info: &KeyInfo) -> String return config_kv.join("="); } info!("Delete configuration {}={}", key, old_value); - String::from("") + String::new() } fn handle_update_key(config_kv: &Vec<&str>, new_config_info: &KeyInfo) -> String { @@ -413,11 +413,20 @@ mod tests { let mut tmp_file = tempfile::NamedTempFile::new().unwrap(); writeln!(tmp_file, "{}", comment).unwrap(); writeln!(tmp_file, "a=0").unwrap(); + writeln!(tmp_file, "d=4").unwrap(); + writeln!(tmp_file, "e=5").unwrap(); + writeln!(tmp_file, "g=7").unwrap(); let kernel_sysctl_persist = KernelSysctlPersist {}; let config_detail = HashMap::from([ ("a".to_string(), KeyInfo { value: "1".to_string(), operation: "".to_string() }), ("b".to_string(), KeyInfo { value: "2".to_string(), operation: "delete".to_string() }), ("c".to_string(), KeyInfo { value: "3".to_string(), operation: "add".to_string() }), + ("d".to_string(), KeyInfo { value: "".to_string(), operation: "".to_string() }), + ("e".to_string(), KeyInfo { value: "".to_string(), operation: "delete".to_string() }), + ("f".to_string(), KeyInfo { value: "".to_string(), operation: "add".to_string() }), + ("g".to_string(), KeyInfo { value: "7".to_string(), operation: "delete".to_string() }), + ("".to_string(), KeyInfo { value: "8".to_string(), operation: "".to_string() }), + ("s=x".to_string(), KeyInfo { value: "8".to_string(), operation: "".to_string() }), ]); let mut config = Sysconfig { model: KERNEL_SYSCTL_PERSIST.to_string(), @@ -426,33 +435,16 @@ mod tests { }; kernel_sysctl_persist.set_config(&mut config).unwrap(); let result = fs::read_to_string(tmp_file.path().to_str().unwrap()).unwrap(); - let expected_res = format!("{}\n{}\n{}\n", comment, "a=1", "c=3"); + let expected_res = format!("{}\n{}\n{}\n{}\n{}\n", comment, "a=1", "d=4", "e=5", "c=3"); assert_eq!(result, expected_res); - - // test config_path is empty - // remember modify DEFAULT_KERNEL_CONFIG_PATH first - // let config_detail = HashMap::from([ - // ( - // "aaa".to_string(), - // KeyInfo { - // value: "3".to_string(), - // operation: "add".to_string(), - // }, - // ), - // ( - // "bbb".to_string(), - // KeyInfo { - // value: "1".to_string(), - // operation: "delete".to_string(), - // }, - // ), - // ]); - // config.config_path = "".to_string(); - // config.contents = config_detail; - // kernel_sysctl_persist.set_config(&mut config).unwrap(); - // let result = fs::read_to_string(crate::sys_mgmt::DEFAULT_KERNEL_CONFIG_PATH).unwrap(); - // let expected_res = format!("{}\n", "aaa=3",); - // assert_eq!(result, expected_res); + let mut config = Sysconfig { + model: KERNEL_SYSCTL_PERSIST.to_string(), + config_path: String::from("/tmp/kubeos-test-kernel-sysctl-persist.txt"), + contents: HashMap::new(), + }; + kernel_sysctl_persist.set_config(&mut config).unwrap(); + assert!(is_file_exist(&config.config_path)); + delete_file_or_dir(&config.config_path).unwrap(); } #[test] @@ -492,7 +484,7 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri initrd /boot/initramfs.img }"; writeln!(tmp_file, "{}", grub_cfg).unwrap(); - let config_first_part = HashMap::from([ + let config_second_part = HashMap::from([ ("debug".to_string(), KeyInfo { value: "".to_string(), operation: "".to_string() }), ("quiet".to_string(), KeyInfo { value: "".to_string(), operation: "delete".to_string() }), ("panic".to_string(), KeyInfo { value: "5".to_string(), operation: "".to_string() }), @@ -506,15 +498,16 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri let mut config = Sysconfig { model: GRUB_CMDLINE_CURRENT.to_string(), config_path: String::new(), - contents: config_first_part, + contents: config_second_part, }; grub_cmdline.set_config(&mut config).unwrap(); grub_cmdline.is_cur_partition = false; - let config_second = HashMap::from([ + let config_first_part = HashMap::from([ ("pci".to_string(), KeyInfo { value: "nomis".to_string(), operation: "".to_string() }), - ("panic".to_string(), KeyInfo { value: "5".to_string(), operation: "".to_string() }), + ("quiet".to_string(), KeyInfo { value: "11".to_string(), operation: "delete".to_string() }), + ("panic".to_string(), KeyInfo { value: "5".to_string(), operation: "update".to_string() }), ]); - config.contents = config_second; + config.contents = config_first_part; config.model = GRUB_CMDLINE_NEXT.to_string(); grub_cmdline.set_config(&mut config).unwrap(); let result = fs::read_to_string(tmp_file.path().to_str().unwrap()).unwrap(); @@ -540,6 +533,11 @@ menuentry 'B' --class KubeOS --class gnu-linux --class gnu --class os --unrestri } "; assert_eq!(result, expected_res); + + // test grub.cfg not exist + grub_cmdline.grub_path = "/tmp/grub-KubeOS-test.cfg".to_string(); + let res = grub_cmdline.set_config(&mut config); + assert!(res.is_err()); } #[test] diff --git a/KubeOS-Rust/manager/src/sys_mgmt/containerd_image.rs b/KubeOS-Rust/manager/src/sys_mgmt/containerd_image.rs index 0b50ad6514588869667e4615206705a1e3910501..dd7036f6c2a7bbfffc3a74527b2307a58087bf4c 100644 --- a/KubeOS-Rust/manager/src/sys_mgmt/containerd_image.rs +++ b/KubeOS-Rust/manager/src/sys_mgmt/containerd_image.rs @@ -48,7 +48,7 @@ impl Default for CtrImageHandler { impl CtrImageHandler { #[cfg(test)] - fn new(paths: PreparePath, executor: T) -> Self { + pub fn new(paths: PreparePath, executor: T) -> Self { Self { paths, executor } } @@ -301,24 +301,4 @@ mod tests { assert!(result.is_ok()); } - - #[test] - #[ignore] - fn test_download_image() { - init(); - let ctr = CtrImageHandler { paths: PreparePath::default(), executor: RealCommandExecutor {} }; - let update_req = UpgradeRequest { - version: "KubeOS v2".to_string(), - image_type: "containerd".to_string(), - container_image: "docker.io/library/busybox:latest".to_string(), - check_sum: "".to_string(), - image_url: "".to_string(), - flag_safe: false, - mtls: false, - certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, - }; - ctr.download_image(&update_req).unwrap(); - let tar_path = "/persist/KubeOS-Update/os.tar"; - assert_eq!(true, Path::new(tar_path).exists()); - } } diff --git a/KubeOS-Rust/manager/src/sys_mgmt/disk_image.rs b/KubeOS-Rust/manager/src/sys_mgmt/disk_image.rs index 4ccb60337645b4b926bc3236d75dfea057694b44..a120db820f81da9eddd915d2f889eac8eef89587 100644 --- a/KubeOS-Rust/manager/src/sys_mgmt/disk_image.rs +++ b/KubeOS-Rust/manager/src/sys_mgmt/disk_image.rs @@ -41,7 +41,7 @@ impl Default for DiskImageHandler { impl DiskImageHandler { #[cfg(test)] - fn new(paths: PreparePath, executor: T, certs_path: String) -> Self { + pub fn new(paths: PreparePath, executor: T, certs_path: String) -> Self { Self { paths, executor, certs_path } } @@ -392,11 +392,14 @@ mod tests { .with_body("This is a test txt file for KubeOS test.\n") .create(); handler.download_image(&upgrade_request).unwrap(); - assert_eq!(true, handler.paths.image_path.exists()); assert_eq!( fs::read(handler.paths.image_path.to_str().unwrap()).unwrap(), "This is a test txt file for KubeOS test.\n".as_bytes() ); + + let _m = mockito::mock("GET", "/test.txt").with_status(404).with_body("Not found").create(); + let res = handler.download_image(&upgrade_request); + assert!(res.is_err()) } } diff --git a/KubeOS-Rust/manager/src/sys_mgmt/docker_image.rs b/KubeOS-Rust/manager/src/sys_mgmt/docker_image.rs index 121e25777da72beaba5bc3082b3a5aeab724f0ec..177dfeb11b7f2aafd9aa04b83864ff1f31947e45 100644 --- a/KubeOS-Rust/manager/src/sys_mgmt/docker_image.rs +++ b/KubeOS-Rust/manager/src/sys_mgmt/docker_image.rs @@ -33,7 +33,7 @@ impl Default for DockerImageHandler { impl DockerImageHandler { #[cfg(test)] - fn new(paths: PreparePath, container_name: String, executor: T) -> Self { + pub fn new(paths: PreparePath, container_name: String, executor: T) -> Self { Self { paths, container_name, executor } } @@ -129,6 +129,8 @@ mod tests { let result = DockerImageHandler::new(PreparePath::default(), "test".into(), mock_executor).check_and_rm_container(); assert!(result.is_ok()); + + assert_eq!(DockerImageHandler::default().container_name, "kubeos-temp"); } #[test] diff --git a/KubeOS-Rust/manager/src/utils/common.rs b/KubeOS-Rust/manager/src/utils/common.rs index 301a8c86af4d8c5e7a8d3d69e6a6a46e4e28e095..da8c8c305de4fc1f76031ef7c8e28a2c5f2b707c 100644 --- a/KubeOS-Rust/manager/src/utils/common.rs +++ b/KubeOS-Rust/manager/src/utils/common.rs @@ -23,14 +23,25 @@ use nix::{mount, mount::MntFlags}; use super::executor::CommandExecutor; use crate::sys_mgmt::{MOUNT_DIR, OS_IMAGE_NAME, PERSIST_DIR, ROOTFS_ARCHIVE, UPDATE_DIR}; +/// * persist_path: /persist +/// +/// * update_path: /persist/KubeOS-Update +/// +/// * mount_path: /persist/KubeOS-Update/kubeos-update +/// +/// * tar_path: /persist/KubeOS-Update/os.tar +/// +/// * image_path: /persist/update.img +/// +/// * rootfs_file: os.tar #[derive(Clone)] pub struct PreparePath { - pub persist_path: PathBuf, // persist_path: /persist - pub update_path: PathBuf, // update_path: /persist/KubeOS-Update - pub mount_path: PathBuf, // mount_path: /persist/KubeOS-Update/kubeos-update - pub tar_path: PathBuf, // tar_path: /persist/KubeOS-Update/os.tar - pub image_path: PathBuf, // image_path: /persist/update.img - pub rootfs_file: String, // rootfs_file: os.tar + pub persist_path: PathBuf, + pub update_path: PathBuf, + pub mount_path: PathBuf, + pub tar_path: PathBuf, + pub image_path: PathBuf, + pub rootfs_file: String, } impl Default for PreparePath { @@ -72,7 +83,7 @@ pub fn check_disk_size>(need_bytes: i64, path: P) -> Result<()> { Ok(()) } -// clean_env will umount the mount path and delete directory /persist/KubeOS-Update and /persist/update.img +/// clean_env will umount the mount path and delete directory /persist/KubeOS-Update and /persist/update.img pub fn clean_env

(update_path: P, mount_path: P, image_path: P) -> Result<()> where P: AsRef, @@ -160,6 +171,7 @@ mod tests { use tempfile::{NamedTempFile, TempDir}; use super::*; + use crate::utils::RealCommandExecutor; // Mock the CommandExecutor trait mock! { @@ -278,10 +290,23 @@ mod tests { } #[test] - #[ignore] fn test_get_boot_mode() { init(); let boot_mode = get_boot_mode(); - assert!(boot_mode == "uefi"); + let executor = RealCommandExecutor {}; + let res = executor.run_command("ls", &["/sys/firmware/efi"]); + if res.is_ok() { + assert!(boot_mode == "uefi"); + } else { + assert!(boot_mode == "bios"); + } + } + + #[test] + fn test_is_command_available() { + init(); + let executor = RealCommandExecutor {}; + assert_eq!(is_command_available("ls", &executor), true); + assert_eq!(is_command_available("aaaabb", &executor), false); } } diff --git a/KubeOS-Rust/manager/src/utils/container_image.rs b/KubeOS-Rust/manager/src/utils/container_image.rs index a54fc19365d256ff05c10f66d191624e978c15d4..7c3aa0222c5354fbbcfbfec4423e895ed2add4d3 100644 --- a/KubeOS-Rust/manager/src/utils/container_image.rs +++ b/KubeOS-Rust/manager/src/utils/container_image.rs @@ -197,12 +197,24 @@ mod tests { let out1 = get_oci_image_digest(container_runtime, image_name, &mock).unwrap(); let expect_output = "1111"; assert_eq!(out1, expect_output); + mock.expect_run_command_with_output().times(1).returning(|_, _| Ok("invalid output".to_string())); + let out2 = get_oci_image_digest(container_runtime, image_name, &mock); + assert!(out2.is_err()); let container_runtime = "crictl"; let command_output2 = "[docker.io/nginx@sha256:1111]"; mock.expect_run_command_with_output().times(1).returning(|_, _| Ok(command_output2.to_string())); - let out2 = get_oci_image_digest(container_runtime, image_name, &mock).unwrap(); - assert_eq!(out2, expect_output); + let out3 = get_oci_image_digest(container_runtime, image_name, &mock).unwrap(); + assert_eq!(out3, expect_output); + + let out4 = get_oci_image_digest("invalid", image_name, &mock); + assert!(out4.is_err()); + + let container_runtime = "crictl"; + let command_output3 = "[docker.io/nginx:sha256:1111]"; + mock.expect_run_command_with_output().times(1).returning(|_, _| Ok(command_output3.to_string())); + let out5 = get_oci_image_digest(container_runtime, image_name, &mock); + assert!(out5.is_err()); } #[test] @@ -211,11 +223,13 @@ mod tests { let mut mock = MockCommandExec::new(); let image_name = "docker.io/nginx:latest"; let container_runtime = "crictl"; - let command_output = "[docker.io/nginx@sha256:1111]"; - let check_sum = "1111"; - mock.expect_run_command_with_output().times(1).returning(|_, _| Ok(command_output.to_string())); + let command_output = "[docker.io/nginx@sha256:1a2b]"; + let check_sum = "1A2B"; + mock.expect_run_command_with_output().times(2).returning(|_, _| Ok(command_output.to_string())); let result = check_oci_image_digest(container_runtime, image_name, check_sum, &mock); assert!(result.is_ok()); + let result = check_oci_image_digest(container_runtime, image_name, "1111", &mock); + assert!(result.is_err()); } #[test] @@ -251,4 +265,26 @@ mod tests { let result = pull_image("aaa", image_name, &mock_executor); assert!(result.is_err()); } + + #[test] + fn test_remove_image_if_exist() { + init(); + let mut mock_executor = MockCommandExec::new(); + mock_executor + .expect_run_command_with_output() + .withf(|cmd, args| cmd == "ctr" && args.contains(&"check")) // simplified with a closure + .times(1) + .returning(|_, _| Ok(String::from("something"))); + mock_executor + .expect_run_command() + .withf(|cmd, args| cmd == "ctr" && args.contains(&"rm")) // simplified with a closure + .times(1) + .returning(|_, _| Ok(())); + let image_name = "docker.io/nginx:latest"; + let res = remove_image_if_exist("ctr", image_name, &mock_executor); + assert!(res.is_ok()); + + let res = remove_image_if_exist("invalid", image_name, &mock_executor); + assert!(res.is_err()); + } } diff --git a/KubeOS-Rust/manager/src/utils/partition.rs b/KubeOS-Rust/manager/src/utils/partition.rs index 0419159b534e6e0ea0ed81e294dca16588733dba..fcfa2d8b87ff7e4e8cf893114dedfc55f3132675 100644 --- a/KubeOS-Rust/manager/src/utils/partition.rs +++ b/KubeOS-Rust/manager/src/utils/partition.rs @@ -22,6 +22,7 @@ pub struct PartitionInfo { pub fs_type: String, } +/// get_partition_info returns the current partition info and the next partition info. pub fn get_partition_info(executor: &T) -> Result<(PartitionInfo, PartitionInfo), anyhow::Error> { let lsblk = executor.run_command_with_output("lsblk", &["-lno", "NAME,MOUNTPOINTS,FSTYPE"])?; // After split whitespace, the root directory line should have 3 elements, which are "sda2 / ext4". @@ -93,5 +94,19 @@ mod tests { PartitionInfo { device: "/dev/sda3".to_string(), menuentry: "B".to_string(), fs_type: "ext4".to_string() }, ); assert_eq!(res, expect_res); + + let command_output2 = "sda\nsda1 /boot/efi vfat\nsda2 ext4\nsda3 / ext4\nsda4 /persist ext4\nsr0 iso9660\n"; + mock.expect_run_command_with_output().times(1).returning(|_, _| Ok(command_output2.to_string())); + let res = get_partition_info(&mock).unwrap(); + let expect_res = ( + PartitionInfo { device: "/dev/sda3".to_string(), menuentry: "B".to_string(), fs_type: "ext4".to_string() }, + PartitionInfo { device: "/dev/sda2".to_string(), menuentry: "A".to_string(), fs_type: "ext4".to_string() }, + ); + assert_eq!(res, expect_res); + + let command_output3 = ""; + mock.expect_run_command_with_output().times(1).returning(|_, _| Ok(command_output3.to_string())); + let res = get_partition_info(&mock); + assert!(res.is_err()); } }