From 9e4a778a44abb8705fb8ef95ad0bdaaebfa0ecfc Mon Sep 17 00:00:00 2001 From: Ben Luddy Date: Wed, 1 Sep 2021 17:54:31 -0400 Subject: [PATCH] Reduce resolver dependencies on operator-registry and operator-lifecycle-manager. (#2337) * Remove resolver dependency on registry.CatalogKey. Signed-off-by: Ben Luddy * Remove hard resolver dependency on the gRPC registry client. The most significant change is the introduction of a new Source interface, which represents a single source of cache entries. In the existing implementation, the cache was hardcoded to fetch content from the operator-registry gRPC API on misses. This was a barrier to off-cluster tools and other applications that should be able to perform resolution without running a registry server. A number of test fixtures were affected (for the better) because it's now possible to directly configure the real Cache implementation to retrieve test content without using of test doubles. Signed-off-by: Ben Luddy --- pkg/controller/operators/catalog/operator.go | 3 +- .../registry/resolver/cache/cache.go | 403 +++++---- .../registry/resolver/cache/cache_test.go | 290 +++--- .../registry/resolver/cache/operators.go | 7 +- .../registry/resolver/cache/operators_test.go | 27 +- .../registry/resolver/cache/predicates.go | 5 +- .../registry/resolver/installabletypes.go | 9 +- .../resolver/instrumented_resolver.go | 5 +- .../resolver/instrumented_resolver_test.go | 7 +- pkg/controller/registry/resolver/resolver.go | 38 +- .../registry/resolver/resolver_test.go | 837 ++++++++---------- .../registry/resolver/source_registry.go | 109 +++ .../registry/resolver/step_resolver.go | 11 +- .../registry/resolver/step_resolver_test.go | 113 ++- pkg/fakes/fake_resolver.go | 14 +- 15 files changed, 880 insertions(+), 998 deletions(-) create mode 100644 pkg/controller/registry/resolver/source_registry.go diff --git a/pkg/controller/operators/catalog/operator.go b/pkg/controller/operators/catalog/operator.go index c91029a3a7..e83e77c282 100644 --- a/pkg/controller/operators/catalog/operator.go +++ b/pkg/controller/operators/catalog/operator.go @@ -55,6 +55,7 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" + resolvercache "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/catalogsource" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients" @@ -463,7 +464,7 @@ func (o *Operator) syncSourceState(state grpc.SourceState) { switch state.State { case connectivity.Ready: - o.resolver.Expire(state.Key) + o.resolver.Expire(resolvercache.SourceKey(state.Key)) if o.namespace == state.Key.Namespace { namespaces, err := index.CatalogSubscriberNamespaces(o.catalogSubscriberIndexer, state.Key.Name, state.Key.Namespace) diff --git a/pkg/controller/registry/resolver/cache/cache.go b/pkg/controller/registry/resolver/cache/cache.go index c26f30d434..10ca6d07d8 100644 --- a/pkg/controller/registry/resolver/cache/cache.go +++ b/pkg/controller/registry/resolver/cache/cache.go @@ -2,88 +2,140 @@ package cache import ( "context" - "encoding/json" "fmt" + "io" "sort" "sync" "time" - "k8s.io/apimachinery/pkg/util/errors" - "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/util/errors" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" - "github.com/operator-framework/operator-registry/pkg/api" - "github.com/operator-framework/operator-registry/pkg/client" - opregistry "github.com/operator-framework/operator-registry/pkg/registry" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" ) -type RegistryClientProvider interface { - ClientsForNamespaces(namespaces ...string) map[registry.CatalogKey]client.Interface +const existingOperatorKey = "@existing" + +type SourceKey struct { + Name string + Namespace string } -type DefaultRegistryClientProvider struct { - logger logrus.FieldLogger - s RegistryClientProvider +func (k *SourceKey) String() string { + return fmt.Sprintf("%s/%s", k.Name, k.Namespace) } -func NewDefaultRegistryClientProvider(log logrus.FieldLogger, store RegistryClientProvider) *DefaultRegistryClientProvider { - return &DefaultRegistryClientProvider{ - logger: log, - s: store, +func (k *SourceKey) Empty() bool { + return k.Name == "" && k.Namespace == "" +} + +func (k *SourceKey) Equal(compare SourceKey) bool { + return k.Name == compare.Name && k.Namespace == compare.Namespace +} + +// Virtual indicates if this is a "virtual" catalog representing the currently installed operators in a namespace +func (k *SourceKey) Virtual() bool { + return k.Name == existingOperatorKey && k.Namespace != "" +} + +func NewVirtualSourceKey(namespace string) SourceKey { + return SourceKey{ + Name: existingOperatorKey, + Namespace: namespace, } } -func (rcp *DefaultRegistryClientProvider) ClientsForNamespaces(namespaces ...string) map[registry.CatalogKey]client.Interface { - return rcp.s.ClientsForNamespaces(namespaces...) +type Source interface { + Snapshot(context.Context) (*Snapshot, error) +} + +type SourceProvider interface { + // TODO: namespaces parameter is an artifact of SourceStore + Sources(namespaces ...string) map[SourceKey]Source +} + +type StaticSourceProvider map[SourceKey]Source + +func (p StaticSourceProvider) Sources(namespaces ...string) map[SourceKey]Source { + result := make(map[SourceKey]Source) + for key, source := range p { + for _, namespace := range namespaces { + if key.Namespace == namespace { + result[key] = source + break + } + } + } + return result } type OperatorCacheProvider interface { Namespaced(namespaces ...string) MultiCatalogOperatorFinder - Expire(catalog registry.CatalogKey) + Expire(catalog SourceKey) } -type OperatorCache struct { - logger logrus.FieldLogger - rcp RegistryClientProvider +type Cache struct { + logger logrus.StdLogger + sp SourceProvider catsrcLister v1alpha1.CatalogSourceLister - snapshots map[registry.CatalogKey]*CatalogSnapshot + snapshots map[SourceKey]*snapshotHeader ttl time.Duration sem chan struct{} m sync.RWMutex } -const defaultCatalogSourcePriority int = 0 - type catalogSourcePriority int -var _ OperatorCacheProvider = &OperatorCache{} +var _ OperatorCacheProvider = &Cache{} + +type Option func(*Cache) + +func WithLogger(logger logrus.StdLogger) Option { + return func(c *Cache) { + c.logger = logger + } +} + +func WithCatalogSourceLister(catalogSourceLister v1alpha1.CatalogSourceLister) Option { + return func(c *Cache) { + c.catsrcLister = catalogSourceLister + } +} -func NewOperatorCache(rcp RegistryClientProvider, log logrus.FieldLogger, catsrcLister v1alpha1.CatalogSourceLister) *OperatorCache { +func New(sp SourceProvider, options ...Option) *Cache { const ( MaxConcurrentSnapshotUpdates = 4 ) - return &OperatorCache{ - logger: log, - rcp: rcp, - catsrcLister: catsrcLister, - snapshots: make(map[registry.CatalogKey]*CatalogSnapshot), + cache := Cache{ + logger: func() logrus.StdLogger { + logger := logrus.New() + logger.SetOutput(io.Discard) + return logger + }(), + sp: sp, + catsrcLister: operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister(), + snapshots: make(map[SourceKey]*snapshotHeader), ttl: 5 * time.Minute, sem: make(chan struct{}, MaxConcurrentSnapshotUpdates), } + + for _, opt := range options { + opt(&cache) + } + + return &cache } type NamespacedOperatorCache struct { - Namespaces []string - existing *registry.CatalogKey - Snapshots map[registry.CatalogKey]*CatalogSnapshot + existing *SourceKey + snapshots map[SourceKey]*snapshotHeader } func (c *NamespacedOperatorCache) Error() error { var errs []error - for key, snapshot := range c.Snapshots { + for key, snapshot := range c.snapshots { snapshot.m.Lock() err := snapshot.err snapshot.m.Unlock() @@ -94,7 +146,7 @@ func (c *NamespacedOperatorCache) Error() error { return errors.NewAggregate(errs) } -func (c *OperatorCache) Expire(catalog registry.CatalogKey) { +func (c *Cache) Expire(catalog SourceKey) { c.m.Lock() defer c.m.Unlock() s, ok := c.snapshots[catalog] @@ -104,31 +156,30 @@ func (c *OperatorCache) Expire(catalog registry.CatalogKey) { s.expiry = time.Unix(0, 0) } -func (c *OperatorCache) Namespaced(namespaces ...string) MultiCatalogOperatorFinder { +func (c *Cache) Namespaced(namespaces ...string) MultiCatalogOperatorFinder { const ( CachePopulateTimeout = time.Minute ) now := time.Now() - clients := c.rcp.ClientsForNamespaces(namespaces...) + sources := c.sp.Sources(namespaces...) result := NamespacedOperatorCache{ - Namespaces: namespaces, - Snapshots: make(map[registry.CatalogKey]*CatalogSnapshot), + snapshots: make(map[SourceKey]*snapshotHeader), } - var misses []registry.CatalogKey + var misses []SourceKey func() { c.m.RLock() defer c.m.RUnlock() - for key := range clients { + for key := range sources { snapshot, ok := c.snapshots[key] if ok { func() { snapshot.m.RLock() defer snapshot.m.RUnlock() - if !snapshot.Expired(now) && snapshot.Operators != nil && len(snapshot.Operators) > 0 { - result.Snapshots[key] = snapshot + if snapshot.Valid(now) { + result.snapshots[key] = snapshot } else { misses = append(misses, key) } @@ -148,9 +199,9 @@ func (c *OperatorCache) Namespaced(namespaces ...string) MultiCatalogOperatorFin defer c.m.Unlock() // Take the opportunity to clear expired snapshots while holding the lock. - var expired []registry.CatalogKey + var expired []SourceKey for key, snapshot := range c.snapshots { - if snapshot.Expired(now) { + if !snapshot.Valid(now) { snapshot.Cancel() expired = append(expired, key) } @@ -162,8 +213,8 @@ func (c *OperatorCache) Namespaced(namespaces ...string) MultiCatalogOperatorFin // Check for any snapshots that were populated while waiting to acquire the lock. var found int for i := range misses { - if snapshot, ok := c.snapshots[misses[i]]; ok && !snapshot.Expired(now) && snapshot.Operators != nil && len(snapshot.Operators) > 0 { - result.Snapshots[misses[i]] = snapshot + if hdr, ok := c.snapshots[misses[i]]; ok && hdr.Valid(now) { + result.snapshots[misses[i]] = hdr misses[found], misses[i] = misses[i], misses[found] found++ } @@ -173,120 +224,49 @@ func (c *OperatorCache) Namespaced(namespaces ...string) MultiCatalogOperatorFin for _, miss := range misses { ctx, cancel := context.WithTimeout(context.Background(), CachePopulateTimeout) - catsrcPriority := defaultCatalogSourcePriority - // Ignoring error and treat catsrc priority as 0 if not found. - catsrc, err := c.catsrcLister.CatalogSources(miss.Namespace).Get(miss.Name) - if err == nil { - catsrcPriority = catsrc.Spec.Priority - } - - s := CatalogSnapshot{ - logger: c.logger.WithField("catalog", miss), - Key: miss, - expiry: now.Add(c.ttl), - pop: cancel, - Priority: catalogSourcePriority(catsrcPriority), + hdr := snapshotHeader{ + key: miss, + expiry: now.Add(c.ttl), + pop: cancel, } - s.m.Lock() - c.snapshots[miss] = &s - result.Snapshots[miss] = &s - go c.populate(ctx, &s, clients[miss]) - } - - return &result -} -func (c *OperatorCache) populate(ctx context.Context, snapshot *CatalogSnapshot, registry client.Interface) { - defer snapshot.m.Unlock() - defer func() { - // Don't cache an errorred snapshot. - if snapshot.err != nil { - snapshot.expiry = time.Time{} + // Ignoring error and treat catsrc priority as 0 if not found. + if catsrc, _ := c.catsrcLister.CatalogSources(miss.Namespace).Get(miss.Name); catsrc != nil { + hdr.priority = catsrc.Spec.Priority } - }() - c.sem <- struct{}{} - defer func() { <-c.sem }() + hdr.m.Lock() + c.snapshots[miss] = &hdr + result.snapshots[miss] = &hdr - // Fetching default channels this way makes many round trips - // -- may need to either add a new API to fetch all at once, - // or embed the information into Bundle. - defaultChannels := make(map[string]string) - - it, err := registry.ListBundles(ctx) - if err != nil { - snapshot.logger.Errorf("failed to list bundles: %s", err.Error()) - snapshot.err = err - return + go func(ctx context.Context, hdr *snapshotHeader, source Source) { + defer hdr.m.Unlock() + c.sem <- struct{}{} + defer func() { <-c.sem }() + hdr.snapshot, hdr.err = source.Snapshot(ctx) + }(ctx, &hdr, sources[miss]) } - c.logger.WithField("catalog", snapshot.Key.String()).Debug("updating cache") - var operators []*Operator - for b := it.Next(); b != nil; b = it.Next() { - defaultChannel, ok := defaultChannels[b.PackageName] - if !ok { - if p, err := registry.GetPackage(ctx, b.PackageName); err != nil { - snapshot.logger.Warnf("failed to retrieve default channel for bundle, continuing: %v", err) - continue - } else { - defaultChannels[b.PackageName] = p.DefaultChannelName - defaultChannel = p.DefaultChannelName - } - } - o, err := NewOperatorFromBundle(b, "", snapshot.Key, defaultChannel) - if err != nil { - snapshot.logger.Warnf("failed to construct operator from bundle, continuing: %v", err) - continue - } - o.ProvidedAPIs = o.ProvidedAPIs.StripPlural() - o.RequiredAPIs = o.RequiredAPIs.StripPlural() - o.Replaces = b.Replaces - EnsurePackageProperty(o, b.PackageName, b.Version) - operators = append(operators, o) - } - if err := it.Error(); err != nil { - snapshot.logger.Warnf("error encountered while listing bundles: %s", err.Error()) - snapshot.err = err - } - snapshot.Operators = operators -} -func EnsurePackageProperty(o *Operator, name, version string) { - for _, p := range o.Properties { - if p.Type == opregistry.PackageType { - return - } - } - prop := opregistry.PackageProperty{ - PackageName: name, - Version: version, - } - bytes, err := json.Marshal(prop) - if err != nil { - return - } - o.Properties = append(o.Properties, &api.Property{ - Type: opregistry.PackageType, - Value: string(bytes), - }) + return &result } -func (c *NamespacedOperatorCache) Catalog(k registry.CatalogKey) OperatorFinder { +func (c *NamespacedOperatorCache) Catalog(k SourceKey) OperatorFinder { // all catalogs match the empty catalog if k.Empty() { return c } - if snapshot, ok := c.Snapshots[k]; ok { + if snapshot, ok := c.snapshots[k]; ok { return snapshot } return EmptyOperatorFinder{} } -func (c *NamespacedOperatorCache) FindPreferred(preferred *registry.CatalogKey, p ...OperatorPredicate) []*Operator { +func (c *NamespacedOperatorCache) FindPreferred(preferred *SourceKey, preferredNamespace string, p ...OperatorPredicate) []*Operator { var result []*Operator if preferred != nil && preferred.Empty() { preferred = nil } - sorted := NewSortableSnapshots(c.existing, preferred, c.Namespaces, c.Snapshots) + sorted := newSortableSnapshots(c.existing, preferred, preferredNamespace, c.snapshots) sort.Sort(sorted) for _, snapshot := range sorted.snapshots { result = append(result, snapshot.Find(p...)...) @@ -294,65 +274,71 @@ func (c *NamespacedOperatorCache) FindPreferred(preferred *registry.CatalogKey, return result } -func (c *NamespacedOperatorCache) WithExistingOperators(snapshot *CatalogSnapshot) MultiCatalogOperatorFinder { +func (c *NamespacedOperatorCache) WithExistingOperators(snapshot *Snapshot, namespace string) MultiCatalogOperatorFinder { + key := NewVirtualSourceKey(namespace) o := &NamespacedOperatorCache{ - Namespaces: c.Namespaces, - existing: &snapshot.Key, - Snapshots: c.Snapshots, + existing: &key, + snapshots: map[SourceKey]*snapshotHeader{ + key: { + key: key, + snapshot: snapshot, + }, + }, + } + for k, v := range c.snapshots { + o.snapshots[k] = v } - o.Snapshots[snapshot.Key] = snapshot return o } func (c *NamespacedOperatorCache) Find(p ...OperatorPredicate) []*Operator { - return c.FindPreferred(nil, p...) + return c.FindPreferred(nil, "", p...) +} + +type Snapshot struct { + Entries []*Operator } -type CatalogSnapshot struct { - logger logrus.FieldLogger - Key registry.CatalogKey - expiry time.Time - Operators []*Operator - m sync.RWMutex - pop context.CancelFunc - Priority catalogSourcePriority - err error +var _ Source = &Snapshot{} + +func (s *Snapshot) Snapshot(context.Context) (*Snapshot, error) { + return s, nil } -func (s *CatalogSnapshot) Cancel() { - s.pop() +type snapshotHeader struct { + snapshot *Snapshot + + key SourceKey + expiry time.Time + m sync.RWMutex + pop context.CancelFunc + err error + priority int } -func (s *CatalogSnapshot) Expired(at time.Time) bool { - return !at.Before(s.expiry) +func (hdr *snapshotHeader) Cancel() { + hdr.pop() } -// NewRunningOperatorSnapshot creates a CatalogSnapshot that represents a set of existing installed operators -// in the cluster. -func NewRunningOperatorSnapshot(logger logrus.FieldLogger, key registry.CatalogKey, o []*Operator) *CatalogSnapshot { - return &CatalogSnapshot{ - logger: logger, - Key: key, - Operators: o, - } +func (hdr *snapshotHeader) Valid(at time.Time) bool { + hdr.m.RLock() + defer hdr.m.RUnlock() + return hdr.snapshot != nil && hdr.err == nil && at.Before(hdr.expiry) } -type SortableSnapshots struct { - snapshots []*CatalogSnapshot - namespaces map[string]int - preferred *registry.CatalogKey - existing *registry.CatalogKey +type sortableSnapshots struct { + snapshots []*snapshotHeader + preferredNamespace string + preferred *SourceKey + existing *SourceKey } -func NewSortableSnapshots(existing, preferred *registry.CatalogKey, namespaces []string, snapshots map[registry.CatalogKey]*CatalogSnapshot) SortableSnapshots { - sorted := SortableSnapshots{ - existing: existing, - preferred: preferred, - snapshots: make([]*CatalogSnapshot, 0), - namespaces: make(map[string]int, 0), - } - for i, n := range namespaces { - sorted.namespaces[n] = i +func newSortableSnapshots(existing, preferred *SourceKey, preferredNamespace string, snapshots map[SourceKey]*snapshotHeader) sortableSnapshots { + sorted := sortableSnapshots{ + existing: existing, + preferred: preferred, + snapshots: make([]*snapshotHeader, 0), + preferredNamespace: preferredNamespace, } for _, s := range snapshots { sorted.snapshots = append(sorted.snapshots, s) @@ -360,60 +346,69 @@ func NewSortableSnapshots(existing, preferred *registry.CatalogKey, namespaces [ return sorted } -var _ sort.Interface = SortableSnapshots{} +var _ sort.Interface = sortableSnapshots{} // Len is the number of elements in the collection. -func (s SortableSnapshots) Len() int { +func (s sortableSnapshots) Len() int { return len(s.snapshots) } // Less reports whether the element with // index i should sort before the element with index j. -func (s SortableSnapshots) Less(i, j int) bool { +func (s sortableSnapshots) Less(i, j int) bool { // existing operators are preferred over catalog operators if s.existing != nil && - s.snapshots[i].Key.Name == s.existing.Name && - s.snapshots[i].Key.Namespace == s.existing.Namespace { + s.snapshots[i].key.Name == s.existing.Name && + s.snapshots[i].key.Namespace == s.existing.Namespace { return true } if s.existing != nil && - s.snapshots[j].Key.Name == s.existing.Name && - s.snapshots[j].Key.Namespace == s.existing.Namespace { + s.snapshots[j].key.Name == s.existing.Name && + s.snapshots[j].key.Namespace == s.existing.Namespace { return false } // preferred catalog is less than all other catalogs if s.preferred != nil && - s.snapshots[i].Key.Name == s.preferred.Name && - s.snapshots[i].Key.Namespace == s.preferred.Namespace { + s.snapshots[i].key.Name == s.preferred.Name && + s.snapshots[i].key.Namespace == s.preferred.Namespace { return true } if s.preferred != nil && - s.snapshots[j].Key.Name == s.preferred.Name && - s.snapshots[j].Key.Namespace == s.preferred.Namespace { + s.snapshots[j].key.Name == s.preferred.Name && + s.snapshots[j].key.Namespace == s.preferred.Namespace { return false } // the rest are sorted first on priority, namespace and then by name - if s.snapshots[i].Priority != s.snapshots[j].Priority { - return s.snapshots[i].Priority > s.snapshots[j].Priority + if s.snapshots[i].priority != s.snapshots[j].priority { + return s.snapshots[i].priority > s.snapshots[j].priority } - if s.snapshots[i].Key.Namespace != s.snapshots[j].Key.Namespace { - return s.namespaces[s.snapshots[i].Key.Namespace] < s.namespaces[s.snapshots[j].Key.Namespace] + + if s.snapshots[i].key.Namespace != s.snapshots[j].key.Namespace { + if s.snapshots[i].key.Namespace == s.preferredNamespace { + return true + } + if s.snapshots[j].key.Namespace == s.preferredNamespace { + return false + } } - return s.snapshots[i].Key.Name < s.snapshots[j].Key.Name + return s.snapshots[i].key.Name < s.snapshots[j].key.Name } // Swap swaps the elements with indexes i and j. -func (s SortableSnapshots) Swap(i, j int) { +func (s sortableSnapshots) Swap(i, j int) { s.snapshots[i], s.snapshots[j] = s.snapshots[j], s.snapshots[i] } -func (s *CatalogSnapshot) Find(p ...OperatorPredicate) []*Operator { +func (s *snapshotHeader) Find(p ...OperatorPredicate) []*Operator { s.m.RLock() defer s.m.RUnlock() - return Filter(s.Operators, p...) + if s.snapshot == nil { + return nil + } + return Filter(s.snapshot.Entries, p...) } type OperatorFinder interface { @@ -421,9 +416,9 @@ type OperatorFinder interface { } type MultiCatalogOperatorFinder interface { - Catalog(registry.CatalogKey) OperatorFinder - FindPreferred(*registry.CatalogKey, ...OperatorPredicate) []*Operator - WithExistingOperators(*CatalogSnapshot) MultiCatalogOperatorFinder + Catalog(SourceKey) OperatorFinder + FindPreferred(preferred *SourceKey, preferredNamespace string, predicates ...OperatorPredicate) []*Operator + WithExistingOperators(snapshot *Snapshot, namespace string) MultiCatalogOperatorFinder Error() error OperatorFinder } diff --git a/pkg/controller/registry/resolver/cache/cache_test.go b/pkg/controller/registry/resolver/cache/cache_test.go index ecb4f14b24..f095456456 100644 --- a/pkg/controller/registry/resolver/cache/cache_test.go +++ b/pkg/controller/registry/resolver/cache/cache_test.go @@ -4,113 +4,37 @@ import ( "context" "errors" "fmt" - "io" "math/rand" "strconv" "testing" "time" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" - "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" "github.com/operator-framework/operator-registry/pkg/api" - "github.com/operator-framework/operator-registry/pkg/client" - opregistry "github.com/operator-framework/operator-registry/pkg/registry" ) -type BundleStreamStub struct { - Bundles []*api.Bundle -} - -func (s *BundleStreamStub) Recv() (*api.Bundle, error) { - if len(s.Bundles) == 0 { - return nil, io.EOF - } - b := s.Bundles[0] - s.Bundles = s.Bundles[1:] - return b, nil -} - -type RegistryClientStub struct { - BundleIterator *client.BundleIterator - - ListBundlesError error -} - -func (s *RegistryClientStub) Get() (client.Interface, error) { - return s, nil -} - -func (s *RegistryClientStub) GetBundle(ctx context.Context, packageName, channelName, csvName string) (*api.Bundle, error) { - return nil, nil -} - -func (s *RegistryClientStub) GetBundleInPackageChannel(ctx context.Context, packageName, channelName string) (*api.Bundle, error) { - return nil, nil -} - -func (s *RegistryClientStub) GetReplacementBundleInPackageChannel(ctx context.Context, currentName, packageName, channelName string) (*api.Bundle, error) { - return nil, nil -} - -func (s *RegistryClientStub) GetBundleThatProvides(ctx context.Context, group, version, kind string) (*api.Bundle, error) { - return nil, nil -} - -func (s *RegistryClientStub) ListBundles(ctx context.Context) (*client.BundleIterator, error) { - return s.BundleIterator, s.ListBundlesError -} - -func (s *RegistryClientStub) GetPackage(ctx context.Context, packageName string) (*api.Package, error) { - return &api.Package{Name: packageName}, nil -} - -func (s *RegistryClientStub) HealthCheck(ctx context.Context, reconnectTimeout time.Duration) (bool, error) { - return false, nil -} - -func (s *RegistryClientStub) Close() error { - return nil -} - -type RegistryClientProviderStub map[registry.CatalogKey]client.Interface - -func (s RegistryClientProviderStub) ClientsForNamespaces(namespaces ...string) map[registry.CatalogKey]client.Interface { - return s -} - func TestOperatorCacheConcurrency(t *testing.T) { const ( NWorkers = 64 ) - rcp := RegistryClientProviderStub{} - catsrcLister := operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister() - var keys []registry.CatalogKey + + sp := make(StaticSourceProvider) + var keys []SourceKey for i := 0; i < 128; i++ { for j := 0; j < 8; j++ { - key := registry.CatalogKey{Namespace: strconv.Itoa(i), Name: strconv.Itoa(j)} + key := SourceKey{Namespace: strconv.Itoa(i), Name: strconv.Itoa(j)} keys = append(keys, key) - rcp[key] = &RegistryClientStub{ - BundleIterator: client.NewBundleIterator(&BundleStreamStub{ - Bundles: []*api.Bundle{{ - CsvName: fmt.Sprintf("%s/%s", key.Namespace, key.Name), - ProvidedApis: []*api.GroupVersionKind{{ - Group: "g", - Version: "v1", - Kind: "K", - Plural: "ks", - }}, - }}, - }), + sp[key] = &Snapshot{ + Entries: []*Operator{ + {Name: fmt.Sprintf("%s/%s", key.Namespace, key.Name)}, + }, } } } - c := NewOperatorCache(rcp, logrus.New(), catsrcLister) + c := New(sp) errs := make(chan error) for w := 0; w < NWorkers; w++ { @@ -143,56 +67,52 @@ func TestOperatorCacheConcurrency(t *testing.T) { } func TestOperatorCacheExpiration(t *testing.T) { - rcp := RegistryClientProviderStub{} - catsrcLister := operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister() - key := registry.CatalogKey{Namespace: "dummynamespace", Name: "dummyname"} - rcp[key] = &RegistryClientStub{ - BundleIterator: client.NewBundleIterator(&BundleStreamStub{ - Bundles: []*api.Bundle{{ - CsvName: "csvname", - ProvidedApis: []*api.GroupVersionKind{{ - Group: "g", - Version: "v1", - Kind: "K", - Plural: "ks", - }}, - }}, - }), - } - - c := NewOperatorCache(rcp, logrus.New(), catsrcLister) + key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} + ssp := make(StaticSourceProvider) + c := New(ssp) c.ttl = 0 // instantly stale - require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("csvname")), 1) + ssp[key] = &Snapshot{ + Entries: []*Operator{ + {Name: "v1"}, + }, + } + require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) + + ssp[key] = &Snapshot{ + Entries: []*Operator{ + {Name: "v2"}, + }, + } + require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 0) } func TestOperatorCacheReuse(t *testing.T) { - rcp := RegistryClientProviderStub{} - catsrcLister := operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister() - key := registry.CatalogKey{Namespace: "dummynamespace", Name: "dummyname"} - rcp[key] = &RegistryClientStub{ - BundleIterator: client.NewBundleIterator(&BundleStreamStub{ - Bundles: []*api.Bundle{{ - CsvName: "csvname", - ProvidedApis: []*api.GroupVersionKind{{ - Group: "g", - Version: "v1", - Kind: "K", - Plural: "ks", - }}, - }}, - }), - } + key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} + ssp := make(StaticSourceProvider) + c := New(ssp) - c := NewOperatorCache(rcp, logrus.New(), catsrcLister) + ssp[key] = &Snapshot{ + Entries: []*Operator{ + {Name: "v1"}, + }, + } + require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) - require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("csvname")), 1) + ssp[key] = &Snapshot{ + Entries: []*Operator{ + {Name: "v2"}, + }, + } + require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) } -func TestCatalogSnapshotExpired(t *testing.T) { +func TestCatalogSnapshotValid(t *testing.T) { type tc struct { Name string Expiry time.Time + Snapshot *Snapshot + Error error At time.Time Expected bool } @@ -201,25 +121,51 @@ func TestCatalogSnapshotExpired(t *testing.T) { { Name: "after expiry", Expiry: time.Unix(0, 1), + Snapshot: &Snapshot{}, + Error: nil, At: time.Unix(0, 2), - Expected: true, + Expected: false, }, { Name: "before expiry", Expiry: time.Unix(0, 2), + Snapshot: &Snapshot{}, + Error: nil, + At: time.Unix(0, 1), + Expected: true, + }, + { + Name: "nil snapshot", + Expiry: time.Unix(0, 2), + Snapshot: nil, + Error: errors.New(""), + At: time.Unix(0, 1), + Expected: false, + }, + { + Name: "non-nil error", + Expiry: time.Unix(0, 2), + Snapshot: &Snapshot{}, + Error: errors.New(""), At: time.Unix(0, 1), Expected: false, }, { Name: "at expiry", Expiry: time.Unix(0, 1), + Snapshot: &Snapshot{}, + Error: nil, At: time.Unix(0, 1), - Expected: true, + Expected: false, }, } { t.Run(tt.Name, func(t *testing.T) { - s := CatalogSnapshot{expiry: tt.Expiry} - assert.Equal(t, tt.Expected, s.Expired(tt.At)) + s := snapshotHeader{ + expiry: tt.Expiry, + snapshot: tt.Snapshot, + err: tt.Error, + } + assert.Equal(t, tt.Expected, s.Valid(tt.At)) }) } @@ -287,7 +233,7 @@ func TestCatalogSnapshotFind(t *testing.T) { }, } { t.Run(tt.Name, func(t *testing.T) { - s := CatalogSnapshot{Operators: tt.Operators} + s := snapshotHeader{snapshot: &Snapshot{Entries: tt.Operators}} assert.Equal(t, tt.Expected, s.Find(tt.Predicate)) }) } @@ -295,69 +241,41 @@ func TestCatalogSnapshotFind(t *testing.T) { } func TestStripPluralRequiredAndProvidedAPIKeys(t *testing.T) { - rcp := RegistryClientProviderStub{} - catsrcLister := operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister() - key := registry.CatalogKey{Namespace: "testnamespace", Name: "testname"} - rcp[key] = &RegistryClientStub{ - BundleIterator: client.NewBundleIterator(&BundleStreamStub{ - Bundles: []*api.Bundle{{ - CsvName: fmt.Sprintf("%s/%s", key.Namespace, key.Name), - ProvidedApis: []*api.GroupVersionKind{{ - Group: "g", - Version: "v1", - Kind: "K", - Plural: "ks", - }}, - RequiredApis: []*api.GroupVersionKind{{ - Group: "g2", - Version: "v2", - Kind: "K2", - Plural: "ks2", - }}, - Properties: APISetToProperties(map[opregistry.APIKey]struct{}{ - { - Group: "g", - Version: "v1", - Kind: "K", - Plural: "ks", - }: {}, - }, nil, false), - Dependencies: APISetToDependencies(map[opregistry.APIKey]struct{}{ - { - Group: "g2", - Version: "v2", - Kind: "K2", - Plural: "ks2", - }: {}, - }, nil), - }}, - }), - } - - c := NewOperatorCache(rcp, logrus.New(), catsrcLister) + key := SourceKey{Namespace: "testnamespace", Name: "testname"} + o, err := NewOperatorFromBundle(&api.Bundle{ + CsvName: fmt.Sprintf("%s/%s", key.Namespace, key.Name), + ProvidedApis: []*api.GroupVersionKind{{ + Group: "g", + Version: "v1", + Kind: "K", + Plural: "ks", + }}, + RequiredApis: []*api.GroupVersionKind{{ + Group: "g2", + Version: "v2", + Kind: "K2", + Plural: "ks2", + }}, + }, "", key, "") - nc := c.Namespaced("testnamespace") - result, err := AtLeast(1, nc.Find(ProvidingAPIPredicate(opregistry.APIKey{Group: "g", Version: "v1", Kind: "K"}))) assert.NoError(t, err) - assert.Equal(t, 1, len(result)) - assert.Equal(t, "K.v1.g", result[0].ProvidedAPIs.String()) - assert.Equal(t, "K2.v2.g2", result[0].RequiredAPIs.String()) + assert.Equal(t, "K.v1.g", o.ProvidedAPIs.String()) + assert.Equal(t, "K2.v2.g2", o.RequiredAPIs.String()) +} + +type ErrorSource struct { + Error error +} + +func (s ErrorSource) Snapshot(context.Context) (*Snapshot, error) { + return nil, s.Error } func TestNamespaceOperatorCacheError(t *testing.T) { - rcp := RegistryClientProviderStub{} - catsrcLister := operatorlister.NewLister().OperatorsV1alpha1().CatalogSourceLister() - key := registry.CatalogKey{Namespace: "dummynamespace", Name: "dummyname"} - rcp[key] = &RegistryClientStub{ - ListBundlesError: errors.New("testing"), - } + key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} + c := New(StaticSourceProvider{ + key: ErrorSource{Error: errors.New("testing")}, + }) - logger, _ := test.NewNullLogger() - c := NewOperatorCache(rcp, logger, catsrcLister) require.EqualError(t, c.Namespaced("dummynamespace").Error(), "error using catalog dummyname (in namespace dummynamespace): testing") - if snapshot, ok := c.snapshots[key]; !ok { - t.Fatalf("cache snapshot not found") - } else { - require.Zero(t, snapshot.expiry) - } } diff --git a/pkg/controller/registry/resolver/cache/operators.go b/pkg/controller/registry/resolver/cache/operators.go index 1df76b270d..1c43077935 100644 --- a/pkg/controller/registry/resolver/cache/operators.go +++ b/pkg/controller/registry/resolver/cache/operators.go @@ -11,7 +11,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-registry/pkg/api" opregistry "github.com/operator-framework/operator-registry/pkg/registry" ) @@ -194,7 +193,7 @@ type OperatorSourceInfo struct { Package string Channel string StartingCSV string - Catalog registry.CatalogKey + Catalog SourceKey DefaultChannel bool Subscription *v1alpha1.Subscription } @@ -203,7 +202,7 @@ func (i *OperatorSourceInfo) String() string { return fmt.Sprintf("%s/%s in %s/%s", i.Package, i.Channel, i.Catalog.Name, i.Catalog.Namespace) } -var NoCatalog = registry.CatalogKey{Name: "", Namespace: ""} +var NoCatalog = SourceKey{Name: "", Namespace: ""} var ExistingOperator = OperatorSourceInfo{Package: "", Channel: "", StartingCSV: "", Catalog: NoCatalog, DefaultChannel: false} type Operator struct { @@ -219,7 +218,7 @@ type Operator struct { Properties []*api.Property } -func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey registry.CatalogKey, defaultChannel string) (*Operator, error) { +func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey SourceKey, defaultChannel string) (*Operator, error) { parsedVersion, err := semver.ParseTolerant(bundle.Version) version := &parsedVersion if err != nil { diff --git a/pkg/controller/registry/resolver/cache/operators_test.go b/pkg/controller/registry/resolver/cache/operators_test.go index cf46ad80f0..5337a5b25d 100644 --- a/pkg/controller/registry/resolver/cache/operators_test.go +++ b/pkg/controller/registry/resolver/cache/operators_test.go @@ -11,7 +11,6 @@ import ( opver "github.com/operator-framework/api/pkg/lib/version" "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-registry/pkg/api" opregistry "github.com/operator-framework/operator-registry/pkg/registry" ) @@ -713,7 +712,7 @@ func TestCatalogKey_String(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - k := ®istry.CatalogKey{ + k := &SourceKey{ Name: tt.fields.Name, Namespace: tt.fields.Namespace, } @@ -878,7 +877,7 @@ func TestOperatorSourceInfo_String(t *testing.T) { i := &OperatorSourceInfo{ Package: tt.fields.Package, Channel: tt.fields.Channel, - Catalog: registry.CatalogKey{Name: tt.fields.CatalogSource, Namespace: tt.fields.CatalogSourceNamespace}, + Catalog: SourceKey{Name: tt.fields.CatalogSource, Namespace: tt.fields.CatalogSourceNamespace}, } if got := i.String(); got != tt.want { t.Errorf("OperatorSourceInfo.String() = %v, want %v", got, tt.want) @@ -1060,7 +1059,7 @@ func TestNewOperatorFromBundle(t *testing.T) { type args struct { bundle *api.Bundle - sourceKey registry.CatalogKey + sourceKey SourceKey replaces string defaultChannel string } @@ -1074,7 +1073,7 @@ func TestNewOperatorFromBundle(t *testing.T) { name: "BundleNoAPIs", args: args{ bundle: bundleNoAPIs, - sourceKey: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + sourceKey: SourceKey{Name: "source", Namespace: "testNamespace"}, }, want: &Operator{ Name: "testBundle", @@ -1085,7 +1084,7 @@ func TestNewOperatorFromBundle(t *testing.T) { SourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + Catalog: SourceKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1093,7 +1092,7 @@ func TestNewOperatorFromBundle(t *testing.T) { name: "BundleWithAPIs", args: args{ bundle: bundleWithAPIs, - sourceKey: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + sourceKey: SourceKey{Name: "source", Namespace: "testNamespace"}, }, want: &Operator{ Name: "testBundle", @@ -1148,7 +1147,7 @@ func TestNewOperatorFromBundle(t *testing.T) { SourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + Catalog: SourceKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1156,7 +1155,7 @@ func TestNewOperatorFromBundle(t *testing.T) { name: "BundleIgnoreCSV", args: args{ bundle: bundleWithAPIsUnextracted, - sourceKey: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + sourceKey: SourceKey{Name: "source", Namespace: "testNamespace"}, }, want: &Operator{ Name: "testBundle", @@ -1166,7 +1165,7 @@ func TestNewOperatorFromBundle(t *testing.T) { SourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + Catalog: SourceKey{Name: "source", Namespace: "testNamespace"}, }, }, }, @@ -1174,7 +1173,7 @@ func TestNewOperatorFromBundle(t *testing.T) { name: "BundleInDefaultChannel", args: args{ bundle: bundleNoAPIs, - sourceKey: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + sourceKey: SourceKey{Name: "source", Namespace: "testNamespace"}, defaultChannel: "testChannel", }, want: &Operator{ @@ -1186,7 +1185,7 @@ func TestNewOperatorFromBundle(t *testing.T) { SourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + Catalog: SourceKey{Name: "source", Namespace: "testNamespace"}, DefaultChannel: true, }, }, @@ -1195,7 +1194,7 @@ func TestNewOperatorFromBundle(t *testing.T) { name: "BundleWithPropertiesAndDependencies", args: args{ bundle: bundleWithPropsAndDeps, - sourceKey: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + sourceKey: SourceKey{Name: "source", Namespace: "testNamespace"}, }, want: &Operator{ Name: "testBundle", @@ -1224,7 +1223,7 @@ func TestNewOperatorFromBundle(t *testing.T) { SourceInfo: &OperatorSourceInfo{ Package: "testPackage", Channel: "testChannel", - Catalog: registry.CatalogKey{Name: "source", Namespace: "testNamespace"}, + Catalog: SourceKey{Name: "source", Namespace: "testNamespace"}, }, }, }, diff --git a/pkg/controller/registry/resolver/cache/predicates.go b/pkg/controller/registry/resolver/cache/predicates.go index 7e8649d5dc..292dda4805 100644 --- a/pkg/controller/registry/resolver/cache/predicates.go +++ b/pkg/controller/registry/resolver/cache/predicates.go @@ -7,7 +7,6 @@ import ( "github.com/blang/semver/v4" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-registry/pkg/api" opregistry "github.com/operator-framework/operator-registry/pkg/registry" ) @@ -140,10 +139,10 @@ func (l labelPredicate) String() string { } type catalogPredicate struct { - key registry.CatalogKey + key SourceKey } -func CatalogPredicate(key registry.CatalogKey) OperatorPredicate { +func CatalogPredicate(key SourceKey) OperatorPredicate { return catalogPredicate{key: key} } diff --git a/pkg/controller/registry/resolver/installabletypes.go b/pkg/controller/registry/resolver/installabletypes.go index ba9cd602bf..695c2b07ab 100644 --- a/pkg/controller/registry/resolver/installabletypes.go +++ b/pkg/controller/registry/resolver/installabletypes.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver" operatorregistry "github.com/operator-framework/operator-registry/pkg/registry" @@ -37,13 +36,13 @@ func (i *BundleInstallable) AddConstraint(c solver.Constraint) { i.constraints = append(i.constraints, c) } -func (i *BundleInstallable) BundleSourceInfo() (string, string, registry.CatalogKey, error) { +func (i *BundleInstallable) BundleSourceInfo() (string, string, cache.SourceKey, error) { info := strings.Split(i.identifier.String(), "/") // This should be enforced by Kube naming constraints if len(info) != 4 { - return "", "", registry.CatalogKey{}, fmt.Errorf("Unable to parse identifier %s for source info", i.identifier) + return "", "", cache.SourceKey{}, fmt.Errorf("Unable to parse identifier %s for source info", i.identifier) } - catalog := registry.CatalogKey{ + catalog := cache.SourceKey{ Name: info[0], Namespace: info[1], } @@ -52,7 +51,7 @@ func (i *BundleInstallable) BundleSourceInfo() (string, string, registry.Catalog return csvName, channel, catalog, nil } -func bundleId(bundle, channel string, catalog registry.CatalogKey) solver.Identifier { +func bundleId(bundle, channel string, catalog cache.SourceKey) solver.Identifier { return solver.IdentifierFromString(fmt.Sprintf("%s/%s/%s", catalog.String(), channel, bundle)) } diff --git a/pkg/controller/registry/resolver/instrumented_resolver.go b/pkg/controller/registry/resolver/instrumented_resolver.go index d8af9350cd..453ae4a0b8 100644 --- a/pkg/controller/registry/resolver/instrumented_resolver.go +++ b/pkg/controller/registry/resolver/instrumented_resolver.go @@ -4,8 +4,7 @@ import ( "time" "github.com/operator-framework/api/pkg/operators/v1alpha1" - - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" ) type InstrumentedResolver struct { @@ -35,6 +34,6 @@ func (ir *InstrumentedResolver) ResolveSteps(namespace string) ([]*v1alpha1.Step return steps, lookups, subs, err } -func (ir *InstrumentedResolver) Expire(key registry.CatalogKey) { +func (ir *InstrumentedResolver) Expire(key cache.SourceKey) { ir.resolver.Expire(key) } diff --git a/pkg/controller/registry/resolver/instrumented_resolver_test.go b/pkg/controller/registry/resolver/instrumented_resolver_test.go index 4b4c57fcf0..3b0aa0d59a 100644 --- a/pkg/controller/registry/resolver/instrumented_resolver_test.go +++ b/pkg/controller/registry/resolver/instrumented_resolver_test.go @@ -5,9 +5,8 @@ import ( "testing" "time" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" - "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" "github.com/stretchr/testify/require" ) @@ -24,14 +23,14 @@ func (r *fakeResolverWithError) ResolveSteps(namespace string) ([]*v1alpha1.Step return nil, nil, nil, errors.New("Fake error") } -func (r *fakeResolverWithError) Expire(key registry.CatalogKey) { +func (r *fakeResolverWithError) Expire(key cache.SourceKey) { } func (r *fakeResolverWithoutError) ResolveSteps(namespace string) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) { return nil, nil, nil, nil } -func (r *fakeResolverWithoutError) Expire(key registry.CatalogKey) { +func (r *fakeResolverWithoutError) Expire(key cache.SourceKey) { } func newFakeResolverWithError() *fakeResolverWithError { diff --git a/pkg/controller/registry/resolver/resolver.go b/pkg/controller/registry/resolver/resolver.go index 06d9c10c71..38e779acd3 100644 --- a/pkg/controller/registry/resolver/resolver.go +++ b/pkg/controller/registry/resolver/resolver.go @@ -13,7 +13,6 @@ import ( "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" "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" @@ -30,10 +29,10 @@ type SatResolver struct { log logrus.FieldLogger } -func NewDefaultSatResolver(rcp cache.RegistryClientProvider, catsrcLister v1alpha1listers.CatalogSourceLister, log logrus.FieldLogger) *SatResolver { +func NewDefaultSatResolver(rcp cache.SourceProvider, catsrcLister v1alpha1listers.CatalogSourceLister, logger logrus.FieldLogger) *SatResolver { return &SatResolver{ - cache: cache.NewOperatorCache(rcp, log, catsrcLister), - log: log, + cache: cache.New(rcp, cache.WithLogger(logger), cache.WithCatalogSourceLister(catsrcLister)), + log: logger, } } @@ -61,9 +60,9 @@ func (r *SatResolver) SolveOperators(namespaces []string, csvs []*v1alpha1.Clust if err != nil { return nil, err } - namespacedCache := r.cache.Namespaced(namespaces...).WithExistingOperators(existingSnapshot) + namespacedCache := r.cache.Namespaced(namespaces...).WithExistingOperators(existingSnapshot, namespaces[0]) - _, existingInstallables, err := r.getBundleInstallables(registry.NewVirtualCatalogKey(namespaces[0]), existingSnapshot.Find(), namespacedCache, visited) + _, existingInstallables, err := r.getBundleInstallables(namespaces[0], cache.Filter(existingSnapshot.Entries, cache.True()), namespacedCache, visited) if err != nil { return nil, err } @@ -177,7 +176,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu var cachePredicates, channelPredicates []cache.OperatorPredicate installables := make(map[solver.Identifier]solver.Installable, 0) - catalog := registry.CatalogKey{ + catalog := cache.SourceKey{ Name: sub.Spec.CatalogSource, Namespace: sub.Spec.CatalogSourceNamespace, } @@ -259,7 +258,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu for _, o := range cache.Filter(sortedBundles, channelPredicates...) { predicates := append(cachePredicates, cache.CSVNamePredicate(o.Name)) stack := namespacedCache.Catalog(catalog).Find(predicates...) - id, installable, err := r.getBundleInstallables(catalog, stack, namespacedCache, visited) + id, installable, err := r.getBundleInstallables(sub.Namespace, stack, namespacedCache, visited) if err != nil { return nil, err } @@ -288,12 +287,12 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu // safe to remove this conflict if properties // annotations are made mandatory for // resolution. - c.AddConflict(bundleId(current.Name, current.Channel(), registry.NewVirtualCatalogKey(sub.GetNamespace()))) + c.AddConflict(bundleId(current.Name, current.Channel(), cache.NewVirtualSourceKey(sub.GetNamespace()))) } depIds = append(depIds, c.Identifier()) } if current != nil { - depIds = append(depIds, bundleId(current.Name, current.Channel(), registry.NewVirtualCatalogKey(sub.GetNamespace()))) + depIds = append(depIds, bundleId(current.Name, current.Channel(), cache.NewVirtualSourceKey(sub.GetNamespace()))) } // all candidates added as options for this constraint @@ -303,7 +302,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu return installables, nil } -func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, bundleStack []*cache.Operator, namespacedCache cache.MultiCatalogOperatorFinder, visited map[*cache.Operator]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { +func (r *SatResolver) getBundleInstallables(preferredNamespace string, bundleStack []*cache.Operator, namespacedCache cache.MultiCatalogOperatorFinder, visited map[*cache.Operator]*BundleInstallable) (map[solver.Identifier]struct{}, map[solver.Identifier]*BundleInstallable, error) { errs := make([]error, 0) installables := make(map[solver.Identifier]*BundleInstallable, 0) // all installables, including dependencies @@ -370,7 +369,8 @@ func (r *SatResolver) getBundleInstallables(catalog registry.CatalogKey, bundleS )) } } - sortedBundles, err := r.sortBundles(namespacedCache.FindPreferred(&bundle.SourceInfo.Catalog, sourcePredicate)) + + sortedBundles, err := r.sortBundles(namespacedCache.FindPreferred(&bundle.SourceInfo.Catalog, preferredNamespace, sourcePredicate)) if err != nil { errs = append(errs, err) continue @@ -422,7 +422,7 @@ func (r *SatResolver) inferProperties(csv *v1alpha1.ClusterServiceVersion, subs // package against catalog contents, updates to the // Subscription spec could result in a bad package // inference. - for _, entry := range r.cache.Namespaced(sub.Namespace).Catalog(registry.CatalogKey{Namespace: sub.Spec.CatalogSourceNamespace, Name: sub.Spec.CatalogSource}).Find(cache.And(cache.CSVNamePredicate(csv.Name), cache.PkgPredicate(sub.Spec.Package))) { + 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))) { if pkg := entry.Package(); pkg != "" { packages[pkg] = struct{}{} } @@ -455,8 +455,8 @@ func (r *SatResolver) inferProperties(csv *v1alpha1.ClusterServiceVersion, subs return properties, nil } -func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*cache.CatalogSnapshot, error) { - existingOperatorCatalog := registry.NewVirtualCatalogKey(namespace) +func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1.Subscription, csvs []*v1alpha1.ClusterServiceVersion) (*cache.Snapshot, error) { + existingOperatorCatalog := cache.NewVirtualSourceKey(namespace) // build a catalog snapshot of CSVs without subscriptions csvSubscriptions := make(map[*v1alpha1.ClusterServiceVersion]*v1alpha1.Subscription) for _, sub := range subs { @@ -517,7 +517,7 @@ func (r *SatResolver) newSnapshotForNamespace(namespace string, subs []*v1alpha1 r.log.Infof("considered csvs without properties annotation during resolution: %v", names) } - return cache.NewRunningOperatorSnapshot(r.log, existingOperatorCatalog, standaloneOperators), nil + return &cache.Snapshot{Entries: standaloneOperators}, nil } func (r *SatResolver) addInvariants(namespacedCache cache.MultiCatalogOperatorFinder, installables map[solver.Identifier]solver.Installable) { @@ -579,17 +579,17 @@ func (r *SatResolver) addInvariants(namespacedCache cache.MultiCatalogOperatorFi func (r *SatResolver) sortBundles(bundles []*cache.Operator) ([]*cache.Operator, error) { // assume bundles have been passed in sorted by catalog already - catalogOrder := make([]registry.CatalogKey, 0) + catalogOrder := make([]cache.SourceKey, 0) type PackageChannel struct { Package, Channel string DefaultChannel bool } // TODO: for now channels will be sorted lexicographically - channelOrder := make(map[registry.CatalogKey][]PackageChannel) + channelOrder := make(map[cache.SourceKey][]PackageChannel) // partition by catalog -> channel -> bundle - partitionedBundles := map[registry.CatalogKey]map[PackageChannel][]*cache.Operator{} + partitionedBundles := map[cache.SourceKey]map[PackageChannel][]*cache.Operator{} for _, b := range bundles { pc := PackageChannel{ Package: b.Package(), diff --git a/pkg/controller/registry/resolver/resolver_test.go b/pkg/controller/registry/resolver/resolver_test.go index a686cbdcf1..1d80b0cd13 100644 --- a/pkg/controller/registry/resolver/resolver_test.go +++ b/pkg/controller/registry/resolver/resolver_test.go @@ -12,10 +12,11 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "github.com/operator-framework/api/pkg/lib/version" "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" + listersv1alpha1 "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/solver" "github.com/operator-framework/operator-registry/pkg/api" @@ -27,7 +28,7 @@ func TestSolveOperators(t *testing.T) { Provides := APISet const namespace = "test-namespace" - catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -35,20 +36,16 @@ func TestSolveOperators(t *testing.T) { newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{sub, newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) @@ -62,27 +59,23 @@ func TestSolveOperators(t *testing.T) { func TestDisjointChannelGraph(t *testing.T) { const namespace = "test-namespace" - catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace} newSub := newSub(namespace, "packageA", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.side1.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), genOperator("packageA.side1.v2", "0.0.2", "packageA.side1.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), genOperator("packageA.side2.v1", "1.0.0", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), genOperator("packageA.side2.v2", "2.0.0", "packageA.side2.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } _, err := satResolver.SolveOperators([]string{namespace}, nil, subs) @@ -93,7 +86,7 @@ func TestPropertiesAnnotationHonored(t *testing.T) { const ( namespace = "olm" ) - community := registry.CatalogKey{"community", namespace} + community := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", nil, nil, nil, nil) csv.Annotations = map[string]string{"operatorframework.io/properties": `{"properties":[{"type":"olm.package","value":{"packageName":"packageA","version":"1.0.0"}}]}`} @@ -104,17 +97,13 @@ func TestPropertiesAnnotationHonored(t *testing.T) { b := genOperator("packageB.v1", "1.0.1", "", "packageB", "alpha", "community", "olm", nil, nil, []*api.Dependency{{Type: "olm.package", Value: `{"packageName":"packageA","version":"1.0.0"}`}}, "", false) - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - community: { - Key: community, - Operators: []*cache.Operator{b}, - }, - }, - } satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + cache: cache.New(cache.StaticSourceProvider{ + community: &cache.Snapshot{ + Entries: []*cache.Operator{b}, + }, + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -131,7 +120,7 @@ func TestSolveOperators_MultipleChannels(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -139,21 +128,17 @@ func TestSolveOperators_MultipleChannels(t *testing.T) { newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{sub, newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "beta", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -172,7 +157,7 @@ func TestSolveOperators_FindLatestVersion(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -180,28 +165,21 @@ func TestSolveOperators_FindLatestVersion(t *testing.T) { newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{sub, newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + cache.SourceKey{ Namespace: "olm", Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + }: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1.0.1", "1.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v0.9.0", "0.9.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1.0.0", "1.0.0", "packageB.v0.9.0", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1.0.1", "1.0.1", "packageB.v1.0.0", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -226,7 +204,7 @@ func TestSolveOperators_FindLatestVersionWithDependencies(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -245,17 +223,10 @@ func TestSolveOperators_FindLatestVersionWithDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1.0.1", "1.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v0.9.0", "0.9.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1.0.0", "1.0.0", "packageB.v0.9.0", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), @@ -267,11 +238,8 @@ func TestSolveOperators_FindLatestVersionWithDependencies(t *testing.T) { genOperator("packageD.v1.0.2", "1.0.2", "packageD.v1.0.1", "packageD", "alpha", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -295,7 +263,7 @@ func TestSolveOperators_FindLatestVersionWithNestedDependencies(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -320,17 +288,10 @@ func TestSolveOperators_FindLatestVersionWithNestedDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1.0.1", "1.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v0.9.0", "0.9.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1.0.0", "1.0.0", "packageB.v0.9.0", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), @@ -342,11 +303,8 @@ func TestSolveOperators_FindLatestVersionWithNestedDependencies(t *testing.T) { genOperator("packageE.v1.0.0", "1.0.0", "", "packageE", "alpha", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -366,6 +324,40 @@ func TestSolveOperators_FindLatestVersionWithNestedDependencies(t *testing.T) { } } +type stubCatalogSourceLister struct { + catsrcs []*v1alpha1.CatalogSource + namespace string +} + +func (l *stubCatalogSourceLister) List(labels.Selector) ([]*v1alpha1.CatalogSource, error) { + if l.namespace == "" { + return l.catsrcs, nil + } + var result []*v1alpha1.CatalogSource + for _, cs := range l.catsrcs { + if cs.Namespace == l.namespace { + result = append(result, cs) + } + } + return result, nil +} + +func (l *stubCatalogSourceLister) Get(name string) (*v1alpha1.CatalogSource, error) { + for _, cs := range l.catsrcs { + if cs.Name == name { + return cs, nil + } + } + return nil, errors.New("stub not found") +} + +func (l *stubCatalogSourceLister) CatalogSources(namespace string) listersv1alpha1.CatalogSourceNamespaceLister { + return &stubCatalogSourceLister{ + catsrcs: l.catsrcs, + namespace: namespace, + } +} + func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { opToAddVersionDeps := []*api.Dependency{ { @@ -375,58 +367,45 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { } namespace := "olm" - customCatalog := registry.CatalogKey{"community", namespace} + customCatalog := cache.SourceKey{"community", namespace} newSub := newSub(namespace, "packageA", "alpha", customCatalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ - genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", namespace, nil, - nil, opToAddVersionDeps, "", false), - }, + ssp := cache.StaticSourceProvider{ + cache.SourceKey{Namespace: "olm", Name: "community"}: &cache.Snapshot{ + Entries: []*cache.Operator{ + genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", namespace, nil, + nil, opToAddVersionDeps, "", false), }, - registry.CatalogKey{ - Namespace: "olm", - Name: "community-operator", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community-operator", - }, - Operators: []*cache.Operator{ - genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community-operator", - namespace, nil, nil, nil, "", false), - }, + }, + cache.SourceKey{Namespace: "olm", Name: "community-operator"}: &cache.Snapshot{ + Entries: []*cache.Operator{ + genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community-operator", + namespace, nil, nil, nil, "", false), }, - registry.CatalogKey{ - Namespace: "olm", - Name: "high-priority-operator", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "high-priority-operator", - }, - Priority: 100, - Operators: []*cache.Operator{ - genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "high-priority-operator", - namespace, nil, nil, nil, "", false), - }, + }, + cache.SourceKey{Namespace: "olm", Name: "high-priority-operator"}: &cache.Snapshot{ + Entries: []*cache.Operator{ + genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "high-priority-operator", + namespace, nil, nil, nil, "", false), }, }, } - // operators sorted by priority. satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + cache: cache.New(ssp, cache.WithCatalogSourceLister(&stubCatalogSourceLister{ + catsrcs: []*v1alpha1.CatalogSource{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "olm", + Name: "high-priority-operator", + }, + Spec: v1alpha1.CatalogSourceSpec{ + Priority: 100, + }, + }, + }, + })), } operators, err := satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) @@ -443,23 +422,39 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { } // Catsrc with the same priority, ns, different name - fakeNamespacedOperatorCache.Snapshots[registry.CatalogKey{ + ssp[cache.SourceKey{ Namespace: "olm", Name: "community-operator", - }] = &cache.CatalogSnapshot{ - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community-operator", - }, - Priority: 100, - Operators: []*cache.Operator{ + }] = &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community-operator", namespace, nil, nil, nil, "", false), }, } satResolver = SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + cache: cache.New(ssp, cache.WithCatalogSourceLister(&stubCatalogSourceLister{ + catsrcs: []*v1alpha1.CatalogSource{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "olm", + Name: "high-priority-operator", + }, + Spec: v1alpha1.CatalogSourceSpec{ + Priority: 100, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "olm", + Name: "community-operator", + }, + Spec: v1alpha1.CatalogSourceSpec{ + Priority: 100, + }, + }, + }, + })), } operators, err = satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) @@ -476,15 +471,11 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { } // operators from the same catalogs source should be prioritized. - fakeNamespacedOperatorCache.Snapshots[registry.CatalogKey{ + ssp[cache.SourceKey{ Namespace: "olm", Name: "community", - }] = &cache.CatalogSnapshot{ - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + }] = &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", namespace, nil, nil, opToAddVersionDeps, "", false), genOperator("packageB.v1", "0.0.1", "", "packageB", "alpha", "community", @@ -493,7 +484,7 @@ func TestSolveOperators_CatsrcPrioritySorting(t *testing.T) { } satResolver = SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + cache: cache.New(ssp), } operators, err = satResolver.SolveOperators([]string{"olm"}, []*v1alpha1.ClusterServiceVersion{}, subs) @@ -516,7 +507,7 @@ func TestSolveOperators_WithDependencies(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -531,27 +522,17 @@ func TestSolveOperators_WithDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1.0.1", "1.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, opToAddVersionDeps, "", false), genOperator("packageC.v1", "0.1.0", "", "packageC", "alpha", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -574,7 +555,7 @@ func TestSolveOperators_WithGVKDependencies(t *testing.T) { Provides := APISet namespace := "olm" - community := registry.CatalogKey{"community", namespace} + community := cache.SourceKey{"community", namespace} csvs := []*v1alpha1.ClusterServiceVersion{ existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", nil, nil, nil, nil), @@ -591,21 +572,17 @@ func TestSolveOperators_WithGVKDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - community: { - Key: community, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + community: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", Provides, nil, deps, "", false), genOperator("packageC.v1", "0.1.0", "", "packageC", "alpha", "community", "olm", nil, Provides, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -624,7 +601,7 @@ func TestSolveOperators_WithGVKDependencies(t *testing.T) { func TestSolveOperators_WithLabelDependencies(t *testing.T) { namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} newSub := newSub(namespace, "packageA", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} @@ -648,25 +625,15 @@ func TestSolveOperators_WithLabelDependencies(t *testing.T) { operatorBv1.Properties = append(operatorBv1.Properties, p) } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, deps, "", false), operatorBv1, }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + }), } operators, err := satResolver.SolveOperators([]string{"olm"}, nil, subs) @@ -685,7 +652,7 @@ func TestSolveOperators_WithLabelDependencies(t *testing.T) { func TestSolveOperators_WithUnsatisfiableLabelDependencies(t *testing.T) { namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} newSub := newSub(namespace, "packageA", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} @@ -697,25 +664,15 @@ func TestSolveOperators_WithUnsatisfiableLabelDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, deps, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), + }), } operators, err := satResolver.SolveOperators([]string{"olm"}, nil, subs) @@ -728,7 +685,7 @@ func TestSolveOperators_WithNestedGVKDependencies(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -750,17 +707,13 @@ func TestSolveOperators_WithNestedGVKDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + cache.SourceKey{ Namespace: "olm", Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + }: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1.0.1", "1.0.1", "packageA.v1", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1.0.0", "1.0.0", "", "packageB", "alpha", "community", "olm", Provides, nil, deps, "", false), genOperator("packageB.v1.0.1", "1.0.1", "packageB.v1.0.0", "packageB", "alpha", "community", "olm", Provides, nil, deps, "", false), @@ -769,25 +722,18 @@ func TestSolveOperators_WithNestedGVKDependencies(t *testing.T) { genOperator("packageD.v1.0.1", "1.0.1", "", "packageD", "alpha", "community", "olm", nil, Provides2, deps2, "", false), }, }, - registry.CatalogKey{ + cache.SourceKey{ Namespace: "olm", Name: "certified", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "certified", - }, - Operators: []*cache.Operator{ + }: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageC.v1.0.0", "1.0.0", "", "packageC", "alpha", "certified", "olm", Provides2, Provides, deps2, "", false), genOperator("packageC.v1.0.1", "1.0.1", "packageC.v1.0.0", "packageC", "alpha", "certified", "olm", Provides2, Provides, deps2, "", false), genOperator("packageD.v1.0.1", "1.0.1", "", "packageD", "alpha", "certified", "olm", nil, Provides2, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -815,7 +761,7 @@ func TestSolveOperators_IgnoreUnsatisfiableDependencies(t *testing.T) { const namespace = "olm" Provides := cache.APISet{opregistry.APIKey{Group: "g", Version: "v", Kind: "k", Plural: "ks"}: struct{}{}} - community := registry.CatalogKey{Name: "community", Namespace: namespace} + community := cache.SourceKey{Name: "community", Namespace: namespace} csvs := []*v1alpha1.ClusterServiceVersion{ existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil), } @@ -837,35 +783,24 @@ func TestSolveOperators_IgnoreUnsatisfiableDependencies(t *testing.T) { }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - community: { - Key: community, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + community: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "community", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, nil, opToAddVersionDeps, "", false), genOperator("packageC.v1", "0.1.0", "", "packageC", "alpha", "community", "olm", nil, nil, unsatisfiableVersionDeps, "", false), }, }, - { - Namespace: "olm", - Name: "certified", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "certified", - }, - Operators: []*cache.Operator{ + {Namespace: "olm", Name: "certified"}: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", "certified", "olm", nil, nil, nil, "", false), genOperator("packageB.v1", "1.0.0", "", "packageB", "alpha", "certified", "olm", nil, nil, opToAddVersionDeps, "", false), genOperator("packageC.v1", "0.1.0", "", "packageC", "alpha", "certified", "olm", nil, nil, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -889,32 +824,28 @@ func TestSolveOperators_PreferCatalogInSameNamespace(t *testing.T) { namespace := "olm" altNamespace := "alt-olm" - catalog := registry.CatalogKey{"community", namespace} - altnsCatalog := registry.CatalogKey{"alt-community", altNamespace} + catalog := cache.SourceKey{"community", namespace} + altnsCatalog := cache.SourceKey{"alt-community", altNamespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} sub := existingSub(namespace, "packageA.v1", "packageA", "alpha", catalog) subs := []*v1alpha1.Subscription{sub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v0.0.1", "0.0.1", "packageA.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, Provides, nil, "", false), }, }, - altnsCatalog: { - Operators: []*cache.Operator{ + altnsCatalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v0.0.1", "0.0.1", "packageA.v1", "packageA", "alpha", altnsCatalog.Name, altnsCatalog.Namespace, nil, Provides, nil, "", false), }, }, - }, - Namespaces: []string{namespace, altNamespace}, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) @@ -933,27 +864,23 @@ func TestSolveOperators_ResolveOnlyInCachedNamespaces(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} - otherCatalog := registry.CatalogKey{Name: "secret", Namespace: "secret"} + catalog := cache.SourceKey{"community", namespace} + otherCatalog := cache.SourceKey{Name: "secret", Namespace: "secret"} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} newSub := newSub(namespace, "packageA", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v0.0.1", "0.0.1", "packageA.v1", "packageA", "alpha", otherCatalog.Name, otherCatalog.Namespace, nil, Provides, nil, "", false), }, }, - }, - Namespaces: []string{otherCatalog.Namespace}, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) @@ -968,7 +895,7 @@ func TestSolveOperators_PreferDefaultChannelInResolution(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{Name: "community", Namespace: namespace} + catalog := cache.SourceKey{Name: "community", Namespace: namespace} csvs := []*v1alpha1.ClusterServiceVersion{} @@ -977,21 +904,17 @@ func TestSolveOperators_PreferDefaultChannelInResolution(t *testing.T) { newSub := newSub(namespace, "packageA", "", catalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ // Default channel is stable in this case genOperator("packageA.v0.0.2", "0.0.2", "packageA.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, Provides, nil, defaultChannel, false), genOperator("packageA.v0.0.1", "0.0.1", "packageA.v1", "packageA", "stable", catalog.Name, catalog.Namespace, nil, Provides, nil, defaultChannel, false), }, }, - }, - } - - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) @@ -1010,7 +933,7 @@ func TestSolveOperators_PreferDefaultChannelInResolutionForTransitiveDependencie Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{Name: "community", Namespace: namespace} + catalog := cache.SourceKey{Name: "community", Namespace: namespace} csvs := []*v1alpha1.ClusterServiceVersion{} @@ -1018,21 +941,18 @@ func TestSolveOperators_PreferDefaultChannelInResolutionForTransitiveDependencie subs := []*v1alpha1.Subscription{newSub} const defaultChannel = "stable" - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Operators: []*cache.Operator{ + + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v0.0.1", "0.0.1", "packageA.v1", "packageA", "alpha", catalog.Name, catalog.Namespace, Provides, nil, cache.APISetToDependencies(nil, Provides), defaultChannel, false), genOperator("packageB.v0.0.1", "0.0.1", "packageB.v1", "packageB", defaultChannel, catalog.Name, catalog.Namespace, nil, Provides, nil, defaultChannel, false), genOperator("packageB.v0.0.2", "0.0.2", "packageB.v0.0.1", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, Provides, nil, defaultChannel, false), }, }, - }, - } - - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, csvs, subs) @@ -1051,7 +971,7 @@ func TestSolveOperators_SubscriptionlessOperatorsSatisfyDependencies(t *testing. Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} @@ -1065,26 +985,16 @@ func TestSolveOperators_SubscriptionlessOperatorsSatisfyDependencies(t *testing. }, } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageB.v1.0.0", "1.0.0", "", "packageB", "alpha", "community", "olm", Provides, nil, deps, "", false), genOperator("packageB.v1.0.1", "1.0.1", "packageB.v1.0.0", "packageB", "alpha", "community", "olm", Provides, nil, deps, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -1104,33 +1014,23 @@ func TestSolveOperators_SubscriptionlessOperatorsCanConflict(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} csv := existingOperator(namespace, "packageA.v1", "packageA", "alpha", "", Provides, nil, nil, nil) csvs := []*v1alpha1.ClusterServiceVersion{csv} newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageB.v1.0.0", "1.0.0", "", "packageB", "alpha", "community", "olm", nil, Provides, nil, "", false), genOperator("packageB.v1.0.1", "1.0.1", "packageB.v1.0.0", "packageB", "alpha", "community", "olm", nil, Provides, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } _, err := satResolver.SolveOperators([]string{"olm"}, csvs, subs) @@ -1146,17 +1046,16 @@ func TestSolveOperators_PackageCannotSelfSatisfy(t *testing.T) { RequiresBoth := Requires1.Union(Requires2) namespace := "olm" - catalog := registry.CatalogKey{Name: "community", Namespace: namespace} - secondaryCatalog := registry.CatalogKey{Namespace: "olm", Name: "secondary"} + catalog := cache.SourceKey{Name: "community", Namespace: namespace} + secondaryCatalog := cache.SourceKey{Namespace: "olm", Name: "secondary"} newSub := newSub(namespace, "packageA", "stable", catalog) subs := []*v1alpha1.Subscription{newSub} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("opA.v1.0.0", "1.0.0", "", "packageA", "stable", catalog.Name, catalog.Namespace, RequiresBoth, nil, nil, "", false), // Despite satisfying dependencies of opA, this is not chosen because it is in the same package genOperator("opABC.v1.0.0", "1.0.0", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, ProvidesBoth, nil, "", false), @@ -1165,19 +1064,15 @@ func TestSolveOperators_PackageCannotSelfSatisfy(t *testing.T) { genOperator("opD.v1.0.0", "1.0.0", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, Provides1, nil, "stable", false), }, }, - secondaryCatalog: { - Key: secondaryCatalog, - Operators: []*cache.Operator{ + secondaryCatalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("opC.v1.0.0", "1.0.0", "", "packageB", "stable", secondaryCatalog.Name, secondaryCatalog.Namespace, nil, Provides2, nil, "stable", false), genOperator("opE.v1.0.0", "1.0.0", "", "packageC", "stable", secondaryCatalog.Name, secondaryCatalog.Namespace, nil, Provides2, nil, "", false), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, nil, subs) @@ -1201,18 +1096,17 @@ func TestSolveOperators_TransferApiOwnership(t *testing.T) { ProvidesBoth := Provides1.Union(Provides2) namespace := "olm" - catalog := registry.CatalogKey{Name: "community", Namespace: namespace} + catalog := cache.SourceKey{Name: "community", Namespace: namespace} phases := []struct { subs []*v1alpha1.Subscription - catalog *cache.CatalogSnapshot + catalog cache.Source expected cache.OperatorSet }{ { subs: []*v1alpha1.Subscription{newSub(namespace, "packageB", "stable", catalog)}, - catalog: &cache.CatalogSnapshot{ - Key: catalog, - Operators: []*cache.Operator{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("opA.v1.0.0", "1.0.0", "", "packageA", "stable", catalog.Name, catalog.Namespace, nil, Provides1, nil, "", false), genOperator("opB.v1.0.0", "1.0.0", "", "packageB", "stable", catalog.Name, catalog.Namespace, Requires1, Provides2, nil, "stable", false), }, @@ -1228,9 +1122,8 @@ func TestSolveOperators_TransferApiOwnership(t *testing.T) { existingSub(namespace, "opA.v1.0.0", "packageA", "stable", catalog), existingSub(namespace, "opB.v1.0.0", "packageB", "stable", catalog), }, - catalog: &cache.CatalogSnapshot{ - Key: catalog, - Operators: []*cache.Operator{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("opA.v1.0.0", "1.0.0", "", "packageA", "stable", catalog.Name, catalog.Namespace, nil, Provides1, nil, "", false), genOperator("opA.v1.0.1", "1.0.1", "opA.v1.0.0", "packageA", "stable", catalog.Name, catalog.Namespace, Requires1, nil, nil, "", false), genOperator("opB.v1.0.0", "1.0.0", "", "packageB", "stable", catalog.Name, catalog.Namespace, Requires1, Provides2, nil, "stable", false), @@ -1245,9 +1138,8 @@ func TestSolveOperators_TransferApiOwnership(t *testing.T) { existingSub(namespace, "opA.v1.0.0", "packageA", "stable", catalog), existingSub(namespace, "opB.v1.0.0", "packageB", "stable", catalog), }, - catalog: &cache.CatalogSnapshot{ - Key: catalog, - Operators: []*cache.Operator{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("opA.v1.0.0", "1.0.0", "", "packageA", "stable", catalog.Name, catalog.Namespace, nil, Provides1, nil, "", false), genOperator("opA.v1.0.1", "1.0.1", "opA.v1.0.0", "packageA", "stable", catalog.Name, catalog.Namespace, Requires1, nil, nil, "", false), genOperator("opB.v1.0.0", "1.0.0", "", "packageB", "stable", catalog.Name, catalog.Namespace, Requires1, Provides2, nil, "stable", false), @@ -1264,14 +1156,11 @@ func TestSolveOperators_TransferApiOwnership(t *testing.T) { var operators cache.OperatorSet for i, p := range phases { t.Run(fmt.Sprintf("phase %d", i+1), func(t *testing.T) { - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: p.catalog, - }, - } satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + cache: cache.New(cache.StaticSourceProvider{ + catalog: p.catalog, + }), + log: logrus.New(), } csvs := make([]*v1alpha1.ClusterServiceVersion, 0) for _, o := range operators { @@ -1295,24 +1184,6 @@ func TestSolveOperators_TransferApiOwnership(t *testing.T) { } } -type FakeOperatorCache struct { - fakedNamespacedOperatorCache cache.NamespacedOperatorCache -} - -func (f *FakeOperatorCache) Namespaced(namespaces ...string) cache.MultiCatalogOperatorFinder { - return &f.fakedNamespacedOperatorCache -} - -func (f *FakeOperatorCache) Expire(key registry.CatalogKey) { - return -} - -func getFakeOperatorCache(fakedNamespacedOperatorCache cache.NamespacedOperatorCache) cache.OperatorCacheProvider { - return &FakeOperatorCache{ - fakedNamespacedOperatorCache: fakedNamespacedOperatorCache, - } -} - func genOperator(name, version, replaces, pkg, channel, catalogName, catalogNamespace string, requiredAPIs, providedAPIs cache.APISet, dependencies []*api.Dependency, defaultChannel string, deprecated bool) *cache.Operator { semversion, _ := semver.Make(version) properties := cache.APISetToProperties(providedAPIs, nil, deprecated) @@ -1341,7 +1212,7 @@ func genOperator(name, version, replaces, pkg, channel, catalogName, catalogName }, Properties: properties, SourceInfo: &cache.OperatorSourceInfo{ - Catalog: registry.CatalogKey{ + Catalog: cache.SourceKey{ Name: catalogName, Namespace: catalogNamespace, }, @@ -1352,7 +1223,7 @@ func genOperator(name, version, replaces, pkg, channel, catalogName, catalogName ProvidedAPIs: providedAPIs, RequiredAPIs: requiredAPIs, } - cache.EnsurePackageProperty(o, pkg, version) + EnsurePackageProperty(o, pkg, version) return o } @@ -1362,25 +1233,21 @@ func stripBundle(o *cache.Operator) *cache.Operator { } func TestSolveOperators_WithoutDeprecated(t *testing.T) { - catalog := registry.CatalogKey{Name: "catalog", Namespace: "namespace"} + catalog := cache.SourceKey{Name: "catalog", Namespace: "namespace"} subs := []*v1alpha1.Subscription{ newSub(catalog.Namespace, "packageA", "alpha", catalog), } - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ genOperator("packageA.v1", "0.0.1", "", "packageA", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", true), }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{catalog.Namespace}, nil, subs) @@ -1389,22 +1256,19 @@ func TestSolveOperators_WithoutDeprecated(t *testing.T) { } func TestSolveOperatorsWithDeprecatedInnerChannelEntry(t *testing.T) { - catalog := registry.CatalogKey{Name: "catalog", Namespace: "namespace"} + catalog := cache.SourceKey{Name: "catalog", Namespace: "namespace"} subs := []*v1alpha1.Subscription{ newSub(catalog.Namespace, "a", "c", catalog), } logger, _ := test.NewNullLogger() resolver := SatResolver{ - cache: getFakeOperatorCache(cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ - genOperator("a-1", "1.0.0", "", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), - genOperator("a-2", "2.0.0", "a-1", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", true), - genOperator("a-3", "3.0.0", "a-2", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), - }, + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ + genOperator("a-1", "1.0.0", "", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), + genOperator("a-2", "2.0.0", "a-1", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", true), + genOperator("a-3", "3.0.0", "a-2", "a", "c", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), }, }, }), @@ -1422,7 +1286,7 @@ func TestSolveOperators_WithSkipsAndStartingCSV(t *testing.T) { Provides := APISet namespace := "olm" - catalog := registry.CatalogKey{"community", namespace} + catalog := cache.SourceKey{"community", namespace} newSub := newSub(namespace, "packageB", "alpha", catalog, withStartingCSV("packageB.v1")) subs := []*v1alpha1.Subscription{newSub} @@ -1446,25 +1310,15 @@ func TestSolveOperators_WithSkipsAndStartingCSV(t *testing.T) { op5.Skips = []string{"packageA.v2", "packageA.v3", "packageA.v4"} op6 := genOperator("packageA.v6", "6.0.0", "packageA.v5", "packageA", "alpha", "community", "olm", nil, Provides, nil, "", false) - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }: { - Key: registry.CatalogKey{ - Namespace: "olm", - Name: "community", - }, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ opB, opB2, op1, op2, op3, op4, op5, op6, }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{"olm"}, nil, subs) @@ -1479,7 +1333,7 @@ func TestSolveOperators_WithSkipsAndStartingCSV(t *testing.T) { func TestSolveOperators_WithSkips(t *testing.T) { const namespace = "test-namespace" - catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace} newSub := newSub(namespace, "packageB", "alpha", catalog) subs := []*v1alpha1.Subscription{newSub} @@ -1488,19 +1342,15 @@ func TestSolveOperators_WithSkips(t *testing.T) { opB2 := genOperator("packageB.v2", "2.0.0", "", "packageB", "alpha", catalog.Name, catalog.Namespace, nil, nil, nil, "", false) opB2.Skips = []string{"packageB.v1"} - fakeNamespacedOperatorCache := cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ + satResolver := SatResolver{ + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ opB, opB2, }, }, - }, - } - satResolver := SatResolver{ - cache: getFakeOperatorCache(fakeNamespacedOperatorCache), - log: logrus.New(), + }), + log: logrus.New(), } operators, err := satResolver.SolveOperators([]string{namespace}, nil, subs) @@ -1513,7 +1363,7 @@ func TestSolveOperators_WithSkips(t *testing.T) { func TestSolveOperatorsWithSkipsPreventingSelection(t *testing.T) { const namespace = "test-namespace" - catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace} gvks := cache.APISet{opregistry.APIKey{Group: "g", Version: "v", Kind: "k", Plural: "ks"}: struct{}{}} // Subscription candidate a-1 requires a GVK provided @@ -1528,12 +1378,9 @@ func TestSolveOperatorsWithSkipsPreventingSelection(t *testing.T) { logger, _ := test.NewNullLogger() satResolver := SatResolver{ - cache: getFakeOperatorCache(cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{a1, b3, b2, b1}, - }, + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{a1, b3, b2, b1}, }, }), log: logger, @@ -1545,7 +1392,7 @@ func TestSolveOperatorsWithSkipsPreventingSelection(t *testing.T) { func TestSolveOperatorsWithClusterServiceVersionHavingDependency(t *testing.T) { const namespace = "test-namespace" - catalog := registry.CatalogKey{Name: "test-catalog", Namespace: namespace} + catalog := cache.SourceKey{Name: "test-catalog", Namespace: namespace} a1 := existingOperator(namespace, "a-1", "a", "default", "", nil, nil, nil, nil) a1.Annotations = map[string]string{ @@ -1564,13 +1411,10 @@ func TestSolveOperatorsWithClusterServiceVersionHavingDependency(t *testing.T) { log, _ := test.NewNullLogger() r := SatResolver{ - cache: getFakeOperatorCache(cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ - genOperator("b-2", "2.0.0", "b-1", "b", "default", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), - }, + cache: cache.New(cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ + genOperator("b-2", "2.0.0", "b-1", "b", "default", catalog.Name, catalog.Namespace, nil, nil, nil, "", false), }, }, }), @@ -1583,11 +1427,11 @@ func TestSolveOperatorsWithClusterServiceVersionHavingDependency(t *testing.T) { } func TestInferProperties(t *testing.T) { - catalog := registry.CatalogKey{Namespace: "namespace", Name: "name"} + catalog := cache.SourceKey{Namespace: "namespace", Name: "name"} for _, tc := range []struct { Name string - Cache cache.NamespacedOperatorCache + Cache cache.StaticSourceProvider CSV *v1alpha1.ClusterServiceVersion Subscriptions []*v1alpha1.Subscription Expected []*api.Property @@ -1664,16 +1508,13 @@ func TestInferProperties(t *testing.T) { }, { Name: "one matching subscription infers package property", - Cache: cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ - { - Name: "a", - Bundle: &api.Bundle{ - PackageName: "x", - }, + Cache: cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ + { + Name: "a", + Bundle: &api.Bundle{ + PackageName: "x", }, }, }, @@ -1706,6 +1547,47 @@ func TestInferProperties(t *testing.T) { }, }, }, + { + Name: "one matching subscription to other-namespace catalogsource infers package property", + Cache: cache.StaticSourceProvider{ + {Namespace: "other-namespace", Name: "other-name"}: &cache.Snapshot{ + Entries: []*cache.Operator{ + { + Name: "a", + Bundle: &api.Bundle{ + PackageName: "x", + }, + }, + }, + }, + }, + CSV: &v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a", + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + Version: version.OperatorVersion{Version: semver.MustParse("1.2.3")}, + }, + }, + Subscriptions: []*v1alpha1.Subscription{ + { + Spec: &v1alpha1.SubscriptionSpec{ + Package: "x", + CatalogSource: "other-name", + CatalogSourceNamespace: "other-namespace", + }, + Status: v1alpha1.SubscriptionStatus{ + InstalledCSV: "a", + }, + }, + }, + Expected: []*api.Property{ + { + Type: "olm.package", + Value: `{"packageName":"x","version":"1.2.3"}`, + }, + }, + }, { Name: "one matching subscription without catalog entry infers no properties", CSV: &v1alpha1.ClusterServiceVersion{ @@ -1729,16 +1611,13 @@ func TestInferProperties(t *testing.T) { }, { Name: "one matching subscription infers package property without csv version", - Cache: cache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*cache.CatalogSnapshot{ - catalog: { - Key: catalog, - Operators: []*cache.Operator{ - { - Name: "a", - Bundle: &api.Bundle{ - PackageName: "x", - }, + Cache: cache.StaticSourceProvider{ + catalog: &cache.Snapshot{ + Entries: []*cache.Operator{ + { + Name: "a", + Bundle: &api.Bundle{ + PackageName: "x", }, }, }, @@ -1773,10 +1652,8 @@ func TestInferProperties(t *testing.T) { require := require.New(t) logger, _ := test.NewNullLogger() r := SatResolver{ - log: logger, - cache: &FakeOperatorCache{ - fakedNamespacedOperatorCache: tc.Cache, - }, + log: logger, + cache: cache.New(tc.Cache), } actual, err := r.inferProperties(tc.CSV, tc.Subscriptions) require.NoError(err) diff --git a/pkg/controller/registry/resolver/source_registry.go b/pkg/controller/registry/resolver/source_registry.go new file mode 100644 index 0000000000..6c5c11998d --- /dev/null +++ b/pkg/controller/registry/resolver/source_registry.go @@ -0,0 +1,109 @@ +package resolver + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" + "github.com/operator-framework/operator-registry/pkg/api" + "github.com/operator-framework/operator-registry/pkg/client" + opregistry "github.com/operator-framework/operator-registry/pkg/registry" + "github.com/sirupsen/logrus" +) + +type RegistryClientProvider interface { + ClientsForNamespaces(namespaces ...string) map[registry.CatalogKey]client.Interface +} + +type registryClientAdapter struct { + rcp RegistryClientProvider + logger logrus.StdLogger +} + +func SourceProviderFromRegistryClientProvider(rcp RegistryClientProvider, logger logrus.StdLogger) cache.SourceProvider { + return ®istryClientAdapter{ + rcp: rcp, + logger: logger, + } +} + +type registrySource struct { + key cache.SourceKey + client client.Interface + logger logrus.StdLogger +} + +func (s *registrySource) Snapshot(ctx context.Context) (*cache.Snapshot, error) { + // Fetching default channels this way makes many round trips + // -- may need to either add a new API to fetch all at once, + // or embed the information into Bundle. + defaultChannels := make(map[string]string) + + it, err := s.client.ListBundles(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list bundles: %w", err) + } + + var operators []*cache.Operator + for b := it.Next(); b != nil; b = it.Next() { + defaultChannel, ok := defaultChannels[b.PackageName] + if !ok { + if p, err := s.client.GetPackage(ctx, b.PackageName); err != nil { + s.logger.Printf("failed to retrieve default channel for bundle, continuing: %v", err) + continue + } else { + defaultChannels[b.PackageName] = p.DefaultChannelName + defaultChannel = p.DefaultChannelName + } + } + o, err := cache.NewOperatorFromBundle(b, "", s.key, defaultChannel) + if err != nil { + s.logger.Printf("failed to construct operator from bundle, continuing: %v", err) + continue + } + o.ProvidedAPIs = o.ProvidedAPIs.StripPlural() + o.RequiredAPIs = o.RequiredAPIs.StripPlural() + o.Replaces = b.Replaces + EnsurePackageProperty(o, b.PackageName, b.Version) + operators = append(operators, o) + } + if err := it.Error(); err != nil { + return nil, fmt.Errorf("error encountered while listing bundles: %w", err) + } + + return &cache.Snapshot{Entries: operators}, nil +} + +func (a *registryClientAdapter) Sources(namespaces ...string) map[cache.SourceKey]cache.Source { + result := make(map[cache.SourceKey]cache.Source) + for key, client := range a.rcp.ClientsForNamespaces(namespaces...) { + result[cache.SourceKey(key)] = ®istrySource{ + key: cache.SourceKey(key), + client: client, + logger: a.logger, + } + } + return result +} + +func EnsurePackageProperty(o *cache.Operator, name, version string) { + for _, p := range o.Properties { + if p.Type == opregistry.PackageType { + return + } + } + prop := opregistry.PackageProperty{ + PackageName: name, + Version: version, + } + bytes, err := json.Marshal(prop) + if err != nil { + return + } + o.Properties = append(o.Properties, &api.Property{ + Type: opregistry.PackageType, + Value: string(bytes), + }) +} diff --git a/pkg/controller/registry/resolver/step_resolver.go b/pkg/controller/registry/resolver/step_resolver.go index a696dfd657..7574dda57c 100644 --- a/pkg/controller/registry/resolver/step_resolver.go +++ b/pkg/controller/registry/resolver/step_resolver.go @@ -17,7 +17,6 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" v1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" controllerbundle "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "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/lib/operatorlister" @@ -31,7 +30,7 @@ var timeNow = func() metav1.Time { return metav1.NewTime(time.Now().UTC()) } type StepResolver interface { ResolveSteps(namespace string) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) - Expire(key registry.CatalogKey) + Expire(key cache.SourceKey) } type OperatorStepResolver struct { @@ -48,7 +47,7 @@ type OperatorStepResolver struct { var _ StepResolver = &OperatorStepResolver{} func NewOperatorStepResolver(lister operatorlister.OperatorLister, client versioned.Interface, kubeclient kubernetes.Interface, - globalCatalogNamespace string, provider cache.RegistryClientProvider, log logrus.FieldLogger) *OperatorStepResolver { + globalCatalogNamespace string, provider RegistryClientProvider, log logrus.FieldLogger) *OperatorStepResolver { return &OperatorStepResolver{ subLister: lister.OperatorsV1alpha1().SubscriptionLister(), csvLister: lister.OperatorsV1alpha1().ClusterServiceVersionLister(), @@ -56,12 +55,12 @@ func NewOperatorStepResolver(lister operatorlister.OperatorLister, client versio client: client, kubeclient: kubeclient, globalCatalogNamespace: globalCatalogNamespace, - satResolver: NewDefaultSatResolver(cache.NewDefaultRegistryClientProvider(log, provider), lister.OperatorsV1alpha1().CatalogSourceLister(), log), + satResolver: NewDefaultSatResolver(SourceProviderFromRegistryClientProvider(provider, log), lister.OperatorsV1alpha1().CatalogSourceLister(), log), log: log, } } -func (r *OperatorStepResolver) Expire(key registry.CatalogKey) { +func (r *OperatorStepResolver) Expire(key cache.SourceKey) { r.satResolver.cache.Expire(key) } @@ -109,7 +108,7 @@ func (r *OperatorStepResolver) ResolveSteps(namespace string) ([]*v1alpha1.Step, if sub.Spec.Channel != "" && sub.Spec.Channel != sourceInfo.Channel { continue } - subCatalogKey := registry.CatalogKey{ + subCatalogKey := cache.SourceKey{ Name: sub.Spec.CatalogSource, Namespace: sub.Spec.CatalogSourceNamespace, } diff --git a/pkg/controller/registry/resolver/step_resolver_test.go b/pkg/controller/registry/resolver/step_resolver_test.go index eeed54fee1..77faa38b9c 100644 --- a/pkg/controller/registry/resolver/step_resolver_test.go +++ b/pkg/controller/registry/resolver/step_resolver_test.go @@ -23,7 +23,6 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" controllerbundle "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" resolvercache "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" @@ -50,7 +49,7 @@ var ( func TestResolver(t *testing.T) { const namespace = "catsrc-namespace" - catalog := registry.CatalogKey{Name: "catsrc", Namespace: namespace} + catalog := resolvercache.SourceKey{Name: "catsrc", Namespace: namespace} type resolverTestOut struct { steps [][]*v1alpha1.Step @@ -62,7 +61,7 @@ func TestResolver(t *testing.T) { type resolverTest struct { name string clusterState []runtime.Object - bundlesByCatalog map[registry.CatalogKey][]*api.Bundle + bundlesByCatalog map[resolvercache.SourceKey][]*api.Bundle out resolverTestOut } @@ -77,7 +76,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "package", "", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("bundle", "package", "channel", "", nil, nil, nil, nil), }, @@ -114,7 +113,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("bundle", "package", "channel", "", nil, nil, nil, nil), }, @@ -137,7 +136,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("bundle", "a", "channel", "", nil, nil, nil, nil), }, @@ -160,7 +159,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog, withStartingCSV("notfound")), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("bundle", "a", "alpha", "", nil, nil, nil, nil), }, @@ -183,7 +182,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), }, @@ -202,7 +201,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), @@ -224,7 +223,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), stripManifests(withBundlePath(bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), "quay.io/test/bundle@sha256:abcd")), @@ -270,7 +269,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { withBundleObject(bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), u(&rbacv1.RoleBinding{TypeMeta: metav1.TypeMeta{Kind: "RoleBinding", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: "test-rb"}})), bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), @@ -292,7 +291,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { withBundleObject(bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), u(&corev1.Service{TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: ""}, ObjectMeta: metav1.ObjectMeta{Name: "test-service"}})), bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), @@ -314,7 +313,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), }, @@ -348,7 +347,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, @@ -362,7 +361,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "b.v1", "b", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), bundle("b.v1", "b", "alpha", "", Provides1, nil, nil, nil), @@ -380,7 +379,7 @@ func TestResolver(t *testing.T) { newSub(namespace, "a", "alpha", catalog), newSub(namespace, "a", "beta", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), bundle("a.v2", "a", "beta", "", Provides1, nil, nil, nil), @@ -406,7 +405,7 @@ func TestResolver(t *testing.T) { return }(), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, @@ -439,7 +438,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v2", "a", "alpha", "a.v1", Provides1, nil, nil, nil), bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), @@ -460,7 +459,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{catalog: { + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{catalog: { stripManifests(withBundlePath(bundle("a.v2", "a", "alpha", "a.v1", Provides1, nil, nil, nil), "quay.io/test/bundle@sha256:abcd"))}, }, out: resolverTestOut{ @@ -501,7 +500,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ existingSub(namespace, "a.v1", "a", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, @@ -520,7 +519,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, Requires1, nil, nil), @@ -544,7 +543,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", nil, nil, Provides1, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, nil, nil, Requires1), @@ -569,7 +568,7 @@ func TestResolver(t *testing.T) { existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), newSub(namespace, "b", "beta", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, nil, nil, nil), @@ -593,7 +592,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), newSub(namespace, "b", "beta", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), bundle("b.v1", "b", "beta", "", nil, nil, nil, nil), @@ -615,7 +614,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), newSub(namespace, "b", "beta", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, Provides1, nil), bundle("b.v1", "b", "beta", "", nil, nil, nil, nil), @@ -640,7 +639,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "b.v1", "b", "alpha", catalog), existingOperator(namespace, "b.v1", "b", "alpha", "", Provides2, Requires1, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v2", "a", "alpha", "a.v1", Provides3, Requires4, nil, nil), bundle("b.v2", "b", "alpha", "b.v1", Provides4, Requires3, nil, nil), @@ -666,7 +665,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "b.v1", "b", "alpha", catalog), existingOperator(namespace, "b.v1", "b", "alpha", "", nil, Requires1, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v2", "a", "alpha", "a.v1", nil, nil, nil, nil), bundle("b.v2", "b", "alpha", "b.v1", Provides1, nil, nil, nil), @@ -688,7 +687,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "b", "alpha", catalog), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, nil, nil, nil), @@ -712,7 +711,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{catalog: { + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{catalog: { bundle("a.v3", "a", "alpha", "a.v2", nil, nil, nil, nil, withVersion("1.0.0"), withSkipRange("< 1.0.0")), }}, out: resolverTestOut{ @@ -732,9 +731,9 @@ func TestResolver(t *testing.T) { existingOperator(namespace, "a.v1", "a", "alpha", "", nil, Requires1, nil, nil), existingOperator(namespace, "b.v1", "b", "beta", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{ + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{ catalog: { - bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), + bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, Requires1, nil, nil), bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), bundle("b.v2", "b", "beta", "b.v1", Provides1, nil, nil, nil), @@ -757,7 +756,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{catalog: { + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{catalog: { bundle("a.v2", "a", "alpha", "", nil, nil, nil, nil, withVersion("1.0.0"), withSkipRange("< 1.0.0")), bundle("a.v3", "a", "alpha", "a.v2", nil, nil, nil, nil, withVersion("1.0.0"), withSkipRange("< 1.0.0")), bundle("a.v4", "a", "alpha", "a.v3", nil, nil, nil, nil, withVersion("1.0.0"), withSkipRange("< 1.0.0 !0.0.0")), @@ -776,7 +775,7 @@ func TestResolver(t *testing.T) { clusterState: []runtime.Object{ newSub(namespace, "a", "alpha", catalog, withStartingCSV("a.v2")), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{catalog: { + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{catalog: { bundle("a.v1", "a", "alpha", "", nil, nil, nil, nil), bundle("a.v2", "a", "alpha", "a.v1", nil, nil, nil, nil), bundle("a.v3", "a", "alpha", "a.v2", nil, nil, nil, nil, withVersion("1.0.0"), withSkipRange("< 1.0.0")), @@ -796,7 +795,7 @@ func TestResolver(t *testing.T) { existingSub(namespace, "a.v1", "a", "alpha", catalog), existingOperator(namespace, "a.v1", "a", "alpha", "", Provides1, nil, nil, nil), }, - bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{catalog: { + bundlesByCatalog: map[resolvercache.SourceKey][]*api.Bundle{catalog: { bundle("a.v2", "a", "alpha", "", nil, nil, nil, nil, withVersion("1.0.0"), withSkips([]string{"a.v1"})), bundle("a.v3", "a", "alpha", "a.v2", nil, nil, nil, nil, withVersion("1.0.0"), withSkips([]string{"a.v1"})), }}, @@ -826,26 +825,21 @@ func TestResolver(t *testing.T) { lister.OperatorsV1alpha1().RegisterClusterServiceVersionLister(namespace, informerFactory.Operators().V1alpha1().ClusterServiceVersions().Lister()) kClientFake := k8sfake.NewSimpleClientset() - stubSnapshot := &resolvercache.CatalogSnapshot{} - for _, bundles := range tt.bundlesByCatalog { + ssp := make(resolvercache.StaticSourceProvider) + for catalog, bundles := range tt.bundlesByCatalog { + snapshot := &resolvercache.Snapshot{} for _, bundle := range bundles { op, err := resolvercache.NewOperatorFromBundle(bundle, "", catalog, "") if err != nil { t.Fatalf("unexpected error: %v", err) } - stubSnapshot.Operators = append(stubSnapshot.Operators, op) + snapshot.Entries = append(snapshot.Entries, op) } - } - stubCache := &stubOperatorCacheProvider{ - noc: &resolvercache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*resolvercache.CatalogSnapshot{ - catalog: stubSnapshot, - }, - }, + ssp[catalog] = snapshot } log := logrus.New() satresolver := &SatResolver{ - cache: stubCache, + cache: resolvercache.New(ssp), log: log, } resolver := NewOperatorStepResolver(lister, clientFake, kClientFake, "", nil, log) @@ -886,13 +880,13 @@ func (stub *stubOperatorCacheProvider) Namespaced(namespaces ...string) resolver return stub.noc } -func (stub *stubOperatorCacheProvider) Expire(key registry.CatalogKey) { +func (stub *stubOperatorCacheProvider) Expire(key resolvercache.SourceKey) { return } func TestNamespaceResolverRBAC(t *testing.T) { namespace := "catsrc-namespace" - catalog := registry.CatalogKey{"catsrc", namespace} + catalog := resolvercache.SourceKey{"catsrc", namespace} simplePermissions := []v1alpha1.StrategyDeploymentPermissions{ { @@ -978,23 +972,18 @@ func TestNamespaceResolverRBAC(t *testing.T) { lister.OperatorsV1alpha1().RegisterSubscriptionLister(namespace, informerFactory.Operators().V1alpha1().Subscriptions().Lister()) lister.OperatorsV1alpha1().RegisterClusterServiceVersionLister(namespace, informerFactory.Operators().V1alpha1().ClusterServiceVersions().Lister()) - stubSnapshot := &resolvercache.CatalogSnapshot{} + stubSnapshot := &resolvercache.Snapshot{} for _, bundle := range tt.bundlesInCatalog { op, err := resolvercache.NewOperatorFromBundle(bundle, "", catalog, "") if err != nil { t.Fatalf("unexpected error: %v", err) } - stubSnapshot.Operators = append(stubSnapshot.Operators, op) - } - stubCache := &stubOperatorCacheProvider{ - noc: &resolvercache.NamespacedOperatorCache{ - Snapshots: map[registry.CatalogKey]*resolvercache.CatalogSnapshot{ - catalog: stubSnapshot, - }, - }, + stubSnapshot.Entries = append(stubSnapshot.Entries, op) } satresolver := &SatResolver{ - cache: stubCache, + cache: resolvercache.New(resolvercache.StaticSourceProvider{ + catalog: stubSnapshot, + }), } resolver := NewOperatorStepResolver(lister, clientFake, kClientFake, "", nil, logrus.New()) resolver.satResolver = satresolver @@ -1038,7 +1027,7 @@ func withStartingCSV(name string) subOption { } } -func newSub(namespace, pkg, channel string, catalog registry.CatalogKey, option ...subOption) *v1alpha1.Subscription { +func newSub(namespace, pkg, channel string, catalog resolvercache.SourceKey, option ...subOption) *v1alpha1.Subscription { s := &v1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: pkg + "-" + channel, @@ -1057,7 +1046,7 @@ func newSub(namespace, pkg, channel string, catalog registry.CatalogKey, option return s } -func updatedSub(namespace, currentOperatorName, installedOperatorName, pkg, channel string, catalog registry.CatalogKey, option ...subOption) *v1alpha1.Subscription { +func updatedSub(namespace, currentOperatorName, installedOperatorName, pkg, channel string, catalog resolvercache.SourceKey, option ...subOption) *v1alpha1.Subscription { s := &v1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: pkg + "-" + channel, @@ -1080,7 +1069,7 @@ func updatedSub(namespace, currentOperatorName, installedOperatorName, pkg, chan return s } -func existingSub(namespace, operatorName, pkg, channel string, catalog registry.CatalogKey) *v1alpha1.Subscription { +func existingSub(namespace, operatorName, pkg, channel string, catalog resolvercache.SourceKey) *v1alpha1.Subscription { return &v1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: pkg + "-" + channel, @@ -1109,7 +1098,7 @@ func existingOperator(namespace, operatorName, pkg, channel, replaces string, pr return csv } -func bundleSteps(bundle *api.Bundle, ns, replaces string, catalog registry.CatalogKey) []*v1alpha1.Step { +func bundleSteps(bundle *api.Bundle, ns, replaces string, catalog resolvercache.SourceKey) []*v1alpha1.Step { if replaces == "" { csv, _ := V1alpha1CSVFromBundle(bundle) replaces = csv.Spec.Replaces @@ -1142,7 +1131,7 @@ func withoutResourceKind(kind string, steps []*v1alpha1.Step) []*v1alpha1.Step { return filtered } -func subSteps(namespace, operatorName, pkgName, channelName string, catalog registry.CatalogKey) []*v1alpha1.Step { +func subSteps(namespace, operatorName, pkgName, channelName string, catalog resolvercache.SourceKey) []*v1alpha1.Step { sub := &v1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: strings.Join([]string{pkgName, channelName, catalog.Name, catalog.Namespace}, "-"), diff --git a/pkg/fakes/fake_resolver.go b/pkg/fakes/fake_resolver.go index 07ec94e0f3..46f1ba9f8e 100644 --- a/pkg/fakes/fake_resolver.go +++ b/pkg/fakes/fake_resolver.go @@ -5,15 +5,15 @@ import ( "sync" "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" ) type FakeStepResolver struct { - ExpireStub func(registry.CatalogKey) + ExpireStub func(cache.SourceKey) expireMutex sync.RWMutex expireArgsForCall []struct { - arg1 registry.CatalogKey + arg1 cache.SourceKey } ResolveStepsStub func(string) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) resolveStepsMutex sync.RWMutex @@ -36,10 +36,10 @@ type FakeStepResolver struct { invocationsMutex sync.RWMutex } -func (fake *FakeStepResolver) Expire(arg1 registry.CatalogKey) { +func (fake *FakeStepResolver) Expire(arg1 cache.SourceKey) { fake.expireMutex.Lock() fake.expireArgsForCall = append(fake.expireArgsForCall, struct { - arg1 registry.CatalogKey + arg1 cache.SourceKey }{arg1}) fake.recordInvocation("Expire", []interface{}{arg1}) fake.expireMutex.Unlock() @@ -54,13 +54,13 @@ func (fake *FakeStepResolver) ExpireCallCount() int { return len(fake.expireArgsForCall) } -func (fake *FakeStepResolver) ExpireCalls(stub func(registry.CatalogKey)) { +func (fake *FakeStepResolver) ExpireCalls(stub func(cache.SourceKey)) { fake.expireMutex.Lock() defer fake.expireMutex.Unlock() fake.ExpireStub = stub } -func (fake *FakeStepResolver) ExpireArgsForCall(i int) registry.CatalogKey { +func (fake *FakeStepResolver) ExpireArgsForCall(i int) cache.SourceKey { fake.expireMutex.RLock() defer fake.expireMutex.RUnlock() argsForCall := fake.expireArgsForCall[i]