Skip to content

Commit 38a890c

Browse files
committed
feat: add kstatus conforment status updates
1 parent 90187e4 commit 38a890c

File tree

4 files changed

+163
-91
lines changed

4 files changed

+163
-91
lines changed

api/v1alpha1/componentversion_types.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package v1alpha1
66

77
import (
8+
"fmt"
89
"time"
910

1011
"github.com/fluxcd/pkg/apis/meta"
@@ -145,11 +146,23 @@ type ComponentVersionStatus struct {
145146
// +optional
146147
ReconciledVersion string `json:"reconciledVersion,omitempty"`
147148

148-
// Verified is a boolean indicating whether all of the specified signatures have been verified and are valid.
149+
// Verified is a boolean indicating whether all the specified signatures have been verified and are valid.
149150
// +optional
150151
Verified bool `json:"verified,omitempty"`
151152
}
152153

154+
func (in *ComponentVersion) GetVID() map[string]string {
155+
vid := fmt.Sprintf("%s:%s", in.Status.ComponentDescriptor.Name, in.Status.ReconciledVersion)
156+
metadata := make(map[string]string)
157+
metadata[GroupVersion.Group+"/component_version"] = vid
158+
159+
return metadata
160+
}
161+
162+
func (in *ComponentVersion) SetObservedGeneration(v int64) {
163+
in.Status.ObservedGeneration = v
164+
}
165+
153166
// GetComponentName returns the name of the component
154167
func (in *ComponentVersion) GetComponentName() string {
155168
return in.Spec.Component
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package v1alpha1
2+
3+
import (
4+
"github.com/fluxcd/pkg/runtime/conditions"
5+
)
6+
7+
// IdentifiableClientObject defines an object which can create an identity for itself.
8+
type IdentifiableClientObject interface {
9+
StatusMutator
10+
conditions.Setter
11+
12+
// GetVID constructs an identifier for an object.
13+
GetVID() map[string]string
14+
}
15+
16+
// StatusMutator allows mutating specific status fields of an object.
17+
type StatusMutator interface {
18+
// SetObservedGeneration mutates the observed generation field of an object.
19+
SetObservedGeneration(v int64)
20+
}

controllers/componentversion_controller.go

Lines changed: 85 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020
"k8s.io/apimachinery/pkg/runtime"
2121
kuberecorder "k8s.io/client-go/tools/record"
22-
"k8s.io/klog/v2"
2322
ctrl "sigs.k8s.io/controller-runtime"
2423
"sigs.k8s.io/controller-runtime/pkg/builder"
2524
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -84,51 +83,29 @@ func (r *ComponentVersionReconciler) Reconcile(ctx context.Context, req ctrl.Req
8483
return
8584
}
8685

87-
patchHelper, err := patch.NewHelper(obj, r.Client)
88-
if err != nil {
89-
return ctrl.Result{}, fmt.Errorf("failed to create patchhelper: %w", err)
90-
}
86+
patchHelper := patch.NewSerialPatcher(obj, r.Client)
9187

9288
// Always attempt to patch the object and status after each reconciliation.
9389
defer func() {
94-
// Patching has not been set up, or the controller errored earlier.
95-
if patchHelper == nil {
96-
return
97-
}
98-
99-
// If still reconciling then reconciliation did not succeed, set to ProgressingWithRetry to
100-
// indicate that reconciliation will be retried.
101-
if conditions.IsReconciling(obj) {
102-
reconciling := conditions.Get(obj, meta.ReconcilingCondition)
103-
reconciling.Reason = meta.ProgressingWithRetryReason
104-
conditions.Set(obj, reconciling)
105-
msg := fmt.Sprintf("Reconciliation did not succeed, retrying in %s", obj.GetRequeueAfter())
106-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
107-
}
108-
109-
// Set status observed generation option if the component is ready.
110-
if conditions.IsReady(obj) {
111-
obj.Status.ObservedGeneration = obj.Generation
112-
msg := fmt.Sprintf("Reconciliation finished, next run in %s", obj.GetRequeueAfter())
113-
vid := fmt.Sprintf("%s:%s", obj.Status.ComponentDescriptor.Name, obj.Status.ReconciledVersion)
114-
metadata := make(map[string]string)
115-
metadata[v1alpha1.GroupVersion.Group+"/component_version"] = vid
116-
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, msg, metadata)
117-
}
118-
119-
// Update the object.
120-
if err := patchHelper.Patch(ctx, obj); err != nil {
121-
retErr = errors.Join(retErr, err)
90+
if derr := DeferredStatusUpdate(ctx, patchHelper, obj, r.EventRecorder, obj.GetRequeueAfter()); derr != nil {
91+
retErr = errors.Join(retErr, derr)
12292
}
12393
}()
12494

125-
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconcilation in progress for component: %s", obj.Spec.Component)
95+
// Starts the progression by setting ReconcilingCondition.
96+
// This will be checked in defer.
97+
// Should only be deleted on a success.
98+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress for component: %s", obj.Spec.Component)
12699

127100
octx, err := r.OCMClient.CreateAuthenticatedOCMContext(ctx, obj)
128101
if err != nil {
129-
msg := fmt.Sprintf("authentication failed for repository: %s with error: %s", obj.Spec.Repository.URL, err)
130-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.AuthenticatedContextCreationFailedReason, msg)
131-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
102+
// we don't fail here, because all manifests might have been applied at once or the secret
103+
// for authentication is being reconciled.
104+
_ = r.markAsFailed(
105+
obj,
106+
v1alpha1.AuthenticatedContextCreationFailedReason,
107+
fmt.Errorf("authentication failed for repository: %s with error: %s", obj.Spec.Repository.URL, err),
108+
)
132109

133110
return ctrl.Result{
134111
RequeueAfter: obj.GetRequeueAfter(),
@@ -138,9 +115,12 @@ func (r *ComponentVersionReconciler) Reconcile(ctx context.Context, req ctrl.Req
138115
// reconcile the version before calling reconcile func
139116
update, version, err := r.checkVersion(ctx, octx, obj)
140117
if err != nil {
141-
msg := fmt.Sprintf("version check failed for %s %s with error: %s", obj.Spec.Component, obj.Spec.Version.Semver, err)
142-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CheckVersionFailedReason, msg)
143-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
118+
// The component might not be there yet. We don't fail but keep polling instead.
119+
_ = r.markAsFailed(
120+
obj,
121+
v1alpha1.CheckVersionFailedReason,
122+
fmt.Errorf("version check failed for %s %s with error: %s", obj.Spec.Component, obj.Spec.Version.Semver, err),
123+
)
144124

145125
return ctrl.Result{
146126
RequeueAfter: obj.GetRequeueAfter(),
@@ -152,58 +132,57 @@ func (r *ComponentVersionReconciler) Reconcile(ctx context.Context, req ctrl.Req
152132
conditions.MarkTrue(obj,
153133
meta.ReadyCondition,
154134
meta.SucceededReason,
155-
fmt.Sprintf("Applied version: %s", version))
135+
"Applied version: %s",
136+
version)
156137

157138
return ctrl.Result{
158139
RequeueAfter: obj.GetRequeueAfter(),
159140
}, nil
160141
}
161142

143+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "updating component to new version: %s: %s", obj.Spec.Component, version)
144+
162145
ok, err := r.OCMClient.VerifyComponent(ctx, octx, obj, version)
163146
if err != nil {
164-
msg := fmt.Sprintf("failed to verify %s with constraint %s with error: %s", obj.Spec.Component, obj.Spec.Version.Semver, err)
165-
conditions.Delete(obj, meta.ReconcilingCondition)
166-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.VerificationFailedReason, msg)
167-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, fmt.Sprintf("%s, retrying in %s", err.Error(), obj.GetRequeueAfter()), nil)
147+
_ = r.markAsFailed(
148+
obj,
149+
v1alpha1.VerificationFailedReason,
150+
fmt.Errorf("failed to verify %s with constraint %s with error: %s", obj.Spec.Component, obj.Spec.Version.Semver, err),
151+
)
168152

169153
return ctrl.Result{
170154
RequeueAfter: obj.GetRequeueAfter(),
171155
}, nil
172156
}
173157

174158
if !ok {
175-
msg := "attempted to verify component, but the digest didn't match"
176-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.VerificationFailedReason, msg)
177-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, fmt.Sprintf("%s, retrying in %s", msg, obj.GetRequeueAfter()), nil)
159+
_ = r.markAsFailed(
160+
obj,
161+
v1alpha1.VerificationFailedReason,
162+
errors.New("attempted to verify component, but the digest didn't match"),
163+
)
178164

179165
return ctrl.Result{
180166
RequeueAfter: obj.GetRequeueAfter(),
181167
}, nil
182168
}
183169

184-
// update the result for the defer call to have the latest information
185-
rresult, err := r.reconcile(ctx, octx, obj, version)
186-
if err != nil {
187-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, fmt.Sprintf("Reconciliation failed: %s, retrying in %s", err.Error(), obj.GetRequeueAfter()), nil)
188-
}
189-
190-
return rresult, err
170+
return r.reconcile(ctx, octx, obj, version)
191171
}
192172

193173
func (r *ComponentVersionReconciler) reconcile(ctx context.Context, octx ocm.Context, obj *v1alpha1.ComponentVersion, version string) (ctrl.Result, error) {
194174
if obj.Generation != obj.Status.ObservedGeneration {
195-
// don't have to patch here since we patch the object in the outer reconcile call.
196175
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason,
197176
"processing object: new generation %d -> %d", obj.Status.ObservedGeneration, obj.Generation)
198177
}
199178

200179
cv, err := r.OCMClient.GetComponentVersion(ctx, octx, obj, obj.Spec.Component, version)
201180
if err != nil {
202-
err = fmt.Errorf("failed to get component version: %w", err)
203-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ComponentVersionInvalidReason, err.Error())
204-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
205-
206-
return ctrl.Result{}, err
181+
return ctrl.Result{}, r.markAsFailed(
182+
obj,
183+
v1alpha1.ComponentVersionInvalidReason,
184+
fmt.Errorf("failed to get component version: %w", err),
185+
)
207186
}
208187

209188
defer cv.Close()
@@ -212,18 +191,25 @@ func (r *ComponentVersionReconciler) reconcile(ctx context.Context, octx ocm.Con
212191
dv := &compdesc.DescriptorVersion{}
213192
cd, err := dv.ConvertFrom(cv.GetDescriptor())
214193
if err != nil {
215-
err = fmt.Errorf("failed to convert component descriptor: %w", err)
216-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ConvertComponentDescriptorFailedReason, err.Error())
217-
return ctrl.Result{}, err
194+
return ctrl.Result{}, r.markAsFailed(
195+
obj,
196+
v1alpha1.ConvertComponentDescriptorFailedReason,
197+
fmt.Errorf("failed to convert component descriptor: %w", err),
198+
)
218199
}
219200

201+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "component fetched, creating descriptors")
202+
220203
// setup the component descriptor kubernetes resource
221204
componentName, err := component.ConstructUniqueName(cd.GetName(), cd.GetVersion(), nil)
222205
if err != nil {
223-
err = fmt.Errorf("failed to generate name: %w", err)
224-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.NameGenerationFailedReason, err.Error())
225-
return ctrl.Result{}, err
206+
return ctrl.Result{}, r.markAsFailed(
207+
obj,
208+
v1alpha1.NameGenerationFailedReason,
209+
fmt.Errorf("failed to generate name: %w", err),
210+
)
226211
}
212+
227213
descriptor := &v1alpha1.ComponentDescriptor{
228214
ObjectMeta: metav1.ObjectMeta{
229215
Namespace: obj.GetNamespace(),
@@ -251,10 +237,11 @@ func (r *ComponentVersionReconciler) reconcile(ctx context.Context, octx ocm.Con
251237
})
252238

253239
if err != nil {
254-
err = fmt.Errorf("failed to create or update component descriptor: %w", err)
255-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CreateOrUpdateComponentDescriptorFailedReason, err.Error())
256-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
257-
return ctrl.Result{}, err
240+
return ctrl.Result{}, r.markAsFailed(
241+
obj,
242+
v1alpha1.CreateOrUpdateComponentDescriptorFailedReason,
243+
fmt.Errorf("failed to create or update component descriptor: %w", err),
244+
)
258245
}
259246

260247
componentDescriptor := v1alpha1.Reference{
@@ -269,10 +256,11 @@ func (r *ComponentVersionReconciler) reconcile(ctx context.Context, octx ocm.Con
269256
if obj.Spec.References.Expand {
270257
componentDescriptor.References, err = r.parseReferences(ctx, octx, obj, cv.GetDescriptor().References)
271258
if err != nil {
272-
err = fmt.Errorf("failed to parse references: %w", err)
273-
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ParseReferencesFailedReason, err.Error())
274-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
275-
return ctrl.Result{}, err
259+
return ctrl.Result{}, r.markAsFailed(
260+
obj,
261+
v1alpha1.ParseReferencesFailedReason,
262+
fmt.Errorf("failed to parse references: %w", err),
263+
)
276264
}
277265
}
278266

@@ -283,21 +271,22 @@ func (r *ComponentVersionReconciler) reconcile(ctx context.Context, octx ocm.Con
283271
conditions.MarkTrue(obj,
284272
meta.ReadyCondition,
285273
meta.SucceededReason,
286-
fmt.Sprintf("Applied version: %s", version))
274+
"Applied version: %s",
275+
version)
287276

288277
conditions.Delete(obj, meta.ReconcilingCondition)
289278

290279
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
291280
}
292281

293282
func (r *ComponentVersionReconciler) checkVersion(ctx context.Context, octx ocm.Context, obj *v1alpha1.ComponentVersion) (bool, string, error) {
294-
log := log.FromContext(ctx).WithName("ocm-component-version-reconcile")
283+
logger := log.FromContext(ctx).WithName("ocm-component-version-reconcile")
295284

296285
latest, err := r.OCMClient.GetLatestValidComponentVersion(ctx, octx, obj)
297286
if err != nil {
298287
return false, "", fmt.Errorf("failed to get latest component version: %w", err)
299288
}
300-
log.V(4).Info("got latest version of component", "version", latest)
289+
logger.V(4).Info("got latest version of component", "version", latest)
301290

302291
latestSemver, err := semver.NewVersion(latest)
303292
if err != nil {
@@ -312,11 +301,11 @@ func (r *ComponentVersionReconciler) checkVersion(ctx context.Context, octx ocm.
312301
if err != nil {
313302
return false, "", fmt.Errorf("failed to parse reconciled version: %w", err)
314303
}
315-
log.V(4).Info("current reconciled version is", "reconciled", current.String())
304+
logger.V(4).Info("current reconciled version is", "reconciled", current.String())
316305

317306
if latestSemver.Equal(current) || current.GreaterThan(latestSemver) {
318-
log.V(4).Info("Reconciled version equal to or greater than newest available version", "version", latestSemver)
319-
return false, "", nil
307+
logger.V(4).Info("Reconciled version equal to or greater than newest available version", "version", latestSemver)
308+
return false, latest, nil
320309
}
321310

322311
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, fmt.Sprintf("Version check succeeded, found latest version: %s", latest), nil)
@@ -380,7 +369,6 @@ func (r *ComponentVersionReconciler) createComponentDescriptor(ctx context.Conte
380369
return nil, fmt.Errorf("failed to convert component descriptor: %w", err)
381370
}
382371

383-
log := log.FromContext(ctx)
384372
// setup the component descriptor kubernetes resource
385373
componentName, err := component.ConstructUniqueName(ref.ComponentName, ref.Version, ref.GetMeta().ExtraIdentity)
386374
if err != nil {
@@ -397,19 +385,26 @@ func (r *ComponentVersionReconciler) createComponentDescriptor(ctx context.Conte
397385
},
398386
}
399387

400-
if err := controllerutil.SetOwnerReference(parent, descriptor, r.Scheme); err != nil {
401-
return nil, fmt.Errorf("failed to set owner reference: %w", err)
402-
}
403-
404388
// create or update the component descriptor kubernetes resource
405389
// we don't need to update it
406-
op, err := controllerutil.CreateOrUpdate(ctx, r.Client, descriptor, func() error {
390+
if _, err = controllerutil.CreateOrUpdate(ctx, r.Client, descriptor, func() error {
391+
if descriptor.ObjectMeta.CreationTimestamp.IsZero() {
392+
if err := controllerutil.SetOwnerReference(parent, descriptor, r.Scheme); err != nil {
393+
return fmt.Errorf("failed to set owner reference: %w", err)
394+
}
395+
}
396+
407397
return nil
408-
})
409-
if err != nil {
398+
}); err != nil {
410399
return nil, fmt.Errorf("failed to create/update component descriptor: %w", err)
411400
}
412-
log.V(4).Info(fmt.Sprintf("%s(ed) descriptor", op), "descriptor", klog.KObj(descriptor))
413401

414402
return descriptor, nil
415403
}
404+
405+
func (r *ComponentVersionReconciler) markAsFailed(obj *v1alpha1.ComponentVersion, reason string, err error) error {
406+
conditions.MarkFalse(obj, meta.ReadyCondition, reason, err.Error())
407+
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
408+
409+
return err
410+
}

0 commit comments

Comments
 (0)