api/v1/status.go

Summary

Maintainability
A
0 mins
Test Coverage
F
0%
package v1

import (
    "k8s.io/apimachinery/pkg/api/meta"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Status defines the observed state of a generic K8up job. It is used for the
// operator to determine what to do.
type Status struct {
    Started   bool `json:"started,omitempty"`
    Finished  bool `json:"finished,omitempty"`
    Exclusive bool `json:"exclusive,omitempty"`

    // Conditions provide a standard mechanism for higher-level status reporting from a controller.
    // They are an extension mechanism which allows tools and other controllers to collect summary information about
    // resources without needing to understand resource-specific status details.
    Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// HasFailed returns true in the following cases:
//
// * If ConditionCompleted is true with any other reason than ReasonSucceeded.
//
// * If ConditionPreBackupPodReady is false with any of the "failed" reasons.
func (in Status) HasFailed() bool {
    if in.HasFailedPreBackup() {
        return true
    }
    completedCond := meta.FindStatusCondition(in.Conditions, ConditionCompleted.String())
    if completedCond != nil && !matchAnyReason(*completedCond, ReasonSucceeded) {
        return completedCond.Status == metav1.ConditionTrue
    }
    return false
}

// HasSucceeded returns true if all cases are true:
//
// * If ConditionCompleted is true with ReasonSucceeded.
//
// * If ConditionPreBackupPodReady has no failure reason.
func (in Status) HasSucceeded() bool {
    if in.HasFailedPreBackup() {
        return false
    }
    completedCond := meta.FindStatusCondition(in.Conditions, ConditionCompleted.String())
    if completedCond != nil && matchAnyReason(*completedCond, ReasonSucceeded) {
        return completedCond.Status == metav1.ConditionTrue
    }
    return false
}

// HasFinished returns true if either HasFailed() or HasSucceeded() return true.
func (in Status) HasFinished() bool {
    return in.HasFailed() || in.HasSucceeded()
}

// HasFailedPreBackup returns true if ConditionPreBackupPodReady is false with any of the "failed" reasons.
func (in Status) HasFailedPreBackup() bool {
    preBackupCond := meta.FindStatusCondition(in.Conditions, ConditionPreBackupPodReady.String())
    // Failed pre backups also count as finished
    if preBackupCond != nil && isPreBackupFailed(*preBackupCond) {
        return preBackupCond.Status == metav1.ConditionFalse
    }
    return false
}

// HasStarted returns true if ConditionProgressing is true with ReasonStarted, false otherwise.
func (in Status) HasStarted() bool {
    condition := meta.FindStatusCondition(in.Conditions, ConditionProgressing.String())
    if condition != nil && matchAnyReason(*condition, ReasonStarted) {
        return condition.Status == metav1.ConditionTrue
    }
    return false
}

// IsWaitingForPreBackup returns true if the ConditionPreBackupPodReady is Unknown with ReasonWaiting, false otherwise.
func (in Status) IsWaitingForPreBackup() bool {
    condition := meta.FindStatusCondition(in.Conditions, ConditionPreBackupPodReady.String())
    if condition != nil && matchAnyReason(*condition, ReasonWaiting) {
        return condition.Status == metav1.ConditionUnknown
    }
    return false
}

// SetStarted sets ConditionReady to true with ReasonReady and ConditionProgressing with ReasonStarted.
// The given message parameter is set as the message for ConditionReady.
// It also sets the deprecated Started flag to true.
func (in *Status) SetStarted(message string) {
    in.Started = true
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    ConditionReady.String(),
        Status:  metav1.ConditionTrue,
        Reason:  ReasonReady.String(),
        Message: message,
    })
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    ConditionProgressing.String(),
        Status:  metav1.ConditionTrue,
        Reason:  ReasonStarted.String(),
        Message: "The job is progressing",
    })
}

// SetFinished sets ConditionProgressing to false with ReasonFinished.
// It also sets the deprecated Finished flag to true.
func (in *Status) SetFinished(message string) {
    in.Finished = true
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    ConditionProgressing.String(),
        Status:  metav1.ConditionFalse,
        Reason:  ReasonFinished.String(),
        Message: message,
    })
    meta.RemoveStatusCondition(&in.Conditions, ConditionReady.String())
}

// SetFailed sets ConditionCompleted to true with ReasonFailed.
func (in *Status) SetFailed(message string) {
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    ConditionCompleted.String(),
        Status:  metav1.ConditionTrue,
        Reason:  ReasonFailed.String(),
        Message: message,
    })
}

// SetSucceeded sets ConditionCompleted to true with ReasonSucceeded.
func (in *Status) SetSucceeded(message string) {
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    ConditionCompleted.String(),
        Status:  metav1.ConditionTrue,
        Reason:  ReasonSucceeded.String(),
        Message: message,
    })
}

// SetCondition sets a generic condition, overwriting existing one by type if present.
func (in *Status) SetCondition(typ ConditionType, reason ConditionReason, status metav1.ConditionStatus, message string) {
    meta.SetStatusCondition(&in.Conditions, metav1.Condition{
        Type:    typ.String(),
        Status:  status,
        Reason:  reason.String(),
        Message: message,
    })
}

func isPreBackupFailed(condition metav1.Condition) bool {
    return !matchAnyReason(condition,
        ReasonSucceeded,
        ReasonWaiting,
        ReasonNoPreBackupPodsFound,
        ReasonReady)
}

func matchAnyReason(condition metav1.Condition, reasons ...ConditionReason) bool {
    for _, reason := range reasons {
        if condition.Reason == reason.String() {
            return true
        }
    }
    return false
}