diff --git a/.talismanrc b/.talismanrc index 81e0581..3c1253f 100644 --- a/.talismanrc +++ b/.talismanrc @@ -18,7 +18,7 @@ fileignoreconfig: - filename: next.config.js checksum: 5204c66ad2ecbce89abaab9e7505590f1c2ac717975ed5d991cad2000fc9f958 - filename: src/components/kube/cronjob.tsx - checksum: b86c07eb18fd26f3442c90f98276c38996a1bf139cf874f4da5423ff5750783a + checksum: 90f7b4d1ff3c0d30b35c52a9c80ccf2677db11b35a8f5c454d281558cf620a8f - filename: src/components/kube/deployment.tsx checksum: 68ad893626c4d340708f5e040cb32a44052e51af55b424a0c397d4bccb83e43a - filename: src/components/kube/namespace.tsx diff --git a/src/components/kube/cronjob.tsx b/src/components/kube/cronjob.tsx index e56ed48..c757aa3 100644 --- a/src/components/kube/cronjob.tsx +++ b/src/components/kube/cronjob.tsx @@ -1,17 +1,22 @@ import dayjs from "dayjs" import _ from "lodash" -import { Cronjob, Job } from "@/lib/kube/types" -import PodWidget from "@/components/kube/pod" +import { + Cronjob, + Job, + getCronjobStatus, + getLastSuccessfullJob, + getJobsAfterlastSuccessfull, +} from "@/lib/kube/types" import Tooltip from "@/components/ui/tooltip" import JsonWidget from "@/components/ui/json" export default function CronjobWidget({ cronjob }: { cronjob: Cronjob }) { - const cronjobIsReady = true // isReady(cluster) + const cronjobIsOk = getCronjobStatus(cronjob) === "ok" return (
@@ -28,9 +33,7 @@ function Meta({ cronjob }: { cronjob: Cronjob }) { {cronjob.name}
CRON
-
- {dayjs(cronjob.raw?.metadata.creationTimestamp).fromNow()} -
{" "} +
{dayjs(cronjob.raw?.metadata.creationTimestamp).fromNow()}
{cronjob.logsUrl && (
@@ -44,32 +47,24 @@ function Meta({ cronjob }: { cronjob: Cronjob }) { } function Jobs({ cronjob }: { cronjob: Cronjob }) { - const lastSuccess = _.chain(cronjob.jobs) - .filter((job) => job.raw.status.succeeded === 1) - .sortBy((job) => job.raw.status.completionTime) - .last() - .value() + const lastSuccess = getLastSuccessfullJob(cronjob) + const jobsAfterLastSuccess = getJobsAfterlastSuccessfull(cronjob) return (
- +
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 }) { -
Jod {job.name}
+
Job {job.name}
} @@ -87,14 +82,10 @@ function Job({ job }: { job: Job }) {
-
- {job.pods.map((pod) => ( - - ))} -
+ {job.name}
) diff --git a/src/components/kube/deployment.tsx b/src/components/kube/deployment.tsx index daca7a8..91a1fcb 100644 --- a/src/components/kube/deployment.tsx +++ b/src/components/kube/deployment.tsx @@ -8,11 +8,11 @@ export default function DeploymentWidget({ }: { deployment: Deployment }) { - const deploymenntIsOk = getDeploymentStatus(deployment) === "ok" + const deploymentIsOk = getDeploymentStatus(deployment) === "ok" return (
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" }