diff --git a/pkg/schema/v1/job.go b/pkg/schema/v1/job.go index 880277eb..e4e89d22 100644 --- a/pkg/schema/v1/job.go +++ b/pkg/schema/v1/job.go @@ -2,10 +2,12 @@ package v1 import ( "database/sql" + "fmt" "github.com/icinga/icinga-go-library/types" "github.com/icinga/icinga-kubernetes/pkg/database" "github.com/icinga/icinga-kubernetes/pkg/strcase" kbatchv1 "k8s.io/api/batch/v1" + kcorev1 "k8s.io/api/core/v1" kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kruntime "k8s.io/apimachinery/pkg/runtime" kserializer "k8s.io/apimachinery/pkg/runtime/serializer" @@ -28,6 +30,8 @@ type Job struct { Succeeded int32 Failed int32 Yaml string + IcingaState IcingaState + IcingaStateReason string Conditions []JobCondition `db:"-"` Labels []Label `db:"-"` JobLabels []JobLabel `db:"-"` @@ -120,6 +124,7 @@ func (j *Job) Obtain(k8s kmetav1.Object) { j.Active = job.Status.Active j.Succeeded = job.Status.Succeeded j.Failed = job.Status.Failed + j.IcingaState, j.IcingaStateReason = j.getIcingaState(job) for _, condition := range job.Status.Conditions { j.Conditions = append(j.Conditions, JobCondition{ @@ -166,6 +171,63 @@ func (j *Job) Obtain(k8s kmetav1.Object) { j.Yaml = string(output) } +func (j *Job) getIcingaState(job *kbatchv1.Job) (IcingaState, string) { + for _, condition := range job.Status.Conditions { + if condition.Status != kcorev1.ConditionTrue { + continue + } + + switch condition.Type { + case kbatchv1.JobSuccessCriteriaMet: + return Ok, fmt.Sprintf( + "Job %s/%s met its sucess criteria.", + j.Namespace, j.Name) + case kbatchv1.JobComplete: + reason := fmt.Sprintf( + "Job %s/%s has completed its execution successfully with", + j.Namespace, j.Name) + + if j.Completions.Valid { + reason += fmt.Sprintf(" %d necessary pod completions.", j.Completions.Int32) + } else { + reason += " any pod completion." + } + + return Ok, reason + case kbatchv1.JobFailed: + return Critical, fmt.Sprintf( + "Job %s/%s has failed its execution. %s: %s.", + j.Namespace, j.Name, condition.Reason, condition.Message) + case kbatchv1.JobFailureTarget: + return Warning, fmt.Sprintf( + "Job %s/%s is about to fail its execution. %s: %s.", + j.Namespace, j.Name, condition.Reason, condition.Message) + case kbatchv1.JobSuspended: + return Ok, fmt.Sprintf( + "Job %s/%s is suspended.", + j.Namespace, j.Name) + } + } + + var completions string + if j.Completions.Valid { + completions = fmt.Sprintf("%d pod completions", j.Completions.Int32) + } else { + completions = "any pod completion" + } + + reason := fmt.Sprintf( + "Job %s/%s is running since %s with currently %d active, %d completed and %d failed pods. "+ + "Successful termination requires %s. The back-off limit is %d.", + j.Namespace, j.Name, job.Status.StartTime, j.Active, j.Succeeded, j.Failed, completions, job.Spec.BackoffLimit) + + if job.Spec.ActiveDeadlineSeconds != nil { + reason += fmt.Sprintf(" Deadline for completion is %d.", job.Spec.ActiveDeadlineSeconds) + } + + return Pending, reason +} + func (j *Job) Relations() []database.Relation { fk := database.WithForeignKey("job_uuid") diff --git a/schema/mysql/schema.sql b/schema/mysql/schema.sql index 39680499..603e5329 100644 --- a/schema/mysql/schema.sql +++ b/schema/mysql/schema.sql @@ -803,6 +803,8 @@ CREATE TABLE job ( succeeded int unsigned NOT NULL, failed int unsigned NOT NULL, yaml mediumblob DEFAULT NULL, + icinga_state enum('pending', 'ok', 'warning', 'critical', 'unknown') COLLATE utf8mb4_unicode_ci NOT NULL, + icinga_state_reason text NOT NULL, created bigint unsigned NOT NULL, PRIMARY KEY (uuid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;