Implement base operator #1
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -868,6 +868,7 @@ dependencies = [
|
||||
"k8s-openapi",
|
||||
"kube",
|
||||
"log",
|
||||
"rand",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -17,3 +17,4 @@ kube = { version = "0.82.2", features = ["runtime", "derive"] }
|
||||
k8s-openapi = { version = "0.18.0", features = ["v1_26"] } # TODO : switch to v1_27
|
||||
futures = "0.3.28"
|
||||
thiserror = "1.0.40"
|
||||
rand = "0.8.5"
|
||||
|
@ -4,3 +4,6 @@ pub const SECRET_MINIO_INSTANCE_SECRET_KEY: &str = "secretKey";
|
||||
|
||||
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;
|
@ -1,4 +1,4 @@
|
||||
pub mod constants;
|
||||
pub mod crd;
|
||||
pub mod secrets;
|
||||
pub mod minio;
|
||||
pub mod secrets;
|
||||
|
39
src/main.rs
39
src/main.rs
@ -1,11 +1,15 @@
|
||||
use std::collections::BTreeMap;
|
||||
use futures::TryStreamExt;
|
||||
use k8s_openapi::api::core::v1::Secret;
|
||||
use kube::{Api, Client};
|
||||
use kube::runtime::{watcher, WatchStreamExt};
|
||||
use minio_operator::constants::{SECRET_MINIO_INSTANCE_ACCESS_KEY, SECRET_MINIO_INSTANCE_SECRET_KEY};
|
||||
use kube::{Api, Client};
|
||||
use minio_operator::constants::{
|
||||
SECRET_MINIO_BUCKET_ACCESS_KEY, SECRET_MINIO_BUCKET_SECRET_KEY,
|
||||
SECRET_MINIO_INSTANCE_ACCESS_KEY, SECRET_MINIO_INSTANCE_SECRET_KEY,
|
||||
};
|
||||
use minio_operator::crd::{MinioBucket, MinioInstance};
|
||||
use minio_operator::minio::MinioService;
|
||||
use minio_operator::secrets::read_secret_str;
|
||||
use minio_operator::minio::{MinioService, MinioUser};
|
||||
use minio_operator::secrets::{create_secret, read_secret_str};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
@ -22,14 +26,17 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
while let Some(b) = bw.try_next().await? {
|
||||
if let Err(e) = apply_bucket(&b, &client).await {
|
||||
log::error!("Failed to apply desired configuration for applied bucket {} : {}", b.spec.name, e)
|
||||
log::error!(
|
||||
"Failed to apply desired configuration for applied bucket {} : {}",
|
||||
b.spec.name,
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Make sure a bucket is compliant with a desired configuration
|
||||
async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> {
|
||||
log::info!("Apply configuration for bucket {}", b.spec.name);
|
||||
@ -47,7 +54,27 @@ async fn apply_bucket(b: &MinioBucket, client: &Client) -> anyhow::Result<()> {
|
||||
secret_key: read_secret_str(&instance_secret, SECRET_MINIO_INSTANCE_SECRET_KEY)?,
|
||||
};
|
||||
|
||||
// Get user key & password
|
||||
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);
|
||||
|
||||
// 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?
|
||||
}
|
||||
};
|
||||
let user = MinioUser {
|
||||
username: read_secret_str(&user_secret, SECRET_MINIO_BUCKET_ACCESS_KEY)?,
|
||||
password: read_secret_str(&user_secret, SECRET_MINIO_BUCKET_SECRET_KEY)?,
|
||||
};
|
||||
|
||||
println!("{:?}", service);
|
||||
println!("{:?}", user);
|
||||
|
||||
Ok(())
|
||||
}
|
19
src/minio.rs
19
src/minio.rs
@ -1,6 +1,25 @@
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::Rng;
|
||||
use crate::constants::{SECRET_MINIO_BUCKET_ACCESS_LEN, SECRET_MINIO_BUCKET_SECRET_LEN};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MinioService {
|
||||
pub hostname: String,
|
||||
pub access_key: String,
|
||||
pub secret_key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MinioUser {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +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;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
enum SecretError {
|
||||
@ -13,8 +17,48 @@ enum SecretError {
|
||||
pub fn read_secret_str(s: &Secret, key: &str) -> anyhow::Result<String> {
|
||||
let data = s.data.as_ref().ok_or(SecretError::MissingData)?;
|
||||
|
||||
let value = data.get(key)
|
||||
let value = data
|
||||
.get(key)
|
||||
.ok_or(SecretError::MissingKey(key.to_string()))?;
|
||||
|
||||
Ok(String::from_utf8(value.0.clone())?)
|
||||
}
|
||||
|
||||
/// Create a secret consisting only of string key / value pairs
|
||||
pub async fn create_secret(
|
||||
secrets: &Api<Secret>,
|
||||
name: &str,
|
||||
values: BTreeMap<String, String>,
|
||||
) -> anyhow::Result<Secret> {
|
||||
Ok(secrets
|
||||
.create(
|
||||
&PostParams::default(),
|
||||
&Secret {
|
||||
data: None,
|
||||
immutable: None,
|
||||
metadata: ObjectMeta {
|
||||
annotations: None,
|
||||
creation_timestamp: None,
|
||||
deletion_grace_period_seconds: None,
|
||||
deletion_timestamp: None,
|
||||
finalizers: None,
|
||||
generate_name: None,
|
||||
generation: None,
|
||||
labels: Some(BTreeMap::from([(
|
||||
"created-by".to_string(),
|
||||
"miniok8sbuckets".to_string(),
|
||||
)])),
|
||||
managed_fields: None,
|
||||
name: Some(name.to_string()),
|
||||
namespace: None,
|
||||
owner_references: None,
|
||||
resource_version: None,
|
||||
self_link: None,
|
||||
uid: None,
|
||||
},
|
||||
string_data: Some(values),
|
||||
type_: None,
|
||||
},
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
28
test/bucket-policy.yaml
Normal file
28
test/bucket-policy.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ListObjectsInBucket",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:ListBucket"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Sid": "AllObjectActions",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:DeleteObject",
|
||||
"s3:Get*",
|
||||
"s3:PutObject",
|
||||
"s3:*Object"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user