Skip to content

Commit

Permalink
feat(resolver): resolve compound dep constraints from bundle properti…
Browse files Browse the repository at this point in the history
…es (#2418)

Signed-off-by: Eric Stroczynski <[email protected]>
  • Loading branch information
Eric Stroczynski authored Dec 14, 2021
1 parent 82fbb1a commit 7a9e1af
Show file tree
Hide file tree
Showing 188 changed files with 46,726 additions and 56 deletions.
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e h1:GCzyKMDDjSGnlpl3clrdAK7I1AaVoaiKDOYkUzChZzg=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
Expand Down Expand Up @@ -553,6 +554,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.9.0 h1:u1hg7lcZ/XWw2d3aV1jFS30ijQQ6q0/h1C2ZBeBD1gY=
github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=
github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
Expand Down Expand Up @@ -1075,6 +1077,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
Expand Down
27 changes: 26 additions & 1 deletion pkg/controller/registry/resolver/cache/predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"

"github.com/blang/semver/v4"

opregistry "github.com/operator-framework/operator-registry/pkg/registry"
)

Expand Down Expand Up @@ -282,6 +281,32 @@ func (p orPredicate) String() string {
return b.String()
}

func Not(p ...Predicate) Predicate {
return notPredicate{
predicates: p,
}
}

type notPredicate struct {
predicates []Predicate
}

func (p notPredicate) Test(o *Entry) bool {
// !pred && !pred is equivalent to !(pred || pred).
return !orPredicate{p.predicates}.Test(o)
}

func (p notPredicate) String() string {
var b bytes.Buffer
for i, predicate := range p.predicates {
b.WriteString(predicate.String())
if i != len(p.predicates)-1 {
b.WriteString(" and not ")
}
}
return b.String()
}

type booleanPredicate struct {
result bool
}
Expand Down
89 changes: 81 additions & 8 deletions pkg/controller/registry/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/sirupsen/logrus"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/operator-framework/api/pkg/constraints"
"github.com/operator-framework/api/pkg/operators/v1alpha1"
v1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
Expand All @@ -27,12 +28,14 @@ type OperatorResolver interface {
type SatResolver struct {
cache cache.OperatorCacheProvider
log logrus.FieldLogger
pc *predicateConverter
}

func NewDefaultSatResolver(rcp cache.SourceProvider, catsrcLister v1alpha1listers.CatalogSourceLister, logger logrus.FieldLogger) *SatResolver {
return &SatResolver{
cache: cache.New(rcp, cache.WithLogger(logger), cache.WithCatalogSourceLister(catsrcLister)),
log: logger,
pc: &predicateConverter{},
}
}

Expand Down Expand Up @@ -337,7 +340,7 @@ func (r *SatResolver) getBundleInstallables(preferredNamespace string, bundleSta

visited[bundle] = &bundleInstallable

dependencyPredicates, err := DependencyPredicates(bundle.Properties)
dependencyPredicates, err := r.pc.convertDependencyProperties(bundle.Properties)
if err != nil {
errs = append(errs, err)
continue
Expand Down Expand Up @@ -733,10 +736,14 @@ func sortChannel(bundles []*cache.Entry) ([]*cache.Entry, error) {
return chains[0], nil
}

func DependencyPredicates(properties []*api.Property) ([]cache.Predicate, error) {
// predicateConverter configures olm.constraint value -> predicate conversion for the resolver.
type predicateConverter struct{}

// convertDependencyProperties converts all known constraint properties to predicates.
func (pc *predicateConverter) convertDependencyProperties(properties []*api.Property) ([]cache.Predicate, error) {
var predicates []cache.Predicate
for _, property := range properties {
predicate, err := predicateForProperty(property)
predicate, err := pc.predicateForProperty(property)
if err != nil {
return nil, err
}
Expand All @@ -748,18 +755,80 @@ func DependencyPredicates(properties []*api.Property) ([]cache.Predicate, error)
return predicates, nil
}

func predicateForProperty(property *api.Property) (cache.Predicate, error) {
func (pc *predicateConverter) predicateForProperty(property *api.Property) (cache.Predicate, error) {
if property == nil {
return nil, nil
}
p, ok := predicates[property.Type]

// olm.constraint holds all constraint types except legacy types,
// so defer error handling to its parser.
if property.Type == constraints.OLMConstraintType {
return pc.predicateForConstraintProperty(property.Value)
}

// Legacy behavior dictates that unknown properties are ignored. See enhancement for details:
// https://github.com/operator-framework/enhancements/blob/master/enhancements/compound-bundle-constraints.md
p, ok := legacyPredicateParsers[property.Type]
if !ok {
return nil, nil
}
return p(property.Value)
}

var predicates = map[string]func(string) (cache.Predicate, error){
func (pc *predicateConverter) predicateForConstraintProperty(value string) (cache.Predicate, error) {
constraint, err := constraints.Parse(json.RawMessage([]byte(value)))
if err != nil {
return nil, fmt.Errorf("parse olm.constraint: %v", err)
}

preds, err := pc.convertConstraints(constraint)
if err != nil {
return nil, fmt.Errorf("convert olm.constraint to resolver predicate: %v", err)
}
return preds[0], nil
}

// convertConstraints creates predicates from each element of constraints, recursing on compound constraints.
// New constraint types added to the constraints package must be handled here.
func (pc *predicateConverter) convertConstraints(constraints ...constraints.Constraint) ([]cache.Predicate, error) {

preds := make([]cache.Predicate, len(constraints))
for i, constraint := range constraints {

var err error
switch {
case constraint.GVK != nil:
preds[i] = cache.ProvidingAPIPredicate(opregistry.APIKey{
Group: constraint.GVK.Group,
Version: constraint.GVK.Version,
Kind: constraint.GVK.Kind,
})
case constraint.Package != nil:
preds[i], err = newPackageRequiredPredicate(constraint.Package.PackageName, constraint.Package.VersionRange)
case constraint.All != nil:
subs, perr := pc.convertConstraints(constraint.All.Constraints...)
preds[i], err = cache.And(subs...), perr
case constraint.Any != nil:
subs, perr := pc.convertConstraints(constraint.Any.Constraints...)
preds[i], err = cache.Or(subs...), perr
case constraint.None != nil:
subs, perr := pc.convertConstraints(constraint.None.Constraints...)
preds[i], err = cache.Not(subs...), perr
default:
// Unknown constraint types are handled by constraints.Parse(),
// but parsed constraints may be empty.
return nil, fmt.Errorf("constraint is empty")
}
if err != nil {
return nil, err
}

}

return preds, nil
}

var legacyPredicateParsers = map[string]func(string) (cache.Predicate, error){
"olm.gvk.required": predicateForRequiredGVKProperty,
"olm.package.required": predicateForRequiredPackageProperty,
"olm.label.required": predicateForRequiredLabelProperty,
Expand Down Expand Up @@ -789,11 +858,15 @@ func predicateForRequiredPackageProperty(value string) (cache.Predicate, error)
if err := json.Unmarshal([]byte(value), &pkg); err != nil {
return nil, err
}
ver, err := semver.ParseRange(pkg.VersionRange)
return newPackageRequiredPredicate(pkg.PackageName, pkg.VersionRange)
}

func newPackageRequiredPredicate(name, verRange string) (cache.Predicate, error) {
ver, err := semver.ParseRange(verRange)
if err != nil {
return nil, err
}
return cache.And(cache.PkgPredicate(pkg.PackageName), cache.VersionInRangePredicate(ver, pkg.VersionRange)), nil
return cache.And(cache.PkgPredicate(name), cache.VersionInRangePredicate(ver, verRange)), nil
}

func predicateForRequiredLabelProperty(value string) (cache.Predicate, error) {
Expand Down
Loading

0 comments on commit 7a9e1af

Please sign in to comment.