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 }