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  }