Skip to content

Commit

Permalink
Select kind from request and use AivenObject trait for further proces…
Browse files Browse the repository at this point in the history
…sing
  • Loading branch information
mortenlj committed Sep 28, 2023
1 parent 677693e commit 06ff962
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 28 deletions.
22 changes: 15 additions & 7 deletions src/mutators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use crate::aiven_types::AivenObject;
use crate::settings::AppConfig;

#[instrument(skip_all)]
pub fn add_location(location: String, obj: &dyn AivenObject, patches: &mut Vec<PatchOperation>) {
pub fn add_location(
location: String,
obj: &Box<dyn AivenObject>,
patches: &mut Vec<PatchOperation>,
) {
let cloud_name = Value::String(format!("google-{}", location));
if obj.get_cloud_name().is_none() {
info!("Adding cloudName");
Expand All @@ -21,7 +25,11 @@ pub fn add_location(location: String, obj: &dyn AivenObject, patches: &mut Vec<P
}

#[instrument(skip_all)]
pub fn add_tags(config: &Arc<AppConfig>, obj: &dyn AivenObject, patches: &mut Vec<PatchOperation>) {
pub fn add_tags(
config: &Arc<AppConfig>,
obj: &Box<dyn AivenObject>,
patches: &mut Vec<PatchOperation>,
) {
let environment = config.tenant.environment.clone();
let tenant = config.tenant.name.clone();
let team = obj.get_team_name().unwrap();
Expand Down Expand Up @@ -72,7 +80,7 @@ fn handle_tag(
}

#[instrument(skip_all)]
pub fn add_termination_protection(obj: &dyn AivenObject, patches: &mut Vec<PatchOperation>) {
pub fn add_termination_protection(obj: &Box<dyn AivenObject>, patches: &mut Vec<PatchOperation>) {
if obj.get_termination_protection().is_none() {
info!("Enabling terminationProtection");
patches.push(add_patch(
Expand All @@ -85,7 +93,7 @@ pub fn add_termination_protection(obj: &dyn AivenObject, patches: &mut Vec<Patch
#[instrument(skip_all)]
pub fn add_project_vpc_id(
project_vpc_id: String,
obj: &dyn AivenObject,
obj: &Box<dyn AivenObject>,
patches: &mut Vec<PatchOperation>,
) {
if obj.get_project_vpc_id().is_none() {
Expand Down Expand Up @@ -279,8 +287,8 @@ mod tests {
.collect()
}

fn create_redis(tags: Option<BTreeMap<String, String>>) -> Redis {
Redis {
fn create_redis(tags: Option<BTreeMap<String, String>>) -> Box<dyn AivenObject> {
Box::new(Redis {
metadata: ObjectMeta {
annotations: None,
cluster_name: None,
Expand Down Expand Up @@ -316,6 +324,6 @@ mod tests {
user_config: None,
},
status: None,
}
})
}
}
60 changes: 39 additions & 21 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,16 @@ fn create_router(state: Arc<AppConfig>) -> Router {
#[instrument(skip_all)]
async fn mutate_handler(
State(config): State<Arc<AppConfig>>,
Json(admission_review): Json<AdmissionReview<Redis>>,
Json(admission_review): Json<AdmissionReview<DynamicObject>>,
) -> (StatusCode, Json<AdmissionReview<DynamicObject>>) {
let req: AdmissionRequest<Redis> = match admission_review.try_into() {
let req: AdmissionRequest<DynamicObject> = match admission_review.try_into() {
Ok(req) => req,
Err(err) => {
warn!(
"Unable to get request from AdmissionReview: {}",
err.to_string()
);
return (
StatusCode::BAD_REQUEST,
Json(AdmissionResponse::invalid("missing request").into_review()),
);
return bad_request("missing request");
},
};

Expand All @@ -86,14 +83,32 @@ async fn mutate_handler(
if let Some(obj) = &req.object {
let name = obj.name_any();
let namespace = obj.namespace().unwrap();
let redis_span = info_span!(
"redis",
let resource_span = info_span!(
"resource",
resource_kind = req.kind.kind,
resource_name = name,
resource_namespace = namespace
);
let _redis_guard = redis_span.enter();
info!("Processing redis resource");
res = match mutate(res.clone(), obj, &config) {
let _resource_guard = resource_span.enter();
info!("Processing {} resource", req.kind.kind);

let resource: Box<dyn AivenObject> = match req.kind.kind.as_str() {
"Redis" => {
let redis: Redis = match obj.clone().try_parse() {
Ok(redis) => redis,
Err(err) => {
error!("Unable to parse Redis object: {}", err.to_string());
return bad_request("unable to parse Redis object");
},
};
Box::new(redis)
},
_ => {
return bad_request("unsupported resource type");
},
};

res = match mutate(res.clone(), resource, &config) {
Ok(res) => {
info!("Processing complete");
res
Expand All @@ -106,29 +121,33 @@ async fn mutate_handler(
(StatusCode::OK, Json(res.into_review()))
} else {
warn!("No object specified in AdmissionRequest: {:?}", req);
(
StatusCode::BAD_REQUEST,
Json(AdmissionResponse::invalid("no object specified").into_review()),
)
bad_request("no object specified")
}
}

#[instrument(skip_all)]
fn mutate(
res: AdmissionResponse,
obj: &dyn AivenObject,
obj: Box<dyn AivenObject>,
config: &Arc<AppConfig>,
) -> Result<AdmissionResponse> {
let mut patches = Vec::new();

mutators::add_project_vpc_id(config.project_vpc_id.clone(), obj, &mut patches);
mutators::add_termination_protection(obj, &mut patches);
mutators::add_tags(config, obj, &mut patches);
mutators::add_location(config.location.clone(), obj, &mut patches);
mutators::add_project_vpc_id(config.project_vpc_id.clone(), &obj, &mut patches);
mutators::add_termination_protection(&obj, &mut patches);
mutators::add_tags(config, &obj, &mut patches);
mutators::add_location(config.location.clone(), &obj, &mut patches);

Ok(res.with_patch(Patch(patches))?)
}

fn bad_request(reason: &str) -> (StatusCode, Json<AdmissionReview<DynamicObject>>) {
(
StatusCode::BAD_REQUEST,
Json(AdmissionResponse::invalid(reason).into_review()),
)
}

#[cfg(test)]
mod tests {
use std::fs::File;
Expand All @@ -143,7 +162,6 @@ mod tests {
use pretty_assertions::assert_eq;
use rstest::*;
use serde::{Deserialize, Serialize};

use serde_yaml;

use crate::settings::{AppConfig, LogLevel, Tenant, WebConfig};
Expand Down

0 comments on commit 06ff962

Please sign in to comment.