github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/errors"
    10  	"github.com/juju/loggo"
    11  	"gopkg.in/tomb.v1"
    12  
    13  	"github.com/juju/juju/api/base"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/core/migration"
    16  	"github.com/juju/juju/watcher"
    17  )
    18  
    19  var logger = loggo.GetLogger("juju.api.watcher")
    20  
    21  // commonWatcher implements common watcher logic in one place to
    22  // reduce code duplication, but it's not in fact a complete watcher;
    23  // it's intended for embedding.
    24  type commonWatcher struct {
    25  	tomb tomb.Tomb
    26  	in   chan interface{}
    27  
    28  	// These fields must be set by the embedding watcher, before
    29  	// calling init().
    30  
    31  	// newResult must return a pointer to a value of the type returned
    32  	// by the watcher's Next call.
    33  	newResult func() interface{}
    34  
    35  	// call should invoke the given API method, placing the call's
    36  	// returned value in result (if any).
    37  	call watcherAPICall
    38  }
    39  
    40  // watcherAPICall wraps up the information about what facade and what watcher
    41  // Id we are calling, and just gives us a simple way to call a common method
    42  // with a given return value.
    43  type watcherAPICall func(method string, result interface{}) error
    44  
    45  // makeWatcherAPICaller creates a watcherAPICall function for a given facade name
    46  // and watcherId.
    47  func makeWatcherAPICaller(caller base.APICaller, facadeName, watcherId string) watcherAPICall {
    48  	bestVersion := caller.BestFacadeVersion(facadeName)
    49  	return func(request string, result interface{}) error {
    50  		return caller.APICall(facadeName, bestVersion,
    51  			watcherId, request, nil, &result)
    52  	}
    53  }
    54  
    55  // init must be called to initialize an embedded commonWatcher's
    56  // fields. Make sure newResult and call fields are set beforehand.
    57  func (w *commonWatcher) init() {
    58  	w.in = make(chan interface{})
    59  	if w.newResult == nil {
    60  		panic("newResult must me set")
    61  	}
    62  	if w.call == nil {
    63  		panic("call must be set")
    64  	}
    65  }
    66  
    67  // commonLoop implements the loop structure common to the client
    68  // watchers. It should be started in a separate goroutine by any
    69  // watcher that embeds commonWatcher. It kills the commonWatcher's
    70  // tomb when an error occurs.
    71  func (w *commonWatcher) commonLoop() {
    72  	defer close(w.in)
    73  	var wg sync.WaitGroup
    74  	wg.Add(1)
    75  	go func() {
    76  		// When the watcher has been stopped, we send a Stop request
    77  		// to the server, which will remove the watcher and return a
    78  		// CodeStopped error to any currently outstanding call to
    79  		// Next. If a call to Next happens just after the watcher has
    80  		// been stopped, we'll get a CodeNotFound error; Either way
    81  		// we'll return, wait for the stop request to complete, and
    82  		// the watcher will die with all resources cleaned up.
    83  		defer wg.Done()
    84  		<-w.tomb.Dying()
    85  		if err := w.call("Stop", nil); err != nil {
    86  			logger.Errorf("error trying to stop watcher: %v", err)
    87  		}
    88  	}()
    89  	wg.Add(1)
    90  	go func() {
    91  		// Because Next blocks until there are changes, we need to
    92  		// call it in a separate goroutine, so the watcher can be
    93  		// stopped normally.
    94  		defer wg.Done()
    95  		for {
    96  			result := w.newResult()
    97  			err := w.call("Next", &result)
    98  			if err != nil {
    99  				if params.IsCodeStopped(err) || params.IsCodeNotFound(err) {
   100  					if w.tomb.Err() != tomb.ErrStillAlive {
   101  						// The watcher has been stopped at the client end, so we're
   102  						// expecting one of the above two kinds of error.
   103  						// We might see the same errors if the server itself
   104  						// has been shut down, in which case we leave them
   105  						// untouched.
   106  						err = tomb.ErrDying
   107  					}
   108  				}
   109  				// Something went wrong, just report the error and bail out.
   110  				w.tomb.Kill(err)
   111  				return
   112  			}
   113  			select {
   114  			case <-w.tomb.Dying():
   115  				return
   116  			case w.in <- result:
   117  				// Report back the result we just got.
   118  			}
   119  		}
   120  	}()
   121  	wg.Wait()
   122  }
   123  
   124  // Kill is part of the worker.Worker interface.
   125  func (w *commonWatcher) Kill() {
   126  	w.tomb.Kill(nil)
   127  }
   128  
   129  // Wait is part of the worker.Worker interface.
   130  func (w *commonWatcher) Wait() error {
   131  	return w.tomb.Wait()
   132  }
   133  
   134  // notifyWatcher will send events when something changes.
   135  // It does not send content for those changes.
   136  type notifyWatcher struct {
   137  	commonWatcher
   138  	caller          base.APICaller
   139  	notifyWatcherId string
   140  	out             chan struct{}
   141  }
   142  
   143  // If an API call returns a NotifyWatchResult, you can use this to turn it into
   144  // a local Watcher.
   145  func NewNotifyWatcher(caller base.APICaller, result params.NotifyWatchResult) watcher.NotifyWatcher {
   146  	w := &notifyWatcher{
   147  		caller:          caller,
   148  		notifyWatcherId: result.NotifyWatcherId,
   149  		out:             make(chan struct{}),
   150  	}
   151  	go func() {
   152  		defer w.tomb.Done()
   153  		w.tomb.Kill(w.loop())
   154  	}()
   155  	return w
   156  }
   157  
   158  func (w *notifyWatcher) loop() error {
   159  	// No results for this watcher type.
   160  	w.newResult = func() interface{} { return nil }
   161  	w.call = makeWatcherAPICaller(w.caller, "NotifyWatcher", w.notifyWatcherId)
   162  	w.commonWatcher.init()
   163  	go w.commonLoop()
   164  
   165  	for {
   166  		select {
   167  		// Since for a notifyWatcher there are no changes to send, we
   168  		// just set the event (initial first, then after each change).
   169  		case w.out <- struct{}{}:
   170  		case <-w.tomb.Dying():
   171  			return nil
   172  		}
   173  		if _, ok := <-w.in; !ok {
   174  			// The tomb is already killed with the correct
   175  			// error at this point, so just return.
   176  			return nil
   177  		}
   178  	}
   179  }
   180  
   181  // Changes returns a channel that receives a value when a given entity
   182  // changes in some way.
   183  func (w *notifyWatcher) Changes() watcher.NotifyChannel {
   184  	return w.out
   185  }
   186  
   187  // stringsWatcher will send events when something changes.
   188  // The content of the changes is a list of strings.
   189  type stringsWatcher struct {
   190  	commonWatcher
   191  	caller           base.APICaller
   192  	stringsWatcherId string
   193  	out              chan []string
   194  }
   195  
   196  func NewStringsWatcher(caller base.APICaller, result params.StringsWatchResult) watcher.StringsWatcher {
   197  	w := &stringsWatcher{
   198  		caller:           caller,
   199  		stringsWatcherId: result.StringsWatcherId,
   200  		out:              make(chan []string),
   201  	}
   202  	go func() {
   203  		defer w.tomb.Done()
   204  		w.tomb.Kill(w.loop(result.Changes))
   205  	}()
   206  	return w
   207  }
   208  
   209  func (w *stringsWatcher) loop(initialChanges []string) error {
   210  	changes := initialChanges
   211  	w.newResult = func() interface{} { return new(params.StringsWatchResult) }
   212  	w.call = makeWatcherAPICaller(w.caller, "StringsWatcher", w.stringsWatcherId)
   213  	w.commonWatcher.init()
   214  	go w.commonLoop()
   215  
   216  	for {
   217  		select {
   218  		// Send the initial event or subsequent change.
   219  		case w.out <- changes:
   220  		case <-w.tomb.Dying():
   221  			return nil
   222  		}
   223  		// Read the next change.
   224  		data, ok := <-w.in
   225  		if !ok {
   226  			// The tomb is already killed with the correct error
   227  			// at this point, so just return.
   228  			return nil
   229  		}
   230  		changes = data.(*params.StringsWatchResult).Changes
   231  	}
   232  }
   233  
   234  // Changes returns a channel that receives a list of strings of watched
   235  // entites with changes.
   236  func (w *stringsWatcher) Changes() watcher.StringsChannel {
   237  	return w.out
   238  }
   239  
   240  // relationUnitsWatcher will sends notifications of units entering and
   241  // leaving the scope of a RelationUnit, and changes to the settings of
   242  // those units known to have entered.
   243  type relationUnitsWatcher struct {
   244  	commonWatcher
   245  	caller                 base.APICaller
   246  	relationUnitsWatcherId string
   247  	out                    chan watcher.RelationUnitsChange
   248  }
   249  
   250  func NewRelationUnitsWatcher(caller base.APICaller, result params.RelationUnitsWatchResult) watcher.RelationUnitsWatcher {
   251  	w := &relationUnitsWatcher{
   252  		caller:                 caller,
   253  		relationUnitsWatcherId: result.RelationUnitsWatcherId,
   254  		out: make(chan watcher.RelationUnitsChange),
   255  	}
   256  	go func() {
   257  		defer w.tomb.Done()
   258  		w.tomb.Kill(w.loop(result.Changes))
   259  	}()
   260  	return w
   261  }
   262  
   263  func copyRelationUnitsChanged(src params.RelationUnitsChange) watcher.RelationUnitsChange {
   264  	dst := watcher.RelationUnitsChange{
   265  		Departed: src.Departed,
   266  	}
   267  	if src.Changed != nil {
   268  		dst.Changed = make(map[string]watcher.UnitSettings)
   269  		for name, unitSettings := range src.Changed {
   270  			dst.Changed[name] = watcher.UnitSettings{
   271  				Version: unitSettings.Version,
   272  			}
   273  		}
   274  	}
   275  	return dst
   276  }
   277  
   278  func (w *relationUnitsWatcher) loop(initialChanges params.RelationUnitsChange) error {
   279  	changes := copyRelationUnitsChanged(initialChanges)
   280  	w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) }
   281  	w.call = makeWatcherAPICaller(w.caller, "RelationUnitsWatcher", w.relationUnitsWatcherId)
   282  	w.commonWatcher.init()
   283  	go w.commonLoop()
   284  
   285  	for {
   286  		select {
   287  		// Send the initial event or subsequent change.
   288  		case w.out <- changes:
   289  		case <-w.tomb.Dying():
   290  			return nil
   291  		}
   292  		// Read the next change.
   293  		data, ok := <-w.in
   294  		if !ok {
   295  			// The tomb is already killed with the correct error
   296  			// at this point, so just return.
   297  			return nil
   298  		}
   299  		changes = copyRelationUnitsChanged(data.(*params.RelationUnitsWatchResult).Changes)
   300  	}
   301  }
   302  
   303  // Changes returns a channel that will receive the changes to
   304  // counterpart units in a relation. The first event on the channel
   305  // holds the initial state of the relation in its Changed field.
   306  func (w *relationUnitsWatcher) Changes() watcher.RelationUnitsChannel {
   307  	return w.out
   308  }
   309  
   310  // machineAttachmentsWatcher will sends notifications of units entering and
   311  // leaving the scope of a MachineStorageId, and changes to the settings of
   312  // those units known to have entered.
   313  type machineAttachmentsWatcher struct {
   314  	commonWatcher
   315  	caller                      base.APICaller
   316  	machineAttachmentsWatcherId string
   317  	out                         chan []watcher.MachineStorageId
   318  }
   319  
   320  // NewVolumeAttachmentsWatcher returns a MachineStorageIdsWatcher which
   321  // communicates with the VolumeAttachmentsWatcher API facade to watch
   322  // volume attachments.
   323  func NewVolumeAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher {
   324  	return newMachineStorageIdsWatcher("VolumeAttachmentsWatcher", caller, result)
   325  }
   326  
   327  // NewFilesystemAttachmentsWatcher returns a MachineStorageIdsWatcher which
   328  // communicates with the FilesystemAttachmentsWatcher API facade to watch
   329  // filesystem attachments.
   330  func NewFilesystemAttachmentsWatcher(caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher {
   331  	return newMachineStorageIdsWatcher("FilesystemAttachmentsWatcher", caller, result)
   332  }
   333  
   334  func newMachineStorageIdsWatcher(facade string, caller base.APICaller, result params.MachineStorageIdsWatchResult) watcher.MachineStorageIdsWatcher {
   335  	w := &machineAttachmentsWatcher{
   336  		caller: caller,
   337  		machineAttachmentsWatcherId: result.MachineStorageIdsWatcherId,
   338  		out: make(chan []watcher.MachineStorageId),
   339  	}
   340  	go func() {
   341  		defer w.tomb.Done()
   342  		w.tomb.Kill(w.loop(facade, result.Changes))
   343  	}()
   344  	return w
   345  }
   346  
   347  func copyMachineStorageIds(src []params.MachineStorageId) []watcher.MachineStorageId {
   348  	dst := make([]watcher.MachineStorageId, len(src))
   349  	for i, msi := range src {
   350  		dst[i] = watcher.MachineStorageId{
   351  			MachineTag:    msi.MachineTag,
   352  			AttachmentTag: msi.AttachmentTag,
   353  		}
   354  	}
   355  	return dst
   356  }
   357  
   358  func (w *machineAttachmentsWatcher) loop(facade string, initialChanges []params.MachineStorageId) error {
   359  	changes := copyMachineStorageIds(initialChanges)
   360  	w.newResult = func() interface{} { return new(params.MachineStorageIdsWatchResult) }
   361  	w.call = makeWatcherAPICaller(w.caller, facade, w.machineAttachmentsWatcherId)
   362  	w.commonWatcher.init()
   363  	go w.commonLoop()
   364  
   365  	for {
   366  		select {
   367  		// Send the initial event or subsequent change.
   368  		case w.out <- changes:
   369  		case <-w.tomb.Dying():
   370  			return nil
   371  		}
   372  		// Read the next change.
   373  		data, ok := <-w.in
   374  		if !ok {
   375  			// The tomb is already killed with the correct error
   376  			// at this point, so just return.
   377  			return nil
   378  		}
   379  		changes = copyMachineStorageIds(data.(*params.MachineStorageIdsWatchResult).Changes)
   380  	}
   381  }
   382  
   383  // Changes returns a channel that will receive the IDs of machine
   384  // storage entity attachments which have changed.
   385  func (w *machineAttachmentsWatcher) Changes() watcher.MachineStorageIdsChannel {
   386  	return w.out
   387  }
   388  
   389  // EntitiesWatcher will send events when something changes.
   390  // The content for the changes is a list of tag strings.
   391  type entitiesWatcher struct {
   392  	commonWatcher
   393  	caller            base.APICaller
   394  	entitiesWatcherId string
   395  	out               chan []string
   396  }
   397  
   398  func NewEntitiesWatcher(caller base.APICaller, result params.EntitiesWatchResult) watcher.EntitiesWatcher {
   399  	w := &entitiesWatcher{
   400  		caller:            caller,
   401  		entitiesWatcherId: result.EntitiesWatcherId,
   402  		out:               make(chan []string),
   403  	}
   404  	go func() {
   405  		defer w.tomb.Done()
   406  		w.tomb.Kill(w.loop(result.Changes))
   407  	}()
   408  	return w
   409  }
   410  
   411  func (w *entitiesWatcher) loop(initialChanges []string) error {
   412  	changes := initialChanges
   413  	w.newResult = func() interface{} { return new(params.EntitiesWatchResult) }
   414  	w.call = makeWatcherAPICaller(w.caller, "EntityWatcher", w.entitiesWatcherId)
   415  	w.commonWatcher.init()
   416  	go w.commonLoop()
   417  
   418  	for {
   419  		select {
   420  		// Send the initial event or subsequent change.
   421  		case w.out <- changes:
   422  		case <-w.tomb.Dying():
   423  			return nil
   424  		}
   425  		// Read the next change.
   426  		data, ok := <-w.in
   427  		if !ok {
   428  			// The tomb is already killed with the correct error
   429  			// at this point, so just return.
   430  			return nil
   431  		}
   432  		// Changes have been transformed at the server side already.
   433  		changes = data.(*params.EntitiesWatchResult).Changes
   434  	}
   435  }
   436  
   437  // Changes returns a channel that receives a list of changes
   438  // as tags (converted to strings) of the watched entities
   439  // with changes.
   440  func (w *entitiesWatcher) Changes() watcher.StringsChannel {
   441  	return w.out
   442  }
   443  
   444  // NewMigrationStatusWatcher takes the NotifyWatcherId returns by the
   445  // MigrationSlave.Watch API and returns a watcher which will report
   446  // status changes for any migration of the model associated with the
   447  // API connection.
   448  func NewMigrationStatusWatcher(caller base.APICaller, watcherId string) watcher.MigrationStatusWatcher {
   449  	w := &migrationStatusWatcher{
   450  		caller: caller,
   451  		id:     watcherId,
   452  		out:    make(chan watcher.MigrationStatus),
   453  	}
   454  	go func() {
   455  		defer w.tomb.Done()
   456  		w.tomb.Kill(w.loop())
   457  	}()
   458  	return w
   459  }
   460  
   461  type migrationStatusWatcher struct {
   462  	commonWatcher
   463  	caller base.APICaller
   464  	id     string
   465  	out    chan watcher.MigrationStatus
   466  }
   467  
   468  func (w *migrationStatusWatcher) loop() error {
   469  	w.newResult = func() interface{} { return new(params.MigrationStatus) }
   470  	w.call = makeWatcherAPICaller(w.caller, "MigrationStatusWatcher", w.id)
   471  	w.commonWatcher.init()
   472  	go w.commonLoop()
   473  
   474  	for {
   475  		var data interface{}
   476  		var ok bool
   477  
   478  		select {
   479  		case data, ok = <-w.in:
   480  			if !ok {
   481  				// The tomb is already killed with the correct error
   482  				// at this point, so just return.
   483  				return nil
   484  			}
   485  		case <-w.tomb.Dying():
   486  			return nil
   487  		}
   488  
   489  		inStatus := *data.(*params.MigrationStatus)
   490  		phase, ok := migration.ParsePhase(inStatus.Phase)
   491  		if !ok {
   492  			return errors.Errorf("invalid phase %q", inStatus.Phase)
   493  		}
   494  		outStatus := watcher.MigrationStatus{
   495  			MigrationId:    inStatus.MigrationId,
   496  			Attempt:        inStatus.Attempt,
   497  			Phase:          phase,
   498  			SourceAPIAddrs: inStatus.SourceAPIAddrs,
   499  			SourceCACert:   inStatus.SourceCACert,
   500  			TargetAPIAddrs: inStatus.TargetAPIAddrs,
   501  			TargetCACert:   inStatus.TargetCACert,
   502  		}
   503  		select {
   504  		case w.out <- outStatus:
   505  		case <-w.tomb.Dying():
   506  			return nil
   507  		}
   508  	}
   509  }
   510  
   511  // Changes returns a channel that reports the latest status of the
   512  // migration of a model.
   513  func (w *migrationStatusWatcher) Changes() <-chan watcher.MigrationStatus {
   514  	return w.out
   515  }