github.com/argoproj/argo-cd/v3@v3.2.1/server/application/broadcaster.go (about) 1 package application 2 3 import ( 4 "sync" 5 6 log "github.com/sirupsen/logrus" 7 "k8s.io/apimachinery/pkg/watch" 8 9 appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 10 applog "github.com/argoproj/argo-cd/v3/util/app/log" 11 ) 12 13 type subscriber struct { 14 ch chan *appv1.ApplicationWatchEvent 15 filters []func(*appv1.ApplicationWatchEvent) bool 16 } 17 18 func (s *subscriber) matches(event *appv1.ApplicationWatchEvent) bool { 19 for i := range s.filters { 20 if !s.filters[i](event) { 21 return false 22 } 23 } 24 return true 25 } 26 27 // Broadcaster is an interface for broadcasting application informer watch events to multiple subscribers. 28 type Broadcaster interface { 29 Subscribe(ch chan *appv1.ApplicationWatchEvent, filters ...func(event *appv1.ApplicationWatchEvent) bool) func() 30 OnAdd(any, bool) 31 OnUpdate(any, any) 32 OnDelete(any) 33 } 34 35 type broadcasterHandler struct { 36 lock sync.Mutex 37 subscribers []*subscriber 38 } 39 40 func (b *broadcasterHandler) notify(event *appv1.ApplicationWatchEvent) { 41 // Make a local copy of b.subscribers, then send channel events outside the lock, 42 // to avoid data race on b.subscribers changes 43 subscribers := []*subscriber{} 44 b.lock.Lock() 45 subscribers = append(subscribers, b.subscribers...) 46 b.lock.Unlock() 47 48 for _, s := range subscribers { 49 if s.matches(event) { 50 select { 51 case s.ch <- event: 52 default: 53 // drop event if cannot send right away 54 log.WithFields(applog.GetAppLogFields(&event.Application)).Warn("unable to send event notification") 55 } 56 } 57 } 58 } 59 60 // Subscribe forward application informer watch events to the provided channel. 61 // The watch events are dropped if no receives are reading events from the channel so the channel must have 62 // buffer if dropping events is not acceptable. 63 func (b *broadcasterHandler) Subscribe(ch chan *appv1.ApplicationWatchEvent, filters ...func(event *appv1.ApplicationWatchEvent) bool) func() { 64 b.lock.Lock() 65 defer b.lock.Unlock() 66 subscriber := &subscriber{ch, filters} 67 b.subscribers = append(b.subscribers, subscriber) 68 return func() { 69 b.lock.Lock() 70 defer b.lock.Unlock() 71 for i := range b.subscribers { 72 if b.subscribers[i] == subscriber { 73 b.subscribers = append(b.subscribers[:i], b.subscribers[i+1:]...) 74 break 75 } 76 } 77 } 78 } 79 80 func (b *broadcasterHandler) OnAdd(obj any, _ bool) { 81 if app, ok := obj.(*appv1.Application); ok { 82 b.notify(&appv1.ApplicationWatchEvent{Application: *app, Type: watch.Added}) 83 } 84 } 85 86 func (b *broadcasterHandler) OnUpdate(_, newObj any) { 87 if app, ok := newObj.(*appv1.Application); ok { 88 b.notify(&appv1.ApplicationWatchEvent{Application: *app, Type: watch.Modified}) 89 } 90 } 91 92 func (b *broadcasterHandler) OnDelete(obj any) { 93 if app, ok := obj.(*appv1.Application); ok { 94 b.notify(&appv1.ApplicationWatchEvent{Application: *app, Type: watch.Deleted}) 95 } 96 }