Skip to content

Commit c62cd2a

Browse files
authored
Merge pull request #322 from justinsb/test_reconciler_sleep_after_apply
testreconciler: pause after reconciliation
2 parents d7a278a + 4d15f5b commit c62cd2a

File tree

3 files changed

+73
-4
lines changed

3 files changed

+73
-4
lines changed

pkg/patterns/declarative/hooks.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,19 @@ type AfterApply interface {
4848
type BeforeApply interface {
4949
BeforeApply(ctx context.Context, op *ApplyOperation) error
5050
}
51+
52+
// UpdateStatusOperation contains the details of an Apply operation
53+
type UpdateStatusOperation struct {
54+
// Subject is the object we are reconciling
55+
Subject DeclarativeObject
56+
}
57+
58+
// AfterUpdateStatus is implemented by hooks that want to be called after the update-status phase
59+
type AfterUpdateStatus interface {
60+
AfterUpdateStatus(ctx context.Context, op *UpdateStatusOperation) error
61+
}
62+
63+
// BeforeUpdateStatus is implemented by hooks that want to be called before the update-status phase
64+
type BeforeUpdateStatus interface {
65+
BeforeUpdateStatus(ctx context.Context, op *UpdateStatusOperation) error
66+
}

pkg/patterns/declarative/reconciler.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,25 +198,56 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
198198
}
199199
}
200200

201+
if err := r.updateStatus(ctx, original, instance); err != nil {
202+
return result, err
203+
}
204+
205+
return result, nil
206+
}
207+
208+
func (r *Reconciler) updateStatus(ctx context.Context, original DeclarativeObject, instance DeclarativeObject) error {
209+
log := log.FromContext(ctx)
210+
201211
// Write the status if it has changed
202212
oldStatus, err := utils.GetCommonStatus(original)
203213
if err != nil {
204214
log.Error(err, "error getting status")
205-
return result, err
215+
return err
206216
}
207217
newStatus, err := utils.GetCommonStatus(instance)
208218
if err != nil {
209219
log.Error(err, "error getting status")
210-
return result, err
220+
return err
221+
}
222+
223+
statusOperation := &UpdateStatusOperation{}
224+
225+
for _, hook := range r.options.hooks {
226+
if beforeUpdateStatus, ok := hook.(BeforeUpdateStatus); ok {
227+
if err := beforeUpdateStatus.BeforeUpdateStatus(ctx, statusOperation); err != nil {
228+
log.Error(err, "calling BeforeUpdateStatus hook")
229+
return fmt.Errorf("error calling BeforeUpdateStatus hook: %w", err)
230+
}
231+
}
211232
}
233+
212234
if !reflect.DeepEqual(oldStatus, newStatus) {
213235
if err := r.client.Status().Update(ctx, instance); err != nil {
214236
log.Error(err, "error updating status")
215-
return result, err
237+
return err
216238
}
217239
}
218240

219-
return result, err
241+
for _, hook := range r.options.hooks {
242+
if afterUpdateStatus, ok := hook.(AfterUpdateStatus); ok {
243+
if err := afterUpdateStatus.AfterUpdateStatus(ctx, statusOperation); err != nil {
244+
log.Error(err, "calling AfterUpdateStatus hook")
245+
return fmt.Errorf("error calling AfterUpdateStatus hook: %w", err)
246+
}
247+
}
248+
}
249+
250+
return nil
220251
}
221252

222253
func (r *Reconciler) reconcileExists(ctx context.Context, name types.NamespacedName, instance DeclarativeObject) (*StatusInfo, error) {

pkg/test/testreconciler/simpletest/controller.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ limitations under the License.
1717
package simpletest
1818

1919
import (
20+
"context"
21+
"time"
22+
2023
"github.com/go-logr/logr"
2124
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/klog/v2"
2226

2327
ctrl "sigs.k8s.io/controller-runtime"
2428
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -76,6 +80,8 @@ func (r *SimpleTestReconciler) setupReconciler(mgr ctrl.Manager) error {
7680

7781
declarative.WithManifestController(r.manifestController),
7882
declarative.WithApplier(r.applier),
83+
84+
declarative.WithHook(&sleepAfterUpdateStatusHook{}),
7985
)
8086
}
8187

@@ -103,3 +109,19 @@ func (r *SimpleTestReconciler) SetupWithManager(mgr ctrl.Manager) error {
103109

104110
return nil
105111
}
112+
113+
// We sleep briefly after updating the status.
114+
// This is a hack to ensure that we see the watch event, because otherwise we can start a re-reconcile based on our derived objects,
115+
// and then reconcile again based on our own status update. This is racy behaviour and causes non-determinism.
116+
// We do this only in this test controller, but maybe we should have similar logic to "debounce" in all controllers.
117+
type sleepAfterUpdateStatusHook struct {
118+
}
119+
120+
var _ declarative.AfterUpdateStatus = &sleepAfterUpdateStatusHook{}
121+
122+
func (h *sleepAfterUpdateStatusHook) AfterUpdateStatus(ctx context.Context, op *declarative.UpdateStatusOperation) error {
123+
sleepFor := 100 * time.Millisecond
124+
klog.Infof("sleeping after apply for %v", sleepFor)
125+
time.Sleep(sleepFor)
126+
return nil
127+
}

0 commit comments

Comments
 (0)