github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  }
   294  
   295  // machineAttachmentsWatcher will sends notifications of units entering and
   296  // leaving the scope of a MachineStorageId, and changes to the settings of
   297  // those units known to have entered.
   298  type machineAttachmentsWatcher struct {
   299  	commonWatcher
   300  	caller                      base.APICaller
   301  	machineAttachmentsWatcherId string
   302  	out                         chan []params.MachineStorageId
   303  }
   304  
   305  // NewVolumeAttachmentsWatcher returns a MachineStorageIdsWatcher which
   306  // communicates with the VolumeAttachmentsWatcher API facade to watch
   307  // volume attachments.
   308  func NewVolumeAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher {
   309  	return newMachineStorageIdsWatcher("VolumeAttachmentsWatcher", caller, result)
   310  }
   311  
   312  // NewFilesystemAttachmentsWatcher returns a MachineStorageIdsWatcher which
   313  // communicates with the FilesystemAttachmentsWatcher API facade to watch
   314  // filesystem attachments.
   315  func NewFilesystemAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher {
   316  	return newMachineStorageIdsWatcher("FilesystemAttachmentsWatcher", caller, result)
   317  }
   318  
   319  func newMachineStorageIdsWatcher(facade string, caller base.APICaller, result params.MachineStorageIdsWatchResult) MachineStorageIdsWatcher {
   320  	w := &machineAttachmentsWatcher{
   321  		caller: caller,
   322  		machineAttachmentsWatcherId: result.MachineStorageIdsWatcherId,
   323  		out: make(chan []params.MachineStorageId),
   324  	}
   325  	go func() {
   326  		defer w.tomb.Done()
   327  		defer close(w.out)
   328  		w.tomb.Kill(w.loop(facade, result.Changes))
   329  	}()
   330  	return w
   331  }
   332  
   333  func (w *machineAttachmentsWatcher) loop(facade string, initialChanges []params.MachineStorageId) error {
   334  	changes := initialChanges
   335  	w.newResult = func() interface{} { return new(params.MachineStorageIdsWatchResult) }
   336  	w.call = makeWatcherAPICaller(w.caller, facade, w.machineAttachmentsWatcherId)
   337  	w.commonWatcher.init()
   338  	go w.commonLoop()
   339  
   340  	for {
   341  		select {
   342  		// Send the initial event or subsequent change.
   343  		case w.out <- changes:
   344  		case <-w.tomb.Dying():
   345  			return nil
   346  		}
   347  		// Read the next change.
   348  		data, ok := <-w.in
   349  		if !ok {
   350  			// The tomb is already killed with the correct error
   351  			// at this point, so just return.
   352  			return nil
   353  		}
   354  		changes = data.(*params.MachineStorageIdsWatchResult).Changes
   355  	}
   356  }
   357  
   358  // Changes returns a channel that will receive the IDs of machine
   359  // storage entity attachments which have changed.
   360  func (w *machineAttachmentsWatcher) Changes() <-chan []params.MachineStorageId {
   361  	return w.out
   362  }
   363  
   364  // EntityWatcher will send events when something changes.
   365  // The content for the changes is a list of tag strings.
   366  type entityWatcher struct {
   367  	commonWatcher
   368  	caller          base.APICaller
   369  	entityWatcherId string
   370  	out             chan []string
   371  }
   372  
   373  func NewEntityWatcher(caller base.APICaller, result params.EntityWatchResult) EntityWatcher {
   374  	w := &entityWatcher{
   375  		caller:          caller,
   376  		entityWatcherId: result.EntityWatcherId,
   377  		out:             make(chan []string),
   378  	}
   379  	go func() {
   380  		defer w.tomb.Done()
   381  		defer close(w.out)
   382  		w.tomb.Kill(w.loop(result.Changes))
   383  	}()
   384  	return w
   385  }
   386  
   387  func (w *entityWatcher) loop(initialChanges []string) error {
   388  	changes := initialChanges
   389  	w.newResult = func() interface{} { return new(params.EntityWatchResult) }
   390  	w.call = makeWatcherAPICaller(w.caller, "EntityWatcher", w.entityWatcherId)
   391  	w.commonWatcher.init()
   392  	go w.commonLoop()
   393  
   394  	for {
   395  		select {
   396  		// Send the initial event or subsequent change.
   397  		case w.out <- changes:
   398  		case <-w.tomb.Dying():
   399  			return nil
   400  		}
   401  		// Read the next change.
   402  		data, ok := <-w.in
   403  		if !ok {
   404  			// The tomb is already killed with the correct error
   405  			// at this point, so just return.
   406  			return nil
   407  		}
   408  		// Changes have been transformed at the server side already.
   409  		changes = data.(*params.EntityWatchResult).Changes
   410  	}
   411  }
   412  
   413  // Changes returns a channel that receives a list of changes
   414  // as tags (converted to strings) of the watched entities
   415  // with changes.
   416  func (w *entityWatcher) Changes() <-chan []string {
   417  	return w.out
   418  }