Skip to content

Commit 9ec5b30

Browse files
committed
Implement UpToDate condition in managed reconciler
Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com>
1 parent d99a3b8 commit 9ec5b30

5 files changed

Lines changed: 42 additions & 42 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.25.0
44

55
require (
66
dario.cat/mergo v1.0.2
7-
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260306220439-153a39424689
7+
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260414170437-8edd7b40ece5
88
github.com/evanphx/json-patch v5.9.11+incompatible
99
github.com/go-logr/logr v1.4.3
1010
github.com/google/go-cmp v0.7.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9
1515
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
1616
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
1717
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
18-
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260306220439-153a39424689 h1:fpdwyUwjAHBPZCewKAU6ko7j6WwMQnkZYX8Qk6L6HUo=
19-
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260306220439-153a39424689/go.mod h1:pl+8j97av1Tv1az8CwIzXepl1YfwI5/fOsWO6Bh4sNI=
18+
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260414170437-8edd7b40ece5 h1:h6CektiH0I75sW4wtZ9rmKIxMcoxkNioU7NJ8IDtLvg=
19+
github.com/crossplane/crossplane/apis/v2 v2.0.0-20260414170437-8edd7b40ece5/go.mod h1:pl+8j97av1Tv1az8CwIzXepl1YfwI5/fOsWO6Bh4sNI=
2020
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2121
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2222
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=

pkg/reconciler/managed/reconciler.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -939,7 +939,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
939939
log.Debug("Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", "annotation", meta.AnnotationKeyReconciliationPaused)
940940
record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation",
941941
"annotation", meta.AnnotationKeyReconciliationPaused))
942-
status.MarkConditions(xpv2.ReconcilePaused())
942+
status.MarkConditions(xpv2.ReconcilePaused(), xpv2.PausedUnknown())
943943
// if the pause annotation is removed or the management policies changed, we will have a chance to reconcile
944944
// again and resume and if status update fails, we will reconcile again to retry to update the status
945945
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
@@ -1436,7 +1436,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
14361436
// https://github.com/crossplane/crossplane/issues/289
14371437
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
14381438
log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter))
1439-
status.MarkConditions(xpv2.ReconcileSuccess())
1439+
status.MarkConditions(xpv2.ReconcileSuccess(), xpv2.ObserveMatched())
14401440
r.metricRecorder.recordFirstTimeReady(managed)
14411441

14421442
// record that we intentionally did not update the managed resource
@@ -1456,7 +1456,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
14561456
if !policy.ShouldUpdate() {
14571457
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
14581458
log.Debug("Skipping update due to managementPolicies. Reconciliation succeeded", "requeue-after", time.Now().Add(reconcileAfter))
1459-
status.MarkConditions(xpv2.ReconcileSuccess())
1459+
status.MarkConditions(xpv2.ReconcileSuccess(), xpv2.UpdateRestricted().WithMessage(observation.Diff))
14601460

14611461
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
14621462
}
@@ -1475,7 +1475,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
14751475
}
14761476

14771477
record.Event(managed, event.Warning(reasonCannotUpdate, err))
1478-
status.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errReconcileUpdate)))
1478+
status.MarkConditions(xpv2.ReconcileError(errors.Wrap(err, errReconcileUpdate)), xpv2.UpdateFailed().WithMessage(observation.Diff))
14791479

14801480
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
14811481
}
@@ -1506,7 +1506,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
15061506
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
15071507
log.Debug("Successfully requested update of external resource", "requeue-after", time.Now().Add(reconcileAfter))
15081508
record.Event(managed, event.Normal(reasonUpdated, "Successfully requested update of external resource"))
1509-
status.MarkConditions(xpv2.ReconcileSuccess())
1509+
status.MarkConditions(xpv2.ReconcileSuccess(), xpv2.UpdateRequested().WithMessage(observation.Diff))
15101510

15111511
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
15121512
}

pkg/reconciler/managed/reconciler_legacy_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func TestReconciler(t *testing.T) {
351351
MockGet: legacyManagedMockGetFn(nil, 42),
352352
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
353353
want := newLegacyManaged(42)
354-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
354+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.ObserveMatched().WithObservedGeneration(42))
355355

356356
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
357357
reason := "A successful no-op reconcile should be reported as a conditioned status."
@@ -1088,7 +1088,7 @@ func TestReconciler(t *testing.T) {
10881088
MockGet: legacyManagedMockGetFn(nil, 42),
10891089
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
10901090
want := newLegacyManaged(42)
1091-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
1091+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.ObserveMatched().WithObservedGeneration(42))
10921092

10931093
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
10941094
reason := "A successful no-op reconcile should be reported as a conditioned status."
@@ -1253,7 +1253,7 @@ func TestReconciler(t *testing.T) {
12531253
MockGet: legacyManagedMockGetFn(nil, 42),
12541254
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
12551255
want := newLegacyManaged(42)
1256-
want.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileUpdate)).WithObservedGeneration(42))
1256+
want.SetConditions(xpv2.ReconcileError(errors.Wrap(errBoom, errReconcileUpdate)).WithObservedGeneration(42), xpv2.UpdateFailed().WithObservedGeneration(42))
12571257

12581258
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
12591259
reason := "Errors while updating an external resource should be reported as a conditioned status."
@@ -1354,7 +1354,7 @@ func TestReconciler(t *testing.T) {
13541354
MockGet: legacyManagedMockGetFn(nil, 42),
13551355
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
13561356
want := newLegacyManaged(42)
1357-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
1357+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.UpdateRequested().WithObservedGeneration(42).WithMessage("this is a diff"))
13581358

13591359
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
13601360
reason := "A successful managed resource update should be reported as a conditioned status."
@@ -1373,7 +1373,7 @@ func TestReconciler(t *testing.T) {
13731373
WithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {
13741374
c := &ExternalClientFns{
13751375
ObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {
1376-
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil
1376+
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false, Diff: "this is a diff"}, nil
13771377
},
13781378
UpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {
13791379
return ExternalUpdate{}, nil
@@ -1398,7 +1398,7 @@ func TestReconciler(t *testing.T) {
13981398
MockGet: legacyManagedMockGetFn(nil, 42),
13991399
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
14001400
want := newLegacyManaged(42)
1401-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
1401+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.UpdateRequested().WithObservedGeneration(42).WithMessage("this is a diff"))
14021402

14031403
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
14041404
reason := "A successful managed resource update should be reported as a conditioned status."
@@ -1417,7 +1417,7 @@ func TestReconciler(t *testing.T) {
14171417
WithTypedExternalConnector(TypedExternalConnectorFn[*fake.LegacyManaged](func(_ context.Context, _ *fake.LegacyManaged) (TypedExternalClient[*fake.LegacyManaged], error) {
14181418
c := &TypedExternalClientFns[*fake.LegacyManaged]{
14191419
ObserveFn: func(_ context.Context, _ *fake.LegacyManaged) (ExternalObservation, error) {
1420-
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil
1420+
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false, Diff: "this is a diff"}, nil
14211421
},
14221422
UpdateFn: func(_ context.Context, _ *fake.LegacyManaged) (ExternalUpdate, error) {
14231423
return ExternalUpdate{}, nil
@@ -1450,7 +1450,7 @@ func TestReconciler(t *testing.T) {
14501450
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
14511451
want := newLegacyManaged(42)
14521452
want.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: "true"})
1453-
want.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))
1453+
want.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42), xpv2.PausedUnknown().WithObservedGeneration(42))
14541454

14551455
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
14561456
reason := `If managed resource has the pause annotation with value "true", it should acquire "Synced" status condition with the status "False" and the reason "ReconcilePaused".`
@@ -1480,7 +1480,7 @@ func TestReconciler(t *testing.T) {
14801480
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
14811481
want := newLegacyManaged(42)
14821482
want.SetManagementPolicies(xpv2.ManagementPolicies{})
1483-
want.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42))
1483+
want.SetConditions(xpv2.ReconcilePaused().WithObservedGeneration(42), xpv2.PausedUnknown().WithObservedGeneration(42))
14841484

14851485
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
14861486
reason := `If managed resource has the pause annotation with value "true", it should acquire "Synced" status condition with the status "False" and the reason "ReconcilePaused".`
@@ -1516,7 +1516,7 @@ func TestReconciler(t *testing.T) {
15161516
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
15171517
want := newLegacyManaged(42)
15181518
want.SetAnnotations(map[string]string{meta.AnnotationKeyReconciliationPaused: "false"})
1519-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
1519+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.ObserveMatched().WithObservedGeneration(42))
15201520

15211521
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
15221522
reason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition after a resume.`
@@ -1778,7 +1778,7 @@ func TestReconciler(t *testing.T) {
17781778
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
17791779
want := newLegacyManaged(42)
17801780
want.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve})
1781-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))
1781+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42), xpv2.UpdateRestricted().WithObservedGeneration(42))
17821782

17831783
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
17841784
reason := "With ObserveOnly, a successful managed resource observation should be reported as a conditioned status."
@@ -1915,7 +1915,7 @@ func TestReconciler(t *testing.T) {
19151915
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
19161916
want := newLegacyManaged(42)
19171917
want.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionLateInitialize, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})
1918-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
1918+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.UpdateRestricted().WithObservedGeneration(42))
19191919

19201920
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
19211921
reason := `Managed resource should acquire Synced=False/ReconcileSuccess status condition.`
@@ -1966,7 +1966,7 @@ func TestReconciler(t *testing.T) {
19661966
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
19671967
want := newLegacyManaged(42)
19681968
want.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})
1969-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42))
1969+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42).WithObservedGeneration(42), xpv2.UpdateRequested().WithObservedGeneration(42).WithMessage("this is a diff"))
19701970

19711971
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
19721972
reason := "A successful managed resource update should be reported as a conditioned status."
@@ -1986,7 +1986,7 @@ func TestReconciler(t *testing.T) {
19861986
WithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {
19871987
c := &ExternalClientFns{
19881988
ObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {
1989-
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil
1989+
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false, Diff: "this is a diff"}, nil
19901990
},
19911991
UpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {
19921992
return ExternalUpdate{}, nil
@@ -2017,7 +2017,7 @@ func TestReconciler(t *testing.T) {
20172017
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
20182018
want := newLegacyManaged(42)
20192019
want.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionAll})
2020-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
2020+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.UpdateRequested().WithObservedGeneration(42).WithMessage("this is a diff"))
20212021

20222022
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
20232023
reason := "A successful managed resource update should be reported as a conditioned status."
@@ -2037,7 +2037,7 @@ func TestReconciler(t *testing.T) {
20372037
WithExternalConnector(ExternalConnectorFn(func(_ context.Context, _ resource.Managed) (ExternalClient, error) {
20382038
c := &ExternalClientFns{
20392039
ObserveFn: func(_ context.Context, _ resource.Managed) (ExternalObservation, error) {
2040-
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false}, nil
2040+
return ExternalObservation{ResourceExists: true, ResourceUpToDate: false, Diff: "this is a diff"}, nil
20412041
},
20422042
UpdateFn: func(_ context.Context, _ resource.Managed) (ExternalUpdate, error) {
20432043
return ExternalUpdate{}, nil
@@ -2069,7 +2069,7 @@ func TestReconciler(t *testing.T) {
20692069
MockStatusUpdate: test.MockSubResourceUpdateFn(func(_ context.Context, obj client.Object, _ ...client.SubResourceUpdateOption) error {
20702070
want := newLegacyManaged(42)
20712071
want.SetManagementPolicies(xpv2.ManagementPolicies{xpv2.ManagementActionObserve, xpv2.ManagementActionUpdate, xpv2.ManagementActionCreate, xpv2.ManagementActionDelete})
2072-
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42))
2072+
want.SetConditions(xpv2.ReconcileSuccess().WithObservedGeneration(42), xpv2.ObserveMatched().WithObservedGeneration(42))
20732073

20742074
if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" {
20752075
reason := "Errors updating a managed resource should be reported as a conditioned status."

0 commit comments

Comments
 (0)