diff --git a/src/runtime-rs/Cargo.lock b/src/runtime-rs/Cargo.lock index 428b8047d0..5741c12084 100644 --- a/src/runtime-rs/Cargo.lock +++ b/src/runtime-rs/Cargo.lock @@ -2153,6 +2153,7 @@ dependencies = [ "serde", "slog", "slog-scope", + "test-utils", "tokio", "uuid", ] @@ -2588,6 +2589,13 @@ dependencies = [ "winapi", ] +[[package]] +name = "test-utils" +version = "0.1.0" +dependencies = [ + "nix 0.24.2", +] + [[package]] name = "tests_utils" version = "0.1.0" diff --git a/src/runtime-rs/crates/resource/Cargo.toml b/src/runtime-rs/crates/resource/Cargo.toml index 408baf522b..e17af442f4 100644 --- a/src/runtime-rs/crates/resource/Cargo.toml +++ b/src/runtime-rs/crates/resource/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["The Kata Containers community "] edition = "2018" +[dev-dependencies] +test-utils = { path = "../../../libs/test-utils" } + [dependencies] anyhow = "^1.0" async-trait = "0.1.48" diff --git a/src/runtime-rs/crates/resource/src/network/endpoint/endpoints_test.rs b/src/runtime-rs/crates/resource/src/network/endpoint/endpoints_test.rs index 90623d59cb..92662a0d00 100644 --- a/src/runtime-rs/crates/resource/src/network/endpoint/endpoints_test.rs +++ b/src/runtime-rs/crates/resource/src/network/endpoint/endpoints_test.rs @@ -20,6 +20,7 @@ mod tests { NetworkModelType, TC_FILTER_NET_MODEL_STR, }, network_pair::{NetworkInterface, NetworkPair, TapInterface}, + utils::link::net_test_utils::delete_link, }; // this unit test tests the integrity of MacVlanEndpoint::new() @@ -124,14 +125,10 @@ mod tests { } assert_eq!(manual.net_pair.network_qos, result.net_pair.network_qos); } - let link_index = fetch_index(&handle, manual_vlan_iface_name.as_str()) + assert!(delete_link(&handle, manual_vlan_iface_name.as_str()) .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); - let link_index = fetch_index(&handle, tap_iface_name.as_str()) - .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); + .is_ok()); + assert!(delete_link(&handle, tap_iface_name.as_str()).await.is_ok()); assert!(handle.link().del(dummy_index).execute().await.is_ok()); } } @@ -253,14 +250,10 @@ mod tests { assert_eq!(manual.net_pair.network_qos, result.net_pair.network_qos); } // delete the manually created links - let link_index = fetch_index(&handle, manual_macvlan_iface_name.as_str()) + assert!(delete_link(&handle, manual_macvlan_iface_name.as_str()) .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); - let link_index = fetch_index(&handle, tap_iface_name.as_str()) - .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); + .is_ok()); + assert!(delete_link(&handle, tap_iface_name.as_str()).await.is_ok()); assert!(handle.link().del(dummy_index).execute().await.is_ok()); } } @@ -355,14 +348,10 @@ mod tests { } assert_eq!(manual.net_pair.network_qos, result.net_pair.network_qos); } - let link_index = fetch_index(&handle, manual_virt_iface_name.as_str()) + assert!(delete_link(&handle, manual_virt_iface_name.as_str()) .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); - let link_index = fetch_index(&handle, tap_iface_name.as_str()) - .await - .expect("failed to fetch index"); - assert!(handle.link().del(link_index).execute().await.is_ok()); + .is_ok()); + assert!(delete_link(&handle, tap_iface_name.as_str()).await.is_ok()); } } } diff --git a/src/runtime-rs/crates/resource/src/network/network_pair.rs b/src/runtime-rs/crates/resource/src/network/network_pair.rs index c96898619b..1bee220fe9 100644 --- a/src/runtime-rs/crates/resource/src/network/network_pair.rs +++ b/src/runtime-rs/crates/resource/src/network/network_pair.rs @@ -177,3 +177,85 @@ pub async fn get_link_by_name( Ok(link::get_link_from_message(msg)) } + +#[cfg(test)] +mod tests { + use scopeguard::defer; + + use super::*; + use crate::network::network_model::TC_FILTER_NET_MODEL_STR; + use test_utils::skip_if_not_root; + use utils::link::net_test_utils::delete_link; + + // this ut tests create_link() and get_link_by_name() + #[actix_rt::test] + async fn test_utils() { + skip_if_not_root!(); + + if let Ok((conn, handle, _)) = + rtnetlink::new_connection().context("failed to create netlink connection") + { + let thread_handler = tokio::spawn(conn); + defer!({ + thread_handler.abort(); + }); + + assert!(create_link(&handle, "kata_test_1", 2).await.is_ok()); + assert!(create_link(&handle, "kata_test_2", 3).await.is_ok()); + assert!(create_link(&handle, "kata_test_3", 4).await.is_ok()); + + assert!(get_link_by_name(&handle, "kata_test_1").await.is_ok()); + assert!(get_link_by_name(&handle, "kata_test_2").await.is_ok()); + assert!(get_link_by_name(&handle, "kata_test_3").await.is_ok()); + + assert!(delete_link(&handle, "kata_test_1").await.is_ok()); + assert!(delete_link(&handle, "kata_test_2").await.is_ok()); + assert!(delete_link(&handle, "kata_test_3").await.is_ok()); + + assert!(get_link_by_name(&handle, "kata_test_1").await.is_err()); + assert!(get_link_by_name(&handle, "kata_test_2").await.is_err()); + assert!(get_link_by_name(&handle, "kata_test_3").await.is_err()); + } + } + + #[actix_rt::test] + async fn test_network_pair() { + let idx = 123456; + let virt_iface_name = format!("eth{}", idx); + let tap_name = format!("tap{}{}", idx, TAP_SUFFIX); + let queues = 2; + let model = TC_FILTER_NET_MODEL_STR; + + skip_if_not_root!(); + + if let Ok((conn, handle, _)) = + rtnetlink::new_connection().context("failed to create netlink connection") + { + let thread_handler = tokio::spawn(conn); + defer!({ + thread_handler.abort(); + }); + // the network pair has not been created + assert!(get_link_by_name(&handle, virt_iface_name.as_str()) + .await + .is_err()); + + // mock containerd to create one end of the network pair + assert!(create_link(&handle, virt_iface_name.as_str(), queues) + .await + .is_ok()); + + if let Ok(_pair) = NetworkPair::new(&handle, idx, "", model, queues).await { + // the pair is created, we can find the two ends of network pair + assert!(get_link_by_name(&handle, virt_iface_name.as_str()) + .await + .is_ok()); + assert!(get_link_by_name(&handle, tap_name.as_str()).await.is_ok()); + + //delete the link created in test + assert!(delete_link(&handle, virt_iface_name.as_str()).await.is_ok()); + assert!(delete_link(&handle, tap_name.as_str()).await.is_ok()); + } + } + } +} diff --git a/src/runtime-rs/crates/resource/src/network/utils/address.rs b/src/runtime-rs/crates/resource/src/network/utils/address.rs index 0484c9f364..ef3b68278c 100644 --- a/src/runtime-rs/crates/resource/src/network/utils/address.rs +++ b/src/runtime-rs/crates/resource/src/network/utils/address.rs @@ -85,3 +85,30 @@ pub(crate) fn parse_ip(ip: &[u8], family: u8) -> Result { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_ip() { + let test_ipv4 = [10, 25, 64, 128]; + let ipv4 = parse_ip(test_ipv4.as_slice(), AF_INET as u8).unwrap(); + let expected_ipv4 = IpAddr::V4(Ipv4Addr::new(10, 25, 64, 128)); + assert_eq!(ipv4, expected_ipv4); + + let test_ipv6 = [0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0]; + let ipv6 = parse_ip(test_ipv6.as_slice(), AF_INET6 as u8).unwrap(); + // two u8 => one u16, (0u8, 2u8 => 0x0002), (4u8, 0u8 => 0x0400) + let expected_ipv6 = IpAddr::V6(Ipv6Addr::new( + 0x0002, 0x0400, 0x0002, 0x0400, 0x0002, 0x0400, 0x0002, 0x0400, + )); + assert_eq!(ipv6, expected_ipv6); + + let fail_ipv4 = [10, 22, 33, 44, 55]; + assert!(parse_ip(fail_ipv4.as_slice(), AF_INET as u8).is_err()); + + let fail_ipv6 = [1, 2, 3, 4, 5, 6, 7, 8, 2, 3]; + assert!(parse_ip(fail_ipv6.as_slice(), AF_INET6 as u8).is_err()); + } +} diff --git a/src/runtime-rs/crates/resource/src/network/utils/link/create.rs b/src/runtime-rs/crates/resource/src/network/utils/link/create.rs index 06bedf79b9..58b2016aa2 100644 --- a/src/runtime-rs/crates/resource/src/network/utils/link/create.rs +++ b/src/runtime-rs/crates/resource/src/network/utils/link/create.rs @@ -127,3 +127,63 @@ fn create_queue(name: &str, flags: libc::c_int) -> Result<(File, String)> { }; Ok((file, req.get_name()?)) } + +#[cfg(test)] +pub mod net_test_utils { + use crate::network::network_model::tc_filter_model::fetch_index; + + // remove a link by its name + #[allow(dead_code)] + pub async fn delete_link( + handle: &rtnetlink::Handle, + name: &str, + ) -> Result<(), rtnetlink::Error> { + let link_index = fetch_index(handle, name) + .await + .expect("failed to fetch index"); + // the ifindex of a link will not change during its lifetime, so the index + // remains the same between the query above and the deletion below + handle.link().del(link_index).execute().await + } +} + +#[cfg(test)] +mod tests { + use scopeguard::defer; + use test_utils::skip_if_not_root; + + use crate::network::{ + network_pair::get_link_by_name, utils::link::create::net_test_utils::delete_link, + }; + + use super::*; + + #[actix_rt::test] + async fn test_create_link() { + let name_tun = "___test_tun"; + let name_tap = "___test_tap"; + + // tests should be taken under root + skip_if_not_root!(); + + if let Ok((conn, handle, _)) = + rtnetlink::new_connection().context("failed to create netlink connection") + { + let thread_handler = tokio::spawn(conn); + defer!({ + thread_handler.abort(); + }); + + assert!(create_link(name_tun, LinkType::Tun, 2).is_ok()); + assert!(create_link(name_tap, LinkType::Tap, 2).is_ok()); + assert!(get_link_by_name(&handle, name_tap).await.is_ok()); + assert!(get_link_by_name(&handle, name_tun).await.is_ok()); + assert!(delete_link(&handle, name_tun).await.is_ok()); + assert!(delete_link(&handle, name_tap).await.is_ok()); + + // link does not present + assert!(get_link_by_name(&handle, name_tun).await.is_err()); + assert!(get_link_by_name(&handle, name_tap).await.is_err()); + } + } +} diff --git a/src/runtime-rs/crates/resource/src/network/utils/link/mod.rs b/src/runtime-rs/crates/resource/src/network/utils/link/mod.rs index 9fcc2b6405..aa5c2631b1 100644 --- a/src/runtime-rs/crates/resource/src/network/utils/link/mod.rs +++ b/src/runtime-rs/crates/resource/src/network/utils/link/mod.rs @@ -16,6 +16,9 @@ use std::os::unix::io::RawFd; use netlink_packet_route::link::nlas::State; +#[cfg(test)] +pub use create::net_test_utils; + #[derive(Debug, PartialEq, Eq, Clone)] pub enum Namespace { NetNsPid(u32), diff --git a/src/runtime-rs/crates/resource/src/network/utils/mod.rs b/src/runtime-rs/crates/resource/src/network/utils/mod.rs index 574178c3de..74635a5d9b 100644 --- a/src/runtime-rs/crates/resource/src/network/utils/mod.rs +++ b/src/runtime-rs/crates/resource/src/network/utils/mod.rs @@ -33,3 +33,34 @@ pub(crate) fn get_mac_addr(b: &[u8]) -> Result { )) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_mac_addr() { + // length is not 6 + let fail_slice = vec![1, 2, 3]; + assert!(get_mac_addr(&fail_slice).is_err()); + + let expected_slice = vec![10, 11, 128, 3, 4, 5]; + let expected_mac = String::from("0a:0b:80:03:04:05"); + let res = get_mac_addr(&expected_slice); + assert!(res.is_ok()); + assert_eq!(expected_mac, res.unwrap()); + } + + #[test] + fn test_parse_mac() { + // length is not 6 + let fail = "1:2:3"; + assert!(parse_mac(fail).is_none()); + + let v = [10, 11, 128, 3, 4, 5]; + let expected_addr = hypervisor::Address(v); + let addr = parse_mac("0a:0b:80:03:04:05"); + assert!(addr.is_some()); + assert_eq!(expected_addr.0, addr.unwrap().0); + } +} diff --git a/src/runtime-rs/crates/resource/src/network/utils/netns.rs b/src/runtime-rs/crates/resource/src/network/utils/netns.rs index a2a29dc971..c0d0306fef 100644 --- a/src/runtime-rs/crates/resource/src/network/utils/netns.rs +++ b/src/runtime-rs/crates/resource/src/network/utils/netns.rs @@ -49,3 +49,22 @@ impl Drop for NetnsGuard { } } } + +#[cfg(test)] +mod tests { + use super::*; + use test_utils::skip_if_not_root; + + #[test] + fn test_new_netns_guard() { + // test run under root + skip_if_not_root!(); + + let new_netns_path = "/proc/1/task/1/ns/net"; // systemd, always exists + let netns_guard = NetnsGuard::new(new_netns_path).unwrap(); + drop(netns_guard); + + let empty_path = ""; + assert!(NetnsGuard::new(empty_path).unwrap().old_netns.is_none()); + } +}