Skip to content

Commit 5681488

Browse files
Read spec.release field from the olm.package property for proper version ordering and comparison
Signed-off-by: Rashmi Gottipati <rgottipa@redhat.com>
1 parent 8dcab49 commit 5681488

File tree

6 files changed

+42
-55
lines changed

6 files changed

+42
-55
lines changed

internal/operator-controller/catalogmetadata/compare/compare.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/operator-framework/operator-registry/alpha/declcfg"
1111

12-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundleutil"
1312
slicesutil "github.com/operator-framework/operator-controller/internal/shared/util/slices"
1413
)
1514

@@ -42,16 +41,16 @@ func newMastermindsRange(versionRange string) (bsemver.Range, error) {
4241
// version and release. Bundles with lower versions/releases are
4342
// considered less than bundles with higher versions/releases.
4443
func ByVersionAndRelease(b1, b2 declcfg.Bundle) int {
45-
vr1, err1 := bundleutil.GetVersionAndRelease(b1)
46-
vr2, err2 := bundleutil.GetVersionAndRelease(b2)
44+
cv1, err1 := b1.CompositeVersion()
45+
cv2, err2 := b2.CompositeVersion()
4746

4847
// We don't really expect errors, because we expect well-formed/validated
4948
// FBC as input. However, just in case we'll check the errors and sort
5049
// invalid bundles as "lower" than valid bundles.
5150
if err1 != nil || err2 != nil {
5251
return compareErrors(err2, err1)
5352
}
54-
return vr2.Compare(*vr1)
53+
return cv2.Compare(cv1)
5554
}
5655

5756
func ByDeprecationFunc(deprecation declcfg.Deprecation) func(a, b declcfg.Bundle) int {

internal/operator-controller/catalogmetadata/filter/bundle_predicates.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@ import (
55

66
"github.com/operator-framework/operator-registry/alpha/declcfg"
77

8-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
9-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundleutil"
108
"github.com/operator-framework/operator-controller/internal/shared/util/filter"
119
)
1210

1311
// ExactVersionRelease returns a predicate that matches bundles with an exact
1412
// version and release match. Both the semver version and the release must match
1513
// exactly for the predicate to return true.
16-
func ExactVersionRelease(expect bundle.VersionRelease) filter.Predicate[declcfg.Bundle] {
14+
func ExactVersionRelease(expect declcfg.CompositeVersion) filter.Predicate[declcfg.Bundle] {
1715
return func(b declcfg.Bundle) bool {
18-
actual, err := bundleutil.GetVersionAndRelease(b)
16+
actual, err := b.CompositeVersion()
1917
if err != nil {
2018
return false
2119
}
22-
return expect.Compare(*actual) == 0
20+
return expect.Compare(actual) == 0
2321
}
2422
}
2523

@@ -28,11 +26,11 @@ func ExactVersionRelease(expect bundle.VersionRelease) filter.Predicate[declcfg.
2826
// ignoring the release metadata.
2927
func InSemverRange(versionRange bsemver.Range) filter.Predicate[declcfg.Bundle] {
3028
return func(b declcfg.Bundle) bool {
31-
vr, err := bundleutil.GetVersionAndRelease(b)
29+
cv, err := b.CompositeVersion()
3230
if err != nil {
3331
return false
3432
}
35-
return versionRange(vr.Version)
33+
return versionRange(cv.Version)
3634
}
3735
}
3836

@@ -50,11 +48,11 @@ func InAnyChannel(channels ...declcfg.Channel) filter.Predicate[declcfg.Bundle]
5048
}
5149

5250
// SameVersionHigherRelease returns a predicate that matches bundles with the same
53-
// semantic version as the provided version-release, but with a higher release value.
51+
// semantic version as the provided composite version, but with a higher release value.
5452
// This is used to identify re-released bundles (e.g., 2.0.0+2 when 2.0.0+1 is installed).
55-
func SameVersionHigherRelease(expect bundle.VersionRelease) filter.Predicate[declcfg.Bundle] {
53+
func SameVersionHigherRelease(expect declcfg.CompositeVersion) filter.Predicate[declcfg.Bundle] {
5654
return func(b declcfg.Bundle) bool {
57-
actual, err := bundleutil.GetVersionAndRelease(b)
55+
actual, err := b.CompositeVersion()
5856
if err != nil {
5957
return false
6058
}
@@ -63,6 +61,7 @@ func SameVersionHigherRelease(expect bundle.VersionRelease) filter.Predicate[dec
6361
return false
6462
}
6563

66-
return expect.Release.Compare(actual.Release) < 0
64+
// Use CompositeVersion.Compare which handles release comparison correctly
65+
return expect.Compare(actual) < 0
6766
}
6867
}

internal/operator-controller/catalogmetadata/filter/successors.go

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,39 @@ import (
88
"github.com/operator-framework/operator-registry/alpha/declcfg"
99

1010
ocv1 "github.com/operator-framework/operator-controller/api/v1"
11-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
12-
"github.com/operator-framework/operator-controller/internal/operator-controller/features"
1311
"github.com/operator-framework/operator-controller/internal/shared/util/filter"
1412
)
1513

16-
func SuccessorsOf(installedBundle ocv1.BundleMetadata, channels ...declcfg.Channel) (filter.Predicate[declcfg.Bundle], error) {
17-
// TODO: We do not have an explicit field in our BundleMetadata for a bundle's release value.
18-
// Legacy registry+v1 bundles embed the release value inside their versions as build metadata
19-
// (in violation of the semver spec). If/when we add explicit release metadata to bundles and/or
20-
// we support a new bundle format, we need to revisit the assumption that all bundles are
21-
// registry+v1 and embed release in build metadata.
22-
installedVersionRelease, err := bundle.NewLegacyRegistryV1VersionRelease(installedBundle.Version)
23-
if err != nil {
24-
return nil, fmt.Errorf("failed to get version and release of installed bundle: %v", err)
14+
func SuccessorsOf(installedBundle ocv1.BundleMetadata, allBundles []declcfg.Bundle, channels ...declcfg.Channel) (filter.Predicate[declcfg.Bundle], error) {
15+
// Find the installed bundle in the catalog to get its CompositeVersion (with explicit Release field)
16+
var installedCompositeVersion *declcfg.CompositeVersion
17+
for _, b := range allBundles {
18+
if b.Name == installedBundle.Name {
19+
cv, err := b.CompositeVersion()
20+
if err != nil {
21+
return nil, fmt.Errorf("failed to get composite version for installed bundle %q: %v", installedBundle.Name, err)
22+
}
23+
installedCompositeVersion = cv
24+
break
25+
}
26+
}
27+
28+
// If installed bundle not found in catalog, we can't determine successors based on release
29+
if installedCompositeVersion == nil {
30+
// Fall back to legacy successor logic only (no release awareness)
31+
return legacySuccessor(installedBundle, channels...)
2532
}
2633

2734
successorsPredicate, err := legacySuccessor(installedBundle, channels...)
2835
if err != nil {
2936
return nil, fmt.Errorf("getting successorsPredicate: %w", err)
3037
}
3138

32-
// Build the final predicate based on feature gate status
33-
predicates := []filter.Predicate[declcfg.Bundle]{
39+
// Bundle matches if it's a channel successor or current version (no upgrade)
40+
return filter.Or(
3441
successorsPredicate,
35-
ExactVersionRelease(*installedVersionRelease),
36-
}
37-
38-
// If release version priority is enabled, also consider bundles
39-
// with the same semantic version but higher release as valid successors.
40-
// This allows upgrades to re-released bundles (e.g., 2.0.0+1 -> 2.0.0+2).
41-
if features.OperatorControllerFeatureGate.Enabled(features.ReleaseVersionPriority) {
42-
predicates = append(predicates, SameVersionHigherRelease(*installedVersionRelease))
43-
}
44-
45-
// Bundle matches if it's a channel successor, current version (no upgrade),
46-
// or (when feature gate enabled) same version with higher release
47-
return filter.Or(predicates...), nil
42+
ExactVersionRelease(*installedCompositeVersion),
43+
), nil
4844
}
4945

5046
func legacySuccessor(installedBundle ocv1.BundleMetadata, channels ...declcfg.Channel) (filter.Predicate[declcfg.Bundle], error) {

internal/operator-controller/controllers/clusterextension_reconcile_steps.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,7 @@ func ResolveBundle(r resolve.Resolver, c client.Client) ReconcileStepFunc {
189189
state.resolvedRevisionMetadata = &RevisionMetadata{
190190
Package: resolvedBundle.Package,
191191
Image: resolvedBundle.Image,
192-
// TODO: Right now, operator-controller only supports registry+v1 bundles and has no concept
193-
// of a "release" field. If/when we add a release field concept or a new bundle format
194-
// we need to re-evaluate use of `AsLegacyRegistryV1Version` so that we avoid propagating
195-
// registry+v1's semver spec violations of treating build metadata as orderable.
196-
BundleMetadata: bundleutil.MetadataFor(resolvedBundle.Name, resolvedBundleVersion.AsLegacyRegistryV1Version()),
192+
BundleMetadata: bundleutil.MetadataFor(resolvedBundle.Name, resolvedBundleVersion.Version),
197193
}
198194
return nil, nil
199195
}

internal/operator-controller/resolve/catalog.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import (
1717
"github.com/operator-framework/operator-registry/alpha/declcfg"
1818

1919
ocv1 "github.com/operator-framework/operator-controller/api/v1"
20-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
21-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundleutil"
2220
"github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/compare"
2321
"github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/filter"
2422
filterutil "github.com/operator-framework/operator-controller/internal/shared/util/filter"
@@ -39,7 +37,7 @@ type foundBundle struct {
3937
}
4038

4139
// Resolve returns a Bundle from a catalog that needs to get installed on the cluster.
42-
func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) {
40+
func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *declcfg.CompositeVersion, *declcfg.Deprecation, error) {
4341
l := log.FromContext(ctx)
4442
packageName := ext.Spec.Source.Catalog.PackageName
4543
versionRange := ext.Spec.Source.Catalog.Version
@@ -111,7 +109,7 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio
111109
}
112110

113111
if ext.Spec.Source.Catalog.UpgradeConstraintPolicy != ocv1.UpgradeConstraintPolicySelfCertified && installedBundle != nil {
114-
successorPredicate, err := filter.SuccessorsOf(*installedBundle, packageFBC.Channels...)
112+
successorPredicate, err := filter.SuccessorsOf(*installedBundle, packageFBC.Bundles, packageFBC.Channels...)
115113
if err != nil {
116114
return fmt.Errorf("error finding upgrade edges: %w", err)
117115
}
@@ -190,7 +188,7 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio
190188
}
191189
}
192190
resolvedBundle := resolvedBundles[0].bundle
193-
resolvedBundleVersion, err := bundleutil.GetVersionAndRelease(*resolvedBundle)
191+
resolvedBundleVersion, err := resolvedBundle.CompositeVersion()
194192
if err != nil {
195193
return nil, nil, nil, fmt.Errorf("error getting resolved bundle version for bundle %q: %w", resolvedBundle.Name, err)
196194
}

internal/operator-controller/resolve/resolver.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import (
66
"github.com/operator-framework/operator-registry/alpha/declcfg"
77

88
ocv1 "github.com/operator-framework/operator-controller/api/v1"
9-
"github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
109
)
1110

1211
type Resolver interface {
13-
Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error)
12+
Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *declcfg.CompositeVersion, *declcfg.Deprecation, error)
1413
}
1514

16-
type Func func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error)
15+
type Func func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *declcfg.CompositeVersion, *declcfg.Deprecation, error)
1716

18-
func (f Func) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) {
17+
func (f Func) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *declcfg.CompositeVersion, *declcfg.Deprecation, error) {
1918
return f(ctx, ext, installedBundle)
2019
}

0 commit comments

Comments
 (0)