Skip to content

Commit 857c9da

Browse files
authored
Replace hardcoded resolver cache mutation with CSV cache.Source. (#2632)
Signed-off-by: Ben Luddy <[email protected]>
1 parent 7088aeb commit 857c9da

File tree

10 files changed

+890
-802
lines changed

10 files changed

+890
-802
lines changed

pkg/controller/operators/catalog/operator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
189189
}
190190
op.sources = grpc.NewSourceStore(logger, 10*time.Second, 10*time.Minute, op.syncSourceState)
191191
op.reconciler = reconciler.NewRegistryReconcilerFactory(lister, opClient, configmapRegistryImage, op.now, ssaClient)
192-
res := resolver.NewOperatorStepResolver(lister, crClient, opClient.KubernetesInterface(), operatorNamespace, op.sources, logger)
192+
res := resolver.NewOperatorStepResolver(lister, crClient, operatorNamespace, op.sources, logger)
193193
op.resolver = resolver.NewInstrumentedResolver(res, metrics.RegisterDependencyResolutionSuccess, metrics.RegisterDependencyResolutionFailure)
194194

195195
// Wire OLM CR sharedIndexInformers

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

+15-21
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ func New(sp SourceProvider, options ...Option) *Cache {
134134
}
135135

136136
type NamespacedOperatorCache struct {
137-
existing *SourceKey
138137
snapshots map[SourceKey]*snapshotHeader
139138
}
140139

@@ -236,6 +235,12 @@ func (c *Cache) Namespaced(namespaces ...string) MultiCatalogOperatorFinder {
236235
priority: c.sourcePriorityProvider.Priority(miss),
237236
}
238237

238+
if miss.Virtual() {
239+
// hack! always refresh virtual catalogs.
240+
// todo: Sources should be responsible for determining when the Snapshots they produce become invalid
241+
hdr.expiry = time.Time{}
242+
}
243+
239244
hdr.m.Lock()
240245
c.snapshots[miss] = &hdr
241246
result.snapshots[miss] = &hdr
@@ -267,31 +272,14 @@ func (c *NamespacedOperatorCache) FindPreferred(preferred *SourceKey, preferredN
267272
if preferred != nil && preferred.Empty() {
268273
preferred = nil
269274
}
270-
sorted := newSortableSnapshots(c.existing, preferred, preferredNamespace, c.snapshots)
275+
sorted := newSortableSnapshots(preferred, preferredNamespace, c.snapshots)
271276
sort.Sort(sorted)
272277
for _, snapshot := range sorted.snapshots {
273278
result = append(result, snapshot.Find(p...)...)
274279
}
275280
return result
276281
}
277282

278-
func (c *NamespacedOperatorCache) WithExistingOperators(snapshot *Snapshot, namespace string) MultiCatalogOperatorFinder {
279-
key := NewVirtualSourceKey(namespace)
280-
o := &NamespacedOperatorCache{
281-
existing: &key,
282-
snapshots: map[SourceKey]*snapshotHeader{
283-
key: {
284-
key: key,
285-
snapshot: snapshot,
286-
},
287-
},
288-
}
289-
for k, v := range c.snapshots {
290-
o.snapshots[k] = v
291-
}
292-
return o
293-
}
294-
295283
func (c *NamespacedOperatorCache) Find(p ...Predicate) []*Entry {
296284
return c.FindPreferred(nil, "", p...)
297285
}
@@ -334,7 +322,14 @@ type sortableSnapshots struct {
334322
existing *SourceKey
335323
}
336324

337-
func newSortableSnapshots(existing, preferred *SourceKey, preferredNamespace string, snapshots map[SourceKey]*snapshotHeader) sortableSnapshots {
325+
func newSortableSnapshots(preferred *SourceKey, preferredNamespace string, snapshots map[SourceKey]*snapshotHeader) sortableSnapshots {
326+
var existing *SourceKey
327+
for key := range snapshots {
328+
if key.Virtual() && key.Namespace == preferredNamespace {
329+
existing = &key
330+
break
331+
}
332+
}
338333
sorted := sortableSnapshots{
339334
existing: existing,
340335
preferred: preferred,
@@ -419,7 +414,6 @@ type OperatorFinder interface {
419414
type MultiCatalogOperatorFinder interface {
420415
Catalog(SourceKey) OperatorFinder
421416
FindPreferred(preferred *SourceKey, preferredNamespace string, predicates ...Predicate) []*Entry
422-
WithExistingOperators(snapshot *Snapshot, namespace string) MultiCatalogOperatorFinder
423417
Error() error
424418
OperatorFinder
425419
}

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

-3
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ func (i *OperatorSourceInfo) String() string {
199199
return fmt.Sprintf("%s/%s in %s/%s", i.Package, i.Channel, i.Catalog.Name, i.Catalog.Namespace)
200200
}
201201

202-
var NoCatalog = SourceKey{Name: "", Namespace: ""}
203-
var ExistingOperator = OperatorSourceInfo{Package: "", Channel: "", StartingCSV: "", Catalog: NoCatalog, DefaultChannel: false}
204-
205202
type Entry struct {
206203
Name string
207204
Replaces string

pkg/controller/registry/resolver/resolver.go

+18-172
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ import (
1414
"github.com/operator-framework/api/pkg/constraints"
1515
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1616
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
17-
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
1817
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
1918
"github.com/operator-framework/operator-registry/pkg/api"
2019
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
2120
)
2221

2322
type OperatorResolver interface {
24-
SolveOperators(csvs []*v1alpha1.ClusterServiceVersion, subs []*v1alpha1.Subscription, add map[cache.OperatorSourceInfo]struct{}) (cache.OperatorSet, error)
23+
SolveOperators(csvs []*v1alpha1.ClusterServiceVersion, add map[cache.OperatorSourceInfo]struct{}) (cache.OperatorSet, error)
2524
}
2625

2726
type SatResolver struct {
@@ -51,7 +50,7 @@ func (w *debugWriter) Write(b []byte) (int, error) {
5150
return n, nil
5251
}
5352

54-
func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.ClusterServiceVersion, subs []*v1alpha1.Subscription) (cache.OperatorSet, error) {
53+
func (r *SatResolver) SolveOperators(namespaces []string, subs []*v1alpha1.Subscription) (cache.OperatorSet, error) {
5554
var errs []error
5655

5756
variables := make(map[solver.Identifier]solver.Variable)
@@ -60,14 +59,15 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust
6059
// TODO: better abstraction
6160
startingCSVs := make(map[string]struct{})
6261

63-
// build a virtual catalog of all currently installed CSVs
64-
existingSnapshot, err := r.newSnapshotForNamespace(namespaces[0], subs, csvs)
65-
if err != nil {
66-
return nil, err
62+
namespacedCache := r.cache.Namespaced(namespaces...)
63+
64+
if len(namespaces) < 1 {
65+
// the first namespace is treated as the preferred namespace today
66+
return nil, fmt.Errorf("at least one namespace must be provided to resolution")
6767
}
68-
namespacedCache := r.cache.Namespaced(namespaces...).WithExistingOperators(existingSnapshot, namespaces[0])
6968

70-
_, existingVariables, err := r.getBundleVariables(namespaces[0], cache.Filter(existingSnapshot.Entries, cache.True()), namespacedCache, visited)
69+
preferredNamespace := namespaces[0]
70+
_, existingVariables, err := r.getBundleVariables(preferredNamespace, namespacedCache.Catalog(cache.NewVirtualSourceKey(preferredNamespace)).Find(cache.True()), namespacedCache, visited)
7171
if err != nil {
7272
return nil, err
7373
}
@@ -79,15 +79,16 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust
7979
for _, sub := range subs {
8080
// find the currently installed operator (if it exists)
8181
var current *cache.Entry
82-
for _, csv := range csvs {
83-
if csv.Name == sub.Status.InstalledCSV {
84-
op, err := newOperatorFromV1Alpha1CSV(csv)
85-
if err != nil {
86-
return nil, err
87-
}
88-
current = op
89-
break
82+
83+
matches := namespacedCache.Catalog(cache.NewVirtualSourceKey(sub.Namespace)).Find(cache.CSVNamePredicate(sub.Status.InstalledCSV))
84+
if len(matches) > 1 {
85+
var names []string
86+
for _, each := range matches {
87+
names = append(names, each.Name)
9088
}
89+
return nil, fmt.Errorf("multiple name matches for status.installedCSV of subscription %s/%s: %s", sub.Namespace, sub.Name, strings.Join(names, ", "))
90+
} else if len(matches) == 1 {
91+
current = matches[0]
9192
}
9293

9394
if current == nil && sub.Spec.StartingCSV != "" {
@@ -460,116 +461,6 @@ func (r *SatResolver) getBundleVariables(preferredNamespace string, bundleStack
460461
return ids, variables, nil
461462
}
462463

463-
func (r *SatResolver) inferProperties(csv *v1alpha1.ClusterServiceVersion, subs []*v1alpha1.Subscription) ([]*api.Property, error) {
464-
var properties []*api.Property
465-
466-
packages := make(map[string]struct{})
467-
for _, sub := range subs {
468-
if sub.Status.InstalledCSV != csv.Name {
469-
continue
470-
}
471-
// Without sanity checking the Subscription spec's
472-
// package against catalog contents, updates to the
473-
// Subscription spec could result in a bad package
474-
// inference.
475-
for _, entry := range r.cache.Namespaced(sub.Spec.CatalogSourceNamespace).Catalog(cache.SourceKey{Namespace: sub.Spec.CatalogSourceNamespace, Name: sub.Spec.CatalogSource}).Find(cache.And(cache.CSVNamePredicate(csv.Name), cache.PkgPredicate(sub.Spec.Package))) {
476-
if pkg := entry.Package(); pkg != "" {
477-
packages[pkg] = struct{}{}
478-
}
479-
}
480-
}
481-
if l := len(packages); l != 1 {
482-
r.log.Warnf("could not unambiguously infer package name for %q (found %d distinct package names)", csv.Name, l)
483-
return properties, nil
484-
}
485-
var pkg string
486-
for pkg = range packages {
487-
// Assign the single key to pkg.
488-
}
489-
var version string // Emit empty string rather than "0.0.0" if .spec.version is zero-valued.
490-
if !csv.Spec.Version.Version.Equals(semver.Version{}) {
491-
version = csv.Spec.Version.String()
492-
}
493-
pp, err := json.Marshal(opregistry.PackageProperty{
494-
PackageName: pkg,
495-
Version: version,
496-
})
497-
if err != nil {
498-
return nil, fmt.Errorf("failed to marshal inferred package property: %w", err)
499-
}
500-
properties = append(properties, &api.Property{
501-
Type: opregistry.PackageType,
502-
Value: string(pp),
503-
})
504-
505-
return properties, nil
506-
}
507-
508-
func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*cache.Snapshot, error) {
509-
existingOperatorCatalog := cache.NewVirtualSourceKey(namespace)
510-
// build a catalog snapshot of CSVs without subscriptions
511-
csvSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]*v1alpha1.Subscription)
512-
for _, sub := range subs {
513-
for _, csv := range csvs {
514-
if csv.Name == sub.Status.InstalledCSV {
515-
csvSubscriptions[csv] = sub
516-
break
517-
}
518-
}
519-
}
520-
var csvsMissingProperties []*v1alpha1.ClusterServiceVersion
521-
standaloneOperators := make([]*cache.Entry, 0)
522-
for _, csv := range csvs {
523-
op, err := newOperatorFromV1Alpha1CSV(csv)
524-
if err != nil {
525-
return nil, err
526-
}
527-
528-
if anno, ok := csv.GetAnnotations()[projection.PropertiesAnnotationKey]; !ok {
529-
csvsMissingProperties = append(csvsMissingProperties, csv)
530-
if inferred, err := r.inferProperties(csv, subs); err != nil {
531-
r.log.Warnf("unable to infer properties for csv %q: %w", csv.Name, err)
532-
} else {
533-
op.Properties = append(op.Properties, inferred...)
534-
}
535-
} else if props, err := projection.PropertyListFromPropertiesAnnotation(anno); err != nil {
536-
return nil, fmt.Errorf("failed to retrieve properties of csv %q: %w", csv.GetName(), err)
537-
} else {
538-
op.Properties = props
539-
}
540-
541-
op.SourceInfo = &cache.OperatorSourceInfo{
542-
Catalog: existingOperatorCatalog,
543-
Subscription: csvSubscriptions[csv],
544-
}
545-
// Try to determine source package name from properties and add to SourceInfo.
546-
for _, p := range op.Properties {
547-
if p.Type != opregistry.PackageType {
548-
continue
549-
}
550-
var pp opregistry.PackageProperty
551-
err := json.Unmarshal([]byte(p.Value), &pp)
552-
if err != nil {
553-
r.log.Warnf("failed to unmarshal package property of csv %q: %w", csv.Name, err)
554-
continue
555-
}
556-
op.SourceInfo.Package = pp.PackageName
557-
}
558-
559-
standaloneOperators = append(standaloneOperators, op)
560-
}
561-
562-
if len(csvsMissingProperties) > 0 {
563-
names := make([]string, len(csvsMissingProperties))
564-
for i, csv := range csvsMissingProperties {
565-
names[i] = csv.GetName()
566-
}
567-
r.log.Infof("considered csvs without properties annotation during resolution: %v", names)
568-
}
569-
570-
return &cache.Snapshot{Entries: standaloneOperators}, nil
571-
}
572-
573464
func (r *SatResolver) addInvariants(namespacedCache cache.MultiCatalogOperatorFinder, variables map[solver.Identifier]solver.Variable) {
574465
// no two operators may provide the same GVK or Package in a namespace
575466
gvkConflictToVariable := make(map[opregistry.GVKProperty][]solver.Identifier)
@@ -923,51 +814,6 @@ func predicateForRequiredLabelProperty(value string) (cache.Predicate, error) {
923814
return cache.LabelPredicate(label.Label), nil
924815
}
925816

926-
func newOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*cache.Entry, error) {
927-
providedAPIs := cache.EmptyAPISet()
928-
for _, crdDef := range csv.Spec.CustomResourceDefinitions.Owned {
929-
parts := strings.SplitN(crdDef.Name, ".", 2)
930-
if len(parts) < 2 {
931-
return nil, fmt.Errorf("error parsing crd name: %s", crdDef.Name)
932-
}
933-
providedAPIs[opregistry.APIKey{Plural: parts[0], Group: parts[1], Version: crdDef.Version, Kind: crdDef.Kind}] = struct{}{}
934-
}
935-
for _, api := range csv.Spec.APIServiceDefinitions.Owned {
936-
providedAPIs[opregistry.APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{}
937-
}
938-
939-
requiredAPIs := cache.EmptyAPISet()
940-
for _, crdDef := range csv.Spec.CustomResourceDefinitions.Required {
941-
parts := strings.SplitN(crdDef.Name, ".", 2)
942-
if len(parts) < 2 {
943-
return nil, fmt.Errorf("error parsing crd name: %s", crdDef.Name)
944-
}
945-
requiredAPIs[opregistry.APIKey{Plural: parts[0], Group: parts[1], Version: crdDef.Version, Kind: crdDef.Kind}] = struct{}{}
946-
}
947-
for _, api := range csv.Spec.APIServiceDefinitions.Required {
948-
requiredAPIs[opregistry.APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{}
949-
}
950-
951-
properties, err := providedAPIsToProperties(providedAPIs)
952-
if err != nil {
953-
return nil, err
954-
}
955-
dependencies, err := requiredAPIsToProperties(requiredAPIs)
956-
if err != nil {
957-
return nil, err
958-
}
959-
properties = append(properties, dependencies...)
960-
961-
return &cache.Entry{
962-
Name: csv.GetName(),
963-
Version: &csv.Spec.Version.Version,
964-
ProvidedAPIs: providedAPIs,
965-
RequiredAPIs: requiredAPIs,
966-
SourceInfo: &cache.ExistingOperator,
967-
Properties: properties,
968-
}, nil
969-
}
970-
971817
func providedAPIsToProperties(apis cache.APISet) (out []*api.Property, err error) {
972818
out = make([]*api.Property, 0)
973819
for a := range apis {

0 commit comments

Comments
 (0)