github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/api/watcher/watcher.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/loggo"
    10  	"launchpad.net/tomb"
    11  
    12  	"github.com/juju/juju/api/base"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state/multiwatcher"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.api.watcher")
    18  
    19  // commonWatcher implements common watcher logic in one place to
    20  // reduce code duplication, but it's not in fact a complete watcher;
    21  // it's intended for embedding.
    22  type commonWatcher struct {
    23  	tomb tomb.Tomb
    24  	in   chan interface{}
    25  
    26  	// These fields must be set by the embedding watcher, before
    27  	// calling init().
    28  
    29  	// newResult must return a pointer to a value of the type returned
    30  	// by the watcher's Next call.
    31  	newResult func() interface{}
    32  
    33  	// call should invoke the given API method, placing the call's
    34  	// returned value in result (if any).
    35  	call watcherAPICall
    36  }
    37  
    38  // watcherAPICall wraps up the information about what facade and what watcher
    39  // Id we are calling, and just gives us a simple way to call a common method
    40  // with a given return value.
    41  type watcherAPICall func(method string, result interface{}) error
    42  
    43  // makeWatcherAPICaller creates a watcherAPICall function for a given facade name
    44  // and watcherId.
    45  func makeWatcherAPICaller(caller base.APICaller, facadeName, watcherId string) watcherAPICall {
    46  	bestVersion := caller.BestFacadeVersion(facadeName)
    47  	return func(request string, result interface{}) error {
    48  		return caller.APICall(facadeName, bestVersion,
    49  			watcherId, request, nil, &result)
    50  	}
    51  }
    52  
    53  // init must be called to initialize an embedded commonWatcher's
    54  // fields. Make sure newResult and call fields are set beforehand.
    55  func (w *commonWatcher) init() {
    56  	w.in = make(chan interface{})
    57  	if w.newResult == nil {
    58  		panic("newResult must me set")
    59  	}
    60  	if w.call == nil {
    61  		panic("call must be set")
    62  	}
    63  }
    64  
    65  // commonLoop implements the loop structure common to the client
    66  // watchers. It should be started in a separate goroutine by any
    67  // watcher that embeds commonWatcher. It kills the commonWatcher's
    68  // tomb when an error occurs.
    69  func (w *commonWatcher) commonLoop() {
    70  	defer close(w.in)
    71  	var wg sync.WaitGroup
    72  	wg.Add(1)
    73  	go func() {
    74  		// When the watcher has been stopped, we send a Stop request
    75  		// to the server, which will remove the watcher and return a
    76  		// CodeStopped error to any currently outstanding call to
    77  		// Next. If a call to Next happens just after the watcher has
    78  		// been stopped, we'll get a CodeNotFound error; Either way
    79  		// we'll return, wait for the stop request to complete, and
    80  		// the watcher will die with all resources cleaned up.
    81  		defer wg.Done()
    82  		<-w.tomb.Dying()
    83  		if err := w.call("Stop", nil); err != nil {
    84  			logger.Errorf("error trying to stop watcher: %v", err)
    85  		}
    86  	}()
    87  	wg.Add(1)
    88  	go func() {
    89  		// Because Next blocks until there are changes, we need to
    90  		// call it in a separate goroutine, so the watcher can be
    91  		// stopped normally.
    92  		defer wg.Done()
    93  		for {
    94  			result := w.newResult()
    95  			err := w.call("Next", &result)
    96  			if err != nil {
    97  				if params.IsCodeStopped(err) || params.IsCodeNotFound(err) {
    98  					if w.tomb.Err() != tomb.ErrStillAlive {
    99  						// The watcher has been stopped at the client end, so we're
   100  						// expecting one of the above two kinds of error.
   101  						// We might see the same errors if the server itself
   102  						// has been shut down, in which case we leave them
   103  						// untouched.
   104  						err = tomb.ErrDying
   105  					}
   106  				}
   107  				// Something went wrong, just report the error and bail out.
   108  				w.tomb.Kill(err)
   109  				return
   110  			}
   111  			select {
   112  			case <-w.tomb.Dying():
   113  				return
   114  			case w.in <- result:
   115  				// Report back the result we just got.
   116  			}
   117  		}
   118  	}()
   119  	wg.Wait()
   120  }
   121  
   122  func (w *commonWatcher) Stop() error {
   123  	w.tomb.Kill(nil)
   124  	return w.tomb.Wait()
   125  }
   126  
   127  func (w *commonWatcher) Err() error {
   128  	return w.tomb.Err()
   129  }
   130  
   131  // notifyWatcher will send events when something changes.
   132  // It does not send content for those changes.
   133  type notifyWatcher struct {
   134  	commonWatcher
   135  	caller          base.APICaller
   136  	notifyWatcherId string
   137  	out             chan struct{}
   138  }
   139  
   140  // If an API call returns a NotifyWatchResult, you can use this to turn it into
   141  // a local Watcher.
   142  func NewNotifyWatcher(caller base.APICaller, result params.NotifyWatchResult) NotifyWatcher {
   143  	w := &notifyWatcher{
   144  		caller:          caller,
   145  		notifyWatcherId: result.NotifyWatcherId,
   146  		out:             make(chan struct{}),
   147  	}
   148  	go func() {
   149  		defer w.tomb.Done()
   150  		defer close(w.out)
   151  		w.tomb.Kill(w.loop())
   152  	}()
   153  	return w
   154  }
   155  
   156  func (w *notifyWatcher) loop() error {
   157  	// No results for this watcher type.
   158  	w.newResult = func() interface{} { return nil }
   159  	w.call = makeWatcherAPICaller(w.caller, "NotifyWatcher", w.notifyWatcherId)
   160  	w.commonWatcher.init()
   161  	go w.commonLoop()
   162  
   163  	for {
   164  		select {
   165  		// Since for a notifyWatcher there are no changes to send, we
   166  		// just set the event (initial first, then after each change).
   167  		case w.out <- struct{}{}:
   168  		case <-w.tomb.Dying():
   169  			return nil
   170  		}
   171  		if _, ok := <-w.in; !ok {
   172  			// The tomb is already killed with the correct
   173  			// error at this point, so just return.
   174  			return nil
   175  		}
   176  	}
   177  }
   178  
   179  // Changes returns a channel that receives a value when a given entity
   180  // changes in some way.
   181  func (w *notifyWatcher) Changes() <-chan struct{} {
   182  	return w.out
   183  }
   184  
   185  // stringsWatcher will send events when something changes.
   186  // The content of the changes is a list of strings.
   187  type stringsWatcher struct {
   188  	commonWatcher
   189  	caller           base.APICaller
   190  	stringsWatcherId string
   191  	out              chan []string
   192  }
   193  
   194  func NewStringsWatcher(caller base.APICaller, result params.StringsWatchResult) StringsWatcher {
   195  	w := &stringsWatcher{
   196  		caller:           caller,
   197  		stringsWatcherId: result.StringsWatcherId,
   198  		out:              make(chan []string),
   199  	}
   200  	go func() {
   201  		defer w.tomb.Done()
   202  		defer close(w.out)
   203  		w.tomb.Kill(w.loop(result.Changes))
   204  	}()
   205  	return w
   206  }
   207  
   208  func (w *stringsWatcher) loop(initialChanges []string) error {
   209  	changes := initialChanges
   210  	w.newResult = func() interface{} { return new(params.StringsWatchResult) }
   211  	w.call = makeWatcherAPICaller(w.caller, "StringsWatcher", w.stringsWatcherId)
   212  	w.commonWatcher.init()
   213  	go w.commonLoop()
   214  
   215  	for {
   216  		select {
   217  		// Send the initial event or subsequent change.
   218  		case w.out <- changes:
   219  		case <-w.tomb.Dying():
   220  			return nil
   221  		}
   222  		// Read the next change.
   223  		data, ok := <-w.in
   224  		if !ok {
   225  			// The tomb is already killed with the correct error
   226  			// at this point, so just return.
   227  			return nil
   228  		}
   229  		changes = data.(*params.StringsWatchResult).Changes
   230  	}
   231  }
   232  
   233  // Changes returns a channel that receives a list of strings of watched
   234  // entites with changes.
   235  func (w *stringsWatcher) Changes() <-chan []string {
   236  	return w.out
   237  }
   238  
   239  // relationUnitsWatcher will sends notifications of units entering and
   240  // leaving the scope of a RelationUnit, and changes to the settings of
   241  // those units known to have entered.
   242  type relationUnitsWatcher struct {
   243  	commonWatcher
   244  	caller                 base.APICaller
   245  	relationUnitsWatcherId string
   246  	out                    chan multiwatcher.RelationUnitsChange
   247  }
   248  
   249  func NewRelationUnitsWatcher(caller base.APICaller, result params.RelationUnitsWatchResult) RelationUnitsWatcher {
   250  	w := &relationUnitsWatcher{
   251  		caller:                 caller,
   252  		relationUnitsWatcherId: result.RelationUnitsWatcherId,
   253  		out: make(chan multiwatcher.RelationUnitsChange),
   254  	}
   255  	go func() {
   256  		defer w.tomb.Done()
   257  		defer close(w.out)
   258  		w.tomb.Kill(w.loop(result.Changes))
   259  	}()
   260  	return w
   261  }
   262  
   263  func (w *relationUnitsWatcher) loop(initialChanges multiwatcher.RelationUnitsChange) error {
   264  	changes := initialChanges
   265  	w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) }
   266  	w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId)
   267  	w.commonWatcher.init()
   268  	go w.commonLoop()
   269  
   270  	for {
   271  		select {
   272  		// Send the initial event or subsequent change.
   273  		case w.out <- changes:
   274  		case <-w.tomb.Dying():
   275  			return nil
   276  		}
   277  		// Read the next change.
   278  		data, ok := <-w.in
   279  		if !ok {
   280  			// The tomb is already killed with the correct error
   281  			// at this point, so just return.
   282  			return nil
   283  		}
   284  		changes = data.(*params.RelationUnitsWatchResult).Changes
   285  	}
   286  }
   287  
   288  // Changes returns a channel that will receive the changes to
   289  // counterpart units in a relation. The first event on the channel
   290  // holds the initial state of the relation in its Changed field.
   291  func (w *relationUnitsWatcher) Changes() <-chan multiwatcher.RelationUnitsChange {
   292  	return w.out
   293  }