@@ -19,8 +19,10 @@ package controllers
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "sort"
22
23
"strings"
23
24
25
+ mmsemver "github.com/Masterminds/semver/v3"
24
26
"github.com/go-logr/logr"
25
27
"k8s.io/apimachinery/pkg/api/equality"
26
28
apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -37,22 +39,21 @@ import (
37
39
"sigs.k8s.io/controller-runtime/pkg/reconcile"
38
40
39
41
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
40
- "github.com/operator-framework/deppy/pkg/deppy"
41
- "github.com/operator-framework/deppy/pkg/deppy/solver"
42
42
"github.com/operator-framework/operator-registry/alpha/declcfg"
43
43
rukpakv1alpha2 "github.com/operator-framework/rukpak/api/v1alpha2"
44
44
45
45
ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
46
46
"github.com/operator-framework/operator-controller/internal/catalogmetadata"
47
- olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables"
47
+ catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
48
+ catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
49
+ "github.com/operator-framework/operator-controller/pkg/features"
48
50
)
49
51
50
52
// ClusterExtensionReconciler reconciles a ClusterExtension object
51
53
type ClusterExtensionReconciler struct {
52
54
client.Client
53
55
BundleProvider BundleProvider
54
56
Scheme * runtime.Scheme
55
- Resolver * solver.Solver
56
57
}
57
58
58
59
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions,verbs=get;list;watch
@@ -116,33 +117,8 @@ func checkForUnexpectedFieldChange(a, b ocv1alpha1.ClusterExtension) bool {
116
117
//
117
118
//nolint:unparam
118
119
func (r * ClusterExtensionReconciler ) reconcile (ctx context.Context , ext * ocv1alpha1.ClusterExtension ) (ctrl.Result , error ) {
119
- // gather vars for resolution
120
- vars , err := r .variables (ctx )
121
- if err != nil {
122
- ext .Status .InstalledBundle = nil
123
- setInstalledStatusConditionUnknown (& ext .Status .Conditions , "installation has not been attempted due to failure to gather data for resolution" , ext .GetGeneration ())
124
- ext .Status .ResolvedBundle = nil
125
- setResolvedStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
126
-
127
- setDeprecationStatusesUnknown (& ext .Status .Conditions , "deprecation checks have not been attempted due to failure to gather data for resolution" , ext .GetGeneration ())
128
- return ctrl.Result {}, err
129
- }
130
-
131
- // run resolution
132
- selection , err := r .Resolver .Solve (vars )
133
- if err != nil {
134
- ext .Status .InstalledBundle = nil
135
- setInstalledStatusConditionUnknown (& ext .Status .Conditions , "installation has not been attempted as resolution failed" , ext .GetGeneration ())
136
- ext .Status .ResolvedBundle = nil
137
- setResolvedStatusConditionFailed (& ext .Status .Conditions , err .Error (), ext .GetGeneration ())
138
-
139
- setDeprecationStatusesUnknown (& ext .Status .Conditions , "deprecation checks have not been attempted as resolution failed" , ext .GetGeneration ())
140
- return ctrl.Result {}, err
141
- }
142
-
143
- // lookup the bundle in the solution that corresponds to the
144
- // ClusterExtension's desired package name.
145
- bundle , err := r .bundleFromSolution (selection , ext .Spec .PackageName )
120
+ // Lookup the bundle that corresponds to the ClusterExtension's desired package.
121
+ bundle , err := r .resolve (ctx , ext )
146
122
if err != nil {
147
123
ext .Status .InstalledBundle = nil
148
124
setInstalledStatusConditionUnknown (& ext .Status .Conditions , "installation has not been attempted as resolution failed" , ext .GetGeneration ())
@@ -202,21 +178,133 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
202
178
return ctrl.Result {}, nil
203
179
}
204
180
205
- func (r * ClusterExtensionReconciler ) variables (ctx context.Context ) ([]deppy. Variable , error ) {
181
+ func (r * ClusterExtensionReconciler ) resolve (ctx context.Context , ext * ocv1alpha1. ClusterExtension ) (* catalogmetadata. Bundle , error ) {
206
182
allBundles , err := r .BundleProvider .Bundles (ctx )
207
183
if err != nil {
208
184
return nil , err
209
185
}
210
- clusterExtensionList := ocv1alpha1.ClusterExtensionList {}
211
- if err := r .Client .List (ctx , & clusterExtensionList ); err != nil {
186
+
187
+ installedBundle , err := r .installedBundle (ctx , allBundles , ext )
188
+ if err != nil {
189
+ return nil , err
190
+ }
191
+
192
+ packageName := ext .Spec .PackageName
193
+ channelName := ext .Spec .Channel
194
+ versionRange := ext .Spec .Version
195
+
196
+ predicates := []catalogfilter.Predicate [catalogmetadata.Bundle ]{
197
+ catalogfilter .WithPackageName (packageName ),
198
+ }
199
+
200
+ if channelName != "" {
201
+ predicates = append (predicates , catalogfilter .InChannel (channelName ))
202
+ }
203
+
204
+ if versionRange != "" {
205
+ vr , err := mmsemver .NewConstraint (versionRange )
206
+ if err != nil {
207
+ return nil , fmt .Errorf ("invalid version range %q: %w" , versionRange , err )
208
+ }
209
+ predicates = append (predicates , catalogfilter .InMastermindsSemverRange (vr ))
210
+ }
211
+
212
+ if ext .Spec .UpgradeConstraintPolicy != ocv1alpha1 .UpgradeConstraintPolicyIgnore && installedBundle != nil {
213
+ upgradePredicate , err := upgradePredicate (installedBundle )
214
+ if err != nil {
215
+ return nil , err
216
+ }
217
+
218
+ predicates = append (predicates , upgradePredicate )
219
+ }
220
+
221
+ resultSet := catalogfilter .Filter (allBundles , catalogfilter .And (predicates ... ))
222
+
223
+ var upgradeErrorPrefix string
224
+ if installedBundle != nil {
225
+ installedBundleVersion , err := installedBundle .Version ()
226
+ if err != nil {
227
+ return nil , err
228
+ }
229
+ upgradeErrorPrefix = fmt .Sprintf ("error upgrading from currently installed version %q: " , installedBundleVersion .String ())
230
+ }
231
+ if len (resultSet ) == 0 {
232
+ if versionRange != "" && channelName != "" {
233
+ return nil , fmt .Errorf ("%sno package %q matching version %q found in channel %q" , upgradeErrorPrefix , packageName , versionRange , channelName )
234
+ }
235
+ if versionRange != "" {
236
+ return nil , fmt .Errorf ("%sno package %q matching version %q found" , upgradeErrorPrefix , packageName , versionRange )
237
+ }
238
+ if channelName != "" {
239
+ return nil , fmt .Errorf ("%sno package %q found in channel %q" , upgradeErrorPrefix , packageName , channelName )
240
+ }
241
+ return nil , fmt .Errorf ("%sno package %q found" , upgradeErrorPrefix , packageName )
242
+ }
243
+ sort .SliceStable (resultSet , func (i , j int ) bool {
244
+ return catalogsort .ByVersion (resultSet [i ], resultSet [j ])
245
+ })
246
+ sort .SliceStable (resultSet , func (i , j int ) bool {
247
+ return catalogsort .ByDeprecated (resultSet [i ], resultSet [j ])
248
+ })
249
+
250
+ return resultSet [0 ], nil
251
+ }
252
+
253
+ func (r * ClusterExtensionReconciler ) installedBundle (ctx context.Context , allBundles []* catalogmetadata.Bundle , ext * ocv1alpha1.ClusterExtension ) (* catalogmetadata.Bundle , error ) {
254
+ bd := & rukpakv1alpha2.BundleDeployment {}
255
+ err := r .Client .Get (ctx , types.NamespacedName {Name : ext .GetName ()}, bd )
256
+ if client .IgnoreNotFound (err ) != nil {
257
+ return nil , err
258
+ }
259
+
260
+ if bd .Spec .Source .Image == nil || bd .Spec .Source .Image .Ref == "" {
261
+ // Bundle not yet installed
262
+ return nil , nil
263
+ }
264
+
265
+ bundleImage := bd .Spec .Source .Image .Ref
266
+ // find corresponding bundle for the installed content
267
+ resultSet := catalogfilter .Filter (allBundles , catalogfilter .And (
268
+ catalogfilter .WithPackageName (ext .Spec .PackageName ),
269
+ catalogfilter .WithBundleImage (bundleImage ),
270
+ ))
271
+ if len (resultSet ) == 0 {
272
+ return nil , fmt .Errorf ("bundle with image %q for package %q not found in available catalogs but is currently installed via BundleDeployment %q" , bundleImage , ext .Spec .PackageName , bd .Name )
273
+ }
274
+
275
+ sort .SliceStable (resultSet , func (i , j int ) bool {
276
+ return catalogsort .ByVersion (resultSet [i ], resultSet [j ])
277
+ })
278
+
279
+ return resultSet [0 ], nil
280
+ }
281
+
282
+ func upgradePredicate (installedBundle * catalogmetadata.Bundle ) (catalogfilter.Predicate [catalogmetadata.Bundle ], error ) {
283
+ var successors SuccessorsPredicateFunc = LegacySemanticsSuccessorsPredicate
284
+ if features .OperatorControllerFeatureGate .Enabled (features .ForceSemverUpgradeConstraints ) {
285
+ successors = SemverSuccessorsPredicate
286
+ }
287
+
288
+ installedBundleVersion , err := installedBundle .Version ()
289
+ if err != nil {
290
+ return nil , err
291
+ }
292
+
293
+ installedVersionConstraint , err := mmsemver .NewConstraint (installedBundleVersion .String ())
294
+ if err != nil {
212
295
return nil , err
213
296
}
214
- bundleDeploymentList := rukpakv1alpha2.BundleDeploymentList {}
215
- if err := r .Client .List (ctx , & bundleDeploymentList ); err != nil {
297
+
298
+ successorsPredicate , err := successors (installedBundle )
299
+ if err != nil {
216
300
return nil , err
217
301
}
218
302
219
- return GenerateVariables (allBundles , clusterExtensionList .Items , bundleDeploymentList .Items )
303
+ // We need either successors or current version (no upgrade)
304
+ return catalogfilter .Or (
305
+ successorsPredicate ,
306
+ catalogfilter .InMastermindsSemverRange (installedVersionConstraint ),
307
+ ), nil
220
308
}
221
309
222
310
func mapBDStatusToInstalledCondition (existingTypedBundleDeployment * rukpakv1alpha2.BundleDeployment , ext * ocv1alpha1.ClusterExtension , bundle * catalogmetadata.Bundle ) {
@@ -346,19 +434,6 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundle *catalogmetad
346
434
}
347
435
}
348
436
349
- func (r * ClusterExtensionReconciler ) bundleFromSolution (selection []deppy.Variable , packageName string ) (* catalogmetadata.Bundle , error ) {
350
- for _ , variable := range selection {
351
- switch v := variable .(type ) {
352
- case * olmvariables.BundleVariable :
353
- bundlePkgName := v .Bundle ().Package
354
- if packageName == bundlePkgName {
355
- return v .Bundle (), nil
356
- }
357
- }
358
- }
359
- return nil , fmt .Errorf ("bundle for package %q not found in solution" , packageName )
360
- }
361
-
362
437
func (r * ClusterExtensionReconciler ) GenerateExpectedBundleDeployment (o ocv1alpha1.ClusterExtension , bundlePath string , bundleProvisioner string ) * unstructured.Unstructured {
363
438
// We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
364
439
// If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
0 commit comments