github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/state/manifold_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_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 14 coreagent "github.com/juju/juju/agent" 15 "github.com/juju/juju/state" 16 statetesting "github.com/juju/juju/state/testing" 17 coretesting "github.com/juju/juju/testing" 18 "github.com/juju/juju/worker" 19 "github.com/juju/juju/worker/dependency" 20 dt "github.com/juju/juju/worker/dependency/testing" 21 workerstate "github.com/juju/juju/worker/state" 22 ) 23 24 type ManifoldSuite struct { 25 statetesting.StateSuite 26 manifold dependency.Manifold 27 openStateCalled bool 28 openStateErr error 29 config workerstate.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.openStateCalled = false 40 s.openStateErr = nil 41 42 s.config = workerstate.ManifoldConfig{ 43 AgentName: "agent", 44 StateConfigWatcherName: "state-config-watcher", 45 OpenState: s.fakeOpenState, 46 PingInterval: 10 * time.Millisecond, 47 } 48 s.manifold = workerstate.Manifold(s.config) 49 s.resources = dt.StubResources{ 50 "agent": dt.StubResource{Output: new(mockAgent)}, 51 "state-config-watcher": dt.StubResource{Output: true}, 52 } 53 } 54 55 func (s *ManifoldSuite) fakeOpenState(coreagent.Config) (*state.State, error) { 56 s.openStateCalled = true 57 if s.openStateErr != nil { 58 return nil, s.openStateErr 59 } 60 // Here's one we prepared earlier... 61 return s.State, 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) TestStartOpenStateNil(c *gc.C) { 86 s.config.OpenState = nil 87 manifold := workerstate.Manifold(s.config) 88 w, err := manifold.Start(s.resources.Context()) 89 c.Check(w, gc.IsNil) 90 c.Check(err, gc.ErrorMatches, "OpenState is nil in config") 91 } 92 93 func (s *ManifoldSuite) TestStartNotStateServer(c *gc.C) { 94 s.resources["state-config-watcher"] = dt.StubResource{Output: 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) TestStartOpenStateFails(c *gc.C) { 101 s.openStateErr = 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.openStateCalled, jc.IsTrue) 110 checkNotExiting(c, w) 111 checkStop(c, w) 112 } 113 114 func (s *ManifoldSuite) TestStatePinging(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, "state ping failed: .+") 122 } 123 124 func (s *ManifoldSuite) TestOutputBadWorker(c *gc.C) { 125 var st *state.State 126 err := s.manifold.Output(dummyWorker{}, &st) 127 c.Check(st, gc.IsNil) 128 c.Check(err, gc.ErrorMatches, `in should be a \*state.stateWorker; .+`) 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 \*state.State; got .+`) 138 } 139 140 func (s *ManifoldSuite) TestOutputSuccess(c *gc.C) { 141 w := s.mustStartManifold(c) 142 143 var stTracker workerstate.StateTracker 144 err := s.manifold.Output(w, &stTracker) 145 c.Assert(err, jc.ErrorIsNil) 146 147 st, err := stTracker.Use() 148 c.Assert(err, jc.ErrorIsNil) 149 c.Check(st, gc.Equals, s.State) 150 c.Assert(stTracker.Done(), jc.ErrorIsNil) 151 152 // Ensure State is closed when the worker is done. 153 checkStop(c, w) 154 assertStateClosed(c, s.State) 155 } 156 157 func (s *ManifoldSuite) TestStateStillInUse(c *gc.C) { 158 w := s.mustStartManifold(c) 159 160 var stTracker workerstate.StateTracker 161 err := s.manifold.Output(w, &stTracker) 162 c.Assert(err, jc.ErrorIsNil) 163 164 st, err := stTracker.Use() 165 c.Assert(err, jc.ErrorIsNil) 166 167 // Close the worker while the State is still in use. 168 checkStop(c, w) 169 assertStateNotClosed(c, st) 170 171 // Now signal that the State is no longer needed. 172 c.Assert(stTracker.Done(), jc.ErrorIsNil) 173 assertStateClosed(c, st) 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 }