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