@@ -11,8 +11,8 @@ use serde_json::Value;
1111use crate :: status:: { age_label, status_label} ;
1212use crate :: {
1313 ContainerResources , KubeSession , ObjectCondition , ObjectDetail , ObjectSummary ,
14- ObjectWatchEvent , PodSummary , ResourceKind , ResourceRatio , ResourceScope , ResourceUsage ,
15- api_resource, namespace_scope, resource_scope,
14+ ObjectWatchEvent , PodStateCount , PodSummary , ResourceKind , ResourceRatio , ResourceScope ,
15+ ResourceUsage , api_resource, namespace_scope, resource_scope,
1616} ;
1717
1818impl KubeSession {
@@ -228,7 +228,7 @@ impl KubeSession {
228228 } ;
229229 let summary = object_summary ( object. clone ( ) , resource, & metrics) ;
230230 let yaml = serde_yaml:: to_string ( & object) . context ( "failed to serialize object YAML" ) ?;
231- let related_pods = self
231+ let deployment_pods = self
232232 . deployment_pods ( resource, & summary. namespace , & object)
233233 . await
234234 . unwrap_or_default ( ) ;
@@ -253,7 +253,8 @@ impl KubeSession {
253253 container_resources,
254254 yaml,
255255 containers,
256- related_pods,
256+ related_pods : deployment_pods. summaries ,
257+ related_pod_states : deployment_pods. states ,
257258 replicas,
258259 node_unschedulable,
259260 conditions,
@@ -267,12 +268,12 @@ impl KubeSession {
267268 resource : & ResourceKind ,
268269 namespace : & str ,
269270 object : & DynamicObject ,
270- ) -> Result < Vec < ObjectSummary > > {
271+ ) -> Result < DeploymentPods > {
271272 if resource. kind != "Deployment" || resource. group != "apps" || namespace == "-" {
272- return Ok ( Vec :: new ( ) ) ;
273+ return Ok ( DeploymentPods :: default ( ) ) ;
273274 }
274275 let Some ( selector) = deployment_label_selector ( object) else {
275- return Ok ( Vec :: new ( ) ) ;
276+ return Ok ( DeploymentPods :: default ( ) ) ;
276277 } ;
277278
278279 let pods: Api < Pod > = Api :: namespaced ( self . client . clone ( ) , namespace) ;
@@ -288,7 +289,7 @@ impl KubeSession {
288289 . resource_metrics ( & pod_resource, Some ( namespace) )
289290 . await
290291 . unwrap_or_default ( ) ;
291- let mut summaries = pods
292+ let pods = pods
292293 . list ( & ListParams :: default ( ) . labels ( & selector) )
293294 . await
294295 . with_context ( || {
@@ -297,7 +298,9 @@ impl KubeSession {
297298 object. name_any( )
298299 )
299300 } ) ?
300- . items
301+ . items ;
302+ let states = pod_state_counts ( & pods) ;
303+ let mut summaries = pods
301304 . into_iter ( )
302305 . map ( |pod| {
303306 let object = serde_json:: to_value ( & pod)
@@ -319,10 +322,46 @@ impl KubeSession {
319322 . collect :: < Vec < _ > > ( ) ;
320323 summaries. sort_by ( |left, right| left. name . cmp ( & right. name ) ) ;
321324
322- Ok ( summaries)
325+ Ok ( DeploymentPods { summaries, states } )
323326 }
324327}
325328
329+ #[ derive( Default ) ]
330+ struct DeploymentPods {
331+ summaries : Vec < ObjectSummary > ,
332+ states : Vec < PodStateCount > ,
333+ }
334+
335+ fn pod_state_counts ( pods : & [ Pod ] ) -> Vec < PodStateCount > {
336+ const ORDER : [ & str ; 5 ] = [ "Running" , "Pending" , "Succeeded" , "Failed" , "Unknown" ] ;
337+
338+ let mut counts = BTreeMap :: < String , u32 > :: new ( ) ;
339+ for pod in pods {
340+ let state = pod
341+ . status
342+ . as_ref ( )
343+ . and_then ( |status| status. phase . as_deref ( ) )
344+ . unwrap_or ( "Unknown" ) ;
345+ * counts. entry ( state. to_owned ( ) ) . or_default ( ) += 1 ;
346+ }
347+
348+ let mut states = ORDER
349+ . into_iter ( )
350+ . filter_map ( |state| {
351+ counts. remove ( state) . map ( |count| PodStateCount {
352+ state : state. to_owned ( ) ,
353+ count,
354+ } )
355+ } )
356+ . collect :: < Vec < _ > > ( ) ;
357+ states. extend (
358+ counts
359+ . into_iter ( )
360+ . map ( |( state, count) | PodStateCount { state, count } ) ,
361+ ) ;
362+ states
363+ }
364+
326365fn sort_object_summaries ( objects : & mut [ ObjectSummary ] ) {
327366 objects. sort_by ( |left, right| {
328367 left. namespace
@@ -651,8 +690,12 @@ fn deployment_label_selector(object: &DynamicObject) -> Option<String> {
651690
652691#[ cfg( test) ]
653692mod tests {
654- use super :: { object_container_resources, object_images, quantity_as_f64, resource_ratio} ;
693+ use super :: {
694+ object_container_resources, object_images, pod_state_counts, quantity_as_f64,
695+ resource_ratio,
696+ } ;
655697 use crate :: { ContainerResources , ResourceKind , ResourceScope } ;
698+ use k8s_openapi:: api:: core:: v1:: Pod ;
656699 use kube:: api:: DynamicObject ;
657700 use serde_json:: json;
658701
@@ -782,4 +825,38 @@ mod tests {
782825 ]
783826 ) ;
784827 }
828+
829+ #[ test]
830+ fn pod_state_counts_groups_phases_in_dashboard_order ( ) {
831+ let pods: Vec < Pod > = serde_json:: from_value ( json ! ( [
832+ { "apiVersion" : "v1" , "kind" : "Pod" , "status" : { "phase" : "Pending" } } ,
833+ { "apiVersion" : "v1" , "kind" : "Pod" , "status" : { "phase" : "Running" } } ,
834+ { "apiVersion" : "v1" , "kind" : "Pod" , "status" : { "phase" : "Running" } } ,
835+ { "apiVersion" : "v1" , "kind" : "Pod" , "status" : { "phase" : "Failed" } } ,
836+ { "apiVersion" : "v1" , "kind" : "Pod" , "status" : { } }
837+ ] ) )
838+ . expect ( "pods should deserialize" ) ;
839+
840+ assert_eq ! (
841+ pod_state_counts( & pods) ,
842+ vec![
843+ crate :: PodStateCount {
844+ state: String :: from( "Running" ) ,
845+ count: 2
846+ } ,
847+ crate :: PodStateCount {
848+ state: String :: from( "Pending" ) ,
849+ count: 1
850+ } ,
851+ crate :: PodStateCount {
852+ state: String :: from( "Failed" ) ,
853+ count: 1
854+ } ,
855+ crate :: PodStateCount {
856+ state: String :: from( "Unknown" ) ,
857+ count: 1
858+ } ,
859+ ]
860+ ) ;
861+ }
785862}
0 commit comments