From d283be898fcbd4c632090d33b34951ae29adf988 Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Tue, 24 Mar 2026 21:57:44 +0000 Subject: [PATCH] feat: enhance Kubernetes resource listings by adding missing cronjobs, daemonsets, deployments, and jobs from k8s objects --- .../src/Pages/Kubernetes/View/CronJobs.tsx | 28 +++++++++++++++ .../src/Pages/Kubernetes/View/DaemonSets.tsx | 30 ++++++++++++++++ .../src/Pages/Kubernetes/View/Deployments.tsx | 35 ++++++++++++++++++- .../src/Pages/Kubernetes/View/Jobs.tsx | 34 ++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/CronJobs.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/CronJobs.tsx index 37f9bf387b..f70b41dc64 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/CronJobs.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/CronJobs.tsx @@ -68,8 +68,12 @@ const KubernetesClusterCronJobs: FunctionComponent< }), ]); + // Build a set of resource keys we already have from metrics + const existingKeys: Set = new Set(); + for (const resource of cronjobList) { const key: string = `${resource.namespace}/${resource.name}`; + existingKeys.add(key); const cjObj: KubernetesObjectType | undefined = cronjobObjects.get(key); if (cjObj) { const cronJob: KubernetesCronJobObject = @@ -85,6 +89,30 @@ const KubernetesClusterCronJobs: FunctionComponent< } } + // Add cronjobs from k8s objects that were not found via metrics + for (const [key, cjObj] of cronjobObjects.entries()) { + if (existingKeys.has(key)) { + continue; + } + const cronJob: KubernetesCronJobObject = + cjObj as KubernetesCronJobObject; + + cronjobList.push({ + name: cronJob.metadata.name, + namespace: cronJob.metadata.namespace, + cpuUtilization: null, + memoryUsageBytes: null, + memoryLimitBytes: null, + status: cronJob.spec.suspend ? "Suspended" : "Active", + age: KubernetesResourceUtils.formatAge( + cronJob.metadata.creationTimestamp, + ), + additionalAttributes: { + schedule: cronJob.spec.schedule || "", + }, + }); + } + setResources(cronjobList); } catch (err) { setError(API.getFriendlyMessage(err)); diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/DaemonSets.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/DaemonSets.tsx index 08bfac1005..480ff18d07 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/DaemonSets.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/DaemonSets.tsx @@ -68,8 +68,11 @@ const KubernetesClusterDaemonSets: FunctionComponent< }), ]); + const existingKeys: Set = new Set(); + for (const resource of daemonsetList) { const key: string = `${resource.namespace}/${resource.name}`; + existingKeys.add(key); const dsObj: KubernetesObjectType | undefined = daemonsetObjects.get(key); if (dsObj) { @@ -90,6 +93,33 @@ const KubernetesClusterDaemonSets: FunctionComponent< } } + // Add daemonsets from k8s objects that were not found via metrics + for (const [key, dsObj] of daemonsetObjects.entries()) { + if (existingKeys.has(key)) { + continue; + } + const ds: KubernetesDaemonSetObject = + dsObj as KubernetesDaemonSetObject; + const numberReady: number = ds.status.numberReady ?? 0; + const desired: number = ds.status.desiredNumberScheduled ?? 0; + + daemonsetList.push({ + name: ds.metadata.name, + namespace: ds.metadata.namespace, + cpuUtilization: null, + memoryUsageBytes: null, + memoryLimitBytes: null, + status: + numberReady === desired && desired > 0 ? "Ready" : "Progressing", + age: KubernetesResourceUtils.formatAge( + ds.metadata.creationTimestamp, + ), + additionalAttributes: { + ready: `${numberReady}/${desired}`, + }, + }); + } + setResources(daemonsetList); } catch (err) { setError(API.getFriendlyMessage(err)); diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Deployments.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Deployments.tsx index 8641fd11ba..2f73f0ac75 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Deployments.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Deployments.tsx @@ -71,8 +71,11 @@ const KubernetesClusterDeployments: FunctionComponent< }), ]); + const existingKeys: Set = new Set(); + for (const resource of deploymentList) { const key: string = `${resource.namespace}/${resource.name}`; + existingKeys.add(key); const depObj: KubernetesObjectType | undefined = deploymentObjects.get(key); if (depObj) { @@ -85,7 +88,6 @@ const KubernetesClusterDeployments: FunctionComponent< if (readyReplicas === replicas && replicas > 0) { resource.status = "Ready"; } else if (readyReplicas < replicas) { - // Check conditions for failure const failedCondition: KubernetesCondition | undefined = deployment.status.conditions.find((c: KubernetesCondition) => { return c.type === "Available" && c.status === "False"; @@ -104,6 +106,37 @@ const KubernetesClusterDeployments: FunctionComponent< } } + // Add deployments from k8s objects that were not found via metrics + for (const [key, depObj] of deploymentObjects.entries()) { + if (existingKeys.has(key)) { + continue; + } + const deployment: KubernetesDeploymentObject = + depObj as KubernetesDeploymentObject; + const readyReplicas: number = deployment.status.readyReplicas ?? 0; + const replicas: number = deployment.spec.replicas ?? 0; + + let status: string = "Progressing"; + if (readyReplicas === replicas && replicas > 0) { + status = "Ready"; + } + + deploymentList.push({ + name: deployment.metadata.name, + namespace: deployment.metadata.namespace, + cpuUtilization: null, + memoryUsageBytes: null, + memoryLimitBytes: null, + status: status, + age: KubernetesResourceUtils.formatAge( + deployment.metadata.creationTimestamp, + ), + additionalAttributes: { + ready: `${readyReplicas}/${replicas}`, + }, + }); + } + setResources(deploymentList); } catch (err) { setError(API.getFriendlyMessage(err)); diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Jobs.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Jobs.tsx index 8d4a9b274f..760c20c7bb 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Jobs.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Jobs.tsx @@ -68,8 +68,12 @@ const KubernetesClusterJobs: FunctionComponent< }), ]); + // Build a set of resource keys we already have from metrics + const existingKeys: Set = new Set(); + for (const resource of jobList) { const key: string = `${resource.namespace}/${resource.name}`; + existingKeys.add(key); const jobObj: KubernetesObjectType | undefined = jobObjects.get(key); if (jobObj) { const job: KubernetesJobObject = jobObj as KubernetesJobObject; @@ -90,6 +94,36 @@ const KubernetesClusterJobs: FunctionComponent< } } + // Add jobs from k8s objects that were not found via metrics + for (const [key, jobObj] of jobObjects.entries()) { + if (existingKeys.has(key)) { + continue; + } + const job: KubernetesJobObject = jobObj as KubernetesJobObject; + + let status: string = "Pending"; + if (job.status.completionTime) { + status = "Complete"; + } else if (job.status.failed > 0) { + status = "Failed"; + } else if (job.status.active > 0) { + status = "Running"; + } + + jobList.push({ + name: job.metadata.name, + namespace: job.metadata.namespace, + cpuUtilization: null, + memoryUsageBytes: null, + memoryLimitBytes: null, + status: status, + age: KubernetesResourceUtils.formatAge( + job.metadata.creationTimestamp, + ), + additionalAttributes: {}, + }); + } + setResources(jobList); } catch (err) { setError(API.getFriendlyMessage(err));