Last success: {dayjs(lastSuccess?.raw.status.completionTime).fromNow()}
-
-
- {cronjob.jobs
- .filter((job) => {
- return (
- job.raw.status.succeeded !== 1 &&
- job.raw.status.completionTime &&
- lastSuccess?.raw.status.completionTime &&
- job.raw.status.completionTime >
- lastSuccess?.raw.status.completionTime
- )
- })
- .map((job) => (
-
- ))}
+ {jobsAfterLastSuccess.length > 0 && (
+
+ Last failed job:
+
+ {jobsAfterLastSuccess.map((job) => (
+
+ ))}
+
+
+ )}
)
}
@@ -79,7 +74,7 @@ function Job({ job }: { job: Job }) {
diff --git a/src/components/kube/pod.tsx b/src/components/kube/pod.tsx
index 86178b5..ce7676a 100644
--- a/src/components/kube/pod.tsx
+++ b/src/components/kube/pod.tsx
@@ -24,13 +24,18 @@ export default function PodWidget({ pod }: { pod: RawPod }) {
>
}
>
-
+ {`${pod.metadata.name.split("-")?.pop()} | restarts: ${pod.status
.containerStatuses?.[0].restartCount} | ${dayjs(
pod.metadata.creationTimestamp
).fromNow()} | ${podStatusInfo}`}
- status={getPodStatus(pod)}
- />
+
)
}
diff --git a/src/components/ui/json.tsx b/src/components/ui/json.tsx
index 8f8c52f..49effcd 100644
--- a/src/components/ui/json.tsx
+++ b/src/components/ui/json.tsx
@@ -4,5 +4,5 @@ import JsonView from "react18-json-view"
import "react18-json-view/src/style.css"
export default function JsonWidget(src: any) {
- return
+ return
}
diff --git a/src/lib/kube/index.ts b/src/lib/kube/index.ts
index b35a981..cf37e05 100644
--- a/src/lib/kube/index.ts
+++ b/src/lib/kube/index.ts
@@ -175,23 +175,18 @@ export async function getKubeData(): Promise
{
})
.value()
- const cleanedCronjobs = _.chain(namespace.pods)
- .filter((pod) => pod.metadata.ownerReferences?.[0].kind === "Job")
+ const cleanedCronjobs = _.chain(namespace.jobs)
.groupBy("metadata.ownerReferences[0].name")
- .map((jobPods, jobName) => {
- const job = namespace.jobs.find((j) => j.metadata.name === jobName)
- const cronjob = namespace.cronjobs.find(
- (cronjob) =>
- cronjob.metadata.name === job?.metadata?.ownerReferences?.[0].name
- )
- return { name: jobName, pods: jobPods, cronjob, raw: job }
- })
- .groupBy("cronjob.metadata.name")
.map((cronJobs, cronjobName) => {
- const rawCronjob = cronJobs[0].cronjob
+ const cleanedJobs = cronJobs.map((job) => {
+ return { name: job.metadata.name, raw: job }
+ })
+ const rawCronjob = namespace.cronjobs.find(
+ (cronjob) => cronjob.metadata.name === cronjobName
+ )
return {
name: cronjobName,
- jobs: cronJobs,
+ jobs: cleanedJobs,
raw: rawCronjob,
logsUrl: rawCronjob ? getLogsUrl(rawCronjob) : undefined,
}
diff --git a/src/lib/kube/types.ts b/src/lib/kube/types.ts
index d1fa6bb..2316633 100644
--- a/src/lib/kube/types.ts
+++ b/src/lib/kube/types.ts
@@ -236,6 +236,8 @@ export const jobSchema = z.object({
completionTime: z.optional(z.coerce.date()),
ready: z.number(),
succeeded: z.optional(z.number()),
+ failed: z.optional(z.number()),
+ active: z.optional(z.number()),
conditions: z.optional(
z.array(
z.object({
@@ -254,11 +256,13 @@ export const cronjobSchema = z.object({
name: z.string(),
namespace: z.string(),
creationTimestamp: z.coerce.date(),
- labels: z.object({
- application: z.optional(z.string()),
- app: z.optional(z.string()),
- component: z.optional(z.string()),
- }),
+ labels: z.optional(
+ z.object({
+ application: z.optional(z.string()),
+ app: z.optional(z.string()),
+ component: z.optional(z.string()),
+ })
+ ),
}),
status: z.object({
lastScheduleTime: z.optional(z.coerce.date()),
@@ -301,7 +305,7 @@ export type Deployment = {
export type Job = {
name: string
raw: RawJob
- pods: RawPod[]
+ // pods: RawPod[]
}
export type Cronjob = {
@@ -383,6 +387,42 @@ export function getPodContainerState(pod: RawPod) {
}
}
+export function getJobStatus(job: RawJob): Status {
+ if (job.status.succeeded || job.status.active) {
+ return "ok"
+ }
+ return "error"
+}
+
+export function getLastSuccessfullJob(cronjob: Cronjob): Job | undefined {
+ const lastSuccess = _.chain(cronjob.jobs)
+ .filter((job) => job.raw.status.succeeded === 1)
+ .sortBy((job) => job.raw.status.completionTime?.getTime())
+ .last()
+ .value()
+ return lastSuccess
+}
+
+export function getJobsAfterlastSuccessfull(cronjob: Cronjob): Job[] {
+ const lastSuccess = getLastSuccessfullJob(cronjob)
+ const lastJobs = cronjob.jobs.filter(
+ (job) =>
+ job.raw.status.succeeded !== 1 &&
+ (lastSuccess?.raw.status.completionTime
+ ? job.raw.metadata.creationTimestamp.getTime() >
+ lastSuccess.raw.status.completionTime.getTime()
+ : true)
+ )
+ return lastJobs
+}
+
+export function getCronjobStatus(cronjob: Cronjob): Status {
+ const jobsAfterLastSuccessfull = getJobsAfterlastSuccessfull(cronjob)
+ return hasError(jobsAfterLastSuccessfull, (job) => getJobStatus(job.raw))
+ ? "error"
+ : "ok"
+}
+
export function getPodStatus(pod: RawPod): Status {
const phase = pod.status.phase
@@ -422,7 +462,8 @@ function hasError- (
export function getNamespaceStatus(namespace: Namespace): Status {
if (
hasError(namespace.clusters, getCnpgClusterStatus) ||
- hasError(namespace.deployments, getDeploymentStatus)
+ hasError(namespace.deployments, getDeploymentStatus) ||
+ hasError(namespace.cronjobs, getCronjobStatus)
) {
return "error"
}