use crate::app::{models::KubeResource, replicasets::KubeReplicaSet};
use super::{DisplayFinding, ResourceKind, Severity};
fn finding(
rs: &KubeReplicaSet,
severity: Severity,
reason: String,
message: String,
) -> DisplayFinding {
DisplayFinding {
severity,
reason,
resource_kind: ResourceKind::ReplicaSet,
namespace: Some(rs.namespace.clone()),
resource_name: rs.name.clone(),
message,
age: rs.age.clone(),
}
}
struct ReplicaCounts {
available: i32,
fully_labeled: i32,
ready: i32,
replicas: i32,
}
impl ReplicaCounts {
fn from_rs(rs: &KubeReplicaSet) -> Self {
let status = rs.get_k8s_obj().status.as_ref();
Self {
available: status
.and_then(|s| s.available_replicas)
.unwrap_or_default(),
fully_labeled: status
.and_then(|s| s.fully_labeled_replicas)
.unwrap_or_default(),
ready: status.and_then(|s| s.ready_replicas).unwrap_or_default(),
replicas: status.map_or(0, |s| s.replicas),
}
}
fn all_equal(&self) -> bool {
self.available == self.fully_labeled
&& self.fully_labeled == self.ready
&& self.ready == self.replicas
}
}
fn check_status(rs: &KubeReplicaSet) -> Option<DisplayFinding> {
let counts = ReplicaCounts::from_rs(rs);
if counts.all_equal() {
return None;
}
Some(finding(
rs,
Severity::Warn,
"Replica counts differ".into(),
format!(
"ReplicaSet status mismatch: available={}, fully_labeled={}, ready={}, replicas={}",
counts.available, counts.fully_labeled, counts.ready, counts.replicas
),
))
}
pub fn evaluate(items: &[KubeReplicaSet]) -> Vec<DisplayFinding> {
items.iter().filter_map(check_status).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use k8s_openapi::api::apps::v1::{ReplicaSet, ReplicaSetSpec, ReplicaSetStatus};
use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
fn build_rs(status: Option<ReplicaSetStatus>) -> KubeReplicaSet {
let rs = ReplicaSet {
metadata: ObjectMeta {
name: Some("rs-1".into()),
namespace: Some("ns-1".into()),
..Default::default()
},
spec: Some(ReplicaSetSpec {
replicas: Some(2),
..Default::default()
}),
status,
};
KubeReplicaSet::from(rs)
}
#[test]
fn test_rs_replica_counts_defaults() {
let rs = build_rs(None);
let counts = ReplicaCounts::from_rs(&rs);
assert_eq!(
(
counts.available,
counts.fully_labeled,
counts.ready,
counts.replicas
),
(0, 0, 0, 0)
);
}
#[test]
fn test_check_rs_status_no_finding_when_equal() {
let status = ReplicaSetStatus {
replicas: 2,
available_replicas: Some(2),
fully_labeled_replicas: Some(2),
ready_replicas: Some(2),
..Default::default()
};
let rs = build_rs(Some(status));
assert!(check_status(&rs).is_none());
}
#[test]
fn test_check_rs_status_finding_on_mismatch() {
let status = ReplicaSetStatus {
replicas: 2,
available_replicas: Some(1),
fully_labeled_replicas: Some(2),
ready_replicas: Some(2),
..Default::default()
};
let rs = build_rs(Some(status));
assert!(check_status(&rs).is_some());
}
}