github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/remoterelations/remoterelations.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package remoterelations
     5  
     6  import (
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"gopkg.in/juju/names.v2"
    14  	"gopkg.in/juju/worker.v1"
    15  	"gopkg.in/juju/worker.v1/catacomb"
    16  	"gopkg.in/macaroon.v2-unstable"
    17  
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/core/status"
    21  	"github.com/juju/juju/core/watcher"
    22  )
    23  
    24  var logger = loggo.GetLogger("juju.worker.remoterelations")
    25  
    26  // RemoteModelRelationsFacadeCloser implements RemoteModelRelationsFacade
    27  // and add a Close() method.
    28  type RemoteModelRelationsFacadeCloser interface {
    29  	io.Closer
    30  	RemoteModelRelationsFacade
    31  }
    32  
    33  // RemoteModelRelationsFacade instances publish local relation changes to the
    34  // model hosting the remote application involved in the relation, and also watches
    35  // for remote relation changes which are then pushed to the local model.
    36  type RemoteModelRelationsFacade interface {
    37  	// RegisterRemoteRelations sets up the remote model to participate
    38  	// in the specified relations.
    39  	RegisterRemoteRelations(relations ...params.RegisterRemoteRelationArg) ([]params.RegisterRemoteRelationResult, error)
    40  
    41  	// PublishRelationChange publishes relation changes to the
    42  	// model hosting the remote application involved in the relation.
    43  	PublishRelationChange(params.RemoteRelationChangeEvent) error
    44  
    45  	// WatchRelationUnits returns a watcher that notifies of changes to the
    46  	// units in the remote model for the relation with the given remote token.
    47  	WatchRelationUnits(arg params.RemoteEntityArg) (watcher.RelationUnitsWatcher, error)
    48  
    49  	// RelationUnitSettings returns the relation unit settings for the given relation units in the remote model.
    50  	RelationUnitSettings([]params.RemoteRelationUnit) ([]params.SettingsResult, error)
    51  
    52  	// WatchRelationSuspendedStatus starts a RelationStatusWatcher for watching the
    53  	// relations of each specified application in the remote model.
    54  	WatchRelationSuspendedStatus(arg params.RemoteEntityArg) (watcher.RelationStatusWatcher, error)
    55  
    56  	// WatchOfferStatus starts an OfferStatusWatcher for watching the status
    57  	// of the specified offer in the remote model.
    58  	WatchOfferStatus(arg params.OfferArg) (watcher.OfferStatusWatcher, error)
    59  }
    60  
    61  // RemoteRelationsFacade exposes remote relation functionality to a worker.
    62  type RemoteRelationsFacade interface {
    63  	// ImportRemoteEntity adds an entity to the remote entities collection
    64  	// with the specified opaque token.
    65  	ImportRemoteEntity(entity names.Tag, token string) error
    66  
    67  	// SaveMacaroon saves the macaroon for the entity.
    68  	SaveMacaroon(entity names.Tag, mac *macaroon.Macaroon) error
    69  
    70  	// ExportEntities allocates unique, remote entity IDs for the
    71  	// given entities in the local model.
    72  	ExportEntities([]names.Tag) ([]params.TokenResult, error)
    73  
    74  	// GetToken returns the token associated with the entity with the given tag.
    75  	GetToken(names.Tag) (string, error)
    76  
    77  	// RelationUnitSettings returns the relation unit settings for the
    78  	// given relation units in the local model.
    79  	RelationUnitSettings([]params.RelationUnit) ([]params.SettingsResult, error)
    80  
    81  	// Relations returns information about the relations
    82  	// with the specified keys in the local model.
    83  	Relations(keys []string) ([]params.RemoteRelationResult, error)
    84  
    85  	// RemoteApplications returns the current state of the remote applications with
    86  	// the specified names in the local model.
    87  	RemoteApplications(names []string) ([]params.RemoteApplicationResult, error)
    88  
    89  	// WatchLocalRelationUnits returns a watcher that notifies of changes to the
    90  	// local units in the relation with the given key.
    91  	WatchLocalRelationUnits(relationKey string) (watcher.RelationUnitsWatcher, error)
    92  
    93  	// WatchRemoteApplications watches for addition, removal and lifecycle
    94  	// changes to remote applications known to the local model.
    95  	WatchRemoteApplications() (watcher.StringsWatcher, error)
    96  
    97  	// WatchRemoteApplicationRelations starts a StringsWatcher for watching the relations of
    98  	// each specified application in the local model, and returns the watcher IDs
    99  	// and initial values, or an error if the application's relations could not be
   100  	// watched.
   101  	WatchRemoteApplicationRelations(application string) (watcher.StringsWatcher, error)
   102  
   103  	// ConsumeRemoteRelationChange consumes a change to settings originating
   104  	// from the remote/offering side of a relation.
   105  	ConsumeRemoteRelationChange(change params.RemoteRelationChangeEvent) error
   106  
   107  	// ControllerAPIInfoForModel returns the controller api info for a model.
   108  	ControllerAPIInfoForModel(modelUUID string) (*api.Info, error)
   109  
   110  	// SetRemoteApplicationStatus sets the status for the specified remote application.
   111  	SetRemoteApplicationStatus(applicationName string, status status.Status, message string) error
   112  }
   113  
   114  type newRemoteRelationsFacadeFunc func(*api.Info) (RemoteModelRelationsFacadeCloser, error)
   115  
   116  // Config defines the operation of a Worker.
   117  type Config struct {
   118  	ModelUUID                string
   119  	RelationsFacade          RemoteRelationsFacade
   120  	NewRemoteModelFacadeFunc newRemoteRelationsFacadeFunc
   121  	Clock                    clock.Clock
   122  }
   123  
   124  // Validate returns an error if config cannot drive a Worker.
   125  func (config Config) Validate() error {
   126  	if config.ModelUUID == "" {
   127  		return errors.NotValidf("empty model uuid")
   128  	}
   129  	if config.RelationsFacade == nil {
   130  		return errors.NotValidf("nil Facade")
   131  	}
   132  	if config.NewRemoteModelFacadeFunc == nil {
   133  		return errors.NotValidf("nil Remote Model Facade func")
   134  	}
   135  	if config.Clock == nil {
   136  		return errors.NotValidf("nil Clock")
   137  	}
   138  	return nil
   139  }
   140  
   141  // New returns a Worker backed by config, or an error.
   142  func New(config Config) (*Worker, error) {
   143  	if err := config.Validate(); err != nil {
   144  		return nil, errors.Trace(err)
   145  	}
   146  
   147  	w := &Worker{
   148  		config: config,
   149  		logger: logger,
   150  		runner: worker.NewRunner(worker.RunnerParams{
   151  			Clock: config.Clock,
   152  
   153  			// One of the remote application workers failing should not
   154  			// prevent the others from running.
   155  			IsFatal: func(error) bool { return false },
   156  
   157  			// For any failures, try again in 1 minute.
   158  			RestartDelay: time.Minute,
   159  		}),
   160  	}
   161  	err := catacomb.Invoke(catacomb.Plan{
   162  		Site: &w.catacomb,
   163  		Work: w.loop,
   164  		Init: []worker.Worker{w.runner},
   165  	})
   166  	return w, errors.Trace(err)
   167  }
   168  
   169  // Worker manages relations and associated settings where
   170  // one end of the relation is a remote application.
   171  type Worker struct {
   172  	catacomb catacomb.Catacomb
   173  	config   Config
   174  	logger   loggo.Logger
   175  
   176  	runner *worker.Runner
   177  }
   178  
   179  // Kill is defined on worker.Worker.
   180  func (w *Worker) Kill() {
   181  	w.catacomb.Kill(nil)
   182  }
   183  
   184  // Wait is defined on worker.Worker.
   185  func (w *Worker) Wait() error {
   186  	return w.catacomb.Wait()
   187  }
   188  
   189  func (w *Worker) loop() (err error) {
   190  	changes, err := w.config.RelationsFacade.WatchRemoteApplications()
   191  	if err != nil {
   192  		return errors.Trace(err)
   193  	}
   194  	if err := w.catacomb.Add(changes); err != nil {
   195  		return errors.Trace(err)
   196  	}
   197  	for {
   198  		select {
   199  		case <-w.catacomb.Dying():
   200  			return w.catacomb.ErrDying()
   201  		case applicationIds, ok := <-changes.Changes():
   202  			if !ok {
   203  				return errors.New("change channel closed")
   204  			}
   205  			err = w.handleApplicationChanges(applicationIds)
   206  			if err != nil {
   207  				return err
   208  			}
   209  		}
   210  	}
   211  }
   212  
   213  func (w *Worker) handleApplicationChanges(applicationIds []string) error {
   214  	// TODO(wallyworld) - watcher should not give empty events
   215  	if len(applicationIds) == 0 {
   216  		return nil
   217  	}
   218  	logger.Debugf("processing remote application changes for: %s", applicationIds)
   219  
   220  	// Fetch the current state of each of the remote applications that have changed.
   221  	results, err := w.config.RelationsFacade.RemoteApplications(applicationIds)
   222  	if err != nil {
   223  		return errors.Annotate(err, "querying remote applications")
   224  	}
   225  
   226  	for i, result := range results {
   227  		name := applicationIds[i]
   228  		if result.Error != nil {
   229  			// The the remote application has been removed, stop its worker.
   230  			if params.IsCodeNotFound(result.Error) {
   231  				if err := w.runner.StopWorker(name); err != nil {
   232  					return err
   233  				}
   234  				continue
   235  			}
   236  			return errors.Annotatef(err, "querying remote application %q", name)
   237  		}
   238  		if _, err := w.runner.Worker(name, w.catacomb.Dying()); err == nil {
   239  			// TODO(wallyworld): handle application dying or dead.
   240  			// As of now, if the worker is already running, that's all we need.
   241  			continue
   242  		}
   243  
   244  		remoteApp := *result.Result
   245  		startFunc := func() (worker.Worker, error) {
   246  			appWorker := &remoteApplicationWorker{
   247  				offerUUID:                         remoteApp.OfferUUID,
   248  				applicationName:                   remoteApp.Name,
   249  				localModelUUID:                    w.config.ModelUUID,
   250  				remoteModelUUID:                   remoteApp.ModelUUID,
   251  				isConsumerProxy:                   remoteApp.IsConsumerProxy,
   252  				offerMacaroon:                     remoteApp.Macaroon,
   253  				localRelationChanges:              make(chan params.RemoteRelationChangeEvent),
   254  				remoteRelationChanges:             make(chan params.RemoteRelationChangeEvent),
   255  				localModelFacade:                  w.config.RelationsFacade,
   256  				newRemoteModelRelationsFacadeFunc: w.config.NewRemoteModelFacadeFunc,
   257  			}
   258  			if err := catacomb.Invoke(catacomb.Plan{
   259  				Site: &appWorker.catacomb,
   260  				Work: appWorker.loop,
   261  			}); err != nil {
   262  				return nil, errors.Trace(err)
   263  			}
   264  			return appWorker, nil
   265  		}
   266  
   267  		logger.Debugf("starting watcher for remote application %q", name)
   268  		// Start the application worker to watch for things like new relations.
   269  		if err := w.runner.StartWorker(name, startFunc); err != nil {
   270  			return errors.Annotate(err, "error starting remote application worker")
   271  		}
   272  	}
   273  	return nil
   274  }