Skip to content

Commit

Permalink
feat(resolver): resolve compound dep constraints from bundle properties
Browse files Browse the repository at this point in the history
This is an alpha-gated feature.

Signed-off-by: Eric Stroczynski <[email protected]>
  • Loading branch information
estroz committed Oct 11, 2021
1 parent 23d84fa commit e12a395
Show file tree
Hide file tree
Showing 223 changed files with 16,043 additions and 8,516 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/onsi/gomega v1.13.0
github.com/openshift/api v0.0.0-20200331152225-585af27e34fd
github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0
github.com/operator-framework/api v0.10.3
github.com/operator-framework/api v0.10.5
github.com/operator-framework/operator-registry v1.17.5
github.com/otiai10/copy v1.2.0
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -66,3 +66,5 @@ replace (
// informers until it can be resolved upstream:
sigs.k8s.io/controller-runtime v0.9.2 => github.com/benluddy/controller-runtime v0.9.3-0.20210720171926-9bcb99bd9bd3
)

replace github.com/operator-framework/operator-registry => github.com/estroz/operator-registry v1.3.1-0.20211011195013-5ccff2185310
102 changes: 25 additions & 77 deletions go.sum

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions pkg/controller/registry/resolver/cache/predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,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
85 changes: 83 additions & 2 deletions pkg/controller/registry/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import (
"github.com/blang/semver/v4"
"github.com/sirupsen/logrus"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/component-base/featuregate"

"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"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
"github.com/operator-framework/operator-lifecycle-manager/pkg/feature"
"github.com/operator-framework/operator-registry/alpha/property"
"github.com/operator-framework/operator-registry/pkg/api"
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
)
Expand Down Expand Up @@ -733,9 +736,17 @@ func sortChannel(bundles []*cache.Entry) ([]*cache.Entry, error) {
return chains[0], nil
}

var propertyFeatureGates = map[string]featuregate.Feature{
"olm.group.required": feature.CompoundConstraintsV1Alpha1,
}

func DependencyPredicates(properties []*api.Property) ([]cache.Predicate, error) {
var predicates []cache.Predicate
for _, property := range properties {
if gate, hasGate := propertyFeatureGates[property.Type]; hasGate && !feature.Gate.Enabled(gate) {
return nil, fmt.Errorf("feature gate %q must be enabled for property %q", gate, property.Type)
}

predicate, err := predicateForProperty(property)
if err != nil {
return nil, err
Expand All @@ -752,17 +763,19 @@ func predicateForProperty(property *api.Property) (cache.Predicate, error) {
if property == nil {
return nil, nil
}
p, ok := predicates[property.Type]
p, ok := predicateParsers[property.Type]
if !ok {
return nil, nil
}
return p(property.Value)
}

var predicates = map[string]func(string) (cache.Predicate, error){
var predicateParsers = map[string]func(string) (cache.Predicate, error){
"olm.gvk.required": predicateForRequiredGVKProperty,
"olm.package.required": predicateForRequiredPackageProperty,
"olm.label.required": predicateForRequiredLabelProperty,
// Feature gate CompoundConstraintsV1Alpha1.
"olm.group.required": predicateForRequiredGroupProperty,
}

func predicateForRequiredGVKProperty(value string) (cache.Predicate, error) {
Expand Down Expand Up @@ -806,6 +819,74 @@ func predicateForRequiredLabelProperty(value string) (cache.Predicate, error) {
return cache.LabelPredicate(label.Label), nil
}

func predicateForRequiredGroupProperty(value string) (cache.Predicate, error) {
gr, err := property.ParseGroupRequired(json.RawMessage(value))
if err != nil {
return nil, err
}
return requiredGroupToPredicate(gr, 0)
}

func requiredGroupToPredicate(gr property.GroupRequired, iteration int) (sourcePredicate cache.Predicate, err error) {
if err := property.ValidateMaxDepth(iteration); err != nil {
return nil, err
}

sourcePredicate = cache.True()

for _, and := range gr.And {
sourcePredicate, err = logicalToPredicate(sourcePredicate, groupLogical(and), cache.And, iteration)
if err != nil {
return nil, err
}
}
for _, or := range gr.Or {
sourcePredicate, err = logicalToPredicate(sourcePredicate, groupLogical(or), cache.Or, iteration)
if err != nil {
return nil, err
}
}
for _, not := range gr.Not {
sourcePredicate, err = logicalToPredicate(sourcePredicate, groupLogical(not), cache.Not, iteration)
if err != nil {
return nil, err
}
}

return sourcePredicate, nil
}

type groupLogical struct {
Packages []property.PackageRequired
GVKs []property.GVKRequired
Groups []property.GroupRequired
}

func logicalToPredicate(src cache.Predicate, gl groupLogical, op func(p ...cache.Predicate) cache.Predicate, iteration int) (cache.Predicate, error) {
for _, gvk := range gl.GVKs {
src = op(src, cache.ProvidingAPIPredicate(opregistry.APIKey{
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
}))
}
for _, pkg := range gl.Packages {
ver, err := semver.ParseRange(pkg.VersionRange)
if err != nil {
return nil, err
}
src = op(src, cache.PkgPredicate(pkg.PackageName), cache.VersionInRangePredicate(ver, pkg.VersionRange))
}
for _, g := range gl.Groups {
pred, err := requiredGroupToPredicate(g, iteration+1)
if err != nil {
return nil, err
}
src = op(src, pred)
}
return src, nil
}

func newOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*cache.Entry, error) {
providedAPIs := cache.EmptyAPISet()
for _, crdDef := range csv.Spec.CustomResourceDefinitions.Owned {
Expand Down
16 changes: 10 additions & 6 deletions pkg/feature/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import (
"k8s.io/component-base/featuregate"
)

// MyFeature featuregate.Feature = "MyFeature"
// owner: @username
// alpha: v1.X
// (see https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go)
const (
// MyFeature featuregate.Feature = "MyFeature"
// owner: @username
// alpha: v1.X
// (see https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go)

// OperatorLifecycleManagerV1 enables OLM's v1 APIs.
// owner: @njhale
// alpha: v0.15.0
OperatorLifecycleManagerV1 featuregate.Feature = "OperatorLifecycleManagerV1"
// CompoundConstraintsV1Alpha1 enables executing compound dependency constraints specified in operator bundles.
// owner: @estroz
// alpha: v0.20.0
CompoundConstraintsV1Alpha1 featuregate.Feature = "CompoundConstraintsV1Alpha1"
)

var (
Expand All @@ -35,5 +38,6 @@ func AddFlag(fs *pflag.FlagSet) {
}

var featureGates = map[featuregate.Feature]featuregate.FeatureSpec{
OperatorLifecycleManagerV1: {Default: true, LockToDefault: true, PreRelease: featuregate.GA},
OperatorLifecycleManagerV1: {Default: true, LockToDefault: true, PreRelease: featuregate.GA},
CompoundConstraintsV1Alpha1: {Default: false, LockToDefault: false, PreRelease: featuregate.Alpha},
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions vendor/github.com/containerd/containerd/metadata/content.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/github.com/containerd/containerd/version/version.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e12a395

Please sign in to comment.