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