Skip to content

Commit 6e06819

Browse files
committed
Add progressive status in helmrepo-oci reconciler
Signed-off-by: Sunny <darkowlzz@protonmail.com>
1 parent 24cb756 commit 6e06819

File tree

2 files changed

+61
-19
lines changed

2 files changed

+61
-19
lines changed

controllers/helmrepository_controller_oci.go

+42-18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os"
2525
"time"
2626

27+
"github.com/google/go-containerregistry/pkg/authn"
2728
helmgetter "helm.sh/helm/v3/pkg/getter"
2829
helmreg "helm.sh/helm/v3/pkg/registry"
2930
corev1 "k8s.io/api/core/v1"
@@ -45,7 +46,7 @@ import (
4546
helper "github.com/fluxcd/pkg/runtime/controller"
4647
"github.com/fluxcd/pkg/runtime/patch"
4748
"github.com/fluxcd/pkg/runtime/predicates"
48-
"github.com/google/go-containerregistry/pkg/authn"
49+
rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
4950

5051
"github.com/fluxcd/source-controller/api/v1beta2"
5152
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -79,6 +80,8 @@ type HelmRepositoryOCIReconciler struct {
7980
Getters helmgetter.Providers
8081
ControllerName string
8182
RegistryClientGenerator RegistryClientGeneratorFunc
83+
84+
patchOptions []patch.Option
8285
}
8386

8487
// RegistryClientGeneratorFunc is a function that returns a registry client
@@ -92,6 +95,8 @@ func (r *HelmRepositoryOCIReconciler) SetupWithManager(mgr ctrl.Manager) error {
9295
}
9396

9497
func (r *HelmRepositoryOCIReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, opts HelmRepositoryReconcilerOptions) error {
98+
r.patchOptions = getPatchOptions(helmRepositoryOCIOwnedConditions, r.ControllerName)
99+
95100
return ctrl.NewControllerManagedBy(mgr).
96101
For(&sourcev1.HelmRepository{}).
97102
WithEventFilter(
@@ -122,34 +127,26 @@ func (r *HelmRepositoryOCIReconciler) Reconcile(ctx context.Context, req ctrl.Re
122127
r.RecordSuspend(ctx, obj, obj.Spec.Suspend)
123128

124129
// Initialize the patch helper with the current version of the object.
125-
patchHelper, err := patch.NewHelper(obj, r.Client)
126-
if err != nil {
127-
return ctrl.Result{}, err
128-
}
130+
serialPatcher := patch.NewSerialPatcher(obj, r.Client)
129131

130132
// Always attempt to patch the object after each reconciliation.
131133
defer func() {
132-
// Patch the object, prioritizing the conditions owned by the controller in
133-
// case of any conflicts.
134-
patchOpts := []patch.Option{
135-
patch.WithOwnedConditions{
136-
Conditions: helmRepositoryOCIOwnedConditions,
137-
},
138-
}
139-
patchOpts = append(patchOpts, patch.WithFieldOwner(r.ControllerName))
140134
// If a reconcile annotation value is found, set it in the object status
141135
// as status.lastHandledReconcileAt.
142136
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
143137
object.SetStatusLastHandledReconcileAt(obj, v)
144138
}
145139

140+
patchOpts := []patch.Option{}
141+
patchOpts = append(patchOpts, r.patchOptions...)
142+
146143
// Set status observed generation option if the object is stalled, or
147144
// if the object is ready.
148145
if conditions.IsStalled(obj) || conditions.IsReady(obj) {
149146
patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
150147
}
151148

152-
if err = patchHelper.Patch(ctx, obj, patchOpts...); err != nil {
149+
if err := serialPatcher.Patch(ctx, obj, patchOpts...); err != nil {
153150
// Ignore patch error "not found" when the object is being deleted.
154151
if !obj.GetDeletionTimestamp().IsZero() {
155152
err = kerrors.FilterOut(err, func(e error) bool { return apierrors.IsNotFound(e) })
@@ -188,7 +185,7 @@ func (r *HelmRepositoryOCIReconciler) Reconcile(ctx context.Context, req ctrl.Re
188185
return ctrl.Result{}, nil
189186
}
190187

191-
result, retErr = r.reconcile(ctx, obj)
188+
result, retErr = r.reconcile(ctx, serialPatcher, obj)
192189
return
193190
}
194191

@@ -198,7 +195,7 @@ func (r *HelmRepositoryOCIReconciler) Reconcile(ctx context.Context, req ctrl.Re
198195
// status conditions and the returned results are evaluated in the deferred
199196
// block at the very end to summarize the conditions to be in a consistent
200197
// state.
201-
func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, obj *v1beta2.HelmRepository) (result ctrl.Result, retErr error) {
198+
func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.SerialPatcher, obj *v1beta2.HelmRepository) (result ctrl.Result, retErr error) {
202199
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
203200
defer cancel()
204201

@@ -224,6 +221,15 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, obj *v1beta
224221
}
225222
}
226223

224+
// Presence of reconciling means that the reconciliation didn't succeed.
225+
// Set the Reconciling reason to ProgressingWithRetry to indicate a
226+
// failure retry.
227+
if conditions.IsReconciling(obj) {
228+
reconciling := conditions.Get(obj, meta.ReconcilingCondition)
229+
reconciling.Reason = meta.ProgressingWithRetryReason
230+
conditions.Set(obj, reconciling)
231+
}
232+
227233
// If it's still a successful reconciliation and it's not reconciling or
228234
// stalled, mark Ready=True.
229235
if !conditions.IsReconciling(obj) && !conditions.IsStalled(obj) &&
@@ -244,8 +250,26 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, obj *v1beta
244250
}()
245251

246252
// Set reconciling condition.
247-
if obj.Generation != obj.Status.ObservedGeneration {
248-
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation)
253+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress")
254+
255+
var reconcileAtVal string
256+
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
257+
reconcileAtVal = v
258+
}
259+
260+
// Persist reconciling if generation differs or reconciliation is requested.
261+
switch {
262+
case obj.Generation != obj.Status.ObservedGeneration:
263+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "processing object: new generation (%d)", obj.Generation)
264+
if err := sp.Patch(ctx, obj, r.patchOptions...); err != nil {
265+
result, retErr = ctrl.Result{}, err
266+
return
267+
}
268+
case reconcileAtVal != obj.Status.GetLastHandledReconcileRequest():
269+
if err := sp.Patch(ctx, obj, r.patchOptions...); err != nil {
270+
result, retErr = ctrl.Result{}, err
271+
return
272+
}
249273
}
250274

251275
// Ensure that it's an OCI URL before continuing.

controllers/helmrepository_controller_oci_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) {
208208
password: "wrong-pass",
209209
},
210210
assertConditions: []metav1.Condition{
211+
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "processing object: new generation"),
211212
*conditions.FalseCondition(meta.ReadyCondition, sourcev1.AuthenticationFailedReason, "failed to login to registry"),
212213
},
213214
},
@@ -217,6 +218,7 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) {
217218
provider: "aws",
218219
providerImg: "oci://123456789000.dkr.ecr.us-east-2.amazonaws.com/test",
219220
assertConditions: []metav1.Condition{
221+
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "processing object: new generation"),
220222
*conditions.FalseCondition(meta.ReadyCondition, sourcev1.AuthenticationFailedReason, "failed to get credential from"),
221223
},
222224
},
@@ -249,6 +251,7 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) {
249251
obj := &sourcev1.HelmRepository{
250252
ObjectMeta: metav1.ObjectMeta{
251253
GenerateName: "auth-strategy-",
254+
Generation: 1,
252255
},
253256
Spec: sourcev1.HelmRepositorySpec{
254257
Interval: metav1.Duration{Duration: interval},
@@ -293,12 +296,27 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) {
293296
EventRecorder: record.NewFakeRecorder(32),
294297
Getters: testGetters,
295298
RegistryClientGenerator: registry.ClientGenerator,
299+
patchOptions: getPatchOptions(helmRepositoryOCIOwnedConditions, "sc"),
296300
}
297301

298-
got, err := r.reconcile(ctx, obj)
302+
g.Expect(r.Client.Create(ctx, obj)).ToNot(HaveOccurred())
303+
defer func() {
304+
g.Expect(r.Client.Delete(ctx, obj)).ToNot(HaveOccurred())
305+
}()
306+
307+
sp := patch.NewSerialPatcher(obj, r.Client)
308+
309+
got, err := r.reconcile(ctx, sp, obj)
299310
g.Expect(err != nil).To(Equal(tt.wantErr))
300311
g.Expect(got).To(Equal(tt.want))
301312
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
313+
314+
// In-progress status condition validity.
315+
checker := conditionscheck.NewInProgressChecker(r.Client)
316+
// NOTE: Check the object directly as reconcile() doesn't apply the
317+
// final patch, the object has unapplied changes.
318+
checker.DisableFetch = true
319+
checker.CheckErr(ctx, obj)
302320
})
303321
}
304322
}

0 commit comments

Comments
 (0)