From 192ac818adc8edcc56807af1765f1e81983c525f Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Tue, 24 Sep 2024 15:08:23 -0700 Subject: [PATCH 01/22] Partially implement the container script in Rust This handles most use cases for local containers. For Kubernetes containers, there's very limited handling in the start function, and pretty much nothing else. Signed-off-by: Saikrishna Arcot --- Cargo.toml | 3 +- crates/container/Cargo.toml | 20 +++ crates/container/src/main.rs | 287 +++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 crates/container/Cargo.toml create mode 100644 crates/container/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 5cca0d0..46c9d38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ members = [ "crates/swbus-core", "crates/swbusd", "crates/hamgrd", - "crates/swss-common" + "crates/swss-common", + "crates/container" ] exclude = [] diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml new file mode 100644 index 0000000..92f3cc7 --- /dev/null +++ b/crates/container/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "container" +version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +documentation.workspace = true +keywords.workspace = true +edition.workspace = true + +[dependencies] +bollard = { version = "0.17.1", features = ["chrono"] } +chrono = "0.4.38" +enumset = "1.1.5" +futures-util = "0.3.30" +swss-common = { version = "0.1.0", path = "../swss-common" } +tokio.workspace = true + +[lints] +workspace = true diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs new file mode 100644 index 0000000..1a2c8e0 --- /dev/null +++ b/crates/container/src/main.rs @@ -0,0 +1,287 @@ +use swss_common::DbConnector; +use std::collections::HashMap; +use clap::{Parser, ValueEnum}; +use chrono::Local; +use bollard::Docker; +use bollard::container::*; +use futures_util::stream::TryStreamExt; +use enumset; + +struct DbConnections { + config_db : DbConnector, + state_db : DbConnector, + remote_ctr_enabled : bool, +} + +// DB field names +const FEATURE_TABLE: &str = "FEATURE"; +const SET_OWNER: &str = "set_owner"; +const NO_FALLBACK: &str = "no_fallback_to_local"; + +const CURRENT_OWNER: &str = "current_owner"; +const UPD_TIMESTAMP: &str = "update_time"; +const CONTAINER_ID: &str = "container_id"; +const REMOTE_STATE: &str = "remote_state"; +const VERSION: &str = "container_version"; +const SYSTEM_STATE: &str = "system_state"; +const STATE: &str = "state"; +const ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; + +const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +const KUBE_LABEL_SET_KEY: &str = "SET"; +const SERVER_TABLE: &str = "KUBERNETES_MASTER"; +const SERVER_KEY: &str = "SERVER"; +const ST_SER_CONNECTED: &str = "connected"; +const ST_SER_UPDATE_TS: &str = "update_time"; + +// Get seconds to wait for remote docker to start. +// If not, revert to local +// +const SONIC_CTR_CONFIG: &str = "/etc/sonic/remote_ctr.config.json"; +const SONIC_CTR_CONFIG_PEND_SECS: &str = "revert_to_local_on_wait_seconds"; +const DEFAULT_PEND_SECS: i32 = 5 * 60; +const WAIT_POLL_SECS: i32 = 2; + +fn read_data(db_connector: &DbConnector, feature: &String, fields: &mut HashMap<&str, String>) { + let table_name : &str; + if feature == SERVER_KEY { + table_name = "KUBERNETES_MASTER"; + } else { + table_name = "FEATURE"; + } + + let data = db_connector.hgetall(&format!("{}|{}", table_name, feature)); + for (field, default) in fields.iter_mut() { + match data.get(field as &str) { + Some(value) => *default = value.to_string(), + None => {}, + } + } +} + +fn read_config(db_connections: &DbConnections, feature: &String) -> HashMap<&'static str, String> { + let mut fields : HashMap<&str, String> = HashMap::from( + [ + (SET_OWNER, "local".to_string()), + (NO_FALLBACK, "False".to_string()), + (STATE, "disabled".to_string()) + ]); + read_data(&db_connections.config_db, feature, &mut fields); + fields +} + +fn read_state(db_connections: &DbConnections, feature: &String) -> HashMap<&'static str, String> { + let mut fields : HashMap<&str, String> = HashMap::from( + [ + (CURRENT_OWNER, "local".to_string()), + (REMOTE_STATE, "none".to_string()), + (CONTAINER_ID, "".to_string()) + ]); + read_data(&db_connections.state_db, feature, &mut fields); + fields +} + +fn update_data(db_connections: &DbConnections, feature: &String, data: &HashMap<&str, String>) { + if db_connections.remote_ctr_enabled { + todo!(); + } +} + +fn container_version(docker: &Docker, feature: &String) -> String { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let container_options = rt.block_on(docker.inspect_container(&feature, None)).expect("Unable to communicate with Docker"); + match container_options.config { + Some(config) => { + match config.env { + Some(envs) => { + for env in envs { + if env.starts_with("IMAGE_VERSION=") { + return env.split('=').collect::>()[1].to_string(); + } + } + return "".to_string(); + }, + None => { + return "".to_string(); + } + } + }, + None => { + return "".to_string(); + } + }; +} + +#[derive(enumset::EnumSetType, Debug)] +enum StartFlags { + StartLocal, + StartKube, +} + +fn container_start(feature: &String) { + let db_connections = DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + remote_ctr_enabled: false, + }; + + let feature_config = read_config(&db_connections, &feature); + let feature_state = read_state(&db_connections, &feature); + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data : HashMap<&str, String> = HashMap::from( + [ + (SYSTEM_STATE, "up".to_string()), + (UPD_TIMESTAMP, timestamp), + ]); + + let mut start_val = enumset::EnumSet::new(); + if feature_config.get(SET_OWNER).unwrap() == "local" { + start_val |= StartFlags::StartLocal; + } else { + start_val |= StartFlags::StartKube; + if feature_config.get(NO_FALLBACK).unwrap() == "True" && feature_state.get(REMOTE_STATE).unwrap() == "none" { + start_val |= StartFlags::StartLocal; + data.insert(REMOTE_STATE, "none".to_string()); + } + } + + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + data.insert(CURRENT_OWNER, "local".to_string()); + data.insert(CONTAINER_ID, feature.clone()); + if start_val == StartFlags::StartLocal { + data.insert(REMOTE_STATE, "none".to_string()); + } + } + + update_data(&db_connections, &feature, &data); + + let docker = Docker::connect_with_local_defaults().unwrap(); + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(docker.start_container(&feature, None::>)).expect("Unable to communicate with Docker"); +} + +fn container_stop(feature: &String) { + let db_connections = DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + remote_ctr_enabled: false, + }; + + let feature_config = read_config(&db_connections, &feature); + let feature_state = read_state(&db_connections, &feature); + let docker_id = feature; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if !docker_id.is_empty() { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(docker.stop_container(&feature, None)).expect("Unable to communicate with Docker"); + } + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data : HashMap<&str, String> = HashMap::from( + [ + (CURRENT_OWNER, "none".to_string()), + (SYSTEM_STATE, "down".to_string()), + (CONTAINER_ID, "".to_string()), + (UPD_TIMESTAMP, timestamp), + ]); + if feature_state.get(REMOTE_STATE).unwrap() == "running" { + data.insert(REMOTE_STATE, "stopped".to_string()); + } + + update_data(&db_connections, &feature, &data); +} + +fn container_kill(feature: &String) { + let db_connections = DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + remote_ctr_enabled: false, + }; + + let feature_config = read_config(&db_connections, &feature); + let feature_state = read_state(&db_connections, &feature); + let docker_id = feature; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if !docker_id.is_empty() { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(docker.kill_container(&feature, Some(KillContainerOptions{ + signal: "SIGINT", + }))).expect("Unable to communicate with Docker"); + } +} + +fn container_wait(feature: &String) { + let db_connections = DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + remote_ctr_enabled: false, + }; + + let feature_config = read_config(&db_connections, &feature); + let feature_state = read_state(&db_connections, &feature); + let docker_id = feature; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if docker_id == feature { + let version = container_version(&docker, &feature); + if !version.is_empty() { + update_data(&db_connections, &feature, &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)])); + } + } + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(docker.wait_container(&feature, Some(WaitContainerOptions{ + condition: "not-running", + })).try_collect::>()).expect("Unable to communicate with Docker"); +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Action { + Start, + Stop, + Kill, + Wait, + Id, +} + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[arg(value_enum)] + action: Action, + /// The name of the container + name: String, +} + +fn main() { + let cli = Cli::parse(); + + match cli.action { + Action::Start => container_start(&cli.name), + Action::Wait => container_wait(&cli.name), + Action::Stop => container_stop(&cli.name), + Action::Kill => container_kill(&cli.name), + Action::Id => todo!(), + }; +} From 73fc12c4ae2b6f73c15e9ba3b884b4aa1b876f6f Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Tue, 24 Sep 2024 15:09:11 -0700 Subject: [PATCH 02/22] Update Cargo.lock for new crates that have been added Signed-off-by: Saikrishna Arcot --- Cargo.lock | 598 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 505 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f0e2a5..238f475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -62,7 +77,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -72,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -241,6 +256,58 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bollard" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "chrono", + "futures-core", + "futures-util", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.45.0-rc.26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" +dependencies = [ + "chrono", + "serde", + "serde_repr", + "serde_with", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytes" version = "1.6.0" @@ -268,6 +335,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -321,7 +403,19 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys 0.52.0", + "windows-sys", +] + +[[package]] +name = "container" +version = "0.1.0" +dependencies = [ + "bollard", + "chrono", + "enumset", + "futures-util", + "swss-common", + "tokio", ] [[package]] @@ -335,12 +429,52 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.58", +] + [[package]] name = "dashmap" version = "6.0.1" @@ -355,6 +489,16 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -384,6 +528,27 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -397,7 +562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -428,6 +593,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -443,6 +617,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -462,9 +647,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -549,6 +736,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "1.1.0" @@ -632,6 +825,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + [[package]] name = "hyper-timeout" version = "0.5.1" @@ -665,6 +873,60 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indenter" version = "0.3.3" @@ -679,6 +941,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -689,6 +952,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -712,6 +976,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -731,7 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -800,13 +1073,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -836,13 +1110,18 @@ dependencies = [ ] [[package]] -name = "num_cpus" -version = "1.16.0" +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "hermit-abi", - "libc", + "autocfg", ] [[package]] @@ -868,7 +1147,7 @@ checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -893,7 +1172,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -944,6 +1223,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1170,7 +1455,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1179,6 +1464,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1205,6 +1496,29 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -1214,6 +1528,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "time", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1270,7 +1613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1404,7 +1747,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1437,28 +1780,73 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.37.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -1677,12 +2065,38 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.2" @@ -1725,6 +2139,61 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + [[package]] name = "winapi" version = "0.3.9" @@ -1748,12 +2217,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1762,22 +2231,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1786,93 +2240,51 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.4" From b71364252e9d00fbfc1576f009b84e727bf28a55 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Tue, 24 Sep 2024 19:59:47 -0700 Subject: [PATCH 03/22] Fix clap missing dependency Signed-off-by: Saikrishna Arcot --- Cargo.lock | 182 +++++++++++++++++++++++++++++++----- crates/container/Cargo.toml | 1 + 2 files changed, 161 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 238f475..e55af88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -87,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -347,7 +347,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -361,6 +361,49 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", + "unicase", + "unicode-width", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "color-eyre" version = "0.6.3" @@ -403,7 +446,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -412,6 +455,7 @@ version = "0.1.0" dependencies = [ "bollard", "chrono", + "clap", "enumset", "futures-util", "swss-common", @@ -562,7 +606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1004,7 +1048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -1080,7 +1124,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1147,7 +1191,7 @@ checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1172,7 +1216,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -1455,7 +1499,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1613,7 +1657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1632,6 +1676,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -1747,7 +1797,17 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -1839,7 +1899,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2065,6 +2125,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-bidi" version = "0.3.17" @@ -2086,6 +2152,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "url" version = "2.5.2" @@ -2222,7 +2294,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -2231,7 +2312,22 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2240,51 +2336,93 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.4" diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml index 92f3cc7..867aa0b 100644 --- a/crates/container/Cargo.toml +++ b/crates/container/Cargo.toml @@ -13,6 +13,7 @@ bollard = { version = "0.17.1", features = ["chrono"] } chrono = "0.4.38" enumset = "1.1.5" futures-util = "0.3.30" +clap = { workspace = true } swss-common = { version = "0.1.0", path = "../swss-common" } tokio.workspace = true From 3ef720f8a237db1676ca1e55d258f11d0587c986 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Fri, 4 Oct 2024 17:29:17 -0700 Subject: [PATCH 04/22] Implement most of the parts that involve Kubernetes Signed-off-by: Saikrishna Arcot --- Cargo.lock | 1 + crates/container/Cargo.toml | 3 +- crates/container/src/main.rs | 146 ++++++++++++++++++++++++++--------- 3 files changed, 114 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e55af88..6e0ba0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -458,6 +458,7 @@ dependencies = [ "clap", "enumset", "futures-util", + "serde_json", "swss-common", "tokio", ] diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml index 867aa0b..86769f8 100644 --- a/crates/container/Cargo.toml +++ b/crates/container/Cargo.toml @@ -15,7 +15,8 @@ enumset = "1.1.5" futures-util = "0.3.30" clap = { workspace = true } swss-common = { version = "0.1.0", path = "../swss-common" } -tokio.workspace = true +tokio = { workspace = true } +serde_json = { workspace = true } [lints] workspace = true diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 1a2c8e0..906d3f7 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -6,6 +6,10 @@ use bollard::Docker; use bollard::container::*; use futures_util::stream::TryStreamExt; use enumset; +use std::fs::File; +use std::io::Read; +use std::time::Duration; +use std::thread::sleep; struct DbConnections { config_db : DbConnector, @@ -39,8 +43,19 @@ const ST_SER_UPDATE_TS: &str = "update_time"; // const SONIC_CTR_CONFIG: &str = "/etc/sonic/remote_ctr.config.json"; const SONIC_CTR_CONFIG_PEND_SECS: &str = "revert_to_local_on_wait_seconds"; -const DEFAULT_PEND_SECS: i32 = 5 * 60; -const WAIT_POLL_SECS: i32 = 2; +const DEFAULT_PEND_SECS: u32 = 5 * 60; +const WAIT_POLL_SECS: u32 = 2; + +fn get_config_data(field: &str) -> Option { + let mut file = match File::open(SONIC_CTR_CONFIG) { + Ok(f) => f, + Err(_e) => return None, + }; + let mut file_contents = String::new(); + file.read_to_string(&mut file_contents).unwrap(); + let data : serde_json::Value = serde_json::from_str(&file_contents).unwrap(); + data.as_object().unwrap().get(field).and_then(|value| Some(value.clone())) +} fn read_data(db_connector: &DbConnector, feature: &String, fields: &mut HashMap<&str, String>) { let table_name : &str; @@ -81,6 +96,12 @@ fn read_state(db_connections: &DbConnections, feature: &String) -> HashMap<&'sta fields } +fn set_label(db_connections: &DbConnections, feature: &String, create: bool) { + if db_connections.remote_ctr_enabled { + todo!(); + } +} + fn update_data(db_connections: &DbConnections, feature: &String, data: &HashMap<&str, String>) { if db_connections.remote_ctr_enabled { todo!(); @@ -115,6 +136,28 @@ fn container_version(docker: &Docker, feature: &String) -> String { }; } +fn initialize_connection() -> DbConnections { + DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), + state_db: DbConnector::new_tcp(6, "localhost", 6379, 0), + remote_ctr_enabled: false, + } +} + +fn get_container_id(feature: &String, db_connections: &DbConnections) -> String { + let data = db_connections.state_db.hgetall(&format!("FEATURE|{}", feature)); + if data.get(CURRENT_OWNER).unwrap_or(&String::new()) == "local" { + return feature.clone(); + } else { + return data.get(CONTAINER_ID).unwrap_or(feature).clone(); + } +} + +fn container_id(feature: &String) { + let db_connections = initialize_connection(); + println!("{}\n", get_container_id(feature, &db_connections)); +} + #[derive(enumset::EnumSetType, Debug)] enum StartFlags { StartLocal, @@ -122,11 +165,7 @@ enum StartFlags { } fn container_start(feature: &String) { - let db_connections = DbConnections { - config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - remote_ctr_enabled: false, - }; + let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, &feature); let feature_state = read_state(&db_connections, &feature); @@ -143,7 +182,7 @@ fn container_start(feature: &String) { start_val |= StartFlags::StartLocal; } else { start_val |= StartFlags::StartKube; - if feature_config.get(NO_FALLBACK).unwrap() == "True" && feature_state.get(REMOTE_STATE).unwrap() == "none" { + if feature_config.get(NO_FALLBACK).unwrap() == "False" && feature_state.get(REMOTE_STATE).unwrap() == "none" { start_val |= StartFlags::StartLocal; data.insert(REMOTE_STATE, "none".to_string()); } @@ -153,30 +192,33 @@ fn container_start(feature: &String) { data.insert(CURRENT_OWNER, "local".to_string()); data.insert(CONTAINER_ID, feature.clone()); if start_val == StartFlags::StartLocal { + set_label(&db_connections, &feature, false); data.insert(REMOTE_STATE, "none".to_string()); } } update_data(&db_connections, &feature, &data); - let docker = Docker::connect_with_local_defaults().unwrap(); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(docker.start_container(&feature, None::>)).expect("Unable to communicate with Docker"); + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + let docker = Docker::connect_with_local_defaults().unwrap(); + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + rt.block_on(docker.start_container(&feature, None::>)).expect("Unable to communicate with Docker"); + } + + if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { + set_label(&db_connections, &feature, true); + } } fn container_stop(feature: &String) { - let db_connections = DbConnections { - config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - remote_ctr_enabled: false, - }; + let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, &feature); let feature_state = read_state(&db_connections, &feature); - let docker_id = feature; + let docker_id = get_container_id(feature, &db_connections); let docker = Docker::connect_with_local_defaults().unwrap(); @@ -204,15 +246,24 @@ fn container_stop(feature: &String) { } fn container_kill(feature: &String) { - let db_connections = DbConnections { - config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - remote_ctr_enabled: false, - }; + let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, &feature); let feature_state = read_state(&db_connections, &feature); - let docker_id = feature; + let docker_id = get_container_id(feature, &db_connections); + let remove_label = (feature_config.get(SET_OWNER).unwrap() != "local") || (feature_state.get(CURRENT_OWNER).unwrap() != "local"); + + if remove_label { + set_label(&db_connections, &feature, false); + } + + if feature_config.get(SET_OWNER).unwrap() == "local" { + let current_state = feature_state.get(STATE).unwrap(); + if current_state != "enabled" && current_state != "always_enabled" { + println!("{} is not enabled", feature); + return; + } + } let docker = Docker::connect_with_local_defaults().unwrap(); @@ -228,25 +279,44 @@ fn container_kill(feature: &String) { } fn container_wait(feature: &String) { - let db_connections = DbConnections { - config_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - state_db: DbConnector::new_tcp(4, "localhost", 6379, 0), - remote_ctr_enabled: false, - }; + let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, &feature); - let feature_state = read_state(&db_connections, &feature); - let docker_id = feature; + let mut feature_state = read_state(&db_connections, &feature); + let mut docker_id = get_container_id(feature, &db_connections); + let mut pend_wait_seconds : u32 = 0; let docker = Docker::connect_with_local_defaults().unwrap(); - if docker_id == feature { + if docker_id == *feature { let version = container_version(&docker, &feature); if !version.is_empty() { update_data(&db_connections, &feature, &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)])); } } + if docker_id.is_empty() && feature_config.get(NO_FALLBACK).unwrap() == "False" { + pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS).and_then(|value| value.as_u64()).and_then(|value| Some(value as u32)).unwrap_or(DEFAULT_PEND_SECS); + } + + while docker_id.is_empty() { + if feature_config.get(NO_FALLBACK).unwrap() == "False" { + if pend_wait_seconds < WAIT_POLL_SECS { + break; + } + pend_wait_seconds = pend_wait_seconds - WAIT_POLL_SECS; + } + + sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); + feature_state = read_state(&db_connections, &feature); + + docker_id = feature_state.get(CONTAINER_ID).unwrap().clone(); + + if feature_state.get(REMOTE_STATE).unwrap() == "pending" { + update_data(&db_connections, &feature, &HashMap::from([(REMOTE_STATE, "ready".to_string())])); + } + } + let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() @@ -269,9 +339,15 @@ enum Action { #[command(version, about, long_about = None)] struct Cli { #[arg(value_enum)] + /// The action to take for the container action: Action, + /// The name of the container name: String, + + /// Timeout for the action to occur + #[arg(short, long)] + timeout: Option, } fn main() { @@ -282,6 +358,6 @@ fn main() { Action::Wait => container_wait(&cli.name), Action::Stop => container_stop(&cli.name), Action::Kill => container_kill(&cli.name), - Action::Id => todo!(), + Action::Id => container_id(&cli.name), }; } From 61f6fa80bb0af59c8538353227f7c8f8ebf96917 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Sun, 3 Nov 2024 15:36:58 -0800 Subject: [PATCH 05/22] Update for using CxxString Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 906d3f7..c6b6126 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -68,7 +68,7 @@ fn read_data(db_connector: &DbConnector, feature: &String, fields: &mut HashMap< let data = db_connector.hgetall(&format!("{}|{}", table_name, feature)); for (field, default) in fields.iter_mut() { match data.get(field as &str) { - Some(value) => *default = value.to_string(), + Some(value) => *default = value.to_str().unwrap().to_string(), None => {}, } } @@ -146,10 +146,10 @@ fn initialize_connection() -> DbConnections { fn get_container_id(feature: &String, db_connections: &DbConnections) -> String { let data = db_connections.state_db.hgetall(&format!("FEATURE|{}", feature)); - if data.get(CURRENT_OWNER).unwrap_or(&String::new()) == "local" { + if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { return feature.clone(); } else { - return data.get(CONTAINER_ID).unwrap_or(feature).clone(); + return data.get(CONTAINER_ID).map_or(feature.clone(), |value| value.to_str().unwrap().to_string()); } } From 500c4f1edaf778fba270bb2c848ce77e5185b65c Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Sun, 3 Nov 2024 17:09:10 -0800 Subject: [PATCH 06/22] Remove potential extra copy by using std::borrow::Cow Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index c6b6126..9fdab13 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -6,6 +6,7 @@ use bollard::Docker; use bollard::container::*; use futures_util::stream::TryStreamExt; use enumset; +use std::borrow::Cow; use std::fs::File; use std::io::Read; use std::time::Duration; @@ -144,12 +145,12 @@ fn initialize_connection() -> DbConnections { } } -fn get_container_id(feature: &String, db_connections: &DbConnections) -> String { +fn get_container_id<'a>(feature: &'a String, db_connections: &DbConnections) -> Cow<'a, String> { let data = db_connections.state_db.hgetall(&format!("FEATURE|{}", feature)); if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { - return feature.clone(); + return Cow::Borrowed(feature); } else { - return data.get(CONTAINER_ID).map_or(feature.clone(), |value| value.to_str().unwrap().to_string()); + return data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| Cow::Owned(value.to_str().unwrap().to_string())); } } @@ -288,7 +289,7 @@ fn container_wait(feature: &String) { let docker = Docker::connect_with_local_defaults().unwrap(); - if docker_id == *feature { + if *docker_id == *feature { let version = container_version(&docker, &feature); if !version.is_empty() { update_data(&db_connections, &feature, &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)])); @@ -310,7 +311,7 @@ fn container_wait(feature: &String) { sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); feature_state = read_state(&db_connections, &feature); - docker_id = feature_state.get(CONTAINER_ID).unwrap().clone(); + docker_id = Cow::Borrowed(feature_state.get(CONTAINER_ID).unwrap()); if feature_state.get(REMOTE_STATE).unwrap() == "pending" { update_data(&db_connections, &feature, &HashMap::from([(REMOTE_STATE, "ready".to_string())])); From 393f44e3f4c09fc4f78c5275f6c3b78b3f4a18cb Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 6 Jan 2025 14:56:36 -0800 Subject: [PATCH 07/22] Add support for stop timeout Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 9fdab13..c0cfef0 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -214,7 +214,7 @@ fn container_start(feature: &String) { } } -fn container_stop(feature: &String) { +fn container_stop(feature: &String, timeout: Option) { let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, &feature); @@ -224,11 +224,16 @@ fn container_stop(feature: &String) { let docker = Docker::connect_with_local_defaults().unwrap(); if !docker_id.is_empty() { + let stop_options : Option; + match timeout { + Some(timeout) => stop_options = Some(StopContainerOptions { t: timeout }), + None => stop_options = None, + } let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap(); - rt.block_on(docker.stop_container(&feature, None)).expect("Unable to communicate with Docker"); + rt.block_on(docker.stop_container(&feature, stop_options)).expect("Unable to communicate with Docker"); } let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); @@ -348,7 +353,7 @@ struct Cli { /// Timeout for the action to occur #[arg(short, long)] - timeout: Option, + timeout: Option, } fn main() { @@ -357,7 +362,7 @@ fn main() { match cli.action { Action::Start => container_start(&cli.name), Action::Wait => container_wait(&cli.name), - Action::Stop => container_stop(&cli.name), + Action::Stop => container_stop(&cli.name, cli.timeout), Action::Kill => container_kill(&cli.name), Action::Id => container_id(&cli.name), }; From d04d9cc8963adb070333a62f4c50ad578867ded1 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 6 Jan 2025 22:31:30 -0800 Subject: [PATCH 08/22] Fix formatting Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 133 ++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index c0cfef0..362663e 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -1,21 +1,21 @@ -use swss_common::DbConnector; -use std::collections::HashMap; -use clap::{Parser, ValueEnum}; -use chrono::Local; -use bollard::Docker; use bollard::container::*; -use futures_util::stream::TryStreamExt; +use bollard::Docker; +use chrono::Local; +use clap::{Parser, ValueEnum}; use enumset; +use futures_util::stream::TryStreamExt; use std::borrow::Cow; +use std::collections::HashMap; use std::fs::File; use std::io::Read; -use std::time::Duration; use std::thread::sleep; +use std::time::Duration; +use swss_common::DbConnector; struct DbConnections { - config_db : DbConnector, - state_db : DbConnector, - remote_ctr_enabled : bool, + config_db: DbConnector, + state_db: DbConnector, + remote_ctr_enabled: bool, } // DB field names @@ -54,12 +54,15 @@ fn get_config_data(field: &str) -> Option { }; let mut file_contents = String::new(); file.read_to_string(&mut file_contents).unwrap(); - let data : serde_json::Value = serde_json::from_str(&file_contents).unwrap(); - data.as_object().unwrap().get(field).and_then(|value| Some(value.clone())) + let data: serde_json::Value = serde_json::from_str(&file_contents).unwrap(); + data.as_object() + .unwrap() + .get(field) + .and_then(|value| Some(value.clone())) } fn read_data(db_connector: &DbConnector, feature: &String, fields: &mut HashMap<&str, String>) { - let table_name : &str; + let table_name: &str; if feature == SERVER_KEY { table_name = "KUBERNETES_MASTER"; } else { @@ -70,29 +73,27 @@ fn read_data(db_connector: &DbConnector, feature: &String, fields: &mut HashMap< for (field, default) in fields.iter_mut() { match data.get(field as &str) { Some(value) => *default = value.to_str().unwrap().to_string(), - None => {}, + None => {} } } } fn read_config(db_connections: &DbConnections, feature: &String) -> HashMap<&'static str, String> { - let mut fields : HashMap<&str, String> = HashMap::from( - [ + let mut fields: HashMap<&str, String> = HashMap::from([ (SET_OWNER, "local".to_string()), (NO_FALLBACK, "False".to_string()), - (STATE, "disabled".to_string()) - ]); + (STATE, "disabled".to_string()), + ]); read_data(&db_connections.config_db, feature, &mut fields); fields } fn read_state(db_connections: &DbConnections, feature: &String) -> HashMap<&'static str, String> { - let mut fields : HashMap<&str, String> = HashMap::from( - [ + let mut fields: HashMap<&str, String> = HashMap::from([ (CURRENT_OWNER, "local".to_string()), (REMOTE_STATE, "none".to_string()), - (CONTAINER_ID, "".to_string()) - ]); + (CONTAINER_ID, "".to_string()), + ]); read_data(&db_connections.state_db, feature, &mut fields); fields } @@ -114,21 +115,21 @@ fn container_version(docker: &Docker, feature: &String) -> String { .enable_all() .build() .unwrap(); - let container_options = rt.block_on(docker.inspect_container(&feature, None)).expect("Unable to communicate with Docker"); + let container_options = rt + .block_on(docker.inspect_container(&feature, None)) + .expect("Unable to communicate with Docker"); match container_options.config { - Some(config) => { - match config.env { - Some(envs) => { - for env in envs { - if env.starts_with("IMAGE_VERSION=") { - return env.split('=').collect::>()[1].to_string(); - } + Some(config) => match config.env { + Some(envs) => { + for env in envs { + if env.starts_with("IMAGE_VERSION=") { + return env.split('=').collect::>()[1].to_string(); } - return "".to_string(); - }, - None => { - return "".to_string(); } + return "".to_string(); + } + None => { + return "".to_string(); } }, None => { @@ -150,7 +151,9 @@ fn get_container_id<'a>(feature: &'a String, db_connections: &DbConnections) -> if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { return Cow::Borrowed(feature); } else { - return data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| Cow::Owned(value.to_str().unwrap().to_string())); + return data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { + Cow::Owned(value.to_str().unwrap().to_string()) + }); } } @@ -172,11 +175,7 @@ fn container_start(feature: &String) { let feature_state = read_state(&db_connections, &feature); let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data : HashMap<&str, String> = HashMap::from( - [ - (SYSTEM_STATE, "up".to_string()), - (UPD_TIMESTAMP, timestamp), - ]); + let mut data: HashMap<&str, String> = HashMap::from([(SYSTEM_STATE, "up".to_string()), (UPD_TIMESTAMP, timestamp)]); let mut start_val = enumset::EnumSet::new(); if feature_config.get(SET_OWNER).unwrap() == "local" { @@ -206,7 +205,8 @@ fn container_start(feature: &String) { .enable_all() .build() .unwrap(); - rt.block_on(docker.start_container(&feature, None::>)).expect("Unable to communicate with Docker"); + rt.block_on(docker.start_container(&feature, None::>)) + .expect("Unable to communicate with Docker"); } if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { @@ -224,7 +224,7 @@ fn container_stop(feature: &String, timeout: Option) { let docker = Docker::connect_with_local_defaults().unwrap(); if !docker_id.is_empty() { - let stop_options : Option; + let stop_options: Option; match timeout { Some(timeout) => stop_options = Some(StopContainerOptions { t: timeout }), None => stop_options = None, @@ -233,17 +233,17 @@ fn container_stop(feature: &String, timeout: Option) { .enable_all() .build() .unwrap(); - rt.block_on(docker.stop_container(&feature, stop_options)).expect("Unable to communicate with Docker"); + rt.block_on(docker.stop_container(&feature, stop_options)) + .expect("Unable to communicate with Docker"); } let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data : HashMap<&str, String> = HashMap::from( - [ + let mut data: HashMap<&str, String> = HashMap::from([ (CURRENT_OWNER, "none".to_string()), (SYSTEM_STATE, "down".to_string()), (CONTAINER_ID, "".to_string()), (UPD_TIMESTAMP, timestamp), - ]); + ]); if feature_state.get(REMOTE_STATE).unwrap() == "running" { data.insert(REMOTE_STATE, "stopped".to_string()); } @@ -257,7 +257,8 @@ fn container_kill(feature: &String) { let feature_config = read_config(&db_connections, &feature); let feature_state = read_state(&db_connections, &feature); let docker_id = get_container_id(feature, &db_connections); - let remove_label = (feature_config.get(SET_OWNER).unwrap() != "local") || (feature_state.get(CURRENT_OWNER).unwrap() != "local"); + let remove_label = + (feature_config.get(SET_OWNER).unwrap() != "local") || (feature_state.get(CURRENT_OWNER).unwrap() != "local"); if remove_label { set_label(&db_connections, &feature, false); @@ -278,9 +279,8 @@ fn container_kill(feature: &String) { .enable_all() .build() .unwrap(); - rt.block_on(docker.kill_container(&feature, Some(KillContainerOptions{ - signal: "SIGINT", - }))).expect("Unable to communicate with Docker"); + rt.block_on(docker.kill_container(&feature, Some(KillContainerOptions { signal: "SIGINT" }))) + .expect("Unable to communicate with Docker"); } } @@ -290,19 +290,26 @@ fn container_wait(feature: &String) { let feature_config = read_config(&db_connections, &feature); let mut feature_state = read_state(&db_connections, &feature); let mut docker_id = get_container_id(feature, &db_connections); - let mut pend_wait_seconds : u32 = 0; + let mut pend_wait_seconds: u32 = 0; let docker = Docker::connect_with_local_defaults().unwrap(); if *docker_id == *feature { let version = container_version(&docker, &feature); if !version.is_empty() { - update_data(&db_connections, &feature, &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)])); + update_data( + &db_connections, + &feature, + &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)]), + ); } } if docker_id.is_empty() && feature_config.get(NO_FALLBACK).unwrap() == "False" { - pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS).and_then(|value| value.as_u64()).and_then(|value| Some(value as u32)).unwrap_or(DEFAULT_PEND_SECS); + pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS) + .and_then(|value| value.as_u64()) + .and_then(|value| Some(value as u32)) + .unwrap_or(DEFAULT_PEND_SECS); } while docker_id.is_empty() { @@ -319,7 +326,11 @@ fn container_wait(feature: &String) { docker_id = Cow::Borrowed(feature_state.get(CONTAINER_ID).unwrap()); if feature_state.get(REMOTE_STATE).unwrap() == "pending" { - update_data(&db_connections, &feature, &HashMap::from([(REMOTE_STATE, "ready".to_string())])); + update_data( + &db_connections, + &feature, + &HashMap::from([(REMOTE_STATE, "ready".to_string())]), + ); } } @@ -327,9 +338,17 @@ fn container_wait(feature: &String) { .enable_all() .build() .unwrap(); - rt.block_on(docker.wait_container(&feature, Some(WaitContainerOptions{ - condition: "not-running", - })).try_collect::>()).expect("Unable to communicate with Docker"); + rt.block_on( + docker + .wait_container( + &feature, + Some(WaitContainerOptions { + condition: "not-running", + }), + ) + .try_collect::>(), + ) + .expect("Unable to communicate with Docker"); } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] From d9fa9efed9b058e9acfc5432dcc07d33d9d10f50 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Thu, 23 Jan 2025 12:22:07 -0800 Subject: [PATCH 09/22] Fix formatting Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 1fe5342..390d1b3 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -68,7 +68,9 @@ fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&st table_name = "FEATURE"; } - let data = db_connector.hgetall(&format!("{}|{}", table_name, feature)).expect("Unable to get data"); + let data = db_connector + .hgetall(&format!("{}|{}", table_name, feature)) + .expect("Unable to get data"); for (field, default) in fields.iter_mut() { match data.get(field as &str) { Some(value) => *default = value.to_str().unwrap().to_string(), @@ -146,7 +148,10 @@ fn initialize_connection() -> DbConnections { } fn get_container_id<'a>(feature: &'a str, db_connections: &DbConnections) -> Cow<'a, str> { - let data = db_connections.state_db.hgetall(&format!("FEATURE|{}", feature)).expect("Unable to get data"); + let data = db_connections + .state_db + .hgetall(&format!("FEATURE|{}", feature)) + .expect("Unable to get data"); if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { return Cow::Borrowed(feature); } else { From c58bf63ba0f730d31b4476bf7aa0336e06d2bcd3 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Thu, 23 Jan 2025 12:33:28 -0800 Subject: [PATCH 10/22] Comment out unused variables Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 390d1b3..e9cd2d4 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -19,7 +19,7 @@ struct DbConnections { } // DB field names -const FEATURE_TABLE: &str = "FEATURE"; +//const FEATURE_TABLE: &str = "FEATURE"; const SET_OWNER: &str = "set_owner"; const NO_FALLBACK: &str = "no_fallback_to_local"; @@ -31,12 +31,12 @@ const SYSTEM_STATE: &str = "system_state"; const STATE: &str = "state"; const ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; -const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; -const KUBE_LABEL_SET_KEY: &str = "SET"; -const SERVER_TABLE: &str = "KUBERNETES_MASTER"; +//const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +//const KUBE_LABEL_SET_KEY: &str = "SET"; +//const SERVER_TABLE: &str = "KUBERNETES_MASTER"; const SERVER_KEY: &str = "SERVER"; -const ST_SER_CONNECTED: &str = "connected"; -const ST_SER_UPDATE_TS: &str = "update_time"; +//const ST_SER_CONNECTED: &str = "connected"; +//const ST_SER_UPDATE_TS: &str = "update_time"; // Get seconds to wait for remote docker to start. // If not, revert to local @@ -99,13 +99,13 @@ fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static fields } -fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { +fn set_label(db_connections: &DbConnections, _feature: &str, _create: bool) { if db_connections.remote_ctr_enabled { todo!(); } } -fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&str, String>) { +fn update_data(db_connections: &DbConnections, _feature: &str, _data: &HashMap<&str, String>) { if db_connections.remote_ctr_enabled { todo!(); } @@ -221,7 +221,7 @@ fn container_start(feature: &str) { fn container_stop(feature: &String, timeout: Option) { let db_connections = initialize_connection(); - let feature_config = read_config(&db_connections, feature); + //let feature_config = read_config(&db_connections, feature); let feature_state = read_state(&db_connections, feature); let docker_id = get_container_id(feature, &db_connections); @@ -292,7 +292,8 @@ fn container_wait(feature: &str) { let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, feature); - let mut feature_state = read_state(&db_connections, feature); + //let mut feature_state = read_state(&db_connections, feature); + let mut feature_state; let mut docker_id = get_container_id(feature, &db_connections); let mut pend_wait_seconds: u32 = 0; From 179d48b1ca7d368ed4c8e93751e31e4824333eb8 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Thu, 23 Jan 2025 12:45:21 -0800 Subject: [PATCH 11/22] Remove extra newline Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index e9cd2d4..c823e37 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -163,7 +163,7 @@ fn get_container_id<'a>(feature: &'a str, db_connections: &DbConnections) -> Cow fn container_id(feature: &str) { let db_connections = initialize_connection(); - println!("{}\n", get_container_id(feature, &db_connections)); + println!("{}", get_container_id(feature, &db_connections)); } #[derive(enumset::EnumSetType, Debug)] From 31d871792cff293019f1d21035878e775686e47c Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Fri, 24 Jan 2025 11:39:17 -0800 Subject: [PATCH 12/22] Fix syntax and formatting Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 72 +++++++++++++++--------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index c823e37..6269a1c 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -2,7 +2,6 @@ use bollard::container::*; use bollard::Docker; use chrono::Local; use clap::{Parser, ValueEnum}; -use enumset; use futures_util::stream::TryStreamExt; use std::borrow::Cow; use std::collections::HashMap; @@ -54,27 +53,22 @@ fn get_config_data(field: &str) -> Option { let mut file_contents = String::new(); file.read_to_string(&mut file_contents).unwrap(); let data: serde_json::Value = serde_json::from_str(&file_contents).unwrap(); - data.as_object() - .unwrap() - .get(field) - .and_then(|value| Some(value.clone())) + data.as_object().unwrap().get(field).cloned() } fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&str, String>) { - let table_name: &str; - if feature == SERVER_KEY { - table_name = "KUBERNETES_MASTER"; + let table_name = if feature == SERVER_KEY { + "KUBERNETES_MASTER" } else { - table_name = "FEATURE"; - } + "FEATURE" + }; let data = db_connector .hgetall(&format!("{}|{}", table_name, feature)) .expect("Unable to get data"); for (field, default) in fields.iter_mut() { - match data.get(field as &str) { - Some(value) => *default = value.to_str().unwrap().to_string(), - None => {} + if let Some(value) = data.get(field as &str) { + *default = value.to_str().unwrap().to_string() } } } @@ -117,7 +111,7 @@ fn container_version(docker: &Docker, feature: &str) -> String { .build() .unwrap(); let container_options = rt - .block_on(docker.inspect_container(&feature, None)) + .block_on(docker.inspect_container(feature, None)) .expect("Unable to communicate with Docker"); match container_options.config { Some(config) => match config.env { @@ -127,16 +121,12 @@ fn container_version(docker: &Docker, feature: &str) -> String { return env.split('=').collect::>()[1].to_string(); } } - return "".to_string(); - } - None => { - return "".to_string(); + String::new() } + None => String::new(), }, - None => { - return "".to_string(); - } - }; + None => String::new(), + } } fn initialize_connection() -> DbConnections { @@ -196,12 +186,12 @@ fn container_start(feature: &str) { data.insert(CURRENT_OWNER, "local".to_string()); data.insert(CONTAINER_ID, feature.to_string()); if start_val == StartFlags::StartLocal { - set_label(&db_connections, &feature, false); + set_label(&db_connections, feature, false); data.insert(REMOTE_STATE, "none".to_string()); } } - update_data(&db_connections, &feature, &data); + update_data(&db_connections, feature, &data); if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { let docker = Docker::connect_with_local_defaults().unwrap(); @@ -209,16 +199,16 @@ fn container_start(feature: &str) { .enable_all() .build() .unwrap(); - rt.block_on(docker.start_container(&feature, None::>)) + rt.block_on(docker.start_container(feature, None::>)) .expect("Unable to communicate with Docker"); } if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { - set_label(&db_connections, &feature, true); + set_label(&db_connections, feature, true); } } -fn container_stop(feature: &String, timeout: Option) { +fn container_stop(feature: &str, timeout: Option) { let db_connections = initialize_connection(); //let feature_config = read_config(&db_connections, feature); @@ -228,16 +218,12 @@ fn container_stop(feature: &String, timeout: Option) { let docker = Docker::connect_with_local_defaults().unwrap(); if !docker_id.is_empty() { - let stop_options: Option; - match timeout { - Some(timeout) => stop_options = Some(StopContainerOptions { t: timeout }), - None => stop_options = None, - } + let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap(); - rt.block_on(docker.stop_container(&feature, stop_options)) + rt.block_on(docker.stop_container(feature, stop_options)) .expect("Unable to communicate with Docker"); } @@ -252,7 +238,7 @@ fn container_stop(feature: &String, timeout: Option) { data.insert(REMOTE_STATE, "stopped".to_string()); } - update_data(&db_connections, &feature, &data); + update_data(&db_connections, feature, &data); } fn container_kill(feature: &str) { @@ -265,7 +251,7 @@ fn container_kill(feature: &str) { (feature_config.get(SET_OWNER).unwrap() != "local") || (feature_state.get(CURRENT_OWNER).unwrap() != "local"); if remove_label { - set_label(&db_connections, &feature, false); + set_label(&db_connections, feature, false); } if feature_config.get(SET_OWNER).unwrap() == "local" { @@ -283,7 +269,7 @@ fn container_kill(feature: &str) { .enable_all() .build() .unwrap(); - rt.block_on(docker.kill_container(&feature, Some(KillContainerOptions { signal: "SIGINT" }))) + rt.block_on(docker.kill_container(feature, Some(KillContainerOptions { signal: "SIGINT" }))) .expect("Unable to communicate with Docker"); } } @@ -300,11 +286,11 @@ fn container_wait(feature: &str) { let docker = Docker::connect_with_local_defaults().unwrap(); if *docker_id == *feature { - let version = container_version(&docker, &feature); + let version = container_version(&docker, feature); if !version.is_empty() { update_data( &db_connections, - &feature, + feature, &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)]), ); } @@ -313,7 +299,7 @@ fn container_wait(feature: &str) { if docker_id.is_empty() && feature_config.get(NO_FALLBACK).unwrap() == "False" { pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS) .and_then(|value| value.as_u64()) - .and_then(|value| Some(value as u32)) + .map(|value| value as u32) .unwrap_or(DEFAULT_PEND_SECS); } @@ -322,18 +308,18 @@ fn container_wait(feature: &str) { if pend_wait_seconds < WAIT_POLL_SECS { break; } - pend_wait_seconds = pend_wait_seconds - WAIT_POLL_SECS; + pend_wait_seconds -= WAIT_POLL_SECS; } sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); - feature_state = read_state(&db_connections, &feature); + feature_state = read_state(&db_connections, feature); docker_id = Cow::Borrowed(feature_state.get(CONTAINER_ID).unwrap()); if feature_state.get(REMOTE_STATE).unwrap() == "pending" { update_data( &db_connections, - &feature, + feature, &HashMap::from([(REMOTE_STATE, "ready".to_string())]), ); } @@ -346,7 +332,7 @@ fn container_wait(feature: &str) { rt.block_on( docker .wait_container( - &feature, + feature, Some(WaitContainerOptions { condition: "not-running", }), From e60b819c91ff3d2e1d21c9f4e68ba1a124d5305a Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 27 Jan 2025 12:14:18 -0800 Subject: [PATCH 13/22] Fix another linter error Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 6269a1c..eaa3948 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -143,11 +143,11 @@ fn get_container_id<'a>(feature: &'a str, db_connections: &DbConnections) -> Cow .hgetall(&format!("FEATURE|{}", feature)) .expect("Unable to get data"); if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { - return Cow::Borrowed(feature); + Cow::Borrowed(feature) } else { - return data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { + data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { Cow::Owned(value.to_str().unwrap().to_string()) - }); + }) } } From 23805db3c075997c3f987ad8e28fc6d93c7cb648 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Sun, 2 Feb 2025 17:58:29 -0800 Subject: [PATCH 14/22] Implement the Kubernetes aspects Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 60 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index eaa3948..356c6f7 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -9,7 +9,7 @@ use std::fs::File; use std::io::Read; use std::thread::sleep; use std::time::Duration; -use swss_common::DbConnector; +use swss_common::{CxxString, DbConnector}; struct DbConnections { config_db: DbConnector, @@ -18,7 +18,7 @@ struct DbConnections { } // DB field names -//const FEATURE_TABLE: &str = "FEATURE"; +const FEATURE_TABLE: &str = "FEATURE"; const SET_OWNER: &str = "set_owner"; const NO_FALLBACK: &str = "no_fallback_to_local"; @@ -30,9 +30,9 @@ const SYSTEM_STATE: &str = "system_state"; const STATE: &str = "state"; const ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; -//const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; -//const KUBE_LABEL_SET_KEY: &str = "SET"; -//const SERVER_TABLE: &str = "KUBERNETES_MASTER"; +const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +const KUBE_LABEL_SET_KEY: &str = "SET"; +const SERVER_TABLE: &str = "KUBERNETES_MASTER"; const SERVER_KEY: &str = "SERVER"; //const ST_SER_CONNECTED: &str = "connected"; //const ST_SER_UPDATE_TS: &str = "update_time"; @@ -58,9 +58,9 @@ fn get_config_data(field: &str) -> Option { fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&str, String>) { let table_name = if feature == SERVER_KEY { - "KUBERNETES_MASTER" + SERVER_TABLE } else { - "FEATURE" + FEATURE_TABLE }; let data = db_connector @@ -93,15 +93,23 @@ fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static fields } -fn set_label(db_connections: &DbConnections, _feature: &str, _create: bool) { +fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { if db_connections.remote_ctr_enabled { - todo!(); + let _ = db_connections.state_db.hset( + KUBE_LABEL_TABLE, + &format!("{}|{}_enabled", KUBE_LABEL_SET_KEY, feature), + &CxxString::new(if create { "true" } else { "false" }), + ); } } -fn update_data(db_connections: &DbConnections, _feature: &str, _data: &HashMap<&str, String>) { +fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&str, &str>) { if db_connections.remote_ctr_enabled { - todo!(); + for (key, value) in data.iter() { + let _ = db_connections + .state_db + .hset(FEATURE_TABLE, &format!("{}|{}", feature, key), &CxxString::new(value)); + } } } @@ -169,7 +177,7 @@ fn container_start(feature: &str) { let feature_state = read_state(&db_connections, feature); let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data: HashMap<&str, String> = HashMap::from([(SYSTEM_STATE, "up".to_string()), (UPD_TIMESTAMP, timestamp)]); + let mut data: HashMap<&str, &str> = HashMap::from([(SYSTEM_STATE, "up"), (UPD_TIMESTAMP, ×tamp)]); let mut start_val = enumset::EnumSet::new(); if feature_config.get(SET_OWNER).unwrap() == "local" { @@ -178,16 +186,16 @@ fn container_start(feature: &str) { start_val |= StartFlags::StartKube; if feature_config.get(NO_FALLBACK).unwrap() == "False" && feature_state.get(REMOTE_STATE).unwrap() == "none" { start_val |= StartFlags::StartLocal; - data.insert(REMOTE_STATE, "none".to_string()); + data.insert(REMOTE_STATE, "none"); } } if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { - data.insert(CURRENT_OWNER, "local".to_string()); - data.insert(CONTAINER_ID, feature.to_string()); + data.insert(CURRENT_OWNER, "local"); + data.insert(CONTAINER_ID, feature); if start_val == StartFlags::StartLocal { set_label(&db_connections, feature, false); - data.insert(REMOTE_STATE, "none".to_string()); + data.insert(REMOTE_STATE, "none"); } } @@ -228,14 +236,14 @@ fn container_stop(feature: &str, timeout: Option) { } let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data: HashMap<&str, String> = HashMap::from([ - (CURRENT_OWNER, "none".to_string()), - (SYSTEM_STATE, "down".to_string()), - (CONTAINER_ID, "".to_string()), - (UPD_TIMESTAMP, timestamp), + let mut data: HashMap<&str, &str> = HashMap::from([ + (CURRENT_OWNER, "none"), + (SYSTEM_STATE, "down"), + (CONTAINER_ID, ""), + (UPD_TIMESTAMP, ×tamp), ]); if feature_state.get(REMOTE_STATE).unwrap() == "running" { - data.insert(REMOTE_STATE, "stopped".to_string()); + data.insert(REMOTE_STATE, "stopped"); } update_data(&db_connections, feature, &data); @@ -291,7 +299,7 @@ fn container_wait(feature: &str) { update_data( &db_connections, feature, - &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version)]), + &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version.as_str())]), ); } } @@ -317,11 +325,7 @@ fn container_wait(feature: &str) { docker_id = Cow::Borrowed(feature_state.get(CONTAINER_ID).unwrap()); if feature_state.get(REMOTE_STATE).unwrap() == "pending" { - update_data( - &db_connections, - feature, - &HashMap::from([(REMOTE_STATE, "ready".to_string())]), - ); + update_data(&db_connections, feature, &HashMap::from([(REMOTE_STATE, "ready")])); } } From 71df3c7b3b8d25c0a9ede22134c38c79d91edb1b Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Sun, 2 Feb 2025 18:23:48 -0800 Subject: [PATCH 15/22] Return an Option for the container version Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 356c6f7..ae8f83f 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -113,7 +113,7 @@ fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&st } } -fn container_version(docker: &Docker, feature: &str) -> String { +fn container_version(docker: &Docker, feature: &str) -> Option { let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() @@ -121,20 +121,16 @@ fn container_version(docker: &Docker, feature: &str) -> String { let container_options = rt .block_on(docker.inspect_container(feature, None)) .expect("Unable to communicate with Docker"); - match container_options.config { - Some(config) => match config.env { - Some(envs) => { - for env in envs { - if env.starts_with("IMAGE_VERSION=") { - return env.split('=').collect::>()[1].to_string(); - } + if let Some(config) = container_options.config { + if let Some(envs) = config.env { + for env in envs { + if env.starts_with("IMAGE_VERSION=") { + return Some(env.split('=').collect::>()[1].to_string()); } - String::new() } - None => String::new(), - }, - None => String::new(), + } } + None } fn initialize_connection() -> DbConnections { @@ -294,8 +290,8 @@ fn container_wait(feature: &str) { let docker = Docker::connect_with_local_defaults().unwrap(); if *docker_id == *feature { - let version = container_version(&docker, feature); - if !version.is_empty() { + let version_option = container_version(&docker, feature); + if let Some(version) = version_option { update_data( &db_connections, feature, From 9595ba18e5c6c167c8b7225d83cf18f5b173ebad Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 3 Feb 2025 13:56:24 -0800 Subject: [PATCH 16/22] Make main method be async Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 109 ++++++++++++++++------------------- 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index ae8f83f..6544fa9 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -95,32 +95,33 @@ fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { if db_connections.remote_ctr_enabled { - let _ = db_connections.state_db.hset( - KUBE_LABEL_TABLE, - &format!("{}|{}_enabled", KUBE_LABEL_SET_KEY, feature), - &CxxString::new(if create { "true" } else { "false" }), - ); + db_connections + .state_db + .hset( + KUBE_LABEL_TABLE, + &format!("{}|{}_enabled", KUBE_LABEL_SET_KEY, feature), + &CxxString::new(if create { "true" } else { "false" }), + ) + .expect("Unable to set label of container in Redis"); } } fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&str, &str>) { if db_connections.remote_ctr_enabled { for (key, value) in data.iter() { - let _ = db_connections - .state_db - .hset(FEATURE_TABLE, &format!("{}|{}", feature, key), &CxxString::new(value)); + let _ = + db_connections + .state_db + .hset(FEATURE_TABLE, &format!("{}|{}", feature, key), &CxxString::new(value)); } } } -fn container_version(docker: &Docker, feature: &str) -> Option { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - let container_options = rt - .block_on(docker.inspect_container(feature, None)) - .expect("Unable to communicate with Docker"); +async fn container_version(docker: &Docker, feature: &str) -> Option { + let container_options = docker + .inspect_container(feature, None) + .await + .expect("Error getting the version of container"); if let Some(config) = container_options.config { if let Some(envs) = config.env { for env in envs { @@ -166,7 +167,7 @@ enum StartFlags { StartKube, } -fn container_start(feature: &str) { +async fn container_start(feature: &str) { let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, feature); @@ -199,12 +200,10 @@ fn container_start(feature: &str) { if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { let docker = Docker::connect_with_local_defaults().unwrap(); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(docker.start_container(feature, None::>)) - .expect("Unable to communicate with Docker"); + docker + .start_container(feature, None::>) + .await + .expect("Unable to start container"); } if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { @@ -212,7 +211,7 @@ fn container_start(feature: &str) { } } -fn container_stop(feature: &str, timeout: Option) { +async fn container_stop(feature: &str, timeout: Option) { let db_connections = initialize_connection(); //let feature_config = read_config(&db_connections, feature); @@ -223,12 +222,10 @@ fn container_stop(feature: &str, timeout: Option) { if !docker_id.is_empty() { let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(docker.stop_container(feature, stop_options)) - .expect("Unable to communicate with Docker"); + docker + .stop_container(feature, stop_options) + .await + .expect("Unable to stop container"); } let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); @@ -245,7 +242,7 @@ fn container_stop(feature: &str, timeout: Option) { update_data(&db_connections, feature, &data); } -fn container_kill(feature: &str) { +async fn container_kill(feature: &str) { let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, feature); @@ -269,16 +266,14 @@ fn container_kill(feature: &str) { let docker = Docker::connect_with_local_defaults().unwrap(); if !docker_id.is_empty() { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on(docker.kill_container(feature, Some(KillContainerOptions { signal: "SIGINT" }))) - .expect("Unable to communicate with Docker"); + docker + .kill_container(feature, Some(KillContainerOptions { signal: "SIGINT" })) + .await + .expect("Unable to kill container"); } } -fn container_wait(feature: &str) { +async fn container_wait(feature: &str) { let db_connections = initialize_connection(); let feature_config = read_config(&db_connections, feature); @@ -290,7 +285,7 @@ fn container_wait(feature: &str) { let docker = Docker::connect_with_local_defaults().unwrap(); if *docker_id == *feature { - let version_option = container_version(&docker, feature); + let version_option = container_version(&docker, feature).await; if let Some(version) = version_option { update_data( &db_connections, @@ -325,21 +320,16 @@ fn container_wait(feature: &str) { } } - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - rt.block_on( - docker - .wait_container( - feature, - Some(WaitContainerOptions { - condition: "not-running", - }), - ) - .try_collect::>(), - ) - .expect("Unable to communicate with Docker"); + docker + .wait_container( + feature, + Some(WaitContainerOptions { + condition: "not-running", + }), + ) + .try_collect::>() + .await + .expect("Unable to wait for container"); } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] @@ -366,14 +356,15 @@ struct Cli { timeout: Option, } -fn main() { +#[tokio::main] +async fn main() { let cli = Cli::parse(); match cli.action { - Action::Start => container_start(&cli.name), - Action::Wait => container_wait(&cli.name), - Action::Stop => container_stop(&cli.name, cli.timeout), - Action::Kill => container_kill(&cli.name), + Action::Start => container_start(&cli.name).await, + Action::Wait => container_wait(&cli.name).await, + Action::Stop => container_stop(&cli.name, cli.timeout).await, + Action::Kill => container_kill(&cli.name).await, Action::Id => container_id(&cli.name), }; } From fcd9550cf8867f258b8cff757ccaed2f03167a95 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 3 Feb 2025 14:02:20 -0800 Subject: [PATCH 17/22] Actually have remote_ctr_enabled do something Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 6544fa9..035a203 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -7,6 +7,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; use std::io::Read; +use std::path::Path; use std::thread::sleep; use std::time::Duration; use swss_common::{CxxString, DbConnector}; @@ -138,7 +139,7 @@ fn initialize_connection() -> DbConnections { DbConnections { config_db: DbConnector::new_tcp(4, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), state_db: DbConnector::new_tcp(6, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), - remote_ctr_enabled: false, + remote_ctr_enabled: Path::new("/lib/systemd/system/ctrmgrd.service").exists(), } } From 204724dbb67922f66cd2b71069fcb340325cbd6b Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 3 Feb 2025 14:46:02 -0800 Subject: [PATCH 18/22] Move crate packages up to the workspace Cargo file Signed-off-by: Saikrishna Arcot --- Cargo.toml | 4 ++++ crates/container/Cargo.toml | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 794f428..45d66d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,10 @@ atomic_enum = "0.3" tempfile = "3" tabled = "0.17" futures-core = "0.3" +futures-util = "0.3" +chrono = "0.4" +enumset = "1" +bollard = { version = "0.17.1", features = ["chrono"] } # Internal dependencies sonic-common = { version = "0.1.0", path = "crates/sonic-common" } diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml index 86769f8..90d8896 100644 --- a/crates/container/Cargo.toml +++ b/crates/container/Cargo.toml @@ -9,12 +9,12 @@ keywords.workspace = true edition.workspace = true [dependencies] -bollard = { version = "0.17.1", features = ["chrono"] } -chrono = "0.4.38" -enumset = "1.1.5" -futures-util = "0.3.30" +bollard = { workspace = true } +chrono = { workspace = true } +enumset = { workspace = true } +futures-util = { workspace = true } clap = { workspace = true } -swss-common = { version = "0.1.0", path = "../swss-common" } +swss-common = { path = "../swss-common" } tokio = { workspace = true } serde_json = { workspace = true } From 95ecadf8b4eaf27ac6f1104afab49c841f8df83d Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 3 Feb 2025 14:46:23 -0800 Subject: [PATCH 19/22] Update the futures-* packages for version consistency with futures-core Signed-off-by: Saikrishna Arcot --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41a803a..46535d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,9 +681,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -698,15 +698,15 @@ checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", From e3ef7499345ba8ea257f4e3bd3b709deec4e9007 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Mon, 3 Mar 2025 09:29:18 -0800 Subject: [PATCH 20/22] Port over some missing code Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index 035a203..f5b04c8 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -35,8 +35,8 @@ const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; const KUBE_LABEL_SET_KEY: &str = "SET"; const SERVER_TABLE: &str = "KUBERNETES_MASTER"; const SERVER_KEY: &str = "SERVER"; -//const ST_SER_CONNECTED: &str = "connected"; -//const ST_SER_UPDATE_TS: &str = "update_time"; +const ST_SER_CONNECTED: &str = "connected"; +const ST_SER_UPDATE_TS: &str = "update_time"; // Get seconds to wait for remote docker to start. // If not, revert to local @@ -94,6 +94,15 @@ fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static fields } +fn read_server_state(db_connections: &DbConnections) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (ST_SER_CONNECTED, "false".to_string()), + (ST_SER_UPDATE_TS, "".to_string()), + ]); + read_data(&db_connections.state_db, SERVER_KEY, &mut fields); + fields +} + fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { if db_connections.remote_ctr_enabled { db_connections @@ -173,6 +182,7 @@ async fn container_start(feature: &str) { let feature_config = read_config(&db_connections, feature); let feature_state = read_state(&db_connections, feature); + let server_state = read_server_state(&db_connections); let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); let mut data: HashMap<&str, &str> = HashMap::from([(SYSTEM_STATE, "up"), (UPD_TIMESTAMP, ×tamp)]); @@ -182,7 +192,10 @@ async fn container_start(feature: &str) { start_val |= StartFlags::StartLocal; } else { start_val |= StartFlags::StartKube; - if feature_config.get(NO_FALLBACK).unwrap() == "False" && feature_state.get(REMOTE_STATE).unwrap() == "none" { + if feature_config.get(NO_FALLBACK).unwrap() == "False" + && (feature_state.get(REMOTE_STATE).unwrap() == "none" + || server_state.get(ST_SER_CONNECTED).unwrap() == "false") + { start_val |= StartFlags::StartLocal; data.insert(REMOTE_STATE, "none"); } @@ -215,12 +228,17 @@ async fn container_start(feature: &str) { async fn container_stop(feature: &str, timeout: Option) { let db_connections = initialize_connection(); - //let feature_config = read_config(&db_connections, feature); + let feature_config = read_config(&db_connections, feature); let feature_state = read_state(&db_connections, feature); let docker_id = get_container_id(feature, &db_connections); + let remove_label = feature_config.get(SET_OWNER).unwrap() != "local"; let docker = Docker::connect_with_local_defaults().unwrap(); + if remove_label { + set_label(&db_connections, feature, false); + } + if !docker_id.is_empty() { let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); docker From 178e210f5770a2a78386e2c1403641c05d88d6d5 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Wed, 5 Mar 2025 17:23:06 -0800 Subject: [PATCH 21/22] Rename DB-related fields to add DB_ or DB_FIELD_ prefix Signed-off-by: Saikrishna Arcot --- crates/container/src/main.rs | 138 +++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index f5b04c8..f6a2da0 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -19,24 +19,24 @@ struct DbConnections { } // DB field names -const FEATURE_TABLE: &str = "FEATURE"; -const SET_OWNER: &str = "set_owner"; -const NO_FALLBACK: &str = "no_fallback_to_local"; - -const CURRENT_OWNER: &str = "current_owner"; -const UPD_TIMESTAMP: &str = "update_time"; -const CONTAINER_ID: &str = "container_id"; -const REMOTE_STATE: &str = "remote_state"; -const SYSTEM_STATE: &str = "system_state"; -const STATE: &str = "state"; -const ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; - -const KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; -const KUBE_LABEL_SET_KEY: &str = "SET"; -const SERVER_TABLE: &str = "KUBERNETES_MASTER"; -const SERVER_KEY: &str = "SERVER"; -const ST_SER_CONNECTED: &str = "connected"; -const ST_SER_UPDATE_TS: &str = "update_time"; +const DB_FEATURE_TABLE: &str = "FEATURE"; +const DB_FIELD_SET_OWNER: &str = "set_owner"; +const DB_FIELD_NO_FALLBACK: &str = "no_fallback_to_local"; + +const DB_FIELD_CURRENT_OWNER: &str = "current_owner"; +const DB_FIELD_UPD_TIMESTAMP: &str = "update_time"; +const DB_FIELD_CONTAINER_ID: &str = "container_id"; +const DB_FIELD_REMOTE_STATE: &str = "remote_state"; +const DB_FIELD_SYSTEM_STATE: &str = "system_state"; +const DB_FIELD_STATE: &str = "state"; +const DB_FIELD_ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; + +const DB_KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +const DB_KUBE_LABEL_SET_KEY: &str = "SET"; +const DB_SERVER_TABLE: &str = "KUBERNETES_MASTER"; +const DB_SERVER_KEY: &str = "SERVER"; +const DB_FIELD_ST_SER_CONNECTED: &str = "connected"; +const DB_FIELD_ST_SER_UPDATE_TS: &str = "update_time"; // Get seconds to wait for remote docker to start. // If not, revert to local @@ -58,10 +58,10 @@ fn get_config_data(field: &str) -> Option { } fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&str, String>) { - let table_name = if feature == SERVER_KEY { - SERVER_TABLE + let table_name = if feature == DB_SERVER_KEY { + DB_SERVER_TABLE } else { - FEATURE_TABLE + DB_FEATURE_TABLE }; let data = db_connector @@ -76,9 +76,9 @@ fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&st fn read_config(db_connections: &DbConnections, feature: &str) -> HashMap<&'static str, String> { let mut fields: HashMap<&str, String> = HashMap::from([ - (SET_OWNER, "local".to_string()), - (NO_FALLBACK, "False".to_string()), - (STATE, "disabled".to_string()), + (DB_FIELD_SET_OWNER, "local".to_string()), + (DB_FIELD_NO_FALLBACK, "False".to_string()), + (DB_FIELD_STATE, "disabled".to_string()), ]); read_data(&db_connections.config_db, feature, &mut fields); fields @@ -86,9 +86,9 @@ fn read_config(db_connections: &DbConnections, feature: &str) -> HashMap<&'stati fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static str, String> { let mut fields: HashMap<&str, String> = HashMap::from([ - (CURRENT_OWNER, "local".to_string()), - (REMOTE_STATE, "none".to_string()), - (CONTAINER_ID, "".to_string()), + (DB_FIELD_CURRENT_OWNER, "local".to_string()), + (DB_FIELD_REMOTE_STATE, "none".to_string()), + (DB_FIELD_CONTAINER_ID, "".to_string()), ]); read_data(&db_connections.state_db, feature, &mut fields); fields @@ -96,10 +96,10 @@ fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static fn read_server_state(db_connections: &DbConnections) -> HashMap<&'static str, String> { let mut fields: HashMap<&str, String> = HashMap::from([ - (ST_SER_CONNECTED, "false".to_string()), - (ST_SER_UPDATE_TS, "".to_string()), + (DB_FIELD_ST_SER_CONNECTED, "false".to_string()), + (DB_FIELD_ST_SER_UPDATE_TS, "".to_string()), ]); - read_data(&db_connections.state_db, SERVER_KEY, &mut fields); + read_data(&db_connections.state_db, DB_SERVER_KEY, &mut fields); fields } @@ -108,8 +108,8 @@ fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { db_connections .state_db .hset( - KUBE_LABEL_TABLE, - &format!("{}|{}_enabled", KUBE_LABEL_SET_KEY, feature), + DB_KUBE_LABEL_TABLE, + &format!("{}|{}_enabled", DB_KUBE_LABEL_SET_KEY, feature), &CxxString::new(if create { "true" } else { "false" }), ) .expect("Unable to set label of container in Redis"); @@ -119,10 +119,11 @@ fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&str, &str>) { if db_connections.remote_ctr_enabled { for (key, value) in data.iter() { - let _ = - db_connections - .state_db - .hset(FEATURE_TABLE, &format!("{}|{}", feature, key), &CxxString::new(value)); + let _ = db_connections.state_db.hset( + DB_FEATURE_TABLE, + &format!("{}|{}", feature, key), + &CxxString::new(value), + ); } } } @@ -157,10 +158,14 @@ fn get_container_id<'a>(feature: &'a str, db_connections: &DbConnections) -> Cow .state_db .hgetall(&format!("FEATURE|{}", feature)) .expect("Unable to get data"); - if data.get(CURRENT_OWNER).map_or("", |value| value.to_str().unwrap()) == "local" { + if data + .get(DB_FIELD_CURRENT_OWNER) + .map_or("", |value| value.to_str().unwrap()) + == "local" + { Cow::Borrowed(feature) } else { - data.get(CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { + data.get(DB_FIELD_CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { Cow::Owned(value.to_str().unwrap().to_string()) }) } @@ -185,28 +190,29 @@ async fn container_start(feature: &str) { let server_state = read_server_state(&db_connections); let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data: HashMap<&str, &str> = HashMap::from([(SYSTEM_STATE, "up"), (UPD_TIMESTAMP, ×tamp)]); + let mut data: HashMap<&str, &str> = + HashMap::from([(DB_FIELD_SYSTEM_STATE, "up"), (DB_FIELD_UPD_TIMESTAMP, ×tamp)]); let mut start_val = enumset::EnumSet::new(); - if feature_config.get(SET_OWNER).unwrap() == "local" { + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { start_val |= StartFlags::StartLocal; } else { start_val |= StartFlags::StartKube; - if feature_config.get(NO_FALLBACK).unwrap() == "False" - && (feature_state.get(REMOTE_STATE).unwrap() == "none" - || server_state.get(ST_SER_CONNECTED).unwrap() == "false") + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" + && (feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "none" + || server_state.get(DB_FIELD_ST_SER_CONNECTED).unwrap() == "false") { start_val |= StartFlags::StartLocal; - data.insert(REMOTE_STATE, "none"); + data.insert(DB_FIELD_REMOTE_STATE, "none"); } } if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { - data.insert(CURRENT_OWNER, "local"); - data.insert(CONTAINER_ID, feature); + data.insert(DB_FIELD_CURRENT_OWNER, "local"); + data.insert(DB_FIELD_CONTAINER_ID, feature); if start_val == StartFlags::StartLocal { set_label(&db_connections, feature, false); - data.insert(REMOTE_STATE, "none"); + data.insert(DB_FIELD_REMOTE_STATE, "none"); } } @@ -231,7 +237,7 @@ async fn container_stop(feature: &str, timeout: Option) { let feature_config = read_config(&db_connections, feature); let feature_state = read_state(&db_connections, feature); let docker_id = get_container_id(feature, &db_connections); - let remove_label = feature_config.get(SET_OWNER).unwrap() != "local"; + let remove_label = feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local"; let docker = Docker::connect_with_local_defaults().unwrap(); @@ -249,13 +255,13 @@ async fn container_stop(feature: &str, timeout: Option) { let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); let mut data: HashMap<&str, &str> = HashMap::from([ - (CURRENT_OWNER, "none"), - (SYSTEM_STATE, "down"), - (CONTAINER_ID, ""), - (UPD_TIMESTAMP, ×tamp), + (DB_FIELD_CURRENT_OWNER, "none"), + (DB_FIELD_SYSTEM_STATE, "down"), + (DB_FIELD_CONTAINER_ID, ""), + (DB_FIELD_UPD_TIMESTAMP, ×tamp), ]); - if feature_state.get(REMOTE_STATE).unwrap() == "running" { - data.insert(REMOTE_STATE, "stopped"); + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "running" { + data.insert(DB_FIELD_REMOTE_STATE, "stopped"); } update_data(&db_connections, feature, &data); @@ -267,15 +273,15 @@ async fn container_kill(feature: &str) { let feature_config = read_config(&db_connections, feature); let feature_state = read_state(&db_connections, feature); let docker_id = get_container_id(feature, &db_connections); - let remove_label = - (feature_config.get(SET_OWNER).unwrap() != "local") || (feature_state.get(CURRENT_OWNER).unwrap() != "local"); + let remove_label = (feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local") + || (feature_state.get(DB_FIELD_CURRENT_OWNER).unwrap() != "local"); if remove_label { set_label(&db_connections, feature, false); } - if feature_config.get(SET_OWNER).unwrap() == "local" { - let current_state = feature_state.get(STATE).unwrap(); + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { + let current_state = feature_state.get(DB_FIELD_STATE).unwrap(); if current_state != "enabled" && current_state != "always_enabled" { println!("{} is not enabled", feature); return; @@ -309,12 +315,12 @@ async fn container_wait(feature: &str) { update_data( &db_connections, feature, - &HashMap::from([(ST_FEAT_CTR_STABLE_VER, version.as_str())]), + &HashMap::from([(DB_FIELD_ST_FEAT_CTR_STABLE_VER, version.as_str())]), ); } } - if docker_id.is_empty() && feature_config.get(NO_FALLBACK).unwrap() == "False" { + if docker_id.is_empty() && feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS) .and_then(|value| value.as_u64()) .map(|value| value as u32) @@ -322,7 +328,7 @@ async fn container_wait(feature: &str) { } while docker_id.is_empty() { - if feature_config.get(NO_FALLBACK).unwrap() == "False" { + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { if pend_wait_seconds < WAIT_POLL_SECS { break; } @@ -332,10 +338,14 @@ async fn container_wait(feature: &str) { sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); feature_state = read_state(&db_connections, feature); - docker_id = Cow::Borrowed(feature_state.get(CONTAINER_ID).unwrap()); + docker_id = Cow::Borrowed(feature_state.get(DB_FIELD_CONTAINER_ID).unwrap()); - if feature_state.get(REMOTE_STATE).unwrap() == "pending" { - update_data(&db_connections, feature, &HashMap::from([(REMOTE_STATE, "ready")])); + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "pending" { + update_data( + &db_connections, + feature, + &HashMap::from([(DB_FIELD_REMOTE_STATE, "ready")]), + ); } } From 8ba0bd65f979176871afdc2fe05f693a501f79de Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Fri, 7 Mar 2025 09:41:20 -0800 Subject: [PATCH 22/22] Move container functions into a class Signed-off-by: Saikrishna Arcot --- crates/container/src/container.rs | 358 ++++++++++++++++++++++++++++ crates/container/src/main.rs | 375 +----------------------------- 2 files changed, 367 insertions(+), 366 deletions(-) create mode 100644 crates/container/src/container.rs diff --git a/crates/container/src/container.rs b/crates/container/src/container.rs new file mode 100644 index 0000000..ea82114 --- /dev/null +++ b/crates/container/src/container.rs @@ -0,0 +1,358 @@ +use bollard::container::*; +use bollard::Docker; +use chrono::Local; +use futures_util::stream::TryStreamExt; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::thread::sleep; +use std::time::Duration; +use swss_common::{CxxString, DbConnector}; + +// DB field names +const DB_FEATURE_TABLE: &str = "FEATURE"; +const DB_FIELD_SET_OWNER: &str = "set_owner"; +const DB_FIELD_NO_FALLBACK: &str = "no_fallback_to_local"; + +const DB_FIELD_CURRENT_OWNER: &str = "current_owner"; +const DB_FIELD_UPD_TIMESTAMP: &str = "update_time"; +const DB_FIELD_CONTAINER_ID: &str = "container_id"; +const DB_FIELD_REMOTE_STATE: &str = "remote_state"; +const DB_FIELD_SYSTEM_STATE: &str = "system_state"; +const DB_FIELD_STATE: &str = "state"; +const DB_FIELD_ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; + +const DB_KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +const DB_KUBE_LABEL_SET_KEY: &str = "SET"; +const DB_SERVER_TABLE: &str = "KUBERNETES_MASTER"; +const DB_SERVER_KEY: &str = "SERVER"; +const DB_FIELD_ST_SER_CONNECTED: &str = "connected"; +const DB_FIELD_ST_SER_UPDATE_TS: &str = "update_time"; + +// Get seconds to wait for remote docker to start. +// If not, revert to local +// +const SONIC_CTR_CONFIG: &str = "/etc/sonic/remote_ctr.config.json"; +const SONIC_CTR_CONFIG_PEND_SECS: &str = "revert_to_local_on_wait_seconds"; +const DEFAULT_PEND_SECS: u32 = 5 * 60; +const WAIT_POLL_SECS: u32 = 2; + +struct DbConnections { + config_db: DbConnector, + state_db: DbConnector, + remote_ctr_enabled: bool, +} + +impl DbConnections { + fn initialize_connection() -> DbConnections { + DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), + state_db: DbConnector::new_tcp(6, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), + remote_ctr_enabled: Path::new("/lib/systemd/system/ctrmgrd.service").exists(), + } + } +} + +pub struct Container<'a> { + feature: &'a str, + db_connections: DbConnections, +} + +#[derive(enumset::EnumSetType, Debug)] +enum StartFlags { + StartLocal, + StartKube, +} + +impl<'a> Container<'a> { + fn get_config_data(field: &str) -> Option { + let mut file = match File::open(SONIC_CTR_CONFIG) { + Ok(f) => f, + Err(_e) => return None, + }; + let mut file_contents = String::new(); + file.read_to_string(&mut file_contents).unwrap(); + let data: serde_json::Value = serde_json::from_str(&file_contents).unwrap(); + data.as_object().unwrap().get(field).cloned() + } + + fn read_data(db_connector: &DbConnector, key: &str, fields: &mut HashMap<&str, String>) { + let table_name = if key == DB_SERVER_KEY { + DB_SERVER_TABLE + } else { + DB_FEATURE_TABLE + }; + + let data = db_connector + .hgetall(&format!("{}|{}", table_name, key)) + .expect("Unable to get data"); + for (field, default) in fields.iter_mut() { + if let Some(value) = data.get(field as &str) { + *default = value.to_str().unwrap().to_string() + } + } + } + + pub fn new(feature: &str) -> Container { + Container { + feature, + db_connections: DbConnections::initialize_connection(), + } + } + + fn read_config(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_SET_OWNER, "local".to_string()), + (DB_FIELD_NO_FALLBACK, "False".to_string()), + (DB_FIELD_STATE, "disabled".to_string()), + ]); + Self::read_data(&self.db_connections.config_db, self.feature, &mut fields); + fields + } + + fn read_state(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_CURRENT_OWNER, "local".to_string()), + (DB_FIELD_REMOTE_STATE, "none".to_string()), + (DB_FIELD_CONTAINER_ID, "".to_string()), + ]); + Self::read_data(&self.db_connections.state_db, self.feature, &mut fields); + fields + } + + fn read_server_state(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_ST_SER_CONNECTED, "false".to_string()), + (DB_FIELD_ST_SER_UPDATE_TS, "".to_string()), + ]); + Self::read_data(&self.db_connections.state_db, DB_SERVER_KEY, &mut fields); + fields + } + + fn set_label(&self, feature: &str, create: bool) { + if self.db_connections.remote_ctr_enabled { + self.db_connections + .state_db + .hset( + DB_KUBE_LABEL_TABLE, + &format!("{}|{}_enabled", DB_KUBE_LABEL_SET_KEY, feature), + &CxxString::new(if create { "true" } else { "false" }), + ) + .expect("Unable to set label of container in Redis"); + } + } + + fn update_data(&self, data: &HashMap<&str, &str>) { + if self.db_connections.remote_ctr_enabled { + for (key, value) in data.iter() { + let _ = self.db_connections.state_db.hset( + DB_FEATURE_TABLE, + &format!("{}|{}", self.feature, key), + &CxxString::new(value), + ); + } + } + } + + async fn container_version(&self, docker: &Docker) -> Option { + let container_options = docker + .inspect_container(self.feature, None) + .await + .expect("Error getting the version of container"); + if let Some(config) = container_options.config { + if let Some(envs) = config.env { + for env in envs { + if env.starts_with("IMAGE_VERSION=") { + return Some(env.split('=').collect::>()[1].to_string()); + } + } + } + } + None + } + + pub fn container_id(&self) -> Cow<'a, str> { + let data = self + .db_connections + .state_db + .hgetall(&format!("FEATURE|{}", self.feature)) + .expect("Unable to get data"); + if data + .get(DB_FIELD_CURRENT_OWNER) + .map_or("", |value| value.to_str().unwrap()) + == "local" + { + Cow::Borrowed(self.feature) + } else { + data.get(DB_FIELD_CONTAINER_ID) + .map_or(Cow::Borrowed(self.feature), |value| { + Cow::Owned(value.to_str().unwrap().to_string()) + }) + } + } + + pub async fn start(&self) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let server_state = self.read_server_state(); + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data: HashMap<&str, &str> = + HashMap::from([(DB_FIELD_SYSTEM_STATE, "up"), (DB_FIELD_UPD_TIMESTAMP, ×tamp)]); + + let mut start_val = enumset::EnumSet::new(); + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { + start_val |= StartFlags::StartLocal; + } else { + start_val |= StartFlags::StartKube; + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" + && (feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "none" + || server_state.get(DB_FIELD_ST_SER_CONNECTED).unwrap() == "false") + { + start_val |= StartFlags::StartLocal; + data.insert(DB_FIELD_REMOTE_STATE, "none"); + } + } + + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + data.insert(DB_FIELD_CURRENT_OWNER, "local"); + data.insert(DB_FIELD_CONTAINER_ID, self.feature); + if start_val == StartFlags::StartLocal { + self.set_label(self.feature, false); + data.insert(DB_FIELD_REMOTE_STATE, "none"); + } + } + + self.update_data(&data); + + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + let docker = Docker::connect_with_local_defaults().unwrap(); + docker + .start_container(self.feature, None::>) + .await + .expect("Unable to start container"); + } + + if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { + self.set_label(self.feature, true); + } + } + + pub async fn stop(&self, timeout: Option) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let docker_id = self.container_id(); + let remove_label = feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local"; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if remove_label { + self.set_label(self.feature, false); + } + + if !docker_id.is_empty() { + let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); + docker + .stop_container(self.feature, stop_options) + .await + .expect("Unable to stop container"); + } + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data: HashMap<&str, &str> = HashMap::from([ + (DB_FIELD_CURRENT_OWNER, "none"), + (DB_FIELD_SYSTEM_STATE, "down"), + (DB_FIELD_CONTAINER_ID, ""), + (DB_FIELD_UPD_TIMESTAMP, ×tamp), + ]); + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "running" { + data.insert(DB_FIELD_REMOTE_STATE, "stopped"); + } + + self.update_data(&data); + } + + pub async fn kill(&self) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let docker_id = self.container_id(); + let remove_label = (feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local") + || (feature_state.get(DB_FIELD_CURRENT_OWNER).unwrap() != "local"); + + if remove_label { + self.set_label(self.feature, false); + } + + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { + let current_state = feature_state.get(DB_FIELD_STATE).unwrap(); + if current_state != "enabled" && current_state != "always_enabled" { + println!("{} is not enabled", self.feature); + return; + } + } + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if !docker_id.is_empty() { + docker + .kill_container(self.feature, Some(KillContainerOptions { signal: "SIGINT" })) + .await + .expect("Unable to kill container"); + } + } + + pub async fn wait(&self) { + let feature_config = self.read_config(); + //let mut feature_state = read_state(&db_connections, feature); + let mut feature_state; + let mut docker_id = self.container_id(); + let mut pend_wait_seconds: u32 = 0; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if *docker_id == *self.feature { + let version_option = self.container_version(&docker).await; + if let Some(version) = version_option { + self.update_data(&HashMap::from([(DB_FIELD_ST_FEAT_CTR_STABLE_VER, version.as_str())])); + } + } + + if docker_id.is_empty() && feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { + pend_wait_seconds = Self::get_config_data(SONIC_CTR_CONFIG_PEND_SECS) + .and_then(|value| value.as_u64()) + .map(|value| value as u32) + .unwrap_or(DEFAULT_PEND_SECS); + } + + while docker_id.is_empty() { + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { + if pend_wait_seconds < WAIT_POLL_SECS { + break; + } + pend_wait_seconds -= WAIT_POLL_SECS; + } + + sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); + feature_state = self.read_state(); + + docker_id = Cow::Borrowed(feature_state.get(DB_FIELD_CONTAINER_ID).unwrap()); + + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "pending" { + self.update_data(&HashMap::from([(DB_FIELD_REMOTE_STATE, "ready")])); + } + } + + docker + .wait_container( + self.feature, + Some(WaitContainerOptions { + condition: "not-running", + }), + ) + .try_collect::>() + .await + .expect("Unable to wait for container"); + } +} diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs index f6a2da0..53accd4 100644 --- a/crates/container/src/main.rs +++ b/crates/container/src/main.rs @@ -1,365 +1,6 @@ -use bollard::container::*; -use bollard::Docker; -use chrono::Local; use clap::{Parser, ValueEnum}; -use futures_util::stream::TryStreamExt; -use std::borrow::Cow; -use std::collections::HashMap; -use std::fs::File; -use std::io::Read; -use std::path::Path; -use std::thread::sleep; -use std::time::Duration; -use swss_common::{CxxString, DbConnector}; - -struct DbConnections { - config_db: DbConnector, - state_db: DbConnector, - remote_ctr_enabled: bool, -} - -// DB field names -const DB_FEATURE_TABLE: &str = "FEATURE"; -const DB_FIELD_SET_OWNER: &str = "set_owner"; -const DB_FIELD_NO_FALLBACK: &str = "no_fallback_to_local"; - -const DB_FIELD_CURRENT_OWNER: &str = "current_owner"; -const DB_FIELD_UPD_TIMESTAMP: &str = "update_time"; -const DB_FIELD_CONTAINER_ID: &str = "container_id"; -const DB_FIELD_REMOTE_STATE: &str = "remote_state"; -const DB_FIELD_SYSTEM_STATE: &str = "system_state"; -const DB_FIELD_STATE: &str = "state"; -const DB_FIELD_ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; - -const DB_KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; -const DB_KUBE_LABEL_SET_KEY: &str = "SET"; -const DB_SERVER_TABLE: &str = "KUBERNETES_MASTER"; -const DB_SERVER_KEY: &str = "SERVER"; -const DB_FIELD_ST_SER_CONNECTED: &str = "connected"; -const DB_FIELD_ST_SER_UPDATE_TS: &str = "update_time"; - -// Get seconds to wait for remote docker to start. -// If not, revert to local -// -const SONIC_CTR_CONFIG: &str = "/etc/sonic/remote_ctr.config.json"; -const SONIC_CTR_CONFIG_PEND_SECS: &str = "revert_to_local_on_wait_seconds"; -const DEFAULT_PEND_SECS: u32 = 5 * 60; -const WAIT_POLL_SECS: u32 = 2; - -fn get_config_data(field: &str) -> Option { - let mut file = match File::open(SONIC_CTR_CONFIG) { - Ok(f) => f, - Err(_e) => return None, - }; - let mut file_contents = String::new(); - file.read_to_string(&mut file_contents).unwrap(); - let data: serde_json::Value = serde_json::from_str(&file_contents).unwrap(); - data.as_object().unwrap().get(field).cloned() -} - -fn read_data(db_connector: &DbConnector, feature: &str, fields: &mut HashMap<&str, String>) { - let table_name = if feature == DB_SERVER_KEY { - DB_SERVER_TABLE - } else { - DB_FEATURE_TABLE - }; - - let data = db_connector - .hgetall(&format!("{}|{}", table_name, feature)) - .expect("Unable to get data"); - for (field, default) in fields.iter_mut() { - if let Some(value) = data.get(field as &str) { - *default = value.to_str().unwrap().to_string() - } - } -} - -fn read_config(db_connections: &DbConnections, feature: &str) -> HashMap<&'static str, String> { - let mut fields: HashMap<&str, String> = HashMap::from([ - (DB_FIELD_SET_OWNER, "local".to_string()), - (DB_FIELD_NO_FALLBACK, "False".to_string()), - (DB_FIELD_STATE, "disabled".to_string()), - ]); - read_data(&db_connections.config_db, feature, &mut fields); - fields -} - -fn read_state(db_connections: &DbConnections, feature: &str) -> HashMap<&'static str, String> { - let mut fields: HashMap<&str, String> = HashMap::from([ - (DB_FIELD_CURRENT_OWNER, "local".to_string()), - (DB_FIELD_REMOTE_STATE, "none".to_string()), - (DB_FIELD_CONTAINER_ID, "".to_string()), - ]); - read_data(&db_connections.state_db, feature, &mut fields); - fields -} - -fn read_server_state(db_connections: &DbConnections) -> HashMap<&'static str, String> { - let mut fields: HashMap<&str, String> = HashMap::from([ - (DB_FIELD_ST_SER_CONNECTED, "false".to_string()), - (DB_FIELD_ST_SER_UPDATE_TS, "".to_string()), - ]); - read_data(&db_connections.state_db, DB_SERVER_KEY, &mut fields); - fields -} - -fn set_label(db_connections: &DbConnections, feature: &str, create: bool) { - if db_connections.remote_ctr_enabled { - db_connections - .state_db - .hset( - DB_KUBE_LABEL_TABLE, - &format!("{}|{}_enabled", DB_KUBE_LABEL_SET_KEY, feature), - &CxxString::new(if create { "true" } else { "false" }), - ) - .expect("Unable to set label of container in Redis"); - } -} - -fn update_data(db_connections: &DbConnections, feature: &str, data: &HashMap<&str, &str>) { - if db_connections.remote_ctr_enabled { - for (key, value) in data.iter() { - let _ = db_connections.state_db.hset( - DB_FEATURE_TABLE, - &format!("{}|{}", feature, key), - &CxxString::new(value), - ); - } - } -} - -async fn container_version(docker: &Docker, feature: &str) -> Option { - let container_options = docker - .inspect_container(feature, None) - .await - .expect("Error getting the version of container"); - if let Some(config) = container_options.config { - if let Some(envs) = config.env { - for env in envs { - if env.starts_with("IMAGE_VERSION=") { - return Some(env.split('=').collect::>()[1].to_string()); - } - } - } - } - None -} - -fn initialize_connection() -> DbConnections { - DbConnections { - config_db: DbConnector::new_tcp(4, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), - state_db: DbConnector::new_tcp(6, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), - remote_ctr_enabled: Path::new("/lib/systemd/system/ctrmgrd.service").exists(), - } -} - -fn get_container_id<'a>(feature: &'a str, db_connections: &DbConnections) -> Cow<'a, str> { - let data = db_connections - .state_db - .hgetall(&format!("FEATURE|{}", feature)) - .expect("Unable to get data"); - if data - .get(DB_FIELD_CURRENT_OWNER) - .map_or("", |value| value.to_str().unwrap()) - == "local" - { - Cow::Borrowed(feature) - } else { - data.get(DB_FIELD_CONTAINER_ID).map_or(Cow::Borrowed(feature), |value| { - Cow::Owned(value.to_str().unwrap().to_string()) - }) - } -} - -fn container_id(feature: &str) { - let db_connections = initialize_connection(); - println!("{}", get_container_id(feature, &db_connections)); -} - -#[derive(enumset::EnumSetType, Debug)] -enum StartFlags { - StartLocal, - StartKube, -} - -async fn container_start(feature: &str) { - let db_connections = initialize_connection(); - - let feature_config = read_config(&db_connections, feature); - let feature_state = read_state(&db_connections, feature); - let server_state = read_server_state(&db_connections); - - let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data: HashMap<&str, &str> = - HashMap::from([(DB_FIELD_SYSTEM_STATE, "up"), (DB_FIELD_UPD_TIMESTAMP, ×tamp)]); - - let mut start_val = enumset::EnumSet::new(); - if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { - start_val |= StartFlags::StartLocal; - } else { - start_val |= StartFlags::StartKube; - if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" - && (feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "none" - || server_state.get(DB_FIELD_ST_SER_CONNECTED).unwrap() == "false") - { - start_val |= StartFlags::StartLocal; - data.insert(DB_FIELD_REMOTE_STATE, "none"); - } - } - - if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { - data.insert(DB_FIELD_CURRENT_OWNER, "local"); - data.insert(DB_FIELD_CONTAINER_ID, feature); - if start_val == StartFlags::StartLocal { - set_label(&db_connections, feature, false); - data.insert(DB_FIELD_REMOTE_STATE, "none"); - } - } - - update_data(&db_connections, feature, &data); - - if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { - let docker = Docker::connect_with_local_defaults().unwrap(); - docker - .start_container(feature, None::>) - .await - .expect("Unable to start container"); - } - - if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { - set_label(&db_connections, feature, true); - } -} - -async fn container_stop(feature: &str, timeout: Option) { - let db_connections = initialize_connection(); - - let feature_config = read_config(&db_connections, feature); - let feature_state = read_state(&db_connections, feature); - let docker_id = get_container_id(feature, &db_connections); - let remove_label = feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local"; - - let docker = Docker::connect_with_local_defaults().unwrap(); - - if remove_label { - set_label(&db_connections, feature, false); - } - - if !docker_id.is_empty() { - let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); - docker - .stop_container(feature, stop_options) - .await - .expect("Unable to stop container"); - } - - let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); - let mut data: HashMap<&str, &str> = HashMap::from([ - (DB_FIELD_CURRENT_OWNER, "none"), - (DB_FIELD_SYSTEM_STATE, "down"), - (DB_FIELD_CONTAINER_ID, ""), - (DB_FIELD_UPD_TIMESTAMP, ×tamp), - ]); - if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "running" { - data.insert(DB_FIELD_REMOTE_STATE, "stopped"); - } - - update_data(&db_connections, feature, &data); -} - -async fn container_kill(feature: &str) { - let db_connections = initialize_connection(); - - let feature_config = read_config(&db_connections, feature); - let feature_state = read_state(&db_connections, feature); - let docker_id = get_container_id(feature, &db_connections); - let remove_label = (feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local") - || (feature_state.get(DB_FIELD_CURRENT_OWNER).unwrap() != "local"); - - if remove_label { - set_label(&db_connections, feature, false); - } - - if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { - let current_state = feature_state.get(DB_FIELD_STATE).unwrap(); - if current_state != "enabled" && current_state != "always_enabled" { - println!("{} is not enabled", feature); - return; - } - } - - let docker = Docker::connect_with_local_defaults().unwrap(); - - if !docker_id.is_empty() { - docker - .kill_container(feature, Some(KillContainerOptions { signal: "SIGINT" })) - .await - .expect("Unable to kill container"); - } -} - -async fn container_wait(feature: &str) { - let db_connections = initialize_connection(); - - let feature_config = read_config(&db_connections, feature); - //let mut feature_state = read_state(&db_connections, feature); - let mut feature_state; - let mut docker_id = get_container_id(feature, &db_connections); - let mut pend_wait_seconds: u32 = 0; - - let docker = Docker::connect_with_local_defaults().unwrap(); - - if *docker_id == *feature { - let version_option = container_version(&docker, feature).await; - if let Some(version) = version_option { - update_data( - &db_connections, - feature, - &HashMap::from([(DB_FIELD_ST_FEAT_CTR_STABLE_VER, version.as_str())]), - ); - } - } - - if docker_id.is_empty() && feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { - pend_wait_seconds = get_config_data(SONIC_CTR_CONFIG_PEND_SECS) - .and_then(|value| value.as_u64()) - .map(|value| value as u32) - .unwrap_or(DEFAULT_PEND_SECS); - } - - while docker_id.is_empty() { - if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { - if pend_wait_seconds < WAIT_POLL_SECS { - break; - } - pend_wait_seconds -= WAIT_POLL_SECS; - } - - sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); - feature_state = read_state(&db_connections, feature); - - docker_id = Cow::Borrowed(feature_state.get(DB_FIELD_CONTAINER_ID).unwrap()); - - if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "pending" { - update_data( - &db_connections, - feature, - &HashMap::from([(DB_FIELD_REMOTE_STATE, "ready")]), - ); - } - } - - docker - .wait_container( - feature, - Some(WaitContainerOptions { - condition: "not-running", - }), - ) - .try_collect::>() - .await - .expect("Unable to wait for container"); -} +use container::Container; +mod container; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum Action { @@ -389,11 +30,13 @@ struct Cli { async fn main() { let cli = Cli::parse(); + let container = Container::new(&cli.name); + match cli.action { - Action::Start => container_start(&cli.name).await, - Action::Wait => container_wait(&cli.name).await, - Action::Stop => container_stop(&cli.name, cli.timeout).await, - Action::Kill => container_kill(&cli.name).await, - Action::Id => container_id(&cli.name), + Action::Start => container.start().await, + Action::Wait => container.wait().await, + Action::Stop => container.stop(cli.timeout).await, + Action::Kill => container.kill().await, + Action::Id => println!("{}", container.container_id()), }; }