Skip to content

Commit 7a9e1af

Browse files
author
Eric Stroczynski
authored
feat(resolver): resolve compound dep constraints from bundle properties (#2418)
Signed-off-by: Eric Stroczynski <[email protected]>
1 parent 82fbb1a commit 7a9e1af

File tree

188 files changed

+46726
-56
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

188 files changed

+46726
-56
lines changed

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp
130130
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
131131
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
132132
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
133+
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e h1:GCzyKMDDjSGnlpl3clrdAK7I1AaVoaiKDOYkUzChZzg=
133134
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
134135
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
135136
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -553,6 +554,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
553554
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
554555
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
555556
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
557+
github.com/google/cel-go v0.9.0 h1:u1hg7lcZ/XWw2d3aV1jFS30ijQQ6q0/h1C2ZBeBD1gY=
556558
github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=
557559
github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=
558560
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -1075,6 +1077,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
10751077
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
10761078
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
10771079
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
1080+
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
10781081
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
10791082
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
10801083
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=

pkg/controller/registry/resolver/cache/predicates.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77

88
"github.com/blang/semver/v4"
9-
109
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
1110
)
1211

@@ -282,6 +281,32 @@ func (p orPredicate) String() string {
282281
return b.String()
283282
}
284283

284+
func Not(p ...Predicate) Predicate {
285+
return notPredicate{
286+
predicates: p,
287+
}
288+
}
289+
290+
type notPredicate struct {
291+
predicates []Predicate
292+
}
293+
294+
func (p notPredicate) Test(o *Entry) bool {
295+
// !pred && !pred is equivalent to !(pred || pred).
296+
return !orPredicate{p.predicates}.Test(o)
297+
}
298+
299+
func (p notPredicate) String() string {
300+
var b bytes.Buffer
301+
for i, predicate := range p.predicates {
302+
b.WriteString(predicate.String())
303+
if i != len(p.predicates)-1 {
304+
b.WriteString(" and not ")
305+
}
306+
}
307+
return b.String()
308+
}
309+
285310
type booleanPredicate struct {
286311
result bool
287312
}

pkg/controller/registry/resolver/resolver.go

+81-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/sirupsen/logrus"
1212
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1313

14+
"github.com/operator-framework/api/pkg/constraints"
1415
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1516
v1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
1617
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
@@ -27,12 +28,14 @@ type OperatorResolver interface {
2728
type SatResolver struct {
2829
cache cache.OperatorCacheProvider
2930
log logrus.FieldLogger
31+
pc *predicateConverter
3032
}
3133

3234
func NewDefaultSatResolver(rcp cache.SourceProvider, catsrcLister v1alpha1listers.CatalogSourceLister, logger logrus.FieldLogger) *SatResolver {
3335
return &SatResolver{
3436
cache: cache.New(rcp, cache.WithLogger(logger), cache.WithCatalogSourceLister(catsrcLister)),
3537
log: logger,
38+
pc: &predicateConverter{},
3639
}
3740
}
3841

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

338341
visited[bundle] = &bundleInstallable
339342

340-
dependencyPredicates, err := DependencyPredicates(bundle.Properties)
343+
dependencyPredicates, err := r.pc.convertDependencyProperties(bundle.Properties)
341344
if err != nil {
342345
errs = append(errs, err)
343346
continue
@@ -733,10 +736,14 @@ func sortChannel(bundles []*cache.Entry) ([]*cache.Entry, error) {
733736
return chains[0], nil
734737
}
735738

736-
func DependencyPredicates(properties []*api.Property) ([]cache.Predicate, error) {
739+
// predicateConverter configures olm.constraint value -> predicate conversion for the resolver.
740+
type predicateConverter struct{}
741+
742+
// convertDependencyProperties converts all known constraint properties to predicates.
743+
func (pc *predicateConverter) convertDependencyProperties(properties []*api.Property) ([]cache.Predicate, error) {
737744
var predicates []cache.Predicate
738745
for _, property := range properties {
739-
predicate, err := predicateForProperty(property)
746+
predicate, err := pc.predicateForProperty(property)
740747
if err != nil {
741748
return nil, err
742749
}
@@ -748,18 +755,80 @@ func DependencyPredicates(properties []*api.Property) ([]cache.Predicate, error)
748755
return predicates, nil
749756
}
750757

751-
func predicateForProperty(property *api.Property) (cache.Predicate, error) {
758+
func (pc *predicateConverter) predicateForProperty(property *api.Property) (cache.Predicate, error) {
752759
if property == nil {
753760
return nil, nil
754761
}
755-
p, ok := predicates[property.Type]
762+
763+
// olm.constraint holds all constraint types except legacy types,
764+
// so defer error handling to its parser.
765+
if property.Type == constraints.OLMConstraintType {
766+
return pc.predicateForConstraintProperty(property.Value)
767+
}
768+
769+
// Legacy behavior dictates that unknown properties are ignored. See enhancement for details:
770+
// https://github.com/operator-framework/enhancements/blob/master/enhancements/compound-bundle-constraints.md
771+
p, ok := legacyPredicateParsers[property.Type]
756772
if !ok {
757773
return nil, nil
758774
}
759775
return p(property.Value)
760776
}
761777

762-
var predicates = map[string]func(string) (cache.Predicate, error){
778+
func (pc *predicateConverter) predicateForConstraintProperty(value string) (cache.Predicate, error) {
779+
constraint, err := constraints.Parse(json.RawMessage([]byte(value)))
780+
if err != nil {
781+
return nil, fmt.Errorf("parse olm.constraint: %v", err)
782+
}
783+
784+
preds, err := pc.convertConstraints(constraint)
785+
if err != nil {
786+
return nil, fmt.Errorf("convert olm.constraint to resolver predicate: %v", err)
787+
}
788+
return preds[0], nil
789+
}
790+
791+
// convertConstraints creates predicates from each element of constraints, recursing on compound constraints.
792+
// New constraint types added to the constraints package must be handled here.
793+
func (pc *predicateConverter) convertConstraints(constraints ...constraints.Constraint) ([]cache.Predicate, error) {
794+
795+
preds := make([]cache.Predicate, len(constraints))
796+
for i, constraint := range constraints {
797+
798+
var err error
799+
switch {
800+
case constraint.GVK != nil:
801+
preds[i] = cache.ProvidingAPIPredicate(opregistry.APIKey{
802+
Group: constraint.GVK.Group,
803+
Version: constraint.GVK.Version,
804+
Kind: constraint.GVK.Kind,
805+
})
806+
case constraint.Package != nil:
807+
preds[i], err = newPackageRequiredPredicate(constraint.Package.PackageName, constraint.Package.VersionRange)
808+
case constraint.All != nil:
809+
subs, perr := pc.convertConstraints(constraint.All.Constraints...)
810+
preds[i], err = cache.And(subs...), perr
811+
case constraint.Any != nil:
812+
subs, perr := pc.convertConstraints(constraint.Any.Constraints...)
813+
preds[i], err = cache.Or(subs...), perr
814+
case constraint.None != nil:
815+
subs, perr := pc.convertConstraints(constraint.None.Constraints...)
816+
preds[i], err = cache.Not(subs...), perr
817+
default:
818+
// Unknown constraint types are handled by constraints.Parse(),
819+
// but parsed constraints may be empty.
820+
return nil, fmt.Errorf("constraint is empty")
821+
}
822+
if err != nil {
823+
return nil, err
824+
}
825+
826+
}
827+
828+
return preds, nil
829+
}
830+
831+
var legacyPredicateParsers = map[string]func(string) (cache.Predicate, error){
763832
"olm.gvk.required": predicateForRequiredGVKProperty,
764833
"olm.package.required": predicateForRequiredPackageProperty,
765834
"olm.label.required": predicateForRequiredLabelProperty,
@@ -789,11 +858,15 @@ func predicateForRequiredPackageProperty(value string) (cache.Predicate, error)
789858
if err := json.Unmarshal([]byte(value), &pkg); err != nil {
790859
return nil, err
791860
}
792-
ver, err := semver.ParseRange(pkg.VersionRange)
861+
return newPackageRequiredPredicate(pkg.PackageName, pkg.VersionRange)
862+
}
863+
864+
func newPackageRequiredPredicate(name, verRange string) (cache.Predicate, error) {
865+
ver, err := semver.ParseRange(verRange)
793866
if err != nil {
794867
return nil, err
795868
}
796-
return cache.And(cache.PkgPredicate(pkg.PackageName), cache.VersionInRangePredicate(ver, pkg.VersionRange)), nil
869+
return cache.And(cache.PkgPredicate(name), cache.VersionInRangePredicate(ver, verRange)), nil
797870
}
798871

799872
func predicateForRequiredLabelProperty(value string) (cache.Predicate, error) {

0 commit comments

Comments
 (0)