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