Skip to content

Commit

Permalink
base reconciler reconciles status
Browse files Browse the repository at this point in the history
  • Loading branch information
eguzki committed Jul 9, 2024
1 parent e627fb3 commit 0524541
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 27 deletions.
31 changes: 30 additions & 1 deletion api/v1beta2/ratelimitpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
"github.com/kuadrant/kuadrant-operator/pkg/library/reconcilers"
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
)

Expand Down Expand Up @@ -170,7 +171,15 @@ type RateLimitPolicyStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

func (s *RateLimitPolicyStatus) Equals(other *RateLimitPolicyStatus, logger logr.Logger) bool {
var _ reconcilers.StatusObject = &RateLimitPolicyStatus{}

func (s *RateLimitPolicyStatus) Equals(otherStatus reconcilers.StatusObject, logger logr.Logger) bool {
other, ok := otherStatus.(*RateLimitPolicyStatus)
if !ok {
logger.Info("ERROR in RateLimitPolicyStatus Equals: type does not match")
return false

Check warning on line 180 in api/v1beta2/ratelimitpolicy_types.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta2/ratelimitpolicy_types.go#L179-L180

Added lines #L179 - L180 were not covered by tests
}

if s.ObservedGeneration != other.ObservedGeneration {
diff := cmp.Diff(s.ObservedGeneration, other.ObservedGeneration)
logger.V(1).Info("ObservedGeneration not equal", "difference", diff)
Expand All @@ -191,12 +200,32 @@ func (s *RateLimitPolicyStatus) Equals(other *RateLimitPolicyStatus, logger logr
return true
}

func (s *RateLimitPolicyStatus) GetObservedGeneration() int64 {
return s.ObservedGeneration
}

func (s *RateLimitPolicyStatus) SetObservedGeneration(o int64) {
s.ObservedGeneration = o
}

func (s *RateLimitPolicyStatus) GetConditions() []metav1.Condition {
return s.Conditions
}

var _ kuadrant.Policy = &RateLimitPolicy{}
var _ kuadrant.Referrer = &RateLimitPolicy{}
var _ reconcilers.ObjectWithStatus = &RateLimitPolicy{}

func (r *RateLimitPolicy) GetStatusObject() reconcilers.StatusObject {
return &r.Status
}

func (r *RateLimitPolicy) SetStatusObject(statusObject reconcilers.StatusObject) {
switch status := statusObject.(type) {
case *RateLimitPolicyStatus:
r.Status = *status
}
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
Expand Down
33 changes: 7 additions & 26 deletions controllers/ratelimitpolicy_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package controllers

import (
"context"
"fmt"
"slices"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

Expand All @@ -19,36 +19,17 @@ import (
func (r *RateLimitPolicyReconciler) reconcileStatus(ctx context.Context, rlp *kuadrantv1beta2.RateLimitPolicy, specErr error) (ctrl.Result, error) {
logger, _ := logr.FromContext(ctx)
newStatus := r.calculateStatus(rlp, specErr)

equalStatus := rlp.Status.Equals(newStatus, logger)
logger.V(1).Info("Status", "status is different", !equalStatus)
logger.V(1).Info("Status", "generation is different", rlp.Generation != rlp.Status.ObservedGeneration)
if equalStatus && rlp.Generation == rlp.Status.ObservedGeneration {
// Steady state
logger.V(1).Info("Status was not updated")
return reconcile.Result{}, nil
}

// Save the generation number we acted on, otherwise we might wrongfully indicate
// that we've seen a spec update when we retry.
// TODO: This can clobber an update if we allow multiple agents to write to the
// same status.
newStatus.ObservedGeneration = rlp.Generation

logger.V(1).Info("Updating Status", "sequence no:", fmt.Sprintf("sequence No: %v->%v", rlp.Status.ObservedGeneration, newStatus.ObservedGeneration))

rlp.Status = *newStatus
updateErr := r.Client().Status().Update(ctx, rlp)
logger.V(1).Info("Updating Status", "err", updateErr)
if updateErr != nil {
err := r.ReconcileResourceStatus(ctx, client.ObjectKeyFromObject(rlp), &kuadrantv1beta2.RateLimitPolicy{}, newStatus)
if err != nil {
// Ignore conflicts, resource might just be outdated.
if apierrors.IsConflict(updateErr) {
logger.Info("Failed to update status: resource might just be outdated")
if apierrors.IsConflict(err) {
logger.V(1).Info("Failed to update status: resource might just be outdated")
return reconcile.Result{Requeue: true}, nil
}

return reconcile.Result{}, fmt.Errorf("failed to update status: %w", updateErr)
return ctrl.Result{}, err

Check warning on line 30 in controllers/ratelimitpolicy_status.go

View check run for this annotation

Codecov / codecov/patch

controllers/ratelimitpolicy_status.go#L30

Added line #L30 was not covered by tests
}

return ctrl.Result{}, nil
}

Expand Down
54 changes: 54 additions & 0 deletions pkg/library/reconcilers/base_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ import (
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
)

type StatusObject interface {
Equals(StatusObject, logr.Logger) bool
GetObservedGeneration() int64
SetObservedGeneration(int64)
}

type ObjectWithStatus interface {
client.Object
GetStatusObject() StatusObject
SetStatusObject(StatusObject)
}

// MutateFn is a function which mutates the existing object into it's desired state.
type MutateFn func(existing, desired client.Object) (bool, error)

Expand Down Expand Up @@ -143,6 +155,48 @@ func (b *BaseReconciler) ReconcileResource(ctx context.Context, obj, desired cli
return nil
}

func (b *BaseReconciler) ReconcileResourceStatus(ctx context.Context, objKey client.ObjectKey, obj client.Object, desired StatusObject) error {
logger, err := logr.FromContext(ctx)
if err != nil {
return err

Check warning on line 161 in pkg/library/reconcilers/base_reconciler.go

View check run for this annotation

Codecov / codecov/patch

pkg/library/reconcilers/base_reconciler.go#L161

Added line #L161 was not covered by tests
}

if err := b.Client().Get(ctx, objKey, obj); err != nil {
return err

Check warning on line 165 in pkg/library/reconcilers/base_reconciler.go

View check run for this annotation

Codecov / codecov/patch

pkg/library/reconcilers/base_reconciler.go#L165

Added line #L165 was not covered by tests
}

objectWithStatus, ok := obj.(ObjectWithStatus)
if !ok {
return fmt.Errorf("StatusObject not implemented")

Check warning on line 170 in pkg/library/reconcilers/base_reconciler.go

View check run for this annotation

Codecov / codecov/patch

pkg/library/reconcilers/base_reconciler.go#L170

Added line #L170 was not covered by tests
}

equalStatus := objectWithStatus.GetStatusObject().Equals(desired, logger)
logger.V(1).Info("Status", "status is different", !equalStatus)
logger.V(1).Info("Status", "generation is different", objectWithStatus.GetGeneration() != objectWithStatus.GetStatusObject().GetObservedGeneration())
if equalStatus && objectWithStatus.GetGeneration() == objectWithStatus.GetStatusObject().GetObservedGeneration() {
// Steady state, early return 🎉
logger.V(1).Info("Status was not updated")
return nil
}

// Save the generation number we acted on, otherwise we might wrongfully indicate
// that we've seen a spec update when we retry.
// TODO: This can clobber an update if we allow multiple agents to write to the
// same status.
desired.SetObservedGeneration(objectWithStatus.GetGeneration())

logger.V(1).Info("Updating Status", "sequence no:", fmt.Sprintf("sequence No: %v->%v", objectWithStatus.GetStatusObject().GetObservedGeneration(), desired.GetObservedGeneration()))

objectWithStatus.SetStatusObject(desired)
updateErr := b.Client().Status().Update(ctx, objectWithStatus)
logger.V(1).Info("Updating Status", "err", updateErr)
if updateErr != nil {
return fmt.Errorf("failed to update status: %w", updateErr)
}

return nil
}

func (b *BaseReconciler) GetResource(ctx context.Context, objKey types.NamespacedName, obj client.Object) error {
logger, _ := logr.FromContext(ctx)
logger.Info("get object", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", objKey.Name, "namespace", objKey.Namespace)
Expand Down

0 comments on commit 0524541

Please sign in to comment.