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 }