Skip to content

Commit

Permalink
Add hooks to ResourceReconciler, AggregateReconciler and AdmissionWeb…
Browse files Browse the repository at this point in the history
…hookAdapter

Signed-off-by: Scott Andrews <[email protected]>
  • Loading branch information
scothis committed Apr 4, 2024
1 parent 95a296f commit f4e5889
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 13 deletions.
49 changes: 42 additions & 7 deletions reconcilers/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ type AggregateReconciler[Type client.Object] struct {
// +optional
Sanitize func(resource Type) interface{}

// BeforeReconcile is called first thing for each reconcile request. A modified context may be
// returned. Errors are returned immediately.
//
// If BeforeReconcile is not defined, there is no effect.
//
// +optional
BeforeReconcile func(ctx context.Context, req Request) (context.Context, Result, error)

// AfterReconcile is called following all work for the reconcile request. The result and error
// are provided and may be modified before returning.
//
// If AfterReconcile is not defined, the result and error are returned directly.
//
// +optional
AfterReconcile func(ctx context.Context, req Request, res Result, err error) (Result, error)

Config Config

// stamp manages the lifecycle of the aggregated resource.
Expand All @@ -128,6 +144,16 @@ func (r *AggregateReconciler[T]) init() {
return resource, nil
}
}
if r.BeforeReconcile == nil {
r.BeforeReconcile = func(ctx context.Context, req reconcile.Request) (context.Context, reconcile.Result, error) {
return ctx, Result{}, nil
}
}
if r.AfterReconcile == nil {
r.AfterReconcile = func(ctx context.Context, req reconcile.Request, res reconcile.Result, err error) (reconcile.Result, error) {
return res, err
}
}

r.stamp = &ResourceManager[T]{
Name: r.Name,
Expand Down Expand Up @@ -231,6 +257,14 @@ func (r *AggregateReconciler[T]) Reconcile(ctx context.Context, req Request) (Re
ctx = StashOriginalResourceType(ctx, r.Type)
ctx = StashResourceType(ctx, r.Type)

beforeCtx, result, err := r.BeforeReconcile(ctx, req)
if err != nil {
return result, err
}
if beforeCtx != nil {
ctx = beforeCtx
}

resource := r.Type.DeepCopyObject().(T)
if err := c.Get(ctx, req.NamespacedName, resource); err != nil {
if apierrs.IsNotFound(err) {
Expand All @@ -241,18 +275,19 @@ func (r *AggregateReconciler[T]) Reconcile(ctx context.Context, req Request) (Re
if !errors.Is(err, ErrQuiet) {
log.Error(err, "unable to fetch resource")
}
return Result{}, err
return r.AfterReconcile(ctx, req, result, err)
}
}

if resource.GetDeletionTimestamp() != nil {
// resource is being deleted, nothing to do
return Result{}, nil
return r.AfterReconcile(ctx, req, result, nil)
}

result, err := r.Reconciler.Reconcile(ctx, resource)
reconcileResult, err := r.Reconciler.Reconcile(ctx, resource)
result = AggregateResults(result, reconcileResult)
if err != nil && !errors.Is(err, ErrHaltSubReconcilers) {
return result, err
return r.AfterReconcile(ctx, req, result, err)
}

// hack, ignore track requests from the child reconciler, we have it covered
Expand All @@ -264,13 +299,13 @@ func (r *AggregateReconciler[T]) Reconcile(ctx context.Context, req Request) (Re
})
desired, err := r.desiredResource(ctx, resource)
if err != nil {
return Result{}, err
return r.AfterReconcile(ctx, req, result, err)
}
_, err = r.stamp.Manage(ctx, resource, resource, desired)
if err != nil {
return Result{}, err
return r.AfterReconcile(ctx, req, result, err)
}
return result, nil
return r.AfterReconcile(ctx, req, result, nil)
}

func (r *AggregateReconciler[T]) desiredResource(ctx context.Context, resource T) (T, error) {
Expand Down
49 changes: 43 additions & 6 deletions reconcilers/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ type ResourceReconciler[Type client.Object] struct {
// returned.
Reconciler SubReconciler[Type]

// BeforeReconcile is called first thing for each reconcile request. A modified context may be
// returned. Errors are returned immediately.
//
// If BeforeReconcile is not defined, there is no effect.
//
// +optional
BeforeReconcile func(ctx context.Context, req Request) (context.Context, Result, error)

// AfterReconcile is called following all work for the reconcile request. The result and error
// are provided and may be modified before returning.
//
// If AfterReconcile is not defined, the result and error are returned directly.
//
// +optional
AfterReconcile func(ctx context.Context, req Request, res Result, err error) (Result, error)

Config Config

lazyInit sync.Once
Expand All @@ -97,6 +113,16 @@ func (r *ResourceReconciler[T]) init() {
if r.Name == "" {
r.Name = fmt.Sprintf("%sResourceReconciler", typeName(r.Type))
}
if r.BeforeReconcile == nil {
r.BeforeReconcile = func(ctx context.Context, req reconcile.Request) (context.Context, reconcile.Result, error) {
return ctx, Result{}, nil
}
}
if r.AfterReconcile == nil {
r.AfterReconcile = func(ctx context.Context, req reconcile.Request, res reconcile.Result, err error) (reconcile.Result, error) {
return res, err
}
}
})
}

Expand Down Expand Up @@ -210,17 +236,26 @@ func (r *ResourceReconciler[T]) Reconcile(ctx context.Context, req Request) (Res
ctx = StashResourceType(ctx, r.Type)
originalResource := r.Type.DeepCopyObject().(T)

beforeCtx, result, err := r.BeforeReconcile(ctx, req)
if err != nil {
return result, err
}
if beforeCtx != nil {
ctx = beforeCtx
}

if err := c.Get(ctx, req.NamespacedName, originalResource); err != nil {
if apierrs.IsNotFound(err) {
// we'll ignore not-found errors, since they can't be fixed by an immediate
// requeue (we'll need to wait for a new notification), and we can get them
// on deleted requests.
return Result{}, nil
return r.AfterReconcile(ctx, req, result, nil)
}
if !errors.Is(err, ErrQuiet) {
log.Error(err, "unable to fetch resource")
}
return Result{}, err

return r.AfterReconcile(ctx, req, result, err)
}
resource := originalResource.DeepCopyObject().(T)

Expand All @@ -230,10 +265,11 @@ func (r *ResourceReconciler[T]) Reconcile(ctx context.Context, req Request) (Res
}

r.initializeConditions(ctx, resource)
result, err := r.reconcile(ctx, resource)
reconcileResult, err := r.reconcile(ctx, resource)
result = AggregateResults(result, reconcileResult)

if r.SkipStatusUpdate {
return result, err
return r.AfterReconcile(ctx, req, result, err)
}

// attempt to restore last transition time for unchanged conditions
Expand All @@ -251,7 +287,8 @@ func (r *ResourceReconciler[T]) Reconcile(ctx context.Context, req Request) (Res
c.Recorder.Eventf(resource, corev1.EventTypeWarning, "StatusPatchFailed",
"Failed to patch status: %v", patchErr)
}
return Result{}, patchErr

return r.AfterReconcile(ctx, req, result, patchErr)
}
c.Recorder.Eventf(resource, corev1.EventTypeNormal, "StatusPatched",
"Patched status")
Expand All @@ -264,7 +301,7 @@ func (r *ResourceReconciler[T]) Reconcile(ctx context.Context, req Request) (Res
c.Recorder.Eventf(resource, corev1.EventTypeWarning, "StatusUpdateFailed",
"Failed to update status: %v", updateErr)
}
return Result{}, updateErr
return r.AfterReconcile(ctx, req, result, updateErr)
}
c.Recorder.Eventf(resource, corev1.EventTypeNormal, "StatusUpdated",
"Updated status")
Expand Down
29 changes: 29 additions & 0 deletions reconcilers/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ type AdmissionWebhookAdapter[Type client.Object] struct {
// Typically, Reconciler is a Sequence of multiple SubReconcilers.
Reconciler SubReconciler[Type]

// BeforeHandle is called first thing for each admission request. A modified context may be
// returned.
//
// If BeforeHandle is not defined, there is no effect.
//
// +optional
BeforeHandle func(ctx context.Context, req admission.Request, resp *admission.Response) context.Context

// AfterHandle is called following all work for the admission request. The response is provided
// and may be modified before returning.
//
// If AfterHandle is not defined, the response is returned directly.
//
// +optional
AfterHandle func(ctx context.Context, req admission.Request, resp *admission.Response)

Config Config

lazyInit sync.Once
Expand All @@ -88,6 +104,14 @@ func (r *AdmissionWebhookAdapter[T]) init() {
if r.Name == "" {
r.Name = fmt.Sprintf("%sAdmissionWebhookAdapter", typeName(r.Type))
}
if r.BeforeHandle == nil {
r.BeforeHandle = func(ctx context.Context, req admission.Request, resp *admission.Response) context.Context {
return ctx
}
}
if r.AfterHandle == nil {
r.AfterHandle = func(ctx context.Context, req admission.Request, resp *admission.Response) {}
}
})
}

Expand Down Expand Up @@ -142,6 +166,10 @@ func (r *AdmissionWebhookAdapter[T]) Handle(ctx context.Context, req admission.R
ctx = StashAdmissionRequest(ctx, req)
ctx = StashAdmissionResponse(ctx, resp)

if beforeCtx := r.BeforeHandle(ctx, req, resp); beforeCtx != nil {
ctx = beforeCtx
}

// defined for compatibility since this is not a reconciler
ctx = StashRequest(ctx, Request{
NamespacedName: types.NamespacedName{
Expand All @@ -163,6 +191,7 @@ func (r *AdmissionWebhookAdapter[T]) Handle(ctx context.Context, req admission.R
}
}

r.AfterHandle(ctx, req, resp)
return *resp
}

Expand Down

0 comments on commit f4e5889

Please sign in to comment.