github.com/opsramp/moby@v1.13.1/daemon/events/events.go (about) 1 package events 2 3 import ( 4 "sync" 5 "time" 6 7 eventtypes "github.com/docker/docker/api/types/events" 8 "github.com/docker/docker/pkg/pubsub" 9 ) 10 11 const ( 12 eventsLimit = 64 13 bufferSize = 1024 14 ) 15 16 // Events is pubsub channel for events generated by the engine. 17 type Events struct { 18 mu sync.Mutex 19 events []eventtypes.Message 20 pub *pubsub.Publisher 21 } 22 23 // New returns new *Events instance 24 func New() *Events { 25 return &Events{ 26 events: make([]eventtypes.Message, 0, eventsLimit), 27 pub: pubsub.NewPublisher(100*time.Millisecond, bufferSize), 28 } 29 } 30 31 // Subscribe adds new listener to events, returns slice of 64 stored 32 // last events, a channel in which you can expect new events (in form 33 // of interface{}, so you need type assertion), and a function to call 34 // to stop the stream of events. 35 func (e *Events) Subscribe() ([]eventtypes.Message, chan interface{}, func()) { 36 eventSubscribers.Inc() 37 e.mu.Lock() 38 current := make([]eventtypes.Message, len(e.events)) 39 copy(current, e.events) 40 l := e.pub.Subscribe() 41 e.mu.Unlock() 42 43 cancel := func() { 44 e.Evict(l) 45 } 46 return current, l, cancel 47 } 48 49 // SubscribeTopic adds new listener to events, returns slice of 64 stored 50 // last events, a channel in which you can expect new events (in form 51 // of interface{}, so you need type assertion). 52 func (e *Events) SubscribeTopic(since, until time.Time, ef *Filter) ([]eventtypes.Message, chan interface{}) { 53 eventSubscribers.Inc() 54 e.mu.Lock() 55 56 var topic func(m interface{}) bool 57 if ef != nil && ef.filter.Len() > 0 { 58 topic = func(m interface{}) bool { return ef.Include(m.(eventtypes.Message)) } 59 } 60 61 buffered := e.loadBufferedEvents(since, until, topic) 62 63 var ch chan interface{} 64 if topic != nil { 65 ch = e.pub.SubscribeTopic(topic) 66 } else { 67 // Subscribe to all events if there are no filters 68 ch = e.pub.Subscribe() 69 } 70 71 e.mu.Unlock() 72 return buffered, ch 73 } 74 75 // Evict evicts listener from pubsub 76 func (e *Events) Evict(l chan interface{}) { 77 eventSubscribers.Dec() 78 e.pub.Evict(l) 79 } 80 81 // Log broadcasts event to listeners. Each listener has 100 millisecond for 82 // receiving event or it will be skipped. 83 func (e *Events) Log(action, eventType string, actor eventtypes.Actor) { 84 eventsCounter.Inc() 85 now := time.Now().UTC() 86 jm := eventtypes.Message{ 87 Action: action, 88 Type: eventType, 89 Actor: actor, 90 Time: now.Unix(), 91 TimeNano: now.UnixNano(), 92 } 93 94 // fill deprecated fields for container and images 95 switch eventType { 96 case eventtypes.ContainerEventType: 97 jm.ID = actor.ID 98 jm.Status = action 99 jm.From = actor.Attributes["image"] 100 case eventtypes.ImageEventType: 101 jm.ID = actor.ID 102 jm.Status = action 103 } 104 105 e.mu.Lock() 106 if len(e.events) == cap(e.events) { 107 // discard oldest event 108 copy(e.events, e.events[1:]) 109 e.events[len(e.events)-1] = jm 110 } else { 111 e.events = append(e.events, jm) 112 } 113 e.mu.Unlock() 114 e.pub.Publish(jm) 115 } 116 117 // SubscribersCount returns number of event listeners 118 func (e *Events) SubscribersCount() int { 119 return e.pub.Len() 120 } 121 122 // loadBufferedEvents iterates over the cached events in the buffer 123 // and returns those that were emitted between two specific dates. 124 // It uses `time.Unix(seconds, nanoseconds)` to generate valid dates with those arguments. 125 // It filters those buffered messages with a topic function if it's not nil, otherwise it adds all messages. 126 func (e *Events) loadBufferedEvents(since, until time.Time, topic func(interface{}) bool) []eventtypes.Message { 127 var buffered []eventtypes.Message 128 if since.IsZero() && until.IsZero() { 129 return buffered 130 } 131 132 var sinceNanoUnix int64 133 if !since.IsZero() { 134 sinceNanoUnix = since.UnixNano() 135 } 136 137 var untilNanoUnix int64 138 if !until.IsZero() { 139 untilNanoUnix = until.UnixNano() 140 } 141 142 for i := len(e.events) - 1; i >= 0; i-- { 143 ev := e.events[i] 144 145 if ev.TimeNano < sinceNanoUnix { 146 break 147 } 148 149 if untilNanoUnix > 0 && ev.TimeNano > untilNanoUnix { 150 continue 151 } 152 153 if topic == nil || topic(ev) { 154 buffered = append([]eventtypes.Message{ev}, buffered...) 155 } 156 } 157 return buffered 158 }