github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lease/manifold/manifold_test.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package manifold_test 5 6 import ( 7 "io" 8 "time" 9 10 "github.com/juju/clock/testclock" 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/pubsub" 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 "github.com/prometheus/client_golang/prometheus" 17 gc "gopkg.in/check.v1" 18 "gopkg.in/juju/names.v2" 19 "gopkg.in/juju/worker.v1" 20 "gopkg.in/juju/worker.v1/dependency" 21 dt "gopkg.in/juju/worker.v1/dependency/testing" 22 "gopkg.in/juju/worker.v1/workertest" 23 "gopkg.in/mgo.v2/txn" 24 25 "github.com/juju/juju/agent" 26 "github.com/juju/juju/core/globalclock" 27 corelease "github.com/juju/juju/core/lease" 28 "github.com/juju/juju/core/raftlease" 29 "github.com/juju/juju/state" 30 coretesting "github.com/juju/juju/testing" 31 "github.com/juju/juju/worker/common" 32 "github.com/juju/juju/worker/lease" 33 leasemanager "github.com/juju/juju/worker/lease/manifold" 34 ) 35 36 type manifoldSuite struct { 37 testing.IsolationSuite 38 39 context dependency.Context 40 manifold dependency.Manifold 41 42 agent *mockAgent 43 clock *testclock.Clock 44 hub *pubsub.StructuredHub 45 stateTracker *stubStateTracker 46 47 fsm *raftlease.FSM 48 logger loggo.Logger 49 metrics prometheus.Registerer 50 51 worker worker.Worker 52 store *raftlease.Store 53 54 stub testing.Stub 55 } 56 57 var _ = gc.Suite(&manifoldSuite{}) 58 59 func (s *manifoldSuite) SetUpTest(c *gc.C) { 60 s.IsolationSuite.SetUpTest(c) 61 62 s.stub.ResetCalls() 63 64 s.agent = &mockAgent{conf: mockAgentConfig{ 65 uuid: "controller-uuid", 66 }} 67 s.clock = testclock.NewClock(time.Now()) 68 s.hub = pubsub.NewStructuredHub(nil) 69 s.stateTracker = &stubStateTracker{ 70 done: make(chan struct{}), 71 } 72 73 s.fsm = raftlease.NewFSM() 74 s.logger = loggo.GetLogger("lease.manifold_test") 75 registerer := struct{ prometheus.Registerer }{} 76 s.metrics = ®isterer 77 78 s.worker = &mockWorker{} 79 s.store = &raftlease.Store{} 80 81 s.context = s.newContext(nil) 82 s.manifold = leasemanager.Manifold(leasemanager.ManifoldConfig{ 83 AgentName: "agent", 84 ClockName: "clock", 85 CentralHubName: "hub", 86 StateName: "state", 87 FSM: s.fsm, 88 RequestTopic: "lease.manifold_test", 89 Logger: &s.logger, 90 PrometheusRegisterer: s.metrics, 91 NewWorker: s.newWorker, 92 NewStore: s.newStore, 93 }) 94 } 95 96 func (s *manifoldSuite) newContext(overlay map[string]interface{}) dependency.Context { 97 resources := map[string]interface{}{ 98 "agent": s.agent, 99 "clock": s.clock, 100 "hub": s.hub, 101 "state": s.stateTracker, 102 } 103 for k, v := range overlay { 104 resources[k] = v 105 } 106 return dt.StubContext(nil, resources) 107 } 108 109 func (s *manifoldSuite) newWorker(config lease.ManagerConfig) (worker.Worker, error) { 110 s.stub.MethodCall(s, "NewWorker", config) 111 if err := s.stub.NextErr(); err != nil { 112 return nil, err 113 } 114 return s.worker, nil 115 } 116 117 func (s *manifoldSuite) newStore(config raftlease.StoreConfig) *raftlease.Store { 118 s.stub.MethodCall(s, "NewStore", config) 119 return s.store 120 } 121 122 var expectedInputs = []string{ 123 "agent", "clock", "hub", "state", 124 } 125 126 func (s *manifoldSuite) TestInputs(c *gc.C) { 127 c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs) 128 } 129 130 func (s *manifoldSuite) TestMissingInputs(c *gc.C) { 131 for _, input := range expectedInputs { 132 context := s.newContext(map[string]interface{}{ 133 input: dependency.ErrMissing, 134 }) 135 _, err := s.manifold.Start(context) 136 c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) 137 } 138 } 139 140 func (s *manifoldSuite) TestStart(c *gc.C) { 141 w, err := s.manifold.Start(s.context) 142 c.Assert(err, jc.ErrorIsNil) 143 underlying, ok := w.(*common.CleanupWorker) 144 c.Assert(ok, gc.Equals, true) 145 c.Assert(underlying.Worker, gc.Equals, s.worker) 146 147 s.stub.CheckCallNames(c, "NewStore", "NewWorker") 148 149 args := s.stub.Calls()[0].Args 150 c.Assert(args, gc.HasLen, 1) 151 c.Assert(args[0], gc.FitsTypeOf, raftlease.StoreConfig{}) 152 storeConfig := args[0].(raftlease.StoreConfig) 153 c.Assert(storeConfig.ResponseTopic(1234), gc.Matches, "lease.manifold_test.[0-9a-f]{8}.1234") 154 storeConfig.ResponseTopic = nil 155 assertTrapdoorFuncsEqual(c, storeConfig.Trapdoor, s.stateTracker.pool.SystemState().LeaseTrapdoorFunc()) 156 storeConfig.Trapdoor = nil 157 c.Assert(storeConfig, gc.DeepEquals, raftlease.StoreConfig{ 158 FSM: s.fsm, 159 Hub: s.hub, 160 RequestTopic: "lease.manifold_test", 161 Clock: s.clock, 162 ForwardTimeout: 5 * time.Second, 163 }) 164 165 args = s.stub.Calls()[1].Args 166 c.Assert(args, gc.HasLen, 1) 167 c.Assert(args[0], gc.FitsTypeOf, lease.ManagerConfig{}) 168 config := args[0].(lease.ManagerConfig) 169 170 secretary, err := config.Secretary(corelease.SingularControllerNamespace) 171 c.Assert(err, jc.ErrorIsNil) 172 // Check that this secretary knows the controller uuid. 173 err = secretary.CheckLease(corelease.Key{"", "", "controller-uuid"}) 174 c.Assert(err, jc.ErrorIsNil) 175 config.Secretary = nil 176 177 c.Assert(config, jc.DeepEquals, lease.ManagerConfig{ 178 Store: s.store, 179 Clock: s.clock, 180 Logger: &s.logger, 181 MaxSleep: time.Minute, 182 EntityUUID: "controller-uuid", 183 PrometheusRegisterer: s.metrics, 184 }) 185 } 186 187 func (s *manifoldSuite) TestOutput(c *gc.C) { 188 s.worker = &lease.Manager{} 189 w, err := s.manifold.Start(s.context) 190 c.Assert(err, jc.ErrorIsNil) 191 192 var updater globalclock.Updater 193 err = s.manifold.Output(w, &updater) 194 c.Assert(err, jc.ErrorIsNil) 195 c.Assert(updater, gc.Equals, s.store) 196 197 var manager corelease.Manager 198 err = s.manifold.Output(w, &manager) 199 c.Assert(err, jc.ErrorIsNil) 200 c.Assert(manager, gc.Equals, s.worker) 201 202 var other io.Writer 203 err = s.manifold.Output(w, &other) 204 c.Assert(err, gc.ErrorMatches, `expected output of type \*globalclock.Updater or \*core/lease.Manager, got \*io.Writer`) 205 } 206 207 func (s *manifoldSuite) TestStoppingWorkerReleasesState(c *gc.C) { 208 w, err := s.manifold.Start(s.context) 209 c.Assert(err, jc.ErrorIsNil) 210 211 s.stateTracker.CheckCallNames(c, "Use") 212 select { 213 case <-s.stateTracker.done: 214 c.Fatal("unexpected state release") 215 case <-time.After(coretesting.ShortWait): 216 } 217 218 // Stopping the worker should cause the state to 219 // eventually be released. 220 workertest.CleanKill(c, w) 221 222 s.stateTracker.waitDone(c) 223 s.stateTracker.CheckCallNames(c, "Use", "Done") 224 } 225 226 func assertTrapdoorFuncsEqual(c *gc.C, actual, expected raftlease.TrapdoorFunc) { 227 if actual == nil { 228 c.Assert(expected, gc.Equals, nil) 229 return 230 } 231 var actualOps, expectedOps []txn.Op 232 err := actual(corelease.Key{"ns", "model", "lease"}, "holder")(0, &actualOps) 233 c.Assert(err, jc.ErrorIsNil) 234 err = expected(corelease.Key{"ns", "model", "lease"}, "holder")(0, &expectedOps) 235 c.Assert(err, jc.ErrorIsNil) 236 c.Assert(actualOps, gc.DeepEquals, expectedOps) 237 } 238 239 type mockAgent struct { 240 agent.Agent 241 conf mockAgentConfig 242 } 243 244 func (ma *mockAgent) CurrentConfig() agent.Config { 245 return &ma.conf 246 } 247 248 type mockAgentConfig struct { 249 agent.Config 250 uuid string 251 } 252 253 func (c *mockAgentConfig) Controller() names.ControllerTag { 254 return names.NewControllerTag(c.uuid) 255 } 256 257 type mockWorker struct{} 258 259 func (w *mockWorker) Kill() {} 260 func (w *mockWorker) Wait() error { 261 return nil 262 } 263 264 type stubStateTracker struct { 265 testing.Stub 266 pool state.StatePool 267 done chan struct{} 268 } 269 270 func (s *stubStateTracker) Use() (*state.StatePool, error) { 271 s.MethodCall(s, "Use") 272 return &s.pool, s.NextErr() 273 } 274 275 func (s *stubStateTracker) Done() error { 276 s.MethodCall(s, "Done") 277 err := s.NextErr() 278 close(s.done) 279 return err 280 } 281 282 func (s *stubStateTracker) Report() map[string]interface{} { 283 return map[string]interface{}{"hey": "mum"} 284 } 285 286 func (s *stubStateTracker) waitDone(c *gc.C) { 287 select { 288 case <-s.done: 289 case <-time.After(coretesting.LongWait): 290 c.Fatal("timed out waiting for state to be released") 291 } 292 }