“We are running a in-house made kubernetes operator do to that for you” is a sentence you might hear from platform team quite often. “You only to to use this simple custom resource (CR)”.
On one hand, it’s super useful that team allows you to solve complex tasks by simple declarative definitions. On contrary most of the operators cover only happy paths and even they are without any verbosity to users. Not talking about failures.
I’m mentioning this because every failure leads to operations which, let’s be honest, everybody hate while the solution is super simple and we are already used to it from natvie kubernetes objects - Conditions
. Don’t get me wrong. Conditions won’t prevent ops. They will just provide transparency to application developers revealing user errors they can fix on their own.
Conditions represents the latest available observations of an object’s state and they provide standard mechanism for higher-level status reporting from a controller. Each object can report multiple conditions therefore they are represented using list of objects.
Conditions should be included as a top level element in status field and follow schema included in k8s.io/apimachinery/pkg/apis/meta/v1/types.go.
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type MyCRStatus struct {
Conditions []metav1.Condition `json:"conditions"`
}
The metav1.Conditions
includes the following fieds:
type Condition struct {
Type string
Status ConditionStatus
ObservedGeneration int64
LastTransitionTime Time
Reason string
Message string
}
Looking at this, it seems to be complex to manage but the opposite is truth. Kubernetes apimachinery package provides a lof of helpers so only thing you need to care about is what status to use.
In a typical workflow, as a first thing to do is set Unknown
status. This is when reconciliation of an objects starts and we don’t know it’s status yet but we are letting know other components in the system know that operator is making something with the resource.
import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
// Condition values Indicates that object is available
MyObjectAvailableType string = "ObjectAvailable"
MyObjectReconcilingReason string = "Reconciling"
MyObjectFailedReason string = "Failed"
MyObjectCreatedReason string = "Created"
)
type MyObjectConditionReason string
meta.SetStatusCondition(
&moObject.Status.Conditions,
metav1.Condition{
Status: metav1.ConditionUnknown,
Reason: MyObjectReconcilingReason,
Type: MyObjectAvailableType,
Message: "Object is at reconcilling phase."
}
)
Besides Status, there are couple other fields:
Reason: Provides a simple status summary - why is status unknown?
Type: Unique identifier of a condition. Condition can have different types where each track different state of the object. For example if underlying object would be database, you would probbably define multiple condition types like DatabaseCreated
, DatabaseHealthy
and simmilar.
Message: Human readable detail of error.
This allow us easily describe what is really happening with the object.
err := thirdPartyClient.create(myObject)
if err != nil {
meta.SetStatusCondition(
&moObject.Status.Conditions,
metav1.Condition{
Status: metav1.ConditionFalse,
Reason: MyObjectFailedReason,
Type: MyObjectAvailableType,
Message: fmt.Sprintf("Could not create MyObject: %v", err)
}
)
return ctrl.Result{}, err
} else {
meta.SetStatusCondition(
&moObject.Status.Conditions,
metav1.Condition{
Status: metav1.ConditionTrue,
Reason: MyObjectCreatedReason,
Type: MyObjectAvailableType,
Message: "Object was sucesfully created",
}
)
}
From user perspective this looks like this:
$ kubectl describe myobect example
Name: example
Kind: MyObject
<redacted>
Status:
Conditions:
Message: "Could not create MyObject: user did something wrong"
Reason: Failed
Status: False
Type: ObjectAvailable
Conditions are much more powerful. In this article, I just wanted to point how simple it is to implement them and can they provide a lot of information for debugging. With just couple lines of code, an user experience can sky rocket.