github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lease/manifold/manifold.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package manifold 5 6 // This needs to be in a different package from the lease manager 7 // because it uses state (to construct the raftlease store), but the 8 // lease manager also runs as a worker in state, so the state package 9 // depends on worker/lease. Having it in worker/lease produces an 10 // import cycle. 11 12 import ( 13 "fmt" 14 "math/rand" 15 "time" 16 17 "github.com/juju/clock" 18 "github.com/juju/errors" 19 "github.com/juju/pubsub" 20 "github.com/prometheus/client_golang/prometheus" 21 "gopkg.in/juju/worker.v1" 22 "gopkg.in/juju/worker.v1/dependency" 23 24 "github.com/juju/juju/agent" 25 "github.com/juju/juju/core/globalclock" 26 corelease "github.com/juju/juju/core/lease" 27 "github.com/juju/juju/core/raftlease" 28 "github.com/juju/juju/worker/common" 29 "github.com/juju/juju/worker/lease" 30 workerstate "github.com/juju/juju/worker/state" 31 ) 32 33 const ( 34 // MaxSleep is the longest the manager will sleep before checking 35 // whether any leases should be expired. If it can see a lease 36 // expiring sooner than that it will still wake up earlier. 37 MaxSleep = time.Minute 38 39 // ForwardTimeout is how long the store should wait for a response 40 // after sending a lease operation over the hub before deciding a 41 // a response is never coming back (for example if we send the 42 // request during a raft-leadership election). This should be long 43 // enough that we can be very confident the request was missed. 44 ForwardTimeout = 5 * time.Second 45 ) 46 47 // TODO(raftlease): This manifold does too much - split out a worker 48 // that holds the lease store and a manifold that creates it. Then 49 // make this one depend on that. 50 51 // ManifoldConfig holds the resources needed to start the lease 52 // manager in a dependency engine. 53 type ManifoldConfig struct { 54 AgentName string 55 ClockName string 56 CentralHubName string 57 StateName string 58 59 FSM *raftlease.FSM 60 RequestTopic string 61 Logger lease.Logger 62 PrometheusRegisterer prometheus.Registerer 63 NewWorker func(lease.ManagerConfig) (worker.Worker, error) 64 NewStore func(raftlease.StoreConfig) *raftlease.Store 65 } 66 67 // Validate checks that the config has all the required values. 68 func (c ManifoldConfig) Validate() error { 69 if c.AgentName == "" { 70 return errors.NotValidf("empty AgentName") 71 } 72 if c.ClockName == "" { 73 return errors.NotValidf("empty ClockName") 74 } 75 if c.CentralHubName == "" { 76 return errors.NotValidf("empty CentralHubName") 77 } 78 if c.StateName == "" { 79 return errors.NotValidf("empty StateName") 80 } 81 if c.FSM == nil { 82 return errors.NotValidf("nil FSM") 83 } 84 if c.RequestTopic == "" { 85 return errors.NotValidf("empty RequestTopic") 86 } 87 if c.Logger == nil { 88 return errors.NotValidf("nil Logger") 89 } 90 if c.PrometheusRegisterer == nil { 91 return errors.NotValidf("nil PrometheusRegisterer") 92 } 93 if c.NewWorker == nil { 94 return errors.NotValidf("nil NewWorker") 95 } 96 if c.NewStore == nil { 97 return errors.NotValidf("nil NewStore") 98 } 99 return nil 100 } 101 102 type manifoldState struct { 103 config ManifoldConfig 104 store *raftlease.Store 105 } 106 107 func (s *manifoldState) start(context dependency.Context) (worker.Worker, error) { 108 if err := s.config.Validate(); err != nil { 109 return nil, errors.Trace(err) 110 } 111 112 var agent agent.Agent 113 if err := context.Get(s.config.AgentName, &agent); err != nil { 114 return nil, errors.Trace(err) 115 } 116 117 var clock clock.Clock 118 if err := context.Get(s.config.ClockName, &clock); err != nil { 119 return nil, errors.Trace(err) 120 } 121 122 var hub *pubsub.StructuredHub 123 if err := context.Get(s.config.CentralHubName, &hub); err != nil { 124 return nil, errors.Trace(err) 125 } 126 127 var stTracker workerstate.StateTracker 128 if err := context.Get(s.config.StateName, &stTracker); err != nil { 129 return nil, errors.Trace(err) 130 } 131 statePool, err := stTracker.Use() 132 if err != nil { 133 return nil, errors.Trace(err) 134 } 135 136 st := statePool.SystemState() 137 138 source := rand.NewSource(clock.Now().UnixNano()) 139 runID := rand.New(source).Int31() 140 141 s.store = s.config.NewStore(raftlease.StoreConfig{ 142 FSM: s.config.FSM, 143 Hub: hub, 144 Trapdoor: st.LeaseTrapdoorFunc(), 145 RequestTopic: s.config.RequestTopic, 146 ResponseTopic: func(requestID uint64) string { 147 return fmt.Sprintf("%s.%08x.%d", s.config.RequestTopic, runID, requestID) 148 }, 149 Clock: clock, 150 ForwardTimeout: ForwardTimeout, 151 }) 152 153 controllerUUID := agent.CurrentConfig().Controller().Id() 154 w, err := s.config.NewWorker(lease.ManagerConfig{ 155 Secretary: lease.SecretaryFinder(controllerUUID), 156 Store: s.store, 157 Clock: clock, 158 Logger: s.config.Logger, 159 MaxSleep: MaxSleep, 160 EntityUUID: controllerUUID, 161 PrometheusRegisterer: s.config.PrometheusRegisterer, 162 }) 163 if err != nil { 164 stTracker.Done() 165 return nil, errors.Trace(err) 166 } 167 return common.NewCleanupWorker(w, func() { stTracker.Done() }), nil 168 } 169 170 func (s *manifoldState) output(in worker.Worker, out interface{}) error { 171 if w, ok := in.(*common.CleanupWorker); ok { 172 in = w.Worker 173 } 174 manager, ok := in.(*lease.Manager) 175 if !ok { 176 return errors.Errorf("expected input of type *worker/lease.Manager, got %T", in) 177 } 178 switch out := out.(type) { 179 case *globalclock.Updater: 180 *out = s.store 181 return nil 182 case *corelease.Manager: 183 *out = manager 184 return nil 185 default: 186 return errors.Errorf("expected output of type *globalclock.Updater or *core/lease.Manager, got %T", out) 187 } 188 } 189 190 // Manifold builds a dependency.Manifold for running a lease manager. 191 func Manifold(config ManifoldConfig) dependency.Manifold { 192 s := manifoldState{config: config} 193 return dependency.Manifold{ 194 Inputs: []string{ 195 config.AgentName, 196 config.ClockName, 197 config.CentralHubName, 198 config.StateName, 199 }, 200 Start: s.start, 201 Output: s.output, 202 } 203 } 204 205 // NewWorker wraps NewManager to return worker.Worker for testability. 206 func NewWorker(config lease.ManagerConfig) (worker.Worker, error) { 207 return lease.NewManager(config) 208 } 209 210 // NewStore is a shim to make a raftlease.Store for testability. 211 func NewStore(config raftlease.StoreConfig) *raftlease.Store { 212 return raftlease.NewStore(config) 213 }