github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"sort"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	"k8s.io/apimachinery/pkg/util/errors"
    13  )
    14  
    15  const existingOperatorKey = "@existing"
    16  
    17  type SourceKey struct {
    18  	Name      string
    19  	Namespace string
    20  }
    21  
    22  func (k *SourceKey) String() string {
    23  	return fmt.Sprintf("%s/%s", k.Name, k.Namespace)
    24  }
    25  
    26  func (k *SourceKey) Empty() bool {
    27  	return k.Name == "" && k.Namespace == ""
    28  }
    29  
    30  func (k *SourceKey) Equal(compare SourceKey) bool {
    31  	return k.Name == compare.Name && k.Namespace == compare.Namespace
    32  }
    33  
    34  // Virtual indicates if this is a "virtual" catalog representing the currently installed operators in a namespace
    35  func (k *SourceKey) Virtual() bool {
    36  	return k.Name == existingOperatorKey && k.Namespace != ""
    37  }
    38  
    39  func NewVirtualSourceKey(namespace string) SourceKey {
    40  	return SourceKey{
    41  		Name:      existingOperatorKey,
    42  		Namespace: namespace,
    43  	}
    44  }
    45  
    46  type Source interface {
    47  	Snapshot(context.Context) (*Snapshot, error)
    48  }
    49  
    50  type SourceProvider interface {
    51  	// TODO: namespaces parameter is an artifact of SourceStore
    52  	Sources(namespaces ...string) map[SourceKey]Source
    53  }
    54  
    55  type StaticSourceProvider map[SourceKey]Source
    56  
    57  func (p StaticSourceProvider) Sources(namespaces ...string) map[SourceKey]Source {
    58  	result := make(map[SourceKey]Source)
    59  	for key, source := range p {
    60  		for _, namespace := range namespaces {
    61  			if key.Namespace == namespace {
    62  				result[key] = source
    63  				break
    64  			}
    65  		}
    66  	}
    67  	return result
    68  }
    69  
    70  type OperatorCacheProvider interface {
    71  	Namespaced(namespaces ...string) MultiCatalogOperatorFinder
    72  }
    73  
    74  type SourcePriorityProvider interface {
    75  	Priority(SourceKey) int
    76  }
    77  
    78  type constantSourcePriorityProvider int
    79  
    80  func (spp constantSourcePriorityProvider) Priority(SourceKey) int {
    81  	return int(spp)
    82  }
    83  
    84  type Cache struct {
    85  	logger                 logrus.StdLogger
    86  	sp                     SourceProvider
    87  	sourcePriorityProvider SourcePriorityProvider
    88  	snapshots              map[SourceKey]*snapshotHeader
    89  	sem                    chan struct{}
    90  	m                      sync.RWMutex
    91  }
    92  
    93  type Option func(*Cache)
    94  
    95  func WithLogger(logger logrus.StdLogger) Option {
    96  	return func(c *Cache) {
    97  		c.logger = logger
    98  	}
    99  }
   100  
   101  func WithSourcePriorityProvider(spp SourcePriorityProvider) Option {
   102  	return func(c *Cache) {
   103  		c.sourcePriorityProvider = spp
   104  	}
   105  }
   106  
   107  func New(sp SourceProvider, options ...Option) *Cache {
   108  	const (
   109  		MaxConcurrentSnapshotUpdates = 4
   110  	)
   111  
   112  	cache := Cache{
   113  		logger: func() logrus.StdLogger {
   114  			logger := logrus.New()
   115  			logger.SetOutput(io.Discard)
   116  			return logger
   117  		}(),
   118  		sp:                     sp,
   119  		sourcePriorityProvider: constantSourcePriorityProvider(0),
   120  		snapshots:              make(map[SourceKey]*snapshotHeader),
   121  		sem:                    make(chan struct{}, MaxConcurrentSnapshotUpdates),
   122  	}
   123  
   124  	for _, opt := range options {
   125  		opt(&cache)
   126  	}
   127  
   128  	return &cache
   129  }
   130  
   131  type NamespacedOperatorCache struct {
   132  	snapshots map[SourceKey]*snapshotHeader
   133  }
   134  
   135  func (c *NamespacedOperatorCache) Error() error {
   136  	var errs []error
   137  	for key, snapshot := range c.snapshots {
   138  		snapshot.m.RLock()
   139  		err := snapshot.err
   140  		snapshot.m.RUnlock()
   141  		if err != nil {
   142  			errs = append(errs, fmt.Errorf("error using catalogsource %s/%s: %w", key.Namespace, key.Name, err))
   143  		}
   144  	}
   145  	return errors.NewAggregate(errs)
   146  }
   147  
   148  func (c *Cache) Namespaced(namespaces ...string) MultiCatalogOperatorFinder {
   149  	const (
   150  		CachePopulateTimeout = time.Minute
   151  	)
   152  
   153  	sources := c.sp.Sources(namespaces...)
   154  
   155  	result := NamespacedOperatorCache{
   156  		snapshots: make(map[SourceKey]*snapshotHeader),
   157  	}
   158  
   159  	var misses []SourceKey
   160  	func() {
   161  		c.m.RLock()
   162  		defer c.m.RUnlock()
   163  		for key := range sources {
   164  			snapshot, ok := c.snapshots[key]
   165  			if ok {
   166  				func() {
   167  					snapshot.m.RLock()
   168  					defer snapshot.m.RUnlock()
   169  					if snapshot.Valid() {
   170  						result.snapshots[key] = snapshot
   171  					} else {
   172  						misses = append(misses, key)
   173  					}
   174  				}()
   175  			}
   176  			if !ok {
   177  				misses = append(misses, key)
   178  			}
   179  		}
   180  	}()
   181  
   182  	if len(misses) == 0 {
   183  		return &result
   184  	}
   185  
   186  	c.m.Lock()
   187  	defer c.m.Unlock()
   188  
   189  	// Take the opportunity to clear expired snapshots while holding the lock.
   190  	var expired []SourceKey
   191  	for key, snapshot := range c.snapshots {
   192  		if !snapshot.Valid() {
   193  			snapshot.Cancel()
   194  			expired = append(expired, key)
   195  		}
   196  	}
   197  	for _, key := range expired {
   198  		delete(c.snapshots, key)
   199  	}
   200  
   201  	// Check for any snapshots that were populated while waiting to acquire the lock.
   202  	var found int
   203  	for i := range misses {
   204  		if hdr, ok := c.snapshots[misses[i]]; ok && hdr.Valid() {
   205  			result.snapshots[misses[i]] = hdr
   206  			misses[found], misses[i] = misses[i], misses[found]
   207  			found++
   208  		}
   209  	}
   210  	misses = misses[found:]
   211  
   212  	for _, miss := range misses {
   213  		ctx, cancel := context.WithTimeout(context.Background(), CachePopulateTimeout)
   214  
   215  		hdr := snapshotHeader{
   216  			key:      miss,
   217  			pop:      cancel,
   218  			priority: c.sourcePriorityProvider.Priority(miss),
   219  		}
   220  
   221  		hdr.m.Lock()
   222  		c.snapshots[miss] = &hdr
   223  		result.snapshots[miss] = &hdr
   224  
   225  		go func(ctx context.Context, hdr *snapshotHeader, source Source) {
   226  			defer hdr.m.Unlock()
   227  			c.sem <- struct{}{}
   228  			defer func() { <-c.sem }()
   229  			if snapshot, err := source.Snapshot(ctx); err != nil {
   230  				hdr.err = err
   231  			} else if snapshot != nil {
   232  				hdr.snapshot = snapshot
   233  			} else {
   234  				hdr.err = fmt.Errorf("source %q produced no snapshot and no error", hdr.key)
   235  			}
   236  		}(ctx, &hdr, sources[miss])
   237  	}
   238  
   239  	return &result
   240  }
   241  
   242  func (c *NamespacedOperatorCache) Catalog(k SourceKey) OperatorFinder {
   243  	// all catalogs match the empty catalog
   244  	if k.Empty() {
   245  		return c
   246  	}
   247  	if snapshot, ok := c.snapshots[k]; ok {
   248  		return snapshot
   249  	}
   250  	return EmptyOperatorFinder{}
   251  }
   252  
   253  func (c *NamespacedOperatorCache) FindPreferred(preferred *SourceKey, preferredNamespace string, p ...Predicate) []*Entry {
   254  	var result []*Entry
   255  	if preferred != nil && preferred.Empty() {
   256  		preferred = nil
   257  	}
   258  	sorted := newSortableSnapshots(preferred, preferredNamespace, c.snapshots)
   259  	sort.Sort(sorted)
   260  	for _, snapshot := range sorted.snapshots {
   261  		result = append(result, snapshot.Find(p...)...)
   262  	}
   263  	return result
   264  }
   265  
   266  func (c *NamespacedOperatorCache) Find(p ...Predicate) []*Entry {
   267  	return c.FindPreferred(nil, "", p...)
   268  }
   269  
   270  type Snapshot struct {
   271  	Entries []*Entry
   272  
   273  	// Unless closed, the Snapshot is valid.
   274  	Valid <-chan struct{}
   275  }
   276  
   277  func ValidOnce() <-chan struct{} {
   278  	c := make(chan struct{})
   279  	close(c)
   280  	return c
   281  }
   282  
   283  var _ Source = &Snapshot{}
   284  
   285  func (s *Snapshot) Snapshot(context.Context) (*Snapshot, error) {
   286  	return s, nil
   287  }
   288  
   289  type snapshotHeader struct {
   290  	snapshot *Snapshot
   291  
   292  	key      SourceKey
   293  	m        sync.RWMutex
   294  	pop      context.CancelFunc
   295  	err      error
   296  	priority int
   297  }
   298  
   299  func (hdr *snapshotHeader) Cancel() {
   300  	hdr.pop()
   301  }
   302  
   303  func (hdr *snapshotHeader) Valid() bool {
   304  	hdr.m.RLock()
   305  	defer hdr.m.RUnlock()
   306  	if hdr.snapshot == nil || hdr.err != nil {
   307  		return false
   308  	}
   309  	select {
   310  	case <-hdr.snapshot.Valid:
   311  		return false
   312  	default:
   313  	}
   314  	return true
   315  }
   316  
   317  type sortableSnapshots struct {
   318  	snapshots          []*snapshotHeader
   319  	preferredNamespace string
   320  	preferred          *SourceKey
   321  	existing           *SourceKey
   322  }
   323  
   324  func newSortableSnapshots(preferred *SourceKey, preferredNamespace string, snapshots map[SourceKey]*snapshotHeader) sortableSnapshots {
   325  	var existing *SourceKey
   326  	for key := range snapshots {
   327  		if key.Virtual() && key.Namespace == preferredNamespace {
   328  			existing = &key
   329  			break
   330  		}
   331  	}
   332  	sorted := sortableSnapshots{
   333  		existing:           existing,
   334  		preferred:          preferred,
   335  		snapshots:          make([]*snapshotHeader, 0),
   336  		preferredNamespace: preferredNamespace,
   337  	}
   338  	for _, s := range snapshots {
   339  		sorted.snapshots = append(sorted.snapshots, s)
   340  	}
   341  	return sorted
   342  }
   343  
   344  var _ sort.Interface = sortableSnapshots{}
   345  
   346  // Len is the number of elements in the collection.
   347  func (s sortableSnapshots) Len() int {
   348  	return len(s.snapshots)
   349  }
   350  
   351  // Less reports whether the element with
   352  // index i should sort before the element with index j.
   353  func (s sortableSnapshots) Less(i, j int) bool {
   354  	// existing operators are preferred over catalog operators
   355  	if s.existing != nil &&
   356  		s.snapshots[i].key.Name == s.existing.Name &&
   357  		s.snapshots[i].key.Namespace == s.existing.Namespace {
   358  		return true
   359  	}
   360  	if s.existing != nil &&
   361  		s.snapshots[j].key.Name == s.existing.Name &&
   362  		s.snapshots[j].key.Namespace == s.existing.Namespace {
   363  		return false
   364  	}
   365  
   366  	// preferred catalog is less than all other catalogs
   367  	if s.preferred != nil &&
   368  		s.snapshots[i].key.Name == s.preferred.Name &&
   369  		s.snapshots[i].key.Namespace == s.preferred.Namespace {
   370  		return true
   371  	}
   372  	if s.preferred != nil &&
   373  		s.snapshots[j].key.Name == s.preferred.Name &&
   374  		s.snapshots[j].key.Namespace == s.preferred.Namespace {
   375  		return false
   376  	}
   377  
   378  	// the rest are sorted first on priority, namespace and then by name
   379  	if s.snapshots[i].priority != s.snapshots[j].priority {
   380  		return s.snapshots[i].priority > s.snapshots[j].priority
   381  	}
   382  
   383  	if s.snapshots[i].key.Namespace != s.snapshots[j].key.Namespace {
   384  		if s.snapshots[i].key.Namespace == s.preferredNamespace {
   385  			return true
   386  		}
   387  		if s.snapshots[j].key.Namespace == s.preferredNamespace {
   388  			return false
   389  		}
   390  	}
   391  
   392  	return s.snapshots[i].key.Name < s.snapshots[j].key.Name
   393  }
   394  
   395  // Swap swaps the elements with indexes i and j.
   396  func (s sortableSnapshots) Swap(i, j int) {
   397  	s.snapshots[i], s.snapshots[j] = s.snapshots[j], s.snapshots[i]
   398  }
   399  
   400  func (hdr *snapshotHeader) Find(p ...Predicate) []*Entry {
   401  	hdr.m.RLock()
   402  	defer hdr.m.RUnlock()
   403  	if hdr.snapshot == nil {
   404  		return nil
   405  	}
   406  	return Filter(hdr.snapshot.Entries, p...)
   407  }
   408  
   409  type OperatorFinder interface {
   410  	Find(...Predicate) []*Entry
   411  }
   412  
   413  type MultiCatalogOperatorFinder interface {
   414  	Catalog(SourceKey) OperatorFinder
   415  	FindPreferred(preferred *SourceKey, preferredNamespace string, predicates ...Predicate) []*Entry
   416  	Error() error
   417  	OperatorFinder
   418  }
   419  
   420  type EmptyOperatorFinder struct{}
   421  
   422  func (f EmptyOperatorFinder) Find(...Predicate) []*Entry {
   423  	return nil
   424  }
   425  
   426  func ExactlyOne(operators []*Entry) (*Entry, error) {
   427  	if len(operators) != 1 {
   428  		return nil, fmt.Errorf("expected exactly one operator, got %d", len(operators))
   429  	}
   430  	return operators[0], nil
   431  }
   432  
   433  func Filter(operators []*Entry, p ...Predicate) []*Entry {
   434  	var result []*Entry
   435  	for _, o := range operators {
   436  		if Matches(o, p...) {
   437  			result = append(result, o)
   438  		}
   439  	}
   440  	return result
   441  }
   442  
   443  func Matches(o *Entry, p ...Predicate) bool {
   444  	return And(p...).Test(o)
   445  }