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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller
     5  
     6  // TODO(menn0) - note that this is currently unused, pending further
     7  // refactoring of state.State and state.Controller.
     8  
     9  import (
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"gopkg.in/juju/worker.v1"
    15  	"gopkg.in/juju/worker.v1/dependency"
    16  	"gopkg.in/tomb.v2"
    17  
    18  	coreagent "github.com/juju/juju/agent"
    19  	"github.com/juju/juju/state"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.worker.controller")
    23  
    24  // ManifoldConfig provides the dependencies for Manifold.
    25  type ManifoldConfig struct {
    26  	AgentName              string
    27  	StateConfigWatcherName string
    28  	OpenController         func(coreagent.Config) (*state.Controller, error)
    29  	MongoPingInterval      time.Duration
    30  }
    31  
    32  const defaultMongoPingInterval = 15 * time.Second
    33  
    34  // Manifold returns a manifold whose worker which wraps a
    35  // *state.State, which is in turn wrapped by a Tracker.  It will
    36  // exit if the State's associated mongodb session dies.
    37  func Manifold(config ManifoldConfig) dependency.Manifold {
    38  	return dependency.Manifold{
    39  		Inputs: []string{
    40  			config.AgentName,
    41  			config.StateConfigWatcherName,
    42  		},
    43  		Start: func(context dependency.Context) (worker.Worker, error) {
    44  			// First, a sanity check.
    45  			if config.OpenController == nil {
    46  				return nil, errors.New("OpenController is nil in config")
    47  			}
    48  
    49  			// Get the agent.
    50  			var agent coreagent.Agent
    51  			if err := context.Get(config.AgentName, &agent); err != nil {
    52  				return nil, err
    53  			}
    54  
    55  			// Confirm we're running in a state server by asking the
    56  			// stateconfigwatcher manifold.
    57  			var haveStateConfig bool
    58  			if err := context.Get(config.StateConfigWatcherName, &haveStateConfig); err != nil {
    59  				return nil, err
    60  			}
    61  			if !haveStateConfig {
    62  				return nil, dependency.ErrMissing
    63  			}
    64  
    65  			ctlr, err := config.OpenController(agent.CurrentConfig())
    66  			if err != nil {
    67  				return nil, errors.Trace(err)
    68  			}
    69  			tracker := newTracker(ctlr)
    70  
    71  			mongoPingInterval := config.MongoPingInterval
    72  			if mongoPingInterval == 0 {
    73  				mongoPingInterval = defaultMongoPingInterval
    74  			}
    75  
    76  			w := &controllerWorker{
    77  				tracker:           tracker,
    78  				mongoPingInterval: mongoPingInterval,
    79  			}
    80  			w.tomb.Go(func() error {
    81  				loopErr := w.loop()
    82  				if err := tracker.Done(); err != nil {
    83  					logger.Errorf("error releasing state: %v", err)
    84  				}
    85  				return loopErr
    86  			})
    87  			return w, nil
    88  		},
    89  		Output: outputFunc,
    90  	}
    91  }
    92  
    93  // outputFunc extracts a *Tracker from a Worker.
    94  func outputFunc(in worker.Worker, out interface{}) error {
    95  	inWorker, _ := in.(*controllerWorker)
    96  	if inWorker == nil {
    97  		return errors.Errorf("in should be a %T; got %T", inWorker, in)
    98  	}
    99  
   100  	switch outPointer := out.(type) {
   101  	case *Tracker:
   102  		*outPointer = inWorker.tracker
   103  	default:
   104  		return errors.Errorf("out should be *Tracker; got %T", out)
   105  	}
   106  	return nil
   107  }
   108  
   109  type controllerWorker struct {
   110  	tomb              tomb.Tomb
   111  	tracker           Tracker
   112  	mongoPingInterval time.Duration
   113  }
   114  
   115  func (w *controllerWorker) loop() error {
   116  	ctlr, err := w.tracker.Use()
   117  	if err != nil {
   118  		return errors.Annotate(err, "failed to obtain controller")
   119  	}
   120  	defer w.tracker.Done()
   121  
   122  	for {
   123  		select {
   124  		case <-w.tomb.Dying():
   125  			return tomb.ErrDying
   126  		case <-time.After(w.mongoPingInterval):
   127  			if err := ctlr.Ping(); err != nil {
   128  				return errors.Annotate(err, "database ping failed")
   129  			}
   130  		}
   131  	}
   132  }
   133  
   134  // Kill is part of the worker.Worker interface.
   135  func (w *controllerWorker) Kill() {
   136  	w.tomb.Kill(nil)
   137  }
   138  
   139  // Wait is part of the worker.Worker interface.
   140  func (w *controllerWorker) Wait() error {
   141  	return w.tomb.Wait()
   142  }