Skip to content

Commit

Permalink
Introduce Masterminds/semver
Browse files Browse the repository at this point in the history
Add support for Masterminds/semver for .spec.Version
This is a bit more entangled into the code than I expected,
most instances of bsemver were replaced.

Signed-off-by: Todd Short <tshort@redhat.com>
  • Loading branch information
tmshort committed Aug 30, 2023
1 parent 8bce29c commit f63044a
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 22 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/operator-framework/operator-controller
go 1.20

require (
github.com/Masterminds/semver/v3 v3.2.0
github.com/blang/semver/v4 v4.0.0
github.com/go-logr/logr v1.2.4
github.com/onsi/ginkgo/v2 v2.11.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package validators
import (
"fmt"

bsemver "github.com/blang/semver/v4"
mmsemver "github.com/Masterminds/semver/v3"

operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
)
Expand All @@ -18,7 +18,7 @@ func validateSemver(operator *operatorsv1alpha1.Operator) error {
if operator.Spec.Version == "" {
return nil
}
if _, err := bsemver.Parse(operator.Spec.Version); err != nil {
if _, err := mmsemver.NewConstraint(operator.Spec.Version); err != nil {
return fmt.Errorf("invalid .spec.version: %w", err)
}
return nil
Expand Down
10 changes: 9 additions & 1 deletion internal/resolution/entities/bundle_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"sync"

mmsemver "github.com/Masterminds/semver/v3"
bsemver "github.com/blang/semver/v4"
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/operator-registry/alpha/property"
Expand Down Expand Up @@ -87,13 +88,20 @@ func (b *BundleEntity) PackageName() (string, error) {
return b.bundlePackage.PackageName, nil
}

func (b *BundleEntity) Version() (*bsemver.Version, error) {
func (b *BundleEntity) VersionBlang() (*bsemver.Version, error) {
if err := b.loadPackage(); err != nil {
return nil, err
}
return b.semVersion, nil
}

func (b *BundleEntity) VersionMasterminds() (*mmsemver.Version, error) {
if err := b.loadPackage(); err != nil {
return nil, err
}
return mmsemver.NewVersion(b.bundlePackage.Version)
}

func (b *BundleEntity) ProvidedGVKs() ([]GVK, error) {
if err := b.loadProvidedGVKs(); err != nil {
return nil, err
Expand Down
30 changes: 25 additions & 5 deletions internal/resolution/entities/bundle_entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"testing"

mmsemver "github.com/Masterminds/semver/v3"
bsemver "github.com/blang/semver/v4"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -48,19 +49,28 @@ var _ = Describe("BundleEntity", func() {
})

Describe("Version", func() {
It("should return the bundle version if present", func() {
It("should return the bundle blang version if present", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.14.0\"}",
})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.Version()
version, err := bundleEntity.VersionBlang()
Expect(err).ToNot(HaveOccurred())
Expect(*version).To(Equal(bsemver.MustParse("0.14.0")))
})
It("should return the bundle Masterminds version if present", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.14.0\"}",
})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.VersionMasterminds()
Expect(err).ToNot(HaveOccurred())
Expect(*version).To(Equal(*mmsemver.MustParse("0.14.0")))
})
It("should return an error if the property is not found", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.Version()
version, err := bundleEntity.VersionBlang()
Expect(version).To(BeNil())
Expect(err.Error()).To(Equal("error determining package for entity 'operatorhub/prometheus/0.14.0': required property 'olm.package' not found"))
})
Expand All @@ -69,7 +79,7 @@ var _ = Describe("BundleEntity", func() {
"olm.package": "badPackageStructure",
})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.Version()
version, err := bundleEntity.VersionBlang()
Expect(version).To(BeNil())
Expect(err.Error()).To(Equal("error determining package for entity 'operatorhub/prometheus/0.14.0': property 'olm.package' ('badPackageStructure') could not be parsed: invalid character 'b' looking for beginning of value"))
})
Expand All @@ -78,8 +88,18 @@ var _ = Describe("BundleEntity", func() {
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"badversion\"}",
})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.Version()
version, err := bundleEntity.VersionBlang()
Expect(version).To(BeNil())
Expect(err.Error()).To(Equal("could not parse semver (badversion) for entity 'operatorhub/prometheus/0.14.0': No Major.Minor.Patch elements found"))
})
It("should return error if the version is malformed", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"badversion\"}",
})
bundleEntity := olmentity.NewBundleEntity(entity)
version, err := bundleEntity.VersionMasterminds()
Expect(version).To(BeNil())
// This is still a blang error, as it does not get to the Masterminds code
Expect(err.Error()).To(Equal("could not parse semver (badversion) for entity 'operatorhub/prometheus/0.14.0': No Major.Minor.Patch elements found"))
})
})
Expand Down
16 changes: 14 additions & 2 deletions internal/resolution/util/predicates/predicates.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package predicates

import (
mmsemver "github.com/Masterminds/semver/v3"
bsemver "github.com/blang/semver/v4"
"github.com/operator-framework/deppy/pkg/deppy/input"

Expand All @@ -18,10 +19,21 @@ func WithPackageName(packageName string) input.Predicate {
}
}

func InSemverRange(semverRange bsemver.Range) input.Predicate {
func InMastermindsSemverRange(semverRange *mmsemver.Constraints) input.Predicate {
return func(entity *input.Entity) bool {
bundleEntity := olmentity.NewBundleEntity(entity)
bundleVersion, err := bundleEntity.Version()
bundleVersion, err := bundleEntity.VersionMasterminds()
if err != nil {
return false
}
return semverRange.Check(bundleVersion)
}
}

func InBlangSemverRange(semverRange bsemver.Range) input.Predicate {
return func(entity *input.Entity) bool {
bundleEntity := olmentity.NewBundleEntity(entity)
bundleVersion, err := bundleEntity.VersionBlang()
if err != nil {
return false
}
Expand Down
29 changes: 25 additions & 4 deletions internal/resolution/util/predicates/predicates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package predicates_test
import (
"testing"

mmsemver "github.com/Masterminds/semver/v3"
bsemver "github.com/blang/semver/v4"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -33,20 +34,40 @@ var _ = Describe("Predicates", func() {
})
})

Describe("InSemverRange", func() {
Describe("InMastermindsSemverRange", func() {
It("should return true when the entity has the has version in the right range", func() {
entity := input.NewEntity("test", map[string]string{
property.TypePackage: `{"packageName": "mypackage", "version": "1.0.0"}`,
})
inRange, err := mmsemver.NewConstraint(">=1.0.0")
Expect(err).NotTo(HaveOccurred())
notInRange, err := mmsemver.NewConstraint(">=2.0.0")
Expect(err).NotTo(HaveOccurred())
Expect(predicates.InMastermindsSemverRange(inRange)(entity)).To(BeTrue())
Expect(predicates.InMastermindsSemverRange(notInRange)(entity)).To(BeFalse())
})
It("should return false when the entity does not have a version", func() {
entity := input.NewEntity("test", map[string]string{})
inRange, err := mmsemver.NewConstraint(">=1.0.0")
Expect(err).NotTo(HaveOccurred())
Expect(predicates.InMastermindsSemverRange(inRange)(entity)).To(BeFalse())
})
})

Describe("InBlangSemverRange", func() {
It("should return true when the entity has the has version in the right range", func() {
entity := input.NewEntity("test", map[string]string{
property.TypePackage: `{"packageName": "mypackage", "version": "1.0.0"}`,
})
inRange := bsemver.MustParseRange(">=1.0.0")
notInRange := bsemver.MustParseRange(">=2.0.0")
Expect(predicates.InSemverRange(inRange)(entity)).To(BeTrue())
Expect(predicates.InSemverRange(notInRange)(entity)).To(BeFalse())
Expect(predicates.InBlangSemverRange(inRange)(entity)).To(BeTrue())
Expect(predicates.InBlangSemverRange(notInRange)(entity)).To(BeFalse())
})
It("should return false when the entity does not have a version", func() {
entity := input.NewEntity("test", map[string]string{})
inRange := bsemver.MustParseRange(">=1.0.0")
Expect(predicates.InSemverRange(inRange)(entity)).To(BeFalse())
Expect(predicates.InBlangSemverRange(inRange)(entity)).To(BeFalse())
})
})

Expand Down
4 changes: 2 additions & 2 deletions internal/resolution/util/sort/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ func channelOrder(e1, e2 *entities.BundleEntity) int {
}

func versionOrder(e1, e2 *entities.BundleEntity) int {
ver1, err1 := e1.Version()
ver2, err2 := e2.Version()
ver1, err1 := e1.VersionBlang()
ver2, err2 := e2.VersionBlang()
errComp := compareErrors(err1, err2)
if errComp != 0 {
// the sign gets inverted because version is sorted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (b *BundlesAndDepsVariableSource) getEntityDependencies(ctx context.Context
if err != nil {
return nil, err
}
packageDependencyBundles, err := entitySource.Filter(ctx, input.And(predicates.WithPackageName(requiredPackage.PackageName), predicates.InSemverRange(semverRange)))
packageDependencyBundles, err := entitySource.Filter(ctx, input.And(predicates.WithPackageName(requiredPackage.PackageName), predicates.InBlangSemverRange(semverRange)))
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions internal/resolution/variablesources/required_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"fmt"

bsemver "github.com/blang/semver/v4"
mmsemver "github.com/Masterminds/semver/v3"
"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"

Expand All @@ -21,14 +21,14 @@ type RequiredPackageVariableSourceOption func(*RequiredPackageVariableSource) er
func InVersionRange(versionRange string) RequiredPackageVariableSourceOption {
return func(r *RequiredPackageVariableSource) error {
if versionRange != "" {
vr, err := bsemver.ParseRange(versionRange)
vr, err := mmsemver.NewConstraint(versionRange)
if err == nil {
r.versionRange = versionRange
r.predicates = append(r.predicates, predicates.InSemverRange(vr))
r.predicates = append(r.predicates, predicates.InMastermindsSemverRange(vr))
return nil
}

return fmt.Errorf("invalid version range '%s': %v", versionRange, err)
return fmt.Errorf("invalid version range '%s': %w", versionRange, err)
}
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ var _ = Describe("RequiredPackageVariableSource", func() {
It("should filter by version range", func() {
// recreate source with version range option
var err error
rpvs, err = variablesources.NewRequiredPackageVariableSource(packageName, variablesources.InVersionRange(">=1.0.0 !2.0.0 <3.0.0"))
rpvs, err = variablesources.NewRequiredPackageVariableSource(packageName, variablesources.InVersionRange(">=1.0.0 !=2.0.0 <3.0.0"))
Expect(err).NotTo(HaveOccurred())

variables, err := rpvs.GetVariables(context.TODO(), mockEntitySource)
Expand Down

0 comments on commit f63044a

Please sign in to comment.