github.com/aimxhaisse/docker@v1.6.2/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  }