github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/controller/manifold_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller_test 5 6 import ( 7 "errors" 8 "time" 9 10 jujutesting "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/worker.v1" 14 "gopkg.in/juju/worker.v1/dependency" 15 dt "gopkg.in/juju/worker.v1/dependency/testing" 16 17 coreagent "github.com/juju/juju/agent" 18 "github.com/juju/juju/state" 19 statetesting "github.com/juju/juju/state/testing" 20 coretesting "github.com/juju/juju/testing" 21 workercontroller "github.com/juju/juju/worker/controller" 22 ) 23 24 type ManifoldSuite struct { 25 statetesting.StateSuite 26 manifold dependency.Manifold 27 openControllerCalled bool 28 openControllerErr error 29 config workercontroller.ManifoldConfig 30 agent *mockAgent 31 resources dt.StubResources 32 } 33 34 var _ = gc.Suite(&ManifoldSuite{}) 35 36 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 37 s.StateSuite.SetUpTest(c) 38 39 s.openControllerCalled = false 40 s.openControllerErr = nil 41 42 s.config = workercontroller.ManifoldConfig{ 43 AgentName: "agent", 44 StateConfigWatcherName: "state-config-watcher", 45 OpenController: s.fakeOpenController, 46 MongoPingInterval: 10 * time.Millisecond, 47 } 48 s.manifold = workercontroller.Manifold(s.config) 49 s.resources = dt.StubResources{ 50 "agent": dt.NewStubResource(new(mockAgent)), 51 "state-config-watcher": dt.NewStubResource(true), 52 } 53 } 54 55 func (s *ManifoldSuite) fakeOpenController(coreagent.Config) (*state.Controller, error) { 56 s.openControllerCalled = true 57 if s.openControllerErr != nil { 58 return nil, s.openControllerErr 59 } 60 // Here's one we prepared earlier... 61 return s.Controller, nil 62 } 63 64 func (s *ManifoldSuite) TestInputs(c *gc.C) { 65 c.Assert(s.manifold.Inputs, jc.SameContents, []string{ 66 "agent", 67 "state-config-watcher", 68 }) 69 } 70 71 func (s *ManifoldSuite) TestStartAgentMissing(c *gc.C) { 72 s.resources["agent"] = dt.StubResource{Error: dependency.ErrMissing} 73 w, err := s.startManifold(c) 74 c.Check(w, gc.IsNil) 75 c.Check(err, gc.Equals, dependency.ErrMissing) 76 } 77 78 func (s *ManifoldSuite) TestStateConfigWatcherMissing(c *gc.C) { 79 s.resources["state-config-watcher"] = dt.StubResource{Error: dependency.ErrMissing} 80 w, err := s.startManifold(c) 81 c.Check(w, gc.IsNil) 82 c.Check(err, gc.Equals, dependency.ErrMissing) 83 } 84 85 func (s *ManifoldSuite) TestStartOpenControllerNil(c *gc.C) { 86 s.config.OpenController = nil 87 manifold := workercontroller.Manifold(s.config) 88 w, err := manifold.Start(s.resources.Context()) 89 c.Check(w, gc.IsNil) 90 c.Check(err, gc.ErrorMatches, "OpenController is nil in config") 91 } 92 93 func (s *ManifoldSuite) TestStartNotController(c *gc.C) { 94 s.resources["state-config-watcher"] = dt.NewStubResource(false) 95 w, err := s.startManifold(c) 96 c.Check(w, gc.IsNil) 97 c.Check(err, gc.Equals, dependency.ErrMissing) 98 } 99 100 func (s *ManifoldSuite) TestStartOpenControllerFails(c *gc.C) { 101 s.openControllerErr = errors.New("boom") 102 w, err := s.startManifold(c) 103 c.Check(w, gc.IsNil) 104 c.Check(err, gc.ErrorMatches, "boom") 105 } 106 107 func (s *ManifoldSuite) TestStartSuccess(c *gc.C) { 108 w := s.mustStartManifold(c) 109 c.Check(s.openControllerCalled, jc.IsTrue) 110 checkNotExiting(c, w) 111 checkStop(c, w) 112 } 113 114 func (s *ManifoldSuite) TestPinging(c *gc.C) { 115 w := s.mustStartManifold(c) 116 checkNotExiting(c, w) 117 118 // Kill the mongod to cause pings to fail. 119 jujutesting.MgoServer.Destroy() 120 121 checkExitsWithError(c, w, "database ping failed: .+") 122 } 123 124 func (s *ManifoldSuite) TestOutputBadWorker(c *gc.C) { 125 var st *state.Controller 126 err := s.manifold.Output(dummyWorker{}, &st) 127 c.Check(st, gc.IsNil) 128 c.Check(err, gc.ErrorMatches, `in should be a \*controller.controllerWorker; .+`) 129 } 130 131 func (s *ManifoldSuite) TestOutputWrongType(c *gc.C) { 132 w := s.mustStartManifold(c) 133 134 var wrong int 135 err := s.manifold.Output(w, &wrong) 136 c.Check(wrong, gc.Equals, 0) 137 c.Check(err, gc.ErrorMatches, `out should be \*Tracker; got .+`) 138 } 139 140 func (s *ManifoldSuite) TestOutputSuccess(c *gc.C) { 141 w := s.mustStartManifold(c) 142 143 var tracker workercontroller.Tracker 144 err := s.manifold.Output(w, &tracker) 145 c.Assert(err, jc.ErrorIsNil) 146 147 ctlr, err := tracker.Use() 148 c.Assert(err, jc.ErrorIsNil) 149 c.Check(ctlr, gc.Equals, s.Controller) 150 c.Assert(tracker.Done(), jc.ErrorIsNil) 151 152 // Ensure Controller is closed when the worker is done. 153 checkStop(c, w) 154 assertControllerClosed(c, s.Controller) 155 } 156 157 func (s *ManifoldSuite) TestControllerStillInUse(c *gc.C) { 158 w := s.mustStartManifold(c) 159 160 var tracker workercontroller.Tracker 161 err := s.manifold.Output(w, &tracker) 162 c.Assert(err, jc.ErrorIsNil) 163 164 ctlr, err := tracker.Use() 165 c.Assert(err, jc.ErrorIsNil) 166 167 // Close the worker while the State is still in use. 168 checkStop(c, w) 169 assertControllerNotClosed(c, ctlr) 170 171 // Now signal that the Controller is no longer needed. 172 c.Assert(tracker.Done(), jc.ErrorIsNil) 173 assertControllerClosed(c, ctlr) 174 } 175 176 func (s *ManifoldSuite) mustStartManifold(c *gc.C) worker.Worker { 177 w, err := s.startManifold(c) 178 c.Assert(err, jc.ErrorIsNil) 179 return w 180 } 181 182 func (s *ManifoldSuite) startManifold(c *gc.C) (worker.Worker, error) { 183 w, err := s.manifold.Start(s.resources.Context()) 184 if w != nil { 185 s.AddCleanup(func(*gc.C) { worker.Stop(w) }) 186 } 187 return w, err 188 } 189 190 func checkStop(c *gc.C, w worker.Worker) { 191 err := worker.Stop(w) 192 c.Check(err, jc.ErrorIsNil) 193 } 194 195 func checkNotExiting(c *gc.C, w worker.Worker) { 196 exited := make(chan bool) 197 go func() { 198 w.Wait() 199 close(exited) 200 }() 201 202 select { 203 case <-exited: 204 c.Fatal("worker exited unexpectedly") 205 case <-time.After(coretesting.ShortWait): 206 // Worker didn't exit (good) 207 } 208 } 209 210 func checkExitsWithError(c *gc.C, w worker.Worker, expectedErr string) { 211 errCh := make(chan error) 212 go func() { 213 errCh <- w.Wait() 214 }() 215 select { 216 case err := <-errCh: 217 c.Check(err, gc.ErrorMatches, expectedErr) 218 case <-time.After(coretesting.LongWait): 219 c.Fatal("timed out waiting for worker to exit") 220 } 221 } 222 223 type mockAgent struct { 224 coreagent.Agent 225 } 226 227 func (ma *mockAgent) CurrentConfig() coreagent.Config { 228 return new(mockConfig) 229 } 230 231 type mockConfig struct { 232 coreagent.Config 233 } 234 235 type dummyWorker struct { 236 worker.Worker 237 }