github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/remoterelations/remoterelationsworker.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package remoterelations
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/worker/v3"
    12  	"github.com/juju/worker/v3/catacomb"
    13  
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/rpc/params"
    16  )
    17  
    18  // remoteRelationsWorker listens for changes to the
    19  // life and status of a relation in the offering model.
    20  type remoteRelationsWorker struct {
    21  	catacomb catacomb.Catacomb
    22  
    23  	mu sync.Mutex
    24  
    25  	// mostRecentEvent is stored here for the engine report.
    26  	mostRecentEvent RelationUnitChangeEvent
    27  	changeSince     time.Time
    28  
    29  	relationTag         names.RelationTag
    30  	remoteRelationToken string
    31  	applicationToken    string
    32  	relationsWatcher    watcher.RelationStatusWatcher
    33  	changes             chan<- RelationUnitChangeEvent
    34  	logger              Logger
    35  }
    36  
    37  func newRemoteRelationsWorker(
    38  	relationTag names.RelationTag,
    39  	applicationToken string,
    40  	remoteRelationToken string,
    41  	relationsWatcher watcher.RelationStatusWatcher,
    42  	changes chan<- RelationUnitChangeEvent,
    43  	logger Logger,
    44  ) (*remoteRelationsWorker, error) {
    45  	w := &remoteRelationsWorker{
    46  		relationsWatcher:    relationsWatcher,
    47  		relationTag:         relationTag,
    48  		remoteRelationToken: remoteRelationToken,
    49  		applicationToken:    applicationToken,
    50  		changes:             changes,
    51  		logger:              logger,
    52  	}
    53  	err := catacomb.Invoke(catacomb.Plan{
    54  		Site: &w.catacomb,
    55  		Work: w.loop,
    56  		Init: []worker.Worker{relationsWatcher},
    57  	})
    58  	return w, err
    59  }
    60  
    61  // Kill is defined on worker.Worker
    62  func (w *remoteRelationsWorker) Kill() {
    63  	w.catacomb.Kill(nil)
    64  }
    65  
    66  // Wait is defined on worker.Worker
    67  func (w *remoteRelationsWorker) Wait() error {
    68  	err := w.catacomb.Wait()
    69  	if err != nil {
    70  		w.logger.Errorf("error in remote relations worker for relation %v: %v", w.relationTag.Id(), err)
    71  	}
    72  	return err
    73  }
    74  
    75  func (w *remoteRelationsWorker) loop() error {
    76  	var (
    77  		changes chan<- RelationUnitChangeEvent
    78  		event   RelationUnitChangeEvent
    79  	)
    80  	for {
    81  		select {
    82  		case <-w.catacomb.Dying():
    83  			return w.catacomb.ErrDying()
    84  
    85  		case relChanges, ok := <-w.relationsWatcher.Changes():
    86  			if !ok {
    87  				// We are dying.
    88  				return w.catacomb.ErrDying()
    89  			}
    90  			if len(relChanges) == 0 {
    91  				w.logger.Warningf("relation status watcher event with no changes")
    92  				continue
    93  			}
    94  			// We only care about the most recent change.
    95  			change := relChanges[len(relChanges)-1]
    96  			w.logger.Debugf("relation status changed for %v: %v", w.relationTag, change)
    97  			suspended := change.Suspended
    98  			w.mu.Lock()
    99  			w.mostRecentEvent = RelationUnitChangeEvent{
   100  				Tag: w.relationTag,
   101  				RemoteRelationChangeEvent: params.RemoteRelationChangeEvent{
   102  					RelationToken:    w.remoteRelationToken,
   103  					ApplicationToken: w.applicationToken,
   104  					Life:             change.Life,
   105  					Suspended:        &suspended,
   106  					SuspendedReason:  change.SuspendedReason,
   107  				},
   108  			}
   109  			w.changeSince = time.Now()
   110  			event = w.mostRecentEvent
   111  			w.mu.Unlock()
   112  			changes = w.changes
   113  
   114  		case changes <- event:
   115  			changes = nil
   116  		}
   117  	}
   118  }
   119  
   120  // Report provides information for the engine report.
   121  func (w *remoteRelationsWorker) Report() map[string]interface{} {
   122  	result := make(map[string]interface{})
   123  	w.mu.Lock()
   124  	defer w.mu.Unlock()
   125  
   126  	if w.mostRecentEvent.Tag.Id() != "" {
   127  		result["life"] = w.mostRecentEvent.Life
   128  		result["suspended"] = w.mostRecentEvent.Suspended
   129  		result["since"] = w.changeSince.Format(time.RFC1123Z)
   130  	}
   131  
   132  	return result
   133  }