Spwan test minio instance
This commit is contained in:
@ -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;
|
||||
|
@ -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;
|
||||
|
27
src/main.rs
27
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 {
|
||||
|
31
src/minio.rs
31
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
113
src/minio_server.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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
11
src/utils.rs
Normal 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()
|
||||
}
|
Reference in New Issue
Block a user