diff --git a/Cargo.lock b/Cargo.lock index 9c74a0e..3720972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,16 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -266,6 +276,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -300,6 +319,15 @@ dependencies = [ "libc", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" @@ -430,6 +458,25 @@ dependencies = [ "wasi", ] +[[package]] +name = "h2" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -507,6 +554,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -550,6 +598,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.56" @@ -620,6 +681,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + [[package]] name = "is-terminal" version = "0.4.7" @@ -791,6 +858,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.142" @@ -868,7 +941,9 @@ dependencies = [ "k8s-openapi", "kube", "log", + "mktemp", "rand", + "reqwest", "schemars", "serde", "serde_json", @@ -888,6 +963,33 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "mktemp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bdc1f74dd7bb717d39f784f844e490d935b3aa7e383008006dbbf29c1f7820a" +dependencies = [ + "uuid", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -949,6 +1051,12 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "openssl-sys" version = "0.9.87" @@ -988,7 +1096,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -1109,6 +1217,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -1116,7 +1233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -1137,6 +1254,43 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "reqwest" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustix" version = "0.37.19" @@ -1157,6 +1311,15 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "schemars" version = "0.8.12" @@ -1203,6 +1366,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.162" @@ -1256,6 +1442,18 @@ 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_yaml" version = "0.9.21" @@ -1331,6 +1529,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1415,6 +1626,16 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-openssl" version = "0.6.3" @@ -1584,6 +1805,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -1637,6 +1867,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.84" @@ -1666,6 +1908,16 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1706,6 +1958,21 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -1838,6 +2105,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index ac465dc..d8af726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,7 @@ k8s-openapi = { version = "0.18.0", features = ["v1_26"] } # TODO : switch to v1 futures = "0.3.28" thiserror = "1.0.40" rand = "0.8.5" + +[dev-dependencies] +mktemp = "0.5.0" +reqwest = "0.11.17" \ No newline at end of file diff --git a/README.md b/README.md index 6c0fb2a..ad717f0 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,6 @@ Apply all K8s config files manually: ```bash cat yaml/*.yaml | kubectl apply -f - -``` \ No newline at end of file +``` + +Note : [mc tool](https://min.io/download) is required \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index f9d9341..9ba1ead 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -6,4 +6,4 @@ pub const SECRET_MINIO_BUCKET_ACCESS_KEY: &str = "accessKey"; pub const SECRET_MINIO_BUCKET_SECRET_KEY: &str = "secretKey"; pub const SECRET_MINIO_BUCKET_ACCESS_LEN: usize = 20; -pub const SECRET_MINIO_BUCKET_SECRET_LEN: usize = 35; \ No newline at end of file +pub const SECRET_MINIO_BUCKET_SECRET_LEN: usize = 35; diff --git a/src/lib.rs b/src/lib.rs index 2130aff..9a6443a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ pub mod constants; pub mod crd; pub mod minio; +#[cfg(test)] +pub mod minio_server; pub mod secrets; +pub mod utils; diff --git a/src/main.rs b/src/main.rs index 8863459..3e1af02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use futures::TryStreamExt; use k8s_openapi::api::core::v1::Secret; use kube::runtime::{watcher, WatchStreamExt}; @@ -10,6 +9,7 @@ use minio_operator::constants::{ use minio_operator::crd::{MinioBucket, MinioInstance}; use minio_operator::minio::{MinioService, MinioUser}; use minio_operator::secrets::{create_secret, read_secret_str}; +use std::collections::BTreeMap; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -58,14 +58,29 @@ async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> { let user_secret = match secrets.get_opt(&b.spec.secret).await? { Some(s) => s, None => { - log::info!("Needs to create the secret {} for the bucket {}", b.spec.secret, b.spec.name); + log::info!( + "Needs to create the secret {} for the bucket {}", + b.spec.secret, + b.spec.name + ); // The secret needs to be created let new_user = MinioUser::gen_random(); - create_secret(&secrets, &b.spec.secret, BTreeMap::from([ - (SECRET_MINIO_BUCKET_ACCESS_KEY.to_string(), new_user.username), - (SECRET_MINIO_BUCKET_SECRET_KEY.to_string(), new_user.password) - ])).await? + create_secret( + &secrets, + &b.spec.secret, + BTreeMap::from([ + ( + SECRET_MINIO_BUCKET_ACCESS_KEY.to_string(), + new_user.username, + ), + ( + SECRET_MINIO_BUCKET_SECRET_KEY.to_string(), + new_user.password, + ), + ]), + ) + .await? } }; let user = MinioUser { diff --git a/src/minio.rs b/src/minio.rs index 408f906..f672b06 100644 --- a/src/minio.rs +++ b/src/minio.rs @@ -1,6 +1,5 @@ -use rand::distributions::Alphanumeric; -use rand::Rng; use crate::constants::{SECRET_MINIO_BUCKET_ACCESS_LEN, SECRET_MINIO_BUCKET_SECRET_LEN}; +use crate::utils::rand_str; #[derive(Debug, Clone)] pub struct MinioService { @@ -18,8 +17,30 @@ pub struct MinioUser { impl MinioUser { pub fn gen_random() -> Self { Self { - username: rand::thread_rng().sample_iter(&Alphanumeric).take(SECRET_MINIO_BUCKET_ACCESS_LEN).map(char::from).collect(), - password: rand::thread_rng().sample_iter(&Alphanumeric).take(SECRET_MINIO_BUCKET_SECRET_LEN).map(char::from).collect(), + username: rand_str(SECRET_MINIO_BUCKET_ACCESS_LEN), + password: rand_str(SECRET_MINIO_BUCKET_SECRET_LEN), } } -} \ No newline at end of file +} + +impl MinioService { + /// Check if the Minio Service is ready to respond to our requests + pub async fn is_ready(&self) -> bool { + match reqwest::get(format!("{}/minio/health/live", self.hostname)).await { + Ok(r) => { + if r.status() == 200 { + log::info!("Minio is ready!"); + return true; + } + + log::info!( + "Minio not ready yet, check failed with status code {}", + r.status() + ); + } + Err(e) => log::info!("Minio not ready yet, check failed with error {e}"), + } + + false + } +} diff --git a/src/minio_server.rs b/src/minio_server.rs new file mode 100644 index 0000000..103add0 --- /dev/null +++ b/src/minio_server.rs @@ -0,0 +1,113 @@ +//! # Minio server controller +//! +//! Used for testing only + +use crate::minio::MinioService; +use crate::utils::rand_str; +use rand::RngCore; +use std::io::ErrorKind; +use std::process::{Child, Command}; + +pub struct MinioServer { + #[allow(dead_code)] + storage_base_dir: mktemp::Temp, + child: Child, + pub api_port: u16, + pub root_user: String, + pub root_password: String, +} + +impl MinioServer { + pub async fn start() -> anyhow::Result { + let storage_dir = mktemp::Temp::new_dir()?; + + let root_user = rand_str(30); + let root_password = rand_str(30); + let api_port = (2000 + rand::thread_rng().next_u64() % 5000) as u16; + log::info!( + "Spwan a new Minio server on port {} with root credentials {}:{}", + api_port, + root_user, + root_password + ); + + let child = Command::new("minio") + .current_dir(storage_dir.clone()) + .arg("server") + .arg("--address") + .arg(format!(":{api_port}")) + .arg(storage_dir.to_str().unwrap()) + .env("MINIO_ROOT_USER", &root_user) + .env("MINIO_ROOT_PASSWORD", &root_password) + .spawn()?; + + let instance = Self { + storage_base_dir: storage_dir, + child, + api_port, + root_user, + root_password, + }; + + // Wait for Minio to become ready + let mut check_count = 0; + loop { + if check_count >= 100 { + log::error!("Minio failed to respond properly in time!"); + return Err(std::io::Error::new( + ErrorKind::Other, + "Minio failed to respond in time!", + ) + .into()); + } + check_count += 1; + + if instance.as_service().is_ready().await { + break; + } + } + + Ok(instance) + } + + pub fn base_url(&self) -> String { + format!("http://127.0.0.1:{}", self.api_port) + } + + /// Get a MinioService instance of this temporary server + pub fn as_service(&self) -> MinioService { + MinioService { + hostname: self.base_url(), + access_key: self.root_user.clone(), + secret_key: self.root_password.clone(), + } + } +} + +impl Drop for MinioServer { + fn drop(&mut self) { + if let Err(e) = self.child.kill() { + log::error!("Failed to kill child server! {}", e); + } + } +} + +#[cfg(test)] +mod test { + use crate::minio_server::MinioServer; + + #[tokio::test] + async fn start_minio() { + let _ = env_logger::builder().is_test(true).try_init(); + + let server = MinioServer::start().await.unwrap(); + let service = server.as_service(); + println!("{:?}", service); + + assert!(service.is_ready().await); + + // Check if minio properly exit + drop(server); + assert!(!service.is_ready().await); + } +} diff --git a/src/secrets.rs b/src/secrets.rs index c03b608..107b275 100644 --- a/src/secrets.rs +++ b/src/secrets.rs @@ -1,8 +1,8 @@ use k8s_openapi::api::core::v1::Secret; use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta; -use kube::{Api}; -use std::collections::{BTreeMap}; use kube::api::PostParams; +use kube::Api; +use std::collections::BTreeMap; #[derive(thiserror::Error, Debug)] enum SecretError { diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f2e11fd --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,11 @@ +use rand::distributions::Alphanumeric; +use rand::Rng; + +/// Generate a random string of a given size +pub fn rand_str(len: usize) -> String { + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(len) + .map(char::from) + .collect() +}