1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
use failure::Error; use kube::{ api::{Api, Object}, client::APIClient, config }; use k8s_openapi::api::core::v1::{ContainerStatus, PodSpec, PodStatus}; #[derive(Debug)] pub enum SuspiciousContainerReason { ContainerWaiting(Option<String>), Restarted { count: i32, exit_code: i32, reason: Option<String> }, TerminatedWithError(i32) } #[derive(Debug)] pub struct SuspiciousContainer { pub name: String, pub reason: SuspiciousContainerReason } #[derive(Debug)] pub struct SuspiciousPod { pub name: String, pub suspicious_containers: Vec<SuspiciousContainer> } pub type Result<T> = std::result::Result<T, Error>; fn is_suspicious(p: Object<PodSpec, PodStatus>) -> Option<SuspiciousPod> { let pod_name = p.metadata.name; let status = p.status .expect(format!("Cannot get status for pod {}", pod_name).as_str()); let statuses: Vec<ContainerStatus> = status.container_statuses .expect(format!("Cannot get container statuses for pod {}", pod_name).as_str()); let suspicious_containers: Vec<_> = statuses.into_iter().filter_map(|status: ContainerStatus| { let container_name = status.name; let state = status.state .expect(format!("Cannot get state for container {} in pod {}", container_name, pod_name).as_str()); let reason = if status.restart_count > 0 { let last_state = status.last_state .expect(format!("Cannot get last state for container {} in pod {}", container_name, pod_name).as_str()) .terminated .unwrap(); Some(SuspiciousContainerReason::Restarted { count: status.restart_count, exit_code: last_state.exit_code, reason: last_state.reason }) } else if let Some(waiting_state) = state.waiting { let msg: Option<String> = waiting_state.reason.or(waiting_state.message); Some(SuspiciousContainerReason::ContainerWaiting(msg)) } else if state.terminated.is_some() && state.terminated.as_ref().unwrap().exit_code != 0 { Some(SuspiciousContainerReason::TerminatedWithError(state.terminated.unwrap().exit_code)) } else { None }; reason.map(|reason| SuspiciousContainer { name: container_name, reason }) }).collect(); if suspicious_containers.is_empty() { None } else { Some(SuspiciousPod { name: pod_name, suspicious_containers }) } } pub fn get_suspicious_pods(namespace: &str) -> Result<Vec<SuspiciousPod>> { let config = config::load_kube_config()?; let client = APIClient::new(config); let pods = Api::v1Pod(client).within(namespace).list(&Default::default())?; Ok(pods.items.into_iter() .filter_map(is_suspicious) .collect()) }