Skip to content

Commit

Permalink
feat: improve and fix cronjob status
Browse files Browse the repository at this point in the history
  • Loading branch information
achauve committed Dec 14, 2023
1 parent 817bcba commit 429a0f1
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 26 additions & 35 deletions src/components/kube/cronjob.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div
className={`col-span-1 rounded-lg bg-white shadow border-l-8 text-left
${cronjobIsReady ? "border-emerald-400" : "border-red-500"}
${cronjobIsOk ? "border-emerald-400" : "border-red-500"}
`}
>
<div className="p-2">
Expand All @@ -28,9 +33,7 @@ function Meta({ cronjob }: { cronjob: Cronjob }) {
<span className="font-bold">{cronjob.name}</span>
<span className="text-xs text-gray-500 text-right">
<div className="font-bold capitalize">CRON</div>
<div>
{dayjs(cronjob.raw?.metadata.creationTimestamp).fromNow()}
</div>{" "}
<div>{dayjs(cronjob.raw?.metadata.creationTimestamp).fromNow()}</div>
{cronjob.logsUrl && (
<div>
<a href={cronjob.logsUrl} target="_blank">
Expand All @@ -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 (
<div>
<span className="text-xs text-gray-500">
<div className="text-xs text-gray-500 text-right">
Last success: {dayjs(lastSuccess?.raw.status.completionTime).fromNow()}
</span>
<div className="grid gap-1">
{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) => (
<Job key={job.name} job={job} />
))}
</div>
{jobsAfterLastSuccess.length > 0 && (
<div className="text-sm text-gray-700 py-2">
Last failed job:
<div className="grid gap-1">
{jobsAfterLastSuccess.map((job) => (
<Job key={job.name} job={job} />
))}
</div>
</div>
)}
</div>
)
}
Expand All @@ -79,22 +74,18 @@ function Job({ job }: { job: Job }) {
<Tooltip
content={
<>
<div className="text-sm text-gray-700 font-bold">Jod {job.name}</div>
<div className="text-sm text-gray-700 font-bold">Job {job.name}</div>
<JsonWidget src={job.raw.status} />
</>
}
>
<div
key={job.name}
className={
"col-span-1 rounded-lg bg-white shadow border-l-8 text-left border-gray-300"
"col-span-1 rounded-lg bg-white shadow border-l-8 text-left border-red-500 p-2"
}
>
<div className="grid gap-1 p-2">
{job.pods.map((pod) => (
<PodWidget key={pod.metadata.name} pod={pod}></PodWidget>
))}
</div>
{job.name}
</div>
</Tooltip>
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/kube/deployment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export default function DeploymentWidget({
}: {
deployment: Deployment
}) {
const deploymenntIsOk = getDeploymentStatus(deployment) === "ok"
const deploymentIsOk = getDeploymentStatus(deployment) === "ok"
return (
<div
className={`col-span-1 rounded-lg bg-white shadow border-l-8 text-left
${deploymenntIsOk ? "border-emerald-400" : "border-red-500"}
${deploymentIsOk ? "border-emerald-400" : "border-red-500"}
`}
>
<div className="p-2">
Expand Down
13 changes: 9 additions & 4 deletions src/components/kube/pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ export default function PodWidget({ pod }: { pod: RawPod }) {
</>
}
>
<Badge
text={`${pod.metadata.name.split("-")?.pop()} | ${pod.status
<div
className={`col-span-1 rounded-lg bg-white shadow border-l-8 text-left text-xs
${
getPodStatus(pod) === "ok" ? "border-emerald-400" : "border-red-500"
}
`}
>
{`${pod.metadata.name.split("-")?.pop()} | restarts: ${pod.status
.containerStatuses?.[0].restartCount} | ${dayjs(
pod.metadata.creationTimestamp
).fromNow()} | ${podStatusInfo}`}
status={getPodStatus(pod)}
/>
</div>
</Tooltip>
)
}
2 changes: 1 addition & 1 deletion src/components/ui/json.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import JsonView from "react18-json-view"
import "react18-json-view/src/style.css"

export default function JsonWidget(src: any) {
return <JsonView src={src} />
return <JsonView src={src} editable={false} />
}
21 changes: 8 additions & 13 deletions src/lib/kube/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,23 +175,18 @@ export async function getKubeData(): Promise<KubeData> {
})
.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,
}
Expand Down
55 changes: 48 additions & 7 deletions src/lib/kube/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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()),
Expand Down Expand Up @@ -301,7 +305,7 @@ export type Deployment = {
export type Job = {
name: string
raw: RawJob
pods: RawPod[]
// pods: RawPod[]
}

export type Cronjob = {
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -422,7 +462,8 @@ function hasError<Item>(
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"
}
Expand Down

0 comments on commit 429a0f1

Please sign in to comment.