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