github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/remoterelations/relationunitsworker.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  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  	"gopkg.in/juju/worker.v1"
    13  	"gopkg.in/juju/worker.v1/catacomb"
    14  	"gopkg.in/macaroon.v2-unstable"
    15  
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/core/watcher"
    18  )
    19  
    20  type relationUnitsSettingsFunc func([]string) ([]params.SettingsResult, error)
    21  
    22  // relationUnitsWorker uses instances of watcher.RelationUnitsWatcher to
    23  // listen to changes to relation settings in a model, local or remote.
    24  // Local changes are exported to the remote model.
    25  // Remote changes are consumed by the local model.
    26  type relationUnitsWorker struct {
    27  	catacomb    catacomb.Catacomb
    28  	relationTag names.RelationTag
    29  	ruw         watcher.RelationUnitsWatcher
    30  	changes     chan<- params.RemoteRelationChangeEvent
    31  
    32  	applicationToken    string
    33  	macaroon            *macaroon.Macaroon
    34  	remoteRelationToken string
    35  
    36  	unitSettingsFunc relationUnitsSettingsFunc
    37  }
    38  
    39  func newRelationUnitsWorker(
    40  	relationTag names.RelationTag,
    41  	applicationToken string,
    42  	macaroon *macaroon.Macaroon,
    43  	remoteRelationToken string,
    44  	ruw watcher.RelationUnitsWatcher,
    45  	changes chan<- params.RemoteRelationChangeEvent,
    46  	unitSettingsFunc relationUnitsSettingsFunc,
    47  ) (*relationUnitsWorker, error) {
    48  	w := &relationUnitsWorker{
    49  		relationTag:         relationTag,
    50  		applicationToken:    applicationToken,
    51  		macaroon:            macaroon,
    52  		remoteRelationToken: remoteRelationToken,
    53  		ruw:                 ruw,
    54  		changes:             changes,
    55  		unitSettingsFunc:    unitSettingsFunc,
    56  	}
    57  	err := catacomb.Invoke(catacomb.Plan{
    58  		Site: &w.catacomb,
    59  		Work: w.loop,
    60  		Init: []worker.Worker{ruw},
    61  	})
    62  	return w, err
    63  }
    64  
    65  // Kill is defined on worker.Worker
    66  func (w *relationUnitsWorker) Kill() {
    67  	w.catacomb.Kill(nil)
    68  }
    69  
    70  // Wait is defined on worker.Worker
    71  func (w *relationUnitsWorker) Wait() error {
    72  	err := w.catacomb.Wait()
    73  	if err != nil {
    74  		logger.Errorf("error in relation units worker for %v: %v", w.relationTag.Id(), err)
    75  	}
    76  	return err
    77  }
    78  
    79  func (w *relationUnitsWorker) loop() error {
    80  	var (
    81  		changes chan<- params.RemoteRelationChangeEvent
    82  		event   params.RemoteRelationChangeEvent
    83  	)
    84  	for {
    85  		select {
    86  		case <-w.catacomb.Dying():
    87  			return w.catacomb.ErrDying()
    88  		case change, ok := <-w.ruw.Changes():
    89  			if !ok {
    90  				// We are dying.
    91  				return w.catacomb.ErrDying()
    92  			}
    93  			logger.Debugf("relation units changed for %v: %#v", w.relationTag, change)
    94  			if evt, err := w.relationUnitsChangeEvent(change); err != nil {
    95  				return errors.Trace(err)
    96  			} else {
    97  				if evt == nil {
    98  					continue
    99  				}
   100  				event = *evt
   101  				changes = w.changes
   102  			}
   103  		case changes <- event:
   104  			changes = nil
   105  		}
   106  	}
   107  }
   108  
   109  func (w *relationUnitsWorker) relationUnitsChangeEvent(
   110  	change watcher.RelationUnitsChange,
   111  ) (*params.RemoteRelationChangeEvent, error) {
   112  	logger.Debugf("update relation units for %v", w.relationTag)
   113  	if len(change.Changed)+len(change.Departed) == 0 {
   114  		return nil, nil
   115  	}
   116  	// Ensure all the changed units have been exported.
   117  	changedUnitNames := make([]string, 0, len(change.Changed))
   118  	for name := range change.Changed {
   119  		changedUnitNames = append(changedUnitNames, name)
   120  	}
   121  
   122  	// unitNum parses a unit name and extracts the unit number.
   123  	unitNum := func(unitName string) (int, error) {
   124  		parts := strings.Split(unitName, "/")
   125  		if len(parts) < 2 {
   126  			return -1, errors.NotValidf("unit name %v", unitName)
   127  		}
   128  		return strconv.Atoi(parts[1])
   129  	}
   130  
   131  	// Construct the event to send to the remote model.
   132  	event := &params.RemoteRelationChangeEvent{
   133  		RelationToken:    w.remoteRelationToken,
   134  		ApplicationToken: w.applicationToken,
   135  		Macaroons:        macaroon.Slice{w.macaroon},
   136  		DepartedUnits:    make([]int, len(change.Departed)),
   137  	}
   138  	for i, u := range change.Departed {
   139  		num, err := unitNum(u)
   140  		if err != nil {
   141  			return nil, errors.Trace(err)
   142  		}
   143  		event.DepartedUnits[i] = num
   144  	}
   145  
   146  	if len(changedUnitNames) > 0 {
   147  		// For changed units, we publish/consume the current settings values.
   148  		results, err := w.unitSettingsFunc(changedUnitNames)
   149  		if err != nil {
   150  			return nil, errors.Annotate(err, "fetching relation units settings")
   151  		}
   152  		for i, result := range results {
   153  			if result.Error != nil {
   154  				return nil, errors.Annotatef(result.Error, "fetching relation unit settings for %v", changedUnitNames[i])
   155  			}
   156  		}
   157  		for i, result := range results {
   158  			num, err := unitNum(changedUnitNames[i])
   159  			if err != nil {
   160  				return nil, errors.Trace(err)
   161  			}
   162  			change := params.RemoteRelationUnitChange{
   163  				UnitId:   num,
   164  				Settings: make(map[string]interface{}),
   165  			}
   166  			for k, v := range result.Settings {
   167  				change.Settings[k] = v
   168  			}
   169  			event.ChangedUnits = append(event.ChangedUnits, change)
   170  		}
   171  	}
   172  	return event, nil
   173  }