github.com/daaku/docker@v1.5.0/events/events.go (about)

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