func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error { logger := watcher.logger.Session("watcher") logger.Info("starting") defer logger.Info("finished") var subscription events.EventSource subscriptionChan := make(chan events.EventSource, 1) go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) eventChan := make(chan models.Event, 1) nextErrCount := 0 close(ready) logger.Info("started") for { select { case subscription = <-subscriptionChan: if subscription != nil { go nextEvent(logger, subscription, eventChan) } else { go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) } case event := <-eventChan: if event != nil { watcher.handleEvent(logger, event) } else { nextErrCount += 1 if nextErrCount > 2 { nextErrCount = 0 go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) break } } go nextEvent(logger, subscription, eventChan) case <-signals: logger.Info("stopping") err := subscription.Close() if err != nil { logger.Error("failed-closing-event-source", err) } return nil } } }
func nextEvent(logger lager.Logger, es events.EventSource, eventChan chan<- models.Event) { event, err := es.Next() switch err { case nil: eventChan <- event case events.ErrSourceClosed: return default: logger.Error("failed-getting-next-event", err) // wait a bit before retrying time.Sleep(time.Second) eventChan <- nil } }
"github.com/cloudfoundry-incubator/bbs/events" "github.com/cloudfoundry-incubator/bbs/models" sonde_events "github.com/cloudfoundry/sonde-go/events" "github.com/tedsuo/ifrit/ginkgomon" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Events API", func() { var ( done chan struct{} eventChannel chan models.Event eventSource events.EventSource baseLRP *models.ActualLRP desiredLRP *models.DesiredLRP key models.ActualLRPKey instanceKey models.ActualLRPInstanceKey newInstanceKey models.ActualLRPInstanceKey netInfo models.ActualLRPNetInfo ) BeforeEach(func() { bbsRunner = testrunner.New(bbsBinPath, bbsArgs) bbsProcess = ginkgomon.Invoke(bbsRunner) }) JustBeforeEach(func() { var err error eventSource, err = client.SubscribeToEvents() Expect(err).NotTo(HaveOccurred())
. "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Hub", func() { var ( hub events.Hub ) BeforeEach(func() { hub = events.NewHub() }) Describe("RegisterCallback", func() { Context("when registering the callback", func() { var eventSource events.EventSource var counts chan int BeforeEach(func() { var err error eventSource, err = hub.Subscribe() Expect(err).NotTo(HaveOccurred()) counts = make(chan int, 1) cbCounts := counts hub.RegisterCallback(func(count int) { cbCounts <- count }) }) It("calls the callback immediately with the current subscriber count", func() {
func (h *EventStreamHandler) EventStream(w http.ResponseWriter, req *http.Request) { logger := h.logger.Session("event-stream-handler") closeNotifier := w.(http.CloseNotifier).CloseNotify() sourceChan := make(chan events.EventSource) flusher := w.(http.Flusher) go func() { source, err := h.bbs.SubscribeToEvents() if err != nil { logger.Error("failed-to-subscribe-to-events", err) close(sourceChan) return } sourceChan <- source }() var source events.EventSource select { case source = <-sourceChan: if source == nil { w.WriteHeader(http.StatusInternalServerError) return } case <-closeNotifier: return } defer source.Close() go func() { <-closeNotifier source.Close() }() w.Header().Add("Content-Type", "text/event-stream; charset=utf-8") w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Add("Connection", "keep-alive") w.WriteHeader(http.StatusOK) flusher.Flush() eventID := 0 for { bbsEvent, err := source.Next() if err != nil { logger.Error("failed-to-get-next-event", err) return } event, err := NewEventFromBBS(bbsEvent) if err != nil { logger.Error("failed-to-marshal-event", err) return } payload, err := json.Marshal(event) if err != nil { logger.Error("failed-to-marshal-event", err) return } err = sse.Event{ ID: strconv.Itoa(eventID), Name: string(event.EventType()), Data: payload, }.Write(w) if err != nil { break } flusher.Flush() eventID++ } }
) var _ = Describe("Events API", func() { Describe("Actual LRPs", func() { const ( processGuid = "some-process-guid" domain = "some-domain" noExpirationTTL = 0 ) var ( done chan struct{} eventChannel chan models.Event eventSource events.EventSource baseLRP *models.ActualLRP key models.ActualLRPKey instanceKey models.ActualLRPInstanceKey newInstanceKey models.ActualLRPInstanceKey netInfo models.ActualLRPNetInfo ) JustBeforeEach(func() { var err error eventSource, err = client.SubscribeToEvents() Expect(err).NotTo(HaveOccurred()) eventChannel = make(chan models.Event) done = make(chan struct{}) go func() {
func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error { watcher.logger.Info("starting") close(ready) watcher.logger.Info("started") defer watcher.logger.Info("finished") var cachedEvents map[string]models.Event eventChan := make(chan models.Event) syncEndChan := make(chan syncEndEvent) syncing := false var eventSource atomic.Value var stopEventSource int32 startEventSource := func() { go func() { var err error var es events.EventSource for { if atomic.LoadInt32(&stopEventSource) == 1 { return } es, err = watcher.bbsClient.SubscribeToEvents() if err != nil { watcher.logger.Error("failed-subscribing-to-events", err) continue } eventSource.Store(es) var event models.Event for { event, err = es.Next() if err != nil { watcher.logger.Error("failed-getting-next-event", err) // wait a bit before retrying time.Sleep(time.Second) break } if event != nil { eventChan <- event } } } }() } startedEventSource := false for { select { case <-watcher.syncEvents.Sync: if syncing == false { logger := watcher.logger.Session("sync") logger.Info("starting") syncing = true if !startedEventSource { startedEventSource = true startEventSource() } cachedEvents = make(map[string]models.Event) go watcher.sync(logger, syncEndChan) } case syncEnd := <-syncEndChan: watcher.completeSync(syncEnd, cachedEvents) cachedEvents = nil syncing = false syncEnd.logger.Info("complete") case <-watcher.syncEvents.Emit: logger := watcher.logger.Session("emit") watcher.emit(logger) case event := <-eventChan: if syncing { watcher.logger.Info("caching-event", lager.Data{ "type": event.EventType(), }) cachedEvents[event.Key()] = event } else { watcher.handleEvent(watcher.logger, event) } case <-signals: watcher.logger.Info("stopping") atomic.StoreInt32(&stopEventSource, 1) if es := eventSource.Load(); es != nil { err := es.(events.EventSource).Close() if err != nil { watcher.logger.Error("failed-closing-event-source", err) } } return nil } } }