Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reconcile: Set observed gen only when conditions exist #729

Merged
merged 1 commit into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions internal/reconcile/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, rb
switch t := recErr.(type) {
case *serror.Stalling:
if res == ResultEmpty {
conditions.MarkStalled(obj, t.Reason, t.Error())
// The current generation has been reconciled successfully and it
// has resulted in a stalled state. Return no error to stop further
// requeuing.
pOpts = append(pOpts, patch.WithStatusObservedGeneration{})
conditions.MarkStalled(obj, t.Reason, t.Error())
pOpts = addPatchOptionWithStatusObservedGeneration(obj, pOpts)
return pOpts, result, nil
}
// NOTE: Non-empty result with stalling error indicates that the
Expand All @@ -150,7 +150,7 @@ func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, rb
if t.Ignore {
// The current generation has been reconciled successfully with
// no-op result. Update status observed generation.
pOpts = append(pOpts, patch.WithStatusObservedGeneration{})
pOpts = addPatchOptionWithStatusObservedGeneration(obj, pOpts)
conditions.Delete(obj, meta.ReconcilingCondition)
return pOpts, result, nil
}
Expand All @@ -159,7 +159,7 @@ func ComputeReconcileResult(obj conditions.Setter, res Result, recErr error, rb
// state. If a requeue is requested, the current generation has not been
// reconciled successfully.
if res != ResultRequeue {
pOpts = append(pOpts, patch.WithStatusObservedGeneration{})
pOpts = addPatchOptionWithStatusObservedGeneration(obj, pOpts)
}
conditions.Delete(obj, meta.StalledCondition)
default:
Expand Down Expand Up @@ -207,3 +207,17 @@ func FailureRecovery(oldObj, newObj conditions.Getter, failConditions []string)
}
return failuresBefore > 0
}

// addPatchOptionWithStatusObservedGeneration adds patch option
// WithStatusObservedGeneration to the provided patch option slice only if there
// is any condition present on the object, and returns it. This is necessary to
// prevent setting status observed generation without any effectual observation.
// An object must have some condition in the status if it has been observed.
// TODO: Move this to fluxcd/pkg/runtime/patch package after it has proven its
// need.
func addPatchOptionWithStatusObservedGeneration(obj conditions.Setter, opts []patch.Option) []patch.Option {
if len(obj.GetConditions()) > 0 {
opts = append(opts, patch.WithStatusObservedGeneration{})
}
return opts
}
69 changes: 67 additions & 2 deletions internal/reconcile/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ func TestComputeReconcileResult(t *testing.T) {
afterFunc func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions)
}{
{
name: "successful result",
result: ResultSuccess,
name: "successful result",
result: ResultSuccess,
beforeFunc: func(obj conditions.Setter) {
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "foo")
},
recErr: nil,
wantResult: ctrl.Result{RequeueAfter: testSuccessInterval},
wantErr: false,
assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "foo"),
},
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
},
Expand All @@ -85,10 +91,14 @@ func TestComputeReconcileResult(t *testing.T) {
result: ResultSuccess,
beforeFunc: func(obj conditions.Setter) {
conditions.MarkReconciling(obj, "NewRevision", "new revision")
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "foo")
},
recErr: nil,
wantResult: ctrl.Result{RequeueAfter: testSuccessInterval},
wantErr: false,
assertConditions: []metav1.Condition{
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "foo"),
},
afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) {
t.Expect(patchOpts.IncludeStatusObservedGeneration).To(BeTrue())
t.Expect(conditions.IsUnknown(obj, meta.ReconcilingCondition)).To(BeTrue())
Expand Down Expand Up @@ -367,3 +377,58 @@ func TestFailureRecovery(t *testing.T) {
})
}
}

func TestAddOptionWithStatusObservedGeneration(t *testing.T) {
tests := []struct {
name string
beforeFunc func(obj conditions.Setter)
patchOpts []patch.Option
want bool
}{
{
name: "no conditions",
want: false,
},
{
name: "some condition",
beforeFunc: func(obj conditions.Setter) {
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "foo")
},
want: true,
},
{
name: "existing option with conditions",
beforeFunc: func(obj conditions.Setter) {
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "foo")
},
patchOpts: []patch.Option{patch.WithForceOverwriteConditions{}, patch.WithStatusObservedGeneration{}},
want: true,
},
{
name: "existing option, no conditions, can't remove",
patchOpts: []patch.Option{patch.WithForceOverwriteConditions{}, patch.WithStatusObservedGeneration{}},
want: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

obj := &sourcev1.GitRepository{}

if tt.beforeFunc != nil {
tt.beforeFunc(obj)
}

tt.patchOpts = addPatchOptionWithStatusObservedGeneration(obj, tt.patchOpts)

// Apply the options and evaluate the result.
options := &patch.HelperOptions{}
for _, opt := range tt.patchOpts {
opt.ApplyToHelper(options)
}
g.Expect(options.IncludeStatusObservedGeneration).To(Equal(tt.want))
})
}
}