github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/stateconfigwatcher/manifold_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package stateconfigwatcher_test 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/voyeur" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 "gopkg.in/juju/worker.v1" 16 "gopkg.in/juju/worker.v1/dependency" 17 dt "gopkg.in/juju/worker.v1/dependency/testing" 18 19 coreagent "github.com/juju/juju/agent" 20 "github.com/juju/juju/apiserver/params" 21 coretesting "github.com/juju/juju/testing" 22 "github.com/juju/juju/worker/stateconfigwatcher" 23 ) 24 25 type ManifoldSuite struct { 26 testing.IsolationSuite 27 agent *mockAgent 28 goodContext dependency.Context 29 agentConfigChanged *voyeur.Value 30 manifold dependency.Manifold 31 worker worker.Worker 32 } 33 34 var _ = gc.Suite(&ManifoldSuite{}) 35 36 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 37 s.IsolationSuite.SetUpTest(c) 38 39 s.agent = new(mockAgent) 40 s.agent.conf.setStateServingInfo(true) 41 s.agent.conf.tag = names.NewMachineTag("99") 42 43 s.goodContext = dt.StubContext(nil, map[string]interface{}{ 44 "agent": s.agent, 45 }) 46 47 s.agentConfigChanged = voyeur.NewValue(0) 48 s.manifold = stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{ 49 AgentName: "agent", 50 AgentConfigChanged: s.agentConfigChanged, 51 }) 52 } 53 54 func (s *ManifoldSuite) TestInputs(c *gc.C) { 55 c.Assert(s.manifold.Inputs, jc.SameContents, []string{"agent"}) 56 } 57 58 func (s *ManifoldSuite) TestNoAgent(c *gc.C) { 59 context := dt.StubContext(nil, map[string]interface{}{ 60 "agent": dependency.ErrMissing, 61 }) 62 _, err := s.manifold.Start(context) 63 c.Assert(err, gc.Equals, dependency.ErrMissing) 64 } 65 66 func (s *ManifoldSuite) TestNilAgentConfigChanged(c *gc.C) { 67 manifold := stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{ 68 AgentName: "agent", 69 }) 70 _, err := manifold.Start(s.goodContext) 71 c.Assert(err, gc.ErrorMatches, "nil AgentConfigChanged .+") 72 } 73 74 func (s *ManifoldSuite) TestNotMachineAgent(c *gc.C) { 75 s.agent.conf.tag = names.NewUnitTag("foo/0") 76 _, err := s.manifold.Start(s.goodContext) 77 c.Assert(err, gc.ErrorMatches, "manifold can only be used with a machine agent") 78 } 79 80 func (s *ManifoldSuite) TestStart(c *gc.C) { 81 w, err := s.manifold.Start(s.goodContext) 82 c.Assert(err, jc.ErrorIsNil) 83 checkStop(c, w) 84 } 85 86 func (s *ManifoldSuite) TestOutputBadWorker(c *gc.C) { 87 var out bool 88 err := s.manifold.Output(dummyWorker{}, &out) 89 c.Check(err, gc.ErrorMatches, `in should be a \*stateconfigwatcher.stateConfigWatcher; .+`) 90 } 91 92 func (s *ManifoldSuite) TestOutputWrongType(c *gc.C) { 93 w, err := s.manifold.Start(s.goodContext) 94 c.Assert(err, jc.ErrorIsNil) 95 defer checkStop(c, w) 96 97 var out int 98 err = s.manifold.Output(w, &out) 99 c.Check(err, gc.ErrorMatches, `out should be \*bool; got .+`) 100 } 101 102 func (s *ManifoldSuite) TestOutputSuccessNotStateServer(c *gc.C) { 103 s.agent.conf.setStateServingInfo(false) 104 w, err := s.manifold.Start(s.goodContext) 105 c.Assert(err, jc.ErrorIsNil) 106 defer checkStop(c, w) 107 108 var out bool 109 err = s.manifold.Output(w, &out) 110 c.Check(err, jc.ErrorIsNil) 111 c.Check(out, jc.IsFalse) 112 } 113 114 func (s *ManifoldSuite) TestOutputSuccessStateServer(c *gc.C) { 115 s.agent.conf.setStateServingInfo(true) 116 w, err := s.manifold.Start(s.goodContext) 117 c.Assert(err, jc.ErrorIsNil) 118 defer checkStop(c, w) 119 120 var out bool 121 err = s.manifold.Output(w, &out) 122 c.Check(err, jc.ErrorIsNil) 123 c.Check(out, jc.IsTrue) 124 } 125 126 func (s *ManifoldSuite) TestBounceOnChange(c *gc.C) { 127 s.agent.conf.setStateServingInfo(false) 128 w, err := s.manifold.Start(s.goodContext) 129 c.Assert(err, jc.ErrorIsNil) 130 checkNotExiting(c, w) 131 132 checkOutput := func(expected bool) { 133 var out bool 134 err = s.manifold.Output(w, &out) 135 c.Assert(err, jc.ErrorIsNil) 136 c.Check(out, gc.Equals, expected) 137 } 138 139 // Not a state server yet, initial output should be False. 140 checkOutput(false) 141 142 // Changing the config without changing the state server status - 143 // worker should keep running and output should remain false. 144 s.agentConfigChanged.Set(0) 145 checkNotExiting(c, w) 146 checkOutput(false) 147 148 // Now change the config to include state serving info, worker 149 // should bounce. 150 s.agent.conf.setStateServingInfo(true) 151 s.agentConfigChanged.Set(0) 152 checkExitsWithError(c, w, dependency.ErrBounce) 153 154 // Restart the worker, the output should now be true. 155 w, err = s.manifold.Start(s.goodContext) 156 c.Assert(err, jc.ErrorIsNil) 157 checkNotExiting(c, w) 158 checkOutput(true) 159 160 // Changing the config again without changing the state serving 161 // info shouldn't cause the agent to exit. 162 s.agentConfigChanged.Set(0) 163 checkNotExiting(c, w) 164 checkOutput(true) 165 166 // Now remove the state serving info, the agent should bounce. 167 s.agent.conf.setStateServingInfo(false) 168 s.agentConfigChanged.Set(0) 169 checkExitsWithError(c, w, dependency.ErrBounce) 170 } 171 172 func (s *ManifoldSuite) TestClosedVoyeur(c *gc.C) { 173 w, err := s.manifold.Start(s.goodContext) 174 c.Assert(err, jc.ErrorIsNil) 175 checkNotExiting(c, w) 176 177 s.agentConfigChanged.Close() 178 179 c.Check(waitForExit(c, w), gc.ErrorMatches, "config changed value closed") 180 } 181 182 func checkStop(c *gc.C, w worker.Worker) { 183 err := worker.Stop(w) 184 c.Check(err, jc.ErrorIsNil) 185 } 186 187 func checkNotExiting(c *gc.C, w worker.Worker) { 188 exited := make(chan bool) 189 go func() { 190 w.Wait() 191 close(exited) 192 }() 193 194 select { 195 case <-exited: 196 c.Fatal("worker exited unexpectedly") 197 case <-time.After(coretesting.ShortWait): 198 // Worker didn't exit (good) 199 } 200 } 201 202 func checkExitsWithError(c *gc.C, w worker.Worker, expectedErr error) { 203 c.Check(waitForExit(c, w), gc.Equals, expectedErr) 204 } 205 206 func waitForExit(c *gc.C, w worker.Worker) error { 207 errCh := make(chan error) 208 go func() { 209 errCh <- w.Wait() 210 }() 211 select { 212 case err := <-errCh: 213 return err 214 case <-time.After(coretesting.LongWait): 215 c.Fatal("timed out waiting for worker to exit") 216 } 217 panic("can't get here") 218 } 219 220 type mockAgent struct { 221 coreagent.Agent 222 conf mockConfig 223 } 224 225 func (ma *mockAgent) CurrentConfig() coreagent.Config { 226 return &ma.conf 227 } 228 229 type mockConfig struct { 230 coreagent.ConfigSetter 231 tag names.Tag 232 mu sync.Mutex 233 ssInfoIsSet bool 234 } 235 236 func (mc *mockConfig) Tag() names.Tag { 237 return mc.tag 238 } 239 240 func (mc *mockConfig) setStateServingInfo(isSet bool) { 241 mc.mu.Lock() 242 defer mc.mu.Unlock() 243 mc.ssInfoIsSet = isSet 244 } 245 246 func (mc *mockConfig) StateServingInfo() (params.StateServingInfo, bool) { 247 mc.mu.Lock() 248 defer mc.mu.Unlock() 249 return params.StateServingInfo{}, mc.ssInfoIsSet 250 } 251 252 type dummyWorker struct { 253 worker.Worker 254 }