github.com/lmars/docker@v1.6.0-rc2/events/events.go (about) 1 package events 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/docker/docker/engine" 12 "github.com/docker/docker/pkg/parsers/filters" 13 "github.com/docker/docker/utils" 14 ) 15 16 const eventsLimit = 64 17 18 type listener chan<- *utils.JSONMessage 19 20 type Events struct { 21 mu sync.RWMutex 22 events []*utils.JSONMessage 23 subscribers []listener 24 } 25 26 func New() *Events { 27 return &Events{ 28 events: make([]*utils.JSONMessage, 0, eventsLimit), 29 } 30 } 31 32 // Install installs events public api in docker engine 33 func (e *Events) Install(eng *engine.Engine) error { 34 // Here you should describe public interface 35 jobs := map[string]engine.Handler{ 36 "events": e.Get, 37 "log": e.Log, 38 "subscribers_count": e.SubscribersCount, 39 } 40 for name, job := range jobs { 41 if err := eng.Register(name, job); err != nil { 42 return err 43 } 44 } 45 return nil 46 } 47 48 func (e *Events) Get(job *engine.Job) engine.Status { 49 var ( 50 since = job.GetenvInt64("since") 51 until = job.GetenvInt64("until") 52 timeout = time.NewTimer(time.Unix(until, 0).Sub(time.Now())) 53 ) 54 55 eventFilters, err := filters.FromParam(job.Getenv("filters")) 56 if err != nil { 57 return job.Error(err) 58 } 59 60 // If no until, disable timeout 61 if until == 0 { 62 timeout.Stop() 63 } 64 65 listener := make(chan *utils.JSONMessage) 66 e.subscribe(listener) 67 defer e.unsubscribe(listener) 68 69 job.Stdout.Write(nil) 70 71 // Resend every event in the [since, until] time interval. 72 if since != 0 { 73 if err := e.writeCurrent(job, since, until, eventFilters); err != nil { 74 return job.Error(err) 75 } 76 } 77 78 for { 79 select { 80 case event, ok := <-listener: 81 if !ok { 82 return engine.StatusOK 83 } 84 if err := writeEvent(job, event, eventFilters); err != nil { 85 return job.Error(err) 86 } 87 case <-timeout.C: 88 return engine.StatusOK 89 } 90 } 91 } 92 93 func (e *Events) Log(job *engine.Job) engine.Status { 94 if len(job.Args) != 3 { 95 return job.Errorf("usage: %s ACTION ID FROM", job.Name) 96 } 97 // not waiting for receivers 98 go e.log(job.Args[0], job.Args[1], job.Args[2]) 99 return engine.StatusOK 100 } 101 102 func (e *Events) SubscribersCount(job *engine.Job) engine.Status { 103 ret := &engine.Env{} 104 ret.SetInt("count", e.subscribersCount()) 105 ret.WriteTo(job.Stdout) 106 return engine.StatusOK 107 } 108 109 func writeEvent(job *engine.Job, event *utils.JSONMessage, eventFilters filters.Args) error { 110 isFiltered := func(field string, filter []string) bool { 111 if len(filter) == 0 { 112 return false 113 } 114 for _, v := range filter { 115 if v == field { 116 return false 117 } 118 if strings.Contains(field, ":") { 119 image := strings.Split(field, ":") 120 if image[0] == v { 121 return false 122 } 123 } 124 } 125 return true 126 } 127 128 //incoming container filter can be name,id or partial id, convert and replace as a full container id 129 for i, cn := range eventFilters["container"] { 130 eventFilters["container"][i] = GetContainerId(job.Eng, cn) 131 } 132 133 if isFiltered(event.Status, eventFilters["event"]) || isFiltered(event.From, eventFilters["image"]) || 134 isFiltered(event.ID, eventFilters["container"]) { 135 return nil 136 } 137 138 // When sending an event JSON serialization errors are ignored, but all 139 // other errors lead to the eviction of the listener. 140 if b, err := json.Marshal(event); err == nil { 141 if _, err = job.Stdout.Write(b); err != nil { 142 return err 143 } 144 } 145 return nil 146 } 147 148 func (e *Events) writeCurrent(job *engine.Job, since, until int64, eventFilters filters.Args) error { 149 e.mu.RLock() 150 for _, event := range e.events { 151 if event.Time >= since && (event.Time <= until || until == 0) { 152 if err := writeEvent(job, event, eventFilters); err != nil { 153 e.mu.RUnlock() 154 return err 155 } 156 } 157 } 158 e.mu.RUnlock() 159 return nil 160 } 161 162 func (e *Events) subscribersCount() int { 163 e.mu.RLock() 164 c := len(e.subscribers) 165 e.mu.RUnlock() 166 return c 167 } 168 169 func (e *Events) log(action, id, from string) { 170 e.mu.Lock() 171 now := time.Now().UTC().Unix() 172 jm := &utils.JSONMessage{Status: action, ID: id, From: from, Time: now} 173 if len(e.events) == cap(e.events) { 174 // discard oldest event 175 copy(e.events, e.events[1:]) 176 e.events[len(e.events)-1] = jm 177 } else { 178 e.events = append(e.events, jm) 179 } 180 for _, s := range e.subscribers { 181 // We give each subscriber a 100ms time window to receive the event, 182 // after which we move to the next. 183 select { 184 case s <- jm: 185 case <-time.After(100 * time.Millisecond): 186 } 187 } 188 e.mu.Unlock() 189 } 190 191 func (e *Events) subscribe(l listener) { 192 e.mu.Lock() 193 e.subscribers = append(e.subscribers, l) 194 e.mu.Unlock() 195 } 196 197 // unsubscribe closes and removes the specified listener from the list of 198 // previously registed ones. 199 // It returns a boolean value indicating if the listener was successfully 200 // found, closed and unregistered. 201 func (e *Events) unsubscribe(l listener) bool { 202 e.mu.Lock() 203 for i, subscriber := range e.subscribers { 204 if subscriber == l { 205 close(l) 206 e.subscribers = append(e.subscribers[:i], e.subscribers[i+1:]...) 207 e.mu.Unlock() 208 return true 209 } 210 } 211 e.mu.Unlock() 212 return false 213 } 214 215 func GetContainerId(eng *engine.Engine, name string) string { 216 var buf bytes.Buffer 217 job := eng.Job("container_inspect", name) 218 219 var outStream io.Writer 220 221 outStream = &buf 222 job.Stdout.Set(outStream) 223 224 if err := job.Run(); err != nil { 225 return "" 226 } 227 var out struct{ ID string } 228 json.NewDecoder(&buf).Decode(&out) 229 return out.ID 230 }