github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/watch.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  	"gopkg.in/tomb.v1"
    13  
    14  	"github.com/juju/juju/apiserver/facade"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/watcher"
    18  )
    19  
    20  // AgentEntityWatcher implements a common Watch method for use by
    21  // various facades.
    22  type AgentEntityWatcher struct {
    23  	st          state.EntityFinder
    24  	resources   facade.Resources
    25  	getCanWatch GetAuthFunc
    26  }
    27  
    28  // NewAgentEntityWatcher returns a new AgentEntityWatcher. The
    29  // GetAuthFunc will be used on each invocation of Watch to determine
    30  // current permissions.
    31  func NewAgentEntityWatcher(st state.EntityFinder, resources facade.Resources, getCanWatch GetAuthFunc) *AgentEntityWatcher {
    32  	return &AgentEntityWatcher{
    33  		st:          st,
    34  		resources:   resources,
    35  		getCanWatch: getCanWatch,
    36  	}
    37  }
    38  
    39  func (a *AgentEntityWatcher) watchEntity(tag names.Tag) (string, error) {
    40  	entity0, err := a.st.FindEntity(tag)
    41  	if err != nil {
    42  		return "", err
    43  	}
    44  	entity, ok := entity0.(state.NotifyWatcherFactory)
    45  	if !ok {
    46  		return "", NotSupportedError(tag, "watching")
    47  	}
    48  	watch := entity.Watch()
    49  	// Consume the initial event. Technically, API
    50  	// calls to Watch 'transmit' the initial event
    51  	// in the Watch response. But NotifyWatchers
    52  	// have no state to transmit.
    53  	if _, ok := <-watch.Changes(); ok {
    54  		return a.resources.Register(watch), nil
    55  	}
    56  	return "", watcher.EnsureErr(watch)
    57  }
    58  
    59  // Watch starts an NotifyWatcher for each given entity.
    60  func (a *AgentEntityWatcher) Watch(args params.Entities) (params.NotifyWatchResults, error) {
    61  	result := params.NotifyWatchResults{
    62  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
    63  	}
    64  	if len(args.Entities) == 0 {
    65  		return result, nil
    66  	}
    67  	canWatch, err := a.getCanWatch()
    68  	if err != nil {
    69  		return params.NotifyWatchResults{}, errors.Trace(err)
    70  	}
    71  	for i, entity := range args.Entities {
    72  		tag, err := names.ParseTag(entity.Tag)
    73  		if err != nil {
    74  			result.Results[i].Error = ServerError(ErrPerm)
    75  			continue
    76  		}
    77  		err = ErrPerm
    78  		watcherId := ""
    79  		if canWatch(tag) {
    80  			watcherId, err = a.watchEntity(tag)
    81  		}
    82  		result.Results[i].NotifyWatcherId = watcherId
    83  		result.Results[i].Error = ServerError(err)
    84  	}
    85  	return result, nil
    86  }
    87  
    88  // MultiNotifyWatcher implements state.NotifyWatcher, combining
    89  // multiple NotifyWatchers.
    90  type MultiNotifyWatcher struct {
    91  	tomb     tomb.Tomb
    92  	watchers []state.NotifyWatcher
    93  	changes  chan struct{}
    94  }
    95  
    96  // NewMultiNotifyWatcher creates a NotifyWatcher that combines
    97  // each of the NotifyWatchers passed in. Each watcher's initial
    98  // event is consumed, and a single initial event is sent.
    99  // Subsequent events are not coalesced.
   100  func NewMultiNotifyWatcher(w ...state.NotifyWatcher) *MultiNotifyWatcher {
   101  	m := &MultiNotifyWatcher{
   102  		watchers: w,
   103  		changes:  make(chan struct{}),
   104  	}
   105  	var wg sync.WaitGroup
   106  	wg.Add(len(w))
   107  	staging := make(chan struct{})
   108  	for _, w := range w {
   109  		// Consume the first event of each watcher.
   110  		<-w.Changes()
   111  		go func(w state.NotifyWatcher) {
   112  			defer wg.Done()
   113  			m.tomb.Kill(w.Wait())
   114  		}(w)
   115  		// Copy events from the watcher to the staging channel.
   116  		go copyEvents(staging, w.Changes(), &m.tomb)
   117  	}
   118  	go func() {
   119  		defer m.tomb.Done()
   120  		m.loop(staging)
   121  		wg.Wait()
   122  	}()
   123  	return m
   124  }
   125  
   126  // loop copies events from the input channel to the output channel,
   127  // coalescing events by waiting a short time between receiving and
   128  // sending.
   129  func (w *MultiNotifyWatcher) loop(in <-chan struct{}) {
   130  	defer close(w.changes)
   131  	// out is initialised to m.changes to send the inital event.
   132  	out := w.changes
   133  	var timer <-chan time.Time
   134  	for {
   135  		select {
   136  		case <-w.tomb.Dying():
   137  			return
   138  		case <-in:
   139  			if timer == nil {
   140  				// TODO(fwereade): 2016-03-17 lp:1558657
   141  				timer = time.After(10 * time.Millisecond)
   142  			}
   143  		case <-timer:
   144  			timer = nil
   145  			out = w.changes
   146  		case out <- struct{}{}:
   147  			out = nil
   148  		}
   149  	}
   150  }
   151  
   152  // copyEvents copies channel events from "in" to "out", coalescing.
   153  func copyEvents(out chan<- struct{}, in <-chan struct{}, tomb *tomb.Tomb) {
   154  	var outC chan<- struct{}
   155  	for {
   156  		select {
   157  		case <-tomb.Dying():
   158  			return
   159  		case _, ok := <-in:
   160  			if !ok {
   161  				return
   162  			}
   163  			outC = out
   164  		case outC <- struct{}{}:
   165  			outC = nil
   166  		}
   167  	}
   168  }
   169  
   170  func (w *MultiNotifyWatcher) Kill() {
   171  	w.tomb.Kill(nil)
   172  	for _, w := range w.watchers {
   173  		w.Kill()
   174  	}
   175  }
   176  
   177  func (w *MultiNotifyWatcher) Wait() error {
   178  	return w.tomb.Wait()
   179  }
   180  
   181  func (w *MultiNotifyWatcher) Stop() error {
   182  	w.Kill()
   183  	return w.Wait()
   184  }
   185  
   186  func (w *MultiNotifyWatcher) Err() error {
   187  	return w.tomb.Err()
   188  }
   189  
   190  func (w *MultiNotifyWatcher) Changes() <-chan struct{} {
   191  	return w.changes
   192  }