Spwan test minio instance

This commit is contained in:
2023-05-06 13:15:17 +02:00
parent 76c22150c0
commit 11d2ba5312
10 changed files with 462 additions and 17 deletions

View File

@ -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;
pub const SECRET_MINIO_BUCKET_SECRET_LEN: usize = 35;

View File

@ -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;

View File

@ -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 {

View File

@ -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),
}
}
}
}
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
}
}

113
src/minio_server.rs Normal file
View File

@ -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<Self> {
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);
}
}

View File

@ -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 {

11
src/utils.rs Normal file
View File

@ -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()
}