The Reconcile Function
The heart of every operator is the Reconcile function. Everything else — the Manager, the cache, the event watchers — exists to call this one function at the right time.
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// Your logic here
}
The system calls this function whenever a relevant event occurs (an object was created, updated, or deleted). However, the request req only contains the NamespacedName — a simple identifier like "production/my-database". It does not contain the actual object data.
This is intentional. By the time your function runs, the event that triggered it might be stale. The object might have been updated again. So your first job is always to fetch the current state from the cache.
The four-step pattern every Reconcile function follows:
1. Fetch — Use the NamespacedName to get the latest state of the object from the API server cache.
var db myapi.Database
if err := r.Get(ctx, req.NamespacedName, &db); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
2. Compare — Check the current state of the world against the desired state in the object's Spec. For example: does a child Deployment exist? Does it have the right number of replicas? The right container image?
3. Act — Create, update, or delete child resources to eliminate any drift between desired and actual state.
// Example: ensure a Deployment exists with the correct replica count
deployment := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{Name: db.Name, Namespace: db.Namespace}, deployment)
if errors.IsNotFound(err) {
// Create it
newDeploy := r.buildDeployment(&db)
return ctrl.Result{}, r.Create(ctx, newDeploy)
}
// Otherwise, check if it needs updating
if *deployment.Spec.Replicas != db.Spec.Replicas {
deployment.Spec.Replicas = &db.Spec.Replicas
return ctrl.Result{}, r.Update(ctx, deployment)
}
4. Update Status — Write back to the object's Status subresource to reflect what actually happened. This is how users and other tools observe the state of the system.
Return values control what happens next:
- ctrl.Result{}, nil — Success. Do nothing until the next event.
- ctrl.Result{RequeueAfter: 30 * time.Second}, nil — Success, but re-run in 30 seconds (useful for polling external systems).
- ctrl.Result{}, err — Failure. Requeue with exponential backoff.