@@ -25,6 +25,7 @@ import (
25
25
"sort"
26
26
"strings"
27
27
"sync"
28
+ "time"
28
29
29
30
mmsemver "github.com/Masterminds/semver/v3"
30
31
bsemver "github.com/blang/semver/v4"
@@ -59,6 +60,7 @@ import (
59
60
"sigs.k8s.io/controller-runtime/pkg/reconcile"
60
61
"sigs.k8s.io/controller-runtime/pkg/source"
61
62
63
+ "github.com/operator-framework/api/pkg/operators/v1alpha1"
62
64
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
63
65
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
64
66
"github.com/operator-framework/operator-registry/alpha/declcfg"
@@ -72,6 +74,7 @@ import (
72
74
"github.com/operator-framework/operator-controller/internal/catalogmetadata"
73
75
catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
74
76
catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
77
+ "github.com/operator-framework/operator-controller/internal/conditionsets"
75
78
"github.com/operator-framework/operator-controller/internal/handler"
76
79
"github.com/operator-framework/operator-controller/internal/labels"
77
80
"github.com/operator-framework/operator-controller/internal/packageerrors"
@@ -111,14 +114,13 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req
111
114
112
115
var existingExt = & ocv1alpha1.ClusterExtension {}
113
116
if err := r .Client .Get (ctx , req .NamespacedName , existingExt ); err != nil {
114
- return ctrl.Result {}, client .IgnoreNotFound (err )
117
+ return ctrl.Result {}, utilerrors . NewAggregate ([] error { client .IgnoreNotFound (err ), nil } )
115
118
}
116
119
117
120
reconciledExt := existingExt .DeepCopy ()
118
121
res , reconcileErr := r .reconcile (ctx , reconciledExt )
119
- if reconcileErr != nil {
120
- return ctrl.Result {}, reconcileErr
121
- }
122
+
123
+ var updateErrors []error
122
124
123
125
// Do checks before any Update()s, as Update() may modify the resource structure!
124
126
updateStatus := ! equality .Semantic .DeepEqual (existingExt .Status , reconciledExt .Status )
@@ -127,7 +129,7 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req
127
129
128
130
if updateStatus {
129
131
if updateErr := r .Client .Status ().Update (ctx , reconciledExt ); updateErr != nil {
130
- return res , utilerrors . NewAggregate ([] error { reconcileErr , updateErr } )
132
+ updateErrors = append ( updateErrors , updateErr )
131
133
}
132
134
}
133
135
@@ -137,11 +139,35 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req
137
139
138
140
if updateFinalizers {
139
141
if updateErr := r .Client .Update (ctx , reconciledExt ); updateErr != nil {
140
- return res , utilerrors . NewAggregate ([] error { reconcileErr , updateErr } )
142
+ updateErrors = append ( updateErrors , updateErr )
141
143
}
142
144
}
143
145
144
- return res , reconcileErr
146
+ if reconcileErr != nil {
147
+ updateErrors = append (updateErrors , reconcileErr )
148
+ }
149
+
150
+ return res , utilerrors .NewAggregate (updateErrors )
151
+ }
152
+
153
+ // ensureAllConditionsWithReason checks that all defined condition types exist in the given ClusterExtension,
154
+ // and assigns a specified reason and custom message to any missing condition.
155
+ func ensureAllConditionsWithReason (ext * ocv1alpha1.ClusterExtension , reason v1alpha1.ConditionReason , message string ) {
156
+ for _ , condType := range conditionsets .ConditionTypes {
157
+ cond := apimeta .FindStatusCondition (ext .Status .Conditions , condType )
158
+ if cond == nil {
159
+ // Create a new condition with a valid reason and add it
160
+ newCond := metav1.Condition {
161
+ Type : condType ,
162
+ Status : metav1 .ConditionFalse ,
163
+ Reason : string (reason ),
164
+ Message : message ,
165
+ ObservedGeneration : ext .GetGeneration (),
166
+ LastTransitionTime : metav1 .NewTime (time .Now ()),
167
+ }
168
+ ext .Status .Conditions = append (ext .Status .Conditions , newCond )
169
+ }
170
+ }
145
171
}
146
172
147
173
// Compare resources - ignoring status & metadata.finalizers
@@ -151,6 +177,34 @@ func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool {
151
177
return ! equality .Semantic .DeepEqual (a , b )
152
178
}
153
179
180
+ func (r * ClusterExtensionReconciler ) handleResolutionErrors (ext * ocv1alpha1.ClusterExtension , err error ) (ctrl.Result , error ) {
181
+ var aggErrs utilerrors.Aggregate
182
+ if errors .As (err , & aggErrs ) {
183
+ for _ , err := range aggErrs .Errors () {
184
+ errorMessage := err .Error ()
185
+ if strings .Contains (errorMessage , "no package" ) {
186
+ // Handle no package found errors, potentially setting status conditions
187
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
188
+ ensureAllConditionsWithReason (ext , "ResolutionFailed" , errorMessage )
189
+ } else if strings .Contains (errorMessage , "invalid version range" ) {
190
+ // Handle invalid version range errors, potentially setting status conditions
191
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
192
+ ensureAllConditionsWithReason (ext , "ResolutionFailed" , errorMessage )
193
+ } else {
194
+ // General error handling
195
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
196
+ ensureAllConditionsWithReason (ext , "InstallationStatusUnknown" , "" )
197
+ }
198
+ }
199
+ } else {
200
+ // If the error is not an aggregate, handle it as a general error
201
+ errorMessage := err .Error ()
202
+ setResolvedStatusConditionFailed (& ext .Status .Conditions , errorMessage , ext .Generation )
203
+ ensureAllConditionsWithReason (ext , "InstallationStatusUnknown" , "" )
204
+ }
205
+ return ctrl.Result {}, err
206
+ }
207
+
154
208
// Helper function to do the actual reconcile
155
209
//
156
210
// Today we always return ctrl.Result{} and an error.
@@ -159,13 +213,12 @@ func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool {
159
213
//
160
214
//nolint:unparam
161
215
func (r * ClusterExtensionReconciler ) reconcile (ctx context.Context , ext * ocv1alpha1.ClusterExtension ) (ctrl.Result , error ) {
162
- // Lookup the bundle that corresponds to the ClusterExtension's desired package.
163
- bundle , err := r .resolve (ctx , ext )
216
+ l := log .FromContext (ctx ).WithName ("operator-controller" )
217
+ // run resolution
218
+ bundle , err := r .resolve (ctx , * ext )
164
219
if err != nil {
165
- ext .Status .ResolvedBundle = nil
166
- ext .Status .InstalledBundle = nil
167
- setInstalledStatusConditionFailed (& ext .Status .Conditions , fmt .Sprintf ("%s:%v" , "unable to get resolved bundle version" , err ), ext .Generation )
168
- return ctrl.Result {}, err
220
+ l .V (1 ).Info ("bundle resolve error" , "error" , err )
221
+ return r .handleResolutionErrors (ext , err )
169
222
}
170
223
171
224
bundleVersion , err := bundle .Version ()
@@ -330,13 +383,12 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
330
383
return ctrl.Result {}, nil
331
384
}
332
385
333
- func (r * ClusterExtensionReconciler ) resolve (ctx context.Context , ext * ocv1alpha1.ClusterExtension ) (* catalogmetadata.Bundle , error ) {
386
+ func (r * ClusterExtensionReconciler ) resolve (ctx context.Context , ext ocv1alpha1.ClusterExtension ) (* catalogmetadata.Bundle , error ) {
334
387
allBundles , err := r .BundleProvider .Bundles (ctx )
335
388
if err != nil {
336
- return nil , err
389
+ return nil , utilerrors . NewAggregate ([] error { fmt . Errorf ( "error fetching bundles: %w" , err )})
337
390
}
338
391
339
- // TODO: change ext spec to contain a source field.
340
392
packageName := ext .Spec .PackageName
341
393
channelName := ext .Spec .Channel
342
394
versionRange := ext .Spec .Version
@@ -352,47 +404,53 @@ func (r *ClusterExtensionReconciler) resolve(ctx context.Context, ext *ocv1alpha
352
404
if versionRange != "" {
353
405
vr , err := mmsemver .NewConstraint (versionRange )
354
406
if err != nil {
355
- return nil , fmt .Errorf ("invalid version range %q : %w" , versionRange , err )
407
+ return nil , utilerrors . NewAggregate ([] error { fmt .Errorf ("invalid version range '%s' : %w" , versionRange , err )} )
356
408
}
357
409
predicates = append (predicates , catalogfilter .InMastermindsSemverRange (vr ))
358
410
}
359
411
360
412
var installedVersion string
361
- // Do not include bundle versions older than currently installed unless UpgradeConstraintPolicy = 'Ignore'
413
+ var upgradeErrorPrefix string
362
414
if ext .Spec .UpgradeConstraintPolicy != ocv1alpha1 .UpgradeConstraintPolicyIgnore {
363
- installedVersionSemver , err := r .getInstalledVersion (ctx , * ext )
364
- if err != nil && ! apierrors . IsNotFound ( err ) {
365
- return nil , err
415
+ installedBundle , err := r .getInstalledVersion (ctx , ext )
416
+ if err != nil {
417
+ return nil , utilerrors . NewAggregate ([] error { fmt . Errorf ( "error fetching installed version: %w" , err )})
366
418
}
367
- if installedVersionSemver != nil {
368
- installedVersion = installedVersionSemver .String ()
369
-
370
- // Based on installed version create a caret range comparison constraint
371
- // to allow only minor and patch version as successors.
419
+ if installedBundle != nil {
420
+ installedVersion = installedBundle .String ()
421
+ upgradeErrorPrefix = fmt .Sprintf ("error upgrading from currently installed version %q: " , installedVersion )
372
422
wantedVersionRangeConstraint , err := mmsemver .NewConstraint (fmt .Sprintf ("^%s" , installedVersion ))
373
423
if err != nil {
374
- return nil , err
424
+ return nil , utilerrors . NewAggregate ([] error { fmt . Errorf ( "%serror creating version constraint: %w" , upgradeErrorPrefix , err )})
375
425
}
376
426
predicates = append (predicates , catalogfilter .InMastermindsSemverRange (wantedVersionRangeConstraint ))
377
427
}
378
428
}
379
429
380
430
resultSet := catalogfilter .Filter (allBundles , catalogfilter .And (predicates ... ))
381
-
382
431
if len (resultSet ) == 0 {
383
- return nil , packageerrors .GenerateFullError (packageName , versionRange , channelName , installedVersion )
432
+ switch {
433
+ case versionRange != "" && channelName != "" :
434
+ return nil , packageerrors .GenerateVersionChannelError (packageName , versionRange , channelName )
435
+ case versionRange != "" :
436
+ return nil , packageerrors .GenerateVersionError (packageName , versionRange )
437
+ case channelName != "" :
438
+ return nil , packageerrors .GenerateChannelError (packageName , channelName )
439
+ default :
440
+ return nil , packageerrors .GenerateError (packageName )
441
+ }
384
442
}
385
-
386
443
sort .SliceStable (resultSet , func (i , j int ) bool {
387
444
return catalogsort .ByVersion (resultSet [i ], resultSet [j ])
388
445
})
389
446
sort .SliceStable (resultSet , func (i , j int ) bool {
390
447
return catalogsort .ByDeprecated (resultSet [i ], resultSet [j ])
391
448
})
449
+
392
450
return resultSet [0 ], nil
393
451
}
394
452
395
- // setDeprecationStatus will set the appropriate deprecation statuses for a ClusterExtension
453
+ // SetDeprecationStatus will set the appropriate deprecation statuses for a ClusterExtension
396
454
// based on the provided bundle
397
455
func SetDeprecationStatus (ext * ocv1alpha1.ClusterExtension , bundle * catalogmetadata.Bundle ) {
398
456
// reset conditions to false
0 commit comments