use std::collections::HashMap;
use std::collections::hash_map::Entry;
use sk_core::errors::*;
use sk_core::k8s::{
GVK,
PodLifecycleData,
format_gvk_name,
};
use tracing::*;
use crate::TraceIndex;
pub type PodLifecyclesMap = HashMap<u64, Vec<PodLifecycleData>>;
#[derive(Clone, Default)]
pub(crate) struct PodOwnersMap {
m: HashMap<(GVK, String), PodLifecyclesMap>,
index: HashMap<String, ((GVK, String), u64, usize)>,
}
impl PodOwnersMap {
pub(crate) fn has_pod(&self, ns_name: &str) -> bool {
self.index.contains_key(ns_name)
}
pub(crate) fn store_new_pod_lifecycle(
&mut self,
pod_ns_name: &str,
owner_gvk: &GVK,
owner_ns_name: &str,
hash: u64,
lifecycle_data: &PodLifecycleData,
) {
let owner_gvk_name = format_gvk_name(owner_gvk, owner_ns_name);
let idx = match self.m.entry((owner_gvk.clone(), owner_ns_name.into())) {
Entry::Vacant(e) => {
e.insert([(hash, vec![lifecycle_data.clone()])].into());
0
},
Entry::Occupied(mut e) => {
let pod_sequence = e.get_mut().entry(hash).or_insert(vec![]);
pod_sequence.push(lifecycle_data.clone());
pod_sequence.len() - 1
},
};
info!("inserting pod {pod_ns_name} owned by {owner_gvk_name} with hash {hash}: {lifecycle_data:?}");
self.index
.insert(pod_ns_name.into(), ((owner_gvk.clone(), owner_ns_name.into()), hash, idx));
}
pub(crate) fn update_pod_lifecycle(&mut self, pod_ns_name: &str, lifecycle_data: &PodLifecycleData) -> EmptyResult {
match self.index.get(pod_ns_name) {
None => bail!("pod {} not present in index", pod_ns_name),
Some(((owner_gvk, owner_ns_name), hash, sequence_idx)) => {
let owner_entry = self
.m
.get_mut(&(owner_gvk.clone(), owner_ns_name.into()))
.ok_or(anyhow!("no owner entry for pod {}", pod_ns_name))?;
let pods = owner_entry.get_mut(hash).ok_or(anyhow!(
"no entry for pod {} matching hash {}",
pod_ns_name,
hash
))?;
let pod_entry = pods.get_mut(*sequence_idx).ok_or(anyhow!(
"no sequence index {} for pod {} matching hash {}",
sequence_idx,
pod_ns_name,
hash
))?;
let owner_gvk_name = format_gvk_name(owner_gvk, owner_ns_name);
info!("updating pod {pod_ns_name} owned by {owner_gvk_name} with hash {hash}: {lifecycle_data:?}");
*pod_entry = lifecycle_data.clone();
Ok(())
},
}
}
pub(crate) fn filter(
&self,
start_ts: i64,
end_ts: i64,
index: &TraceIndex,
) -> HashMap<(GVK, String), PodLifecyclesMap> {
self.m
.iter()
.filter_map(|((owner_gvk, owner_ns_name), lifecycles_map)| {
if !index.contains(owner_gvk, owner_ns_name) {
return None;
}
Some((
(owner_gvk.clone(), owner_ns_name.clone()),
filter_lifecycles_map(start_ts, end_ts, lifecycles_map)?,
))
})
.collect()
}
}
pub(crate) fn filter_lifecycles_map(
start_ts: i64,
end_ts: i64,
lifecycles_map: &PodLifecyclesMap,
) -> Option<PodLifecyclesMap> {
let filtered_map: PodLifecyclesMap = lifecycles_map
.iter()
.filter_map(|(hash, lifecycles)| {
let new_lifecycles: Vec<_> = lifecycles.iter().filter(|l| l.overlaps(start_ts, end_ts)).cloned().collect();
if new_lifecycles.is_empty() {
return None;
}
Some((*hash, new_lifecycles))
})
.collect();
if filtered_map.is_empty() {
return None;
}
Some(filtered_map)
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
impl PodOwnersMap {
pub(crate) fn lifecycle_data_for<'a>(
&'a self,
owner_gvk: &GVK,
owner_ns_name: &str,
pod_hash: u64,
) -> Option<&'a Vec<PodLifecycleData>> {
self.m.get(&(owner_gvk.clone(), owner_ns_name.into()))?.get(&pod_hash)
}
pub fn new_from_parts(
m: HashMap<(GVK, String), PodLifecyclesMap>,
index: HashMap<String, ((GVK, String), u64, usize)>,
) -> PodOwnersMap {
PodOwnersMap { m, index }
}
pub fn pod_owner_meta(&self, pod_ns_name: &str) -> Option<&((GVK, String), u64, usize)> {
self.index.get(pod_ns_name)
}
}