github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  				timer = time.After(10 * time.Millisecond)
   140  			}
   141  		case <-timer:
   142  			timer = nil
   143  			out = w.changes
   144  		case out <- struct{}{}:
   145  			out = nil
   146  		}
   147  	}
   148  }
   149  
   150  // copyEvents copies channel events from "in" to "out", coalescing.
   151  func copyEvents(out chan<- struct{}, in <-chan struct{}, tomb *tomb.Tomb) {
   152  	var outC chan<- struct{}
   153  	for {
   154  		select {
   155  		case <-tomb.Dying():
   156  			return
   157  		case _, ok := <-in:
   158  			if !ok {
   159  				return
   160  			}
   161  			outC = out
   162  		case outC <- struct{}{}:
   163  			outC = nil
   164  		}
   165  	}
   166  }
   167  
   168  func (w *MultiNotifyWatcher) Kill() {
   169  	w.tomb.Kill(nil)
   170  	for _, w := range w.watchers {
   171  		w.Kill()
   172  	}
   173  }
   174  
   175  func (w *MultiNotifyWatcher) Wait() error {
   176  	return w.tomb.Wait()
   177  }
   178  
   179  func (w *MultiNotifyWatcher) Stop() error {
   180  	w.Kill()
   181  	return w.Wait()
   182  }
   183  
   184  func (w *MultiNotifyWatcher) Err() error {
   185  	return w.tomb.Err()
   186  }
   187  
   188  func (w *MultiNotifyWatcher) Changes() <-chan struct{} {
   189  	return w.changes
   190  }