@@ -38,14 +38,12 @@ import (
38
38
"helm.sh/helm/v3/pkg/storage/driver"
39
39
corev1 "k8s.io/api/core/v1"
40
40
"k8s.io/apimachinery/pkg/api/equality"
41
- apierrors "k8s.io/apimachinery/pkg/api/errors"
42
41
apimeta "k8s.io/apimachinery/pkg/api/meta"
43
42
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
44
43
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
45
44
"k8s.io/apimachinery/pkg/runtime"
46
45
"k8s.io/apimachinery/pkg/runtime/schema"
47
46
"k8s.io/apimachinery/pkg/types"
48
- utilerrors "k8s.io/apimachinery/pkg/util/errors"
49
47
"k8s.io/apimachinery/pkg/util/sets"
50
48
apimachyaml "k8s.io/apimachinery/pkg/util/yaml"
51
49
ctrl "sigs.k8s.io/controller-runtime"
@@ -80,6 +78,17 @@ import (
80
78
"github.com/operator-framework/operator-controller/internal/labels"
81
79
)
82
80
81
+ // ResolutionError type
82
+ // If a more specific error type needs to be distinguished,
83
+ // add another type here
84
+ type ResolutionError struct {
85
+ message string
86
+ }
87
+
88
+ func (e ResolutionError ) Error () string {
89
+ return e .message
90
+ }
91
+
83
92
// ClusterExtensionReconciler reconciles a ClusterExtension object
84
93
type ClusterExtensionReconciler struct {
85
94
client.Client
@@ -120,40 +129,35 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req
120
129
121
130
var existingExt = & ocv1alpha1.ClusterExtension {}
122
131
if err := r .Client .Get (ctx , req .NamespacedName , existingExt ); err != nil {
123
- return ctrl.Result {}, utilerrors . NewAggregate ([] error { client .IgnoreNotFound (err ), nil } )
132
+ return ctrl.Result {}, client .IgnoreNotFound (err )
124
133
}
125
134
126
- reconciledExt := existingExt .DeepCopy ()
127
- res , reconcileErr := r .reconcile (ctx , reconciledExt )
135
+ var updateError error
128
136
129
- var updateErrors []error
137
+ reconciledExt := existingExt .DeepCopy ()
138
+ res , err := r .reconcile (ctx , reconciledExt )
139
+ updateError = errors .Join (updateError , err )
130
140
131
141
// Do checks before any Update()s, as Update() may modify the resource structure!
132
142
updateStatus := ! equality .Semantic .DeepEqual (existingExt .Status , reconciledExt .Status )
133
143
updateFinalizers := ! equality .Semantic .DeepEqual (existingExt .Finalizers , reconciledExt .Finalizers )
134
144
unexpectedFieldsChanged := checkForUnexpectedFieldChange (* existingExt , * reconciledExt )
135
145
136
146
if updateStatus {
137
- if updateErr := r .Client .Status ().Update (ctx , reconciledExt ); updateErr != nil {
138
- updateErrors = append (updateErrors , updateErr )
139
- }
147
+ err = r .Client .Status ().Update (ctx , reconciledExt )
148
+ updateError = errors .Join (updateError , err )
140
149
}
141
150
142
151
if unexpectedFieldsChanged {
143
152
panic ("spec or metadata changed by reconciler" )
144
153
}
145
154
146
155
if updateFinalizers {
147
- if updateErr := r .Client .Update (ctx , reconciledExt ); updateErr != nil {
148
- updateErrors = append (updateErrors , updateErr )
149
- }
156
+ err = r .Client .Update (ctx , reconciledExt )
157
+ updateError = errors .Join (updateError , err )
150
158
}
151
159
152
- if reconcileErr != nil {
153
- updateErrors = append (updateErrors , reconcileErr )
154
- }
155
-
156
- return res , utilerrors .NewAggregate (updateErrors )
160
+ return res , updateError
157
161
}
158
162
159
163
// ensureAllConditionsWithReason checks that all defined condition types exist in the given ClusterExtension,
@@ -183,35 +187,6 @@ func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool {
183
187
return ! equality .Semantic .DeepEqual (a , b )
184
188
}
185
189
186
- func (r * ClusterExtensionReconciler ) handleResolutionErrors (ext * ocv1alpha1.ClusterExtension , err error ) (ctrl.Result , error ) {
187
- var aggErrs utilerrors.Aggregate
188
- if errors .As (err , & aggErrs ) {
189
- for _ , err := range aggErrs .Errors () {
190
- errorMessage := err .Error ()
191
- if strings .Contains (errorMessage , "no package" ) {
192
- // Handle no package found errors, potentially setting status conditions
193
- setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
194
- ensureAllConditionsWithReason (ext , "ResolutionFailed" , errorMessage )
195
- } else if strings .Contains (errorMessage , "invalid version range" ) {
196
- // Handle invalid version range errors, potentially setting status conditions
197
- setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
198
- ensureAllConditionsWithReason (ext , "ResolutionFailed" , errorMessage )
199
- } else {
200
- // General error handling
201
- setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
202
- ensureAllConditionsWithReason (ext , "InstallationStatusUnknown" , "" )
203
- }
204
- }
205
- } else {
206
- // If the error is not an aggregate, handle it as a general error
207
- errorMessage := err .Error ()
208
- setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
209
- ensureAllConditionsWithReason (ext , "InstallationStatusUnknown" , "" )
210
- }
211
- ext .Status .ResolvedBundle = nil
212
- return ctrl.Result {}, err
213
- }
214
-
215
190
// Helper function to do the actual reconcile
216
191
//
217
192
// Today we always return ctrl.Result{} and an error.
@@ -232,10 +207,22 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
232
207
// run resolution
233
208
bundle , err := r .resolve (ctx , * ext )
234
209
if err != nil {
235
- return r .handleResolutionErrors (ext , err )
210
+ ext .Status .ResolvedBundle = nil
211
+ ext .Status .InstalledBundle = nil
212
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
213
+ // TODO: indicate Progressing state based on whether error is ResolutionError or not
214
+ if errors .As (err , & ResolutionError {}) {
215
+ ensureAllConditionsWithReason (ext , ocv1alpha1 .ReasonResolutionFailed , err .Error ())
216
+ } else {
217
+ ensureAllConditionsWithReason (ext , ocv1alpha1 .ReasonInstallationStatusUnknown , err .Error ())
218
+ }
219
+ return ctrl.Result {}, err
236
220
}
237
221
238
222
if err := r .validateBundle (bundle ); err != nil {
223
+ ext .Status .ResolvedBundle = nil
224
+ ext .Status .InstalledBundle = nil
225
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
239
226
setInstalledStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
240
227
setDeprecationStatusesUnknown (& ext .Status .Conditions , "deprecation checks have not been attempted as installation has failed" , ext .GetGeneration ())
241
228
return ctrl.Result {}, err
@@ -291,23 +278,13 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
291
278
292
279
bundleFS , err := r .Storage .Load (ctx , ext )
293
280
if err != nil {
294
- apimeta .SetStatusCondition (& ext .Status .Conditions , metav1.Condition {
295
- Type : ocv1alpha1 .TypeHasValidBundle ,
296
- Status : metav1 .ConditionFalse ,
297
- Reason : ocv1alpha1 .ReasonBundleLoadFailed ,
298
- Message : err .Error (),
299
- })
281
+ setHasValidBundleFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
300
282
return ctrl.Result {}, err
301
283
}
302
284
303
285
chrt , values , err := r .Handler .Handle (ctx , bundleFS , ext )
304
286
if err != nil {
305
- apimeta .SetStatusCondition (& ext .Status .Conditions , metav1.Condition {
306
- Type : ocv1alpha1 .TypeInstalled ,
307
- Status : metav1 .ConditionFalse ,
308
- Reason : ocv1alpha1 .ReasonInstallationFailed ,
309
- Message : err .Error (),
310
- })
287
+ setInstalledStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
311
288
return ctrl.Result {}, err
312
289
}
313
290
@@ -342,26 +319,17 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
342
319
return nil
343
320
}, helmclient .AppendInstallPostRenderer (post ))
344
321
if err != nil {
345
- if isResourceNotFoundErr (err ) {
346
- err = errRequiredResourceNotFound {err }
347
- }
348
322
setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonInstallationFailed , err ), ext .Generation )
349
323
return ctrl.Result {}, err
350
324
}
351
325
case stateNeedsUpgrade :
352
326
rel , err = ac .Upgrade (ext .GetName (), r .ReleaseNamespace , chrt , values , helmclient .AppendUpgradePostRenderer (post ))
353
327
if err != nil {
354
- if isResourceNotFoundErr (err ) {
355
- err = errRequiredResourceNotFound {err }
356
- }
357
328
setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonUpgradeFailed , err ), ext .Generation )
358
329
return ctrl.Result {}, err
359
330
}
360
331
case stateUnchanged :
361
332
if err := ac .Reconcile (rel ); err != nil {
362
- if isResourceNotFoundErr (err ) {
363
- err = errRequiredResourceNotFound {err }
364
- }
365
333
setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , ocv1alpha1 .ReasonResolutionFailed , err ), ext .Generation )
366
334
return ctrl.Result {}, err
367
335
}
@@ -414,7 +382,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
414
382
func (r * ClusterExtensionReconciler ) resolve (ctx context.Context , ext ocv1alpha1.ClusterExtension ) (* catalogmetadata.Bundle , error ) {
415
383
allBundles , err := r .BundleProvider .Bundles (ctx )
416
384
if err != nil {
417
- return nil , utilerrors . NewAggregate ([] error { fmt .Errorf ("error fetching bundles: %w" , err )} )
385
+ return nil , fmt .Errorf ("error fetching bundles: %w" , err )
418
386
}
419
387
420
388
packageName := ext .Spec .PackageName
@@ -437,7 +405,7 @@ func (r *ClusterExtensionReconciler) resolve(ctx context.Context, ext ocv1alpha1
437
405
if versionRange != "" {
438
406
vr , err := mmsemver .NewConstraint (versionRange )
439
407
if err != nil {
440
- return nil , utilerrors . NewAggregate ([] error {fmt .Errorf ("invalid version range '%s' : %w " , versionRange , err )})
408
+ return nil , ResolutionError {fmt .Sprintf ("invalid version range %q : %v " , versionRange , err )}
441
409
}
442
410
predicates = append (predicates , catalogfilter .InMastermindsSemverRange (vr ))
443
411
}
@@ -464,13 +432,13 @@ func (r *ClusterExtensionReconciler) resolve(ctx context.Context, ext ocv1alpha1
464
432
if len (resultSet ) == 0 {
465
433
switch {
466
434
case versionRange != "" && channelName != "" :
467
- return nil , fmt .Errorf ("%sno package %q matching version %q in channel %q found" , upgradeErrorPrefix , packageName , versionRange , channelName )
435
+ return nil , ResolutionError { fmt .Sprintf ("%sno package %q matching version %q in channel %q found" , upgradeErrorPrefix , packageName , versionRange , channelName )}
468
436
case versionRange != "" :
469
- return nil , fmt .Errorf ("%sno package %q matching version %q found" , upgradeErrorPrefix , packageName , versionRange )
437
+ return nil , ResolutionError { fmt .Sprintf ("%sno package %q matching version %q found" , upgradeErrorPrefix , packageName , versionRange )}
470
438
case channelName != "" :
471
- return nil , fmt .Errorf ("%sno package %q in channel %q found" , upgradeErrorPrefix , packageName , channelName )
439
+ return nil , ResolutionError { fmt .Sprintf ("%sno package %q in channel %q found" , upgradeErrorPrefix , packageName , channelName )}
472
440
default :
473
- return nil , fmt .Errorf ("%sno package %q found" , upgradeErrorPrefix , packageName )
441
+ return nil , ResolutionError { fmt .Sprintf ("%sno package %q found" , upgradeErrorPrefix , packageName )}
474
442
}
475
443
}
476
444
@@ -753,37 +721,6 @@ func (d *DefaultInstalledBundleGetter) GetInstalledBundle(ctx context.Context, a
753
721
return resultSet [0 ], nil
754
722
}
755
723
756
- type errRequiredResourceNotFound struct {
757
- error
758
- }
759
-
760
- func (err errRequiredResourceNotFound ) Error () string {
761
- return fmt .Sprintf ("required resource not found: %v" , err .error )
762
- }
763
-
764
- func isResourceNotFoundErr (err error ) bool {
765
- var agg utilerrors.Aggregate
766
- if errors .As (err , & agg ) {
767
- for _ , err := range agg .Errors () {
768
- return isResourceNotFoundErr (err )
769
- }
770
- }
771
-
772
- nkme := & apimeta.NoKindMatchError {}
773
- if errors .As (err , & nkme ) {
774
- return true
775
- }
776
- if apierrors .IsNotFound (err ) {
777
- return true
778
- }
779
-
780
- // TODO: improve NoKindMatchError matching
781
- // An error that is bubbled up from the k8s.io/cli-runtime library
782
- // does not wrap meta.NoKindMatchError, so we need to fallback to
783
- // the use of string comparisons for now.
784
- return strings .Contains (err .Error (), "no matches for kind" )
785
- }
786
-
787
724
type postrenderer struct {
788
725
labels map [string ]string
789
726
cascade postrender.PostRenderer
0 commit comments