From edf5d68d0afdd9f1607f57ee470f533700958915 Mon Sep 17 00:00:00 2001 From: Samuel Dai Date: Wed, 16 Apr 2025 17:01:44 +0800 Subject: [PATCH] feat(cni): add unit test for commands (#70) --- Cargo.lock | 218 +++++++++++++++++++++++++++++++------- crates/cni/Cargo.toml | 2 + crates/cni/src/command.rs | 92 ++++++++++++++++ crates/cni/src/lib.rs | 151 ++++---------------------- crates/cni/src/netns.rs | 32 ++++++ crates/cni/src/util.rs | 97 +++++++++++++++++ crates/service/src/lib.rs | 13 +-- 7 files changed, 427 insertions(+), 178 deletions(-) create mode 100644 crates/cni/src/command.rs create mode 100644 crates/cni/src/netns.rs create mode 100644 crates/cni/src/util.rs diff --git a/Cargo.lock b/Cargo.lock index 590e5d6..86a41d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -182,7 +182,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -252,6 +252,56 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.95" @@ -283,7 +333,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -294,7 +344,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -517,7 +567,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "cni" version = "0.1.0" dependencies = [ + "defer", "dotenv", + "env_logger 0.11.8", "lazy_static", "log", "my-workspace-hack", @@ -525,6 +577,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "config" version = "0.11.0" @@ -672,7 +730,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -694,9 +752,15 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.96", + "syn 2.0.100", ] +[[package]] +name = "defer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" + [[package]] name = "deranged" version = "0.3.11" @@ -724,7 +788,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -734,7 +798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -747,7 +811,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -768,7 +832,7 @@ dependencies = [ "convert_case 0.7.1", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "unicode-xid", ] @@ -790,7 +854,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -814,6 +878,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -827,6 +901,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -849,7 +936,7 @@ version = "0.1.0" dependencies = [ "actix-web", "dotenv", - "env_logger", + "env_logger 0.10.2", "log", "my-workspace-hack", "provider", @@ -967,7 +1054,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1042,7 +1129,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1425,7 +1512,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1498,6 +1585,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.13.0" @@ -1513,6 +1606,30 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "jiff" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde 1.0.217", +] + +[[package]] +name = "jiff-static" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "jobserver" version = "0.1.32" @@ -1688,7 +1805,7 @@ dependencies = [ "regex-syntax", "serde 1.0.217", "smallvec", - "syn 2.0.96", + "syn 2.0.100", "tokio", "tokio-util", "tower 0.4.13", @@ -1824,7 +1941,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1911,7 +2028,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1952,7 +2069,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -1973,6 +2090,21 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1995,7 +2127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2017,7 +2149,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2070,7 +2202,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.96", + "syn 2.0.100", "tempfile", ] @@ -2084,7 +2216,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2393,7 +2525,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2448,7 +2580,7 @@ version = "0.1.0" dependencies = [ "cni", "containerd-client", - "env_logger", + "env_logger 0.10.2", "handlebars", "hex", "lazy_static", @@ -2565,7 +2697,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2581,9 +2713,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -2610,7 +2742,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2683,7 +2815,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2694,7 +2826,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2764,7 +2896,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2851,7 +2983,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2920,7 +3052,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -2991,6 +3123,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.15.1" @@ -3058,7 +3196,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -3093,7 +3231,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3356,7 +3494,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "synstructure", ] @@ -3378,7 +3516,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] @@ -3398,7 +3536,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", "synstructure", ] @@ -3421,7 +3559,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.100", ] [[package]] diff --git a/crates/cni/Cargo.toml b/crates/cni/Cargo.toml index d45cbf6..61ac459 100644 --- a/crates/cni/Cargo.toml +++ b/crates/cni/Cargo.toml @@ -12,3 +12,5 @@ log = "0.4.27" dotenv = "0.15.0" netns-rs = "0.1.0" lazy_static = "1.4.0" +env_logger = "0.11.8" +defer = "0.2.1" diff --git a/crates/cni/src/command.rs b/crates/cni/src/command.rs new file mode 100644 index 0000000..ba84c0c --- /dev/null +++ b/crates/cni/src/command.rs @@ -0,0 +1,92 @@ +use std::{ + io::Error, + process::{Command, Output}, +}; + +lazy_static::lazy_static! { + static ref CNI_BIN_DIR: String = + std::env::var("CNI_BIN_DIR").expect("Environment variable CNI_BIN_DIR is not set"); + static ref CNI_TOOL: String = + std::env::var("CNI_TOOL").expect("Environment variable CNI_TOOL is not set"); +} + +#[inline(always)] +fn netns_path(netns: &str) -> String { + "/var/run/netns/".to_string() + netns +} + +pub(super) fn cni_add_bridge(netns: &str, bridge_network_name: &str) -> Result { + Command::new(CNI_TOOL.as_str()) + .arg("add") + .arg(bridge_network_name) + .arg(netns_path(netns)) + .env("CNI_PATH", CNI_BIN_DIR.as_str()) + .output() +} + +pub(super) fn cni_del_bridge(netns: &str, bridge_network_name: &str) -> Result { + Command::new(CNI_TOOL.as_str()) + .arg("del") + .arg(bridge_network_name) + .arg(netns_path(netns)) + .env("CNI_PATH", CNI_BIN_DIR.as_str()) + .output() +} + +/// THESE TESTS SHOULD BE RUN WITH ROOT PRIVILEGES +#[cfg(test)] +mod test { + use crate::{netns, util}; + use std::path::Path; + + use super::*; + + const CNI_DATA_DIR: &str = "/var/run/cni"; + const TEST_CNI_CONF_FILENAME: &str = "11-faasrstest.conflist"; + const TEST_NETWORK_NAME: &str = "faasrstest-cni-bridge"; + const TEST_BRIDGE_NAME: &str = "faasrstest0"; + const TEST_SUBNET: &str = "10.99.0.0/16"; + const CNI_CONF_DIR: &str = "/etc/cni/net.d"; + + fn init_test_net_fs() { + crate::util::init_net_fs( + Path::new(CNI_CONF_DIR), + TEST_CNI_CONF_FILENAME, + TEST_NETWORK_NAME, + TEST_BRIDGE_NAME, + TEST_SUBNET, + CNI_DATA_DIR, + ) + .unwrap() + } + + #[test] + #[ignore] + fn test_cni_resource() { + dotenv::dotenv().unwrap(); + env_logger::init_from_env(env_logger::Env::new().default_filter_or("trace")); + init_test_net_fs(); + let netns = util::netns_from_cid_and_cns("123456", "cns"); + + netns::create(&netns).unwrap(); + defer::defer!({ + let _ = netns::remove(&netns); + }); + + let result = cni_add_bridge(&netns, TEST_NETWORK_NAME); + log::debug!("add CNI result: {:?}", result); + assert!( + result.is_ok_and(|output| output.status.success()), + "Failed to add CNI" + ); + + defer::defer!({ + let result = cni_del_bridge(&netns, TEST_NETWORK_NAME); + log::debug!("del CNI result: {:?}", result); + assert!( + result.is_ok_and(|output| output.status.success()), + "Failed to delete CNI" + ); + }); + } +} diff --git a/crates/cni/src/lib.rs b/crates/cni/src/lib.rs index 4150ad1..5f1a82e 100644 --- a/crates/cni/src/lib.rs +++ b/crates/cni/src/lib.rs @@ -1,23 +1,17 @@ type Err = Box; use lazy_static::lazy_static; -use netns_rs::NetNs; use serde_json::Value; -use std::{ - fmt::Error, - fs::{self, File}, - io::Write, - net::IpAddr, - path::Path, -}; +use std::{fmt::Error, net::IpAddr, path::Path}; + +mod command; +mod netns; +mod util; +use command as cmd; lazy_static! { - static ref CNI_BIN_DIR: String = - std::env::var("CNI_BIN_DIR").expect("Environment variable CNI_BIN_DIR is not set"); static ref CNI_CONF_DIR: String = std::env::var("CNI_CONF_DIR").expect("Environment variable CNI_CONF_DIR is not set"); - static ref CNI_TOOL: String = - std::env::var("CNI_TOOL").expect("Environment variable CNI_TOOL is not set"); } // const NET_NS_PATH_FMT: &str = "/proc/{}/ns/net"; @@ -28,74 +22,25 @@ const DEFAULT_BRIDGE_NAME: &str = "faasrs0"; const DEFAULT_SUBNET: &str = "10.66.0.0/16"; // const DEFAULT_IF_PREFIX: &str = "eth"; -fn default_cni_conf() -> String { - format!( - r#" -{{ - "cniVersion": "0.4.0", - "name": "{}", - "plugins": [ - {{ - "type": "bridge", - "bridge": "{}", - "isGateway": true, - "ipMasq": true, - "ipam": {{ - "type": "host-local", - "subnet": "{}", - "dataDir": "{}", - "routes": [ - {{ "dst": "0.0.0.0/0" }} - ] - }} - }}, - {{ - "type": "firewall" - }} - ] -}} -"#, - DEFAULT_NETWORK_NAME, DEFAULT_BRIDGE_NAME, DEFAULT_SUBNET, CNI_DATA_DIR +pub fn init_net_work() -> Result<(), Err> { + util::init_net_fs( + Path::new(CNI_CONF_DIR.as_str()), + DEFAULT_CNI_CONF_FILENAME, + DEFAULT_NETWORK_NAME, + DEFAULT_BRIDGE_NAME, + DEFAULT_SUBNET, + CNI_DATA_DIR, ) } -pub fn init_net_work() -> Result<(), Err> { - let cni_conf_dir = CNI_CONF_DIR.as_str(); - if !dir_exists(Path::new(cni_conf_dir)) { - fs::create_dir_all(cni_conf_dir)?; - } - let net_config = Path::new(cni_conf_dir).join(DEFAULT_CNI_CONF_FILENAME); - let mut file = File::create(&net_config)?; - file.write_all(default_cni_conf().as_bytes())?; - - Ok(()) -} - -fn get_netns(ns: &str, cid: &str) -> String { - format!("{}-{}", ns, cid) -} - -fn get_path(netns: &str) -> String { - format!("/var/run/netns/{}", netns) -} - //TODO: 创建网络和删除网络的错误处理 -pub fn create_cni_network(cid: String, ns: String) -> Result<(String, String), Err> { - // let netid = format!("{}-{}", cid, pid); - let netns = get_netns(ns.as_str(), cid.as_str()); - let path = get_path(netns.as_str()); +pub fn create_cni_network(cid: String, ns: String) -> Result { + let netns = util::netns_from_cid_and_cns(&cid, &ns); let mut ip = String::new(); - create_netns(&netns); + netns::create(&netns)?; - let bin = CNI_BIN_DIR.as_str(); - let cnitool = CNI_TOOL.as_str(); - let output = std::process::Command::new(cnitool) - .arg("add") - .arg("faasrs-cni-bridge") - .arg(&path) - .env("CNI_PATH", bin) - .output(); + let output = cmd::cni_add_bridge(netns.as_str(), DEFAULT_NETWORK_NAME); match output { Ok(output) => { @@ -124,55 +69,14 @@ pub fn create_cni_network(cid: String, ns: String) -> Result<(String, String), E } } - Ok((ip, path)) + Ok(ip) } pub fn delete_cni_network(ns: &str, cid: &str) { - let netns = get_netns(ns, cid); - let path = get_path(&netns); - let bin = CNI_BIN_DIR.as_str(); - let cnitool = CNI_TOOL.as_str(); + let netns = util::netns_from_cid_and_cns(cid, ns); - let _output_del = std::process::Command::new(cnitool) - .arg("del") - .arg("faasrs-cni-bridge") - .arg(&path) - .env("CNI_PATH", bin) - .output(); - delete_netns(&netns); -} - -fn create_netns(namespace_name: &str) { - match NetNs::new(namespace_name) { - Ok(ns) => { - log::info!("Created netns: {}", ns); - } - Err(e) => { - log::error!("Error creating netns: {}", e); - } - } -} - -fn delete_netns(namespace_name: &str) { - match NetNs::get(namespace_name) { - Ok(ns) => { - ns.remove() - .map_err(|e| log::error!("Error deleting netns: {}", e)) - .unwrap(); - log::info!("Deleted netns: {}", namespace_name); - } - Err(e) => { - log::error!("Error getting netns: {}, NotFound", e); - } - } -} - -fn dir_exists(dirname: &Path) -> bool { - path_exists(dirname).is_some_and(|info| info.is_dir()) -} - -fn path_exists(path: &Path) -> Option { - fs::metadata(path).ok() + let _ = cmd::cni_del_bridge(&netns, DEFAULT_NETWORK_NAME); + let _ = netns::remove(&netns); } #[allow(unused)] @@ -185,14 +89,3 @@ fn cni_gateway() -> Result { } Err(Box::new(Error)) } - -#[allow(unused)] -fn dir_empty(dirname: &Path) -> bool { - if !dir_exists(dirname) { - return false; - } - match fs::read_dir(dirname) { - Ok(mut entries) => entries.next().is_none(), - Err(_) => false, - } -} diff --git a/crates/cni/src/netns.rs b/crates/cni/src/netns.rs new file mode 100644 index 0000000..766ac35 --- /dev/null +++ b/crates/cni/src/netns.rs @@ -0,0 +1,32 @@ +use netns_rs::{Error, NetNs}; + +pub(super) fn create(netns: &str) -> Result { + NetNs::new(netns) +} + +pub(super) fn remove(netns: &str) -> Result<(), Error> { + match NetNs::get(netns) { + Ok(ns) => { + ns.remove()?; + Ok(()) + } + Err(e) => { + log::error!("Failed to get netns {}: {}", netns, e); + Err(e) + } + } +} + +/// THESE TESTS SHOULD BE RUN WITH ROOT PRIVILEGES +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[ignore] + fn test_create_and_remove() { + let netns_name = "test_netns"; + create(netns_name).unwrap(); + assert!(remove(netns_name).is_ok()); + } +} diff --git a/crates/cni/src/util.rs b/crates/cni/src/util.rs new file mode 100644 index 0000000..6dac047 --- /dev/null +++ b/crates/cni/src/util.rs @@ -0,0 +1,97 @@ +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; + +static mut CNI_CONFIG_FILE: Option = None; + +/// Generate "cns-cid" +#[inline(always)] +pub fn netns_from_cid_and_cns(cid: &str, cns: &str) -> String { + format!("{}-{}", cns, cid) +} + +pub fn init_net_fs( + conf_dir: &Path, + conf_filename: &str, + net_name: &str, + bridge: &str, + subnet: &str, + data_dir: &str, +) -> Result<(), Box> { + let conf_file = CniConfFile::new(conf_dir, conf_filename, net_name, bridge, subnet, data_dir)?; + unsafe { + CNI_CONFIG_FILE = Some(conf_file); + } + Ok(()) +} + +fn cni_conf(name: &str, bridge: &str, subnet: &str, data_dir: &str) -> String { + format!( + r#" +{{ + "cniVersion": "0.4.0", + "name": "{}", + "plugins": [ + {{ + "type": "bridge", + "bridge": "{}", + "isGateway": true, + "ipMasq": true, + "ipam": {{ + "type": "host-local", + "subnet": "{}", + "dataDir": "{}", + "routes": [ + {{ "dst": "0.0.0.0/0" }} + ] + }} + }}, + {{ + "type": "firewall" + }} + ] +}} +"#, + name, bridge, subnet, data_dir + ) +} + +struct CniConfFile { + conf_dir: PathBuf, + conf_filename: String, +} + +impl CniConfFile { + fn new( + conf_dir: &Path, + conf_filename: &str, + net_name: &str, + bridge: &str, + subnet: &str, + data_dir: &str, + ) -> Result> { + if !conf_dir.exists() { + std::fs::create_dir_all(conf_dir)?; + } + if !conf_dir.is_dir() { + log::error!("CNI_CONF_DIR is not a directory"); + panic!("CNI_CONF_DIR is not a directory"); + } + let net_config = conf_dir.join(conf_filename); + File::create(&net_config)? + .write_all(cni_conf(net_name, bridge, subnet, data_dir).as_bytes())?; + Ok(Self { + conf_dir: conf_dir.to_path_buf(), + conf_filename: conf_filename.to_string(), + }) + } +} + +impl Drop for CniConfFile { + fn drop(&mut self) { + let net_config = self.conf_dir.join(&self.conf_filename); + if net_config.exists() { + std::fs::remove_file(&net_config).unwrap(); + } + } +} diff --git a/crates/service/src/lib.rs b/crates/service/src/lib.rs index 9aa741a..79b0411 100644 --- a/crates/service/src/lib.rs +++ b/crates/service/src/lib.rs @@ -232,9 +232,9 @@ impl Service { log::info!("drop sc ok"); let _ = cni::init_net_work(); log::info!("init_net_work ok"); - let (ip, path) = cni::create_cni_network(cid.to_string(), ns.to_string())?; + let ip = cni::create_cni_network(cid.to_string(), ns.to_string())?; let ports = ImageManager::get_runtime_config(img_name).unwrap().ports; - let network_config = NetworkConfig::new(path, ip, ports); + let network_config = NetworkConfig::new(ip, ports); log::info!("create_cni_network ok"); self.save_network_config(cid, network_config.clone()).await; log::info!("save_netns_ip ok, netconfig: {:?}", network_config); @@ -474,18 +474,13 @@ impl Service { #[derive(Debug, Clone)] pub struct NetworkConfig { - netns: String, ip: String, ports: Vec, } impl NetworkConfig { - pub fn new(netns: String, ip: String, ports: Vec) -> Self { - NetworkConfig { netns, ip, ports } - } - - pub fn get_netns(&self) -> String { - self.netns.clone() + pub fn new(ip: String, ports: Vec) -> Self { + NetworkConfig { ip, ports } } pub fn get_ip(&self) -> String {