github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/meterstatus/manifold_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package meterstatus_test 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/names" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils/fslock" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/agent" 17 "github.com/juju/juju/api/base" 18 msapi "github.com/juju/juju/api/meterstatus" 19 coretesting "github.com/juju/juju/testing" 20 "github.com/juju/juju/watcher" 21 "github.com/juju/juju/worker" 22 "github.com/juju/juju/worker/dependency" 23 dt "github.com/juju/juju/worker/dependency/testing" 24 "github.com/juju/juju/worker/meterstatus" 25 "github.com/juju/juju/worker/uniter/runner" 26 ) 27 28 type ManifoldSuite struct { 29 coretesting.BaseSuite 30 31 stub *testing.Stub 32 33 dataDir string 34 35 manifoldConfig meterstatus.ManifoldConfig 36 manifold dependency.Manifold 37 resources dt.StubResources 38 } 39 40 var _ = gc.Suite(&ManifoldSuite{}) 41 42 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 43 s.BaseSuite.SetUpTest(c) 44 s.stub = &testing.Stub{} 45 46 s.manifoldConfig = meterstatus.ManifoldConfig{ 47 AgentName: "agent-name", 48 APICallerName: "apicaller-name", 49 MachineLockName: "machine-lock-name", 50 NewHookRunner: meterstatus.NewHookRunner, 51 NewMeterStatusAPIClient: msapi.NewClient, 52 53 NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker, 54 NewIsolatedStatusWorker: meterstatus.NewIsolatedStatusWorker, 55 } 56 s.manifold = meterstatus.Manifold(s.manifoldConfig) 57 s.dataDir = c.MkDir() 58 59 locksDir := c.MkDir() 60 lock, err := fslock.NewLock(locksDir, "machine-lock", fslock.Defaults()) 61 c.Assert(err, jc.ErrorIsNil) 62 63 s.resources = dt.StubResources{ 64 "agent-name": dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}}, 65 "apicaller-name": dt.StubResource{Output: &dummyAPICaller{}}, 66 "machine-lock-name": dt.StubResource{Output: lock}, 67 } 68 } 69 70 // TestInputs ensures the collect manifold has the expected defined inputs. 71 func (s *ManifoldSuite) TestInputs(c *gc.C) { 72 c.Check(s.manifold.Inputs, jc.DeepEquals, []string{ 73 "agent-name", "apicaller-name", "machine-lock-name", 74 }) 75 } 76 77 // TestStartMissingDeps ensures that the manifold correctly handles a missing 78 // resource dependency. 79 func (s *ManifoldSuite) TestStartMissingDeps(c *gc.C) { 80 for _, missingDep := range []string{ 81 "agent-name", "machine-lock-name", 82 } { 83 testResources := dt.StubResources{} 84 for k, v := range s.resources { 85 if k == missingDep { 86 testResources[k] = dt.StubResource{Error: dependency.ErrMissing} 87 } else { 88 testResources[k] = v 89 } 90 } 91 worker, err := s.manifold.Start(testResources.Context()) 92 c.Check(worker, gc.IsNil) 93 c.Check(err, gc.Equals, dependency.ErrMissing) 94 } 95 } 96 97 type PatchedManifoldSuite struct { 98 coretesting.BaseSuite 99 msClient *stubMeterStatusClient 100 manifoldConfig meterstatus.ManifoldConfig 101 stub *testing.Stub 102 resources dt.StubResources 103 } 104 105 func (s *PatchedManifoldSuite) SetUpTest(c *gc.C) { 106 s.BaseSuite.SetUpTest(c) 107 108 s.stub = &testing.Stub{} 109 s.msClient = &stubMeterStatusClient{stub: s.stub, changes: make(chan struct{})} 110 newMSClient := func(_ base.APICaller, _ names.UnitTag) msapi.MeterStatusClient { 111 return s.msClient 112 } 113 newHookRunner := func(_ names.UnitTag, _ *fslock.Lock, _ agent.Config) meterstatus.HookRunner { 114 return &stubRunner{stub: s.stub} 115 } 116 117 s.manifoldConfig = meterstatus.ManifoldConfig{ 118 AgentName: "agent-name", 119 APICallerName: "apicaller-name", 120 MachineLockName: "machine-lock-name", 121 NewHookRunner: newHookRunner, 122 NewMeterStatusAPIClient: newMSClient, 123 } 124 } 125 126 // TestStatusWorkerStarts ensures that the manifold correctly sets up the connected worker. 127 func (s *PatchedManifoldSuite) TestStatusWorkerStarts(c *gc.C) { 128 var called bool 129 s.manifoldConfig.NewConnectedStatusWorker = func(cfg meterstatus.ConnectedConfig) (worker.Worker, error) { 130 called = true 131 return meterstatus.NewConnectedStatusWorker(cfg) 132 } 133 manifold := meterstatus.Manifold(s.manifoldConfig) 134 worker, err := manifold.Start(s.resources.Context()) 135 c.Assert(called, jc.IsTrue) 136 c.Assert(err, jc.ErrorIsNil) 137 c.Assert(worker, gc.NotNil) 138 worker.Kill() 139 err = worker.Wait() 140 c.Assert(err, jc.ErrorIsNil) 141 s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus") 142 } 143 144 // TestInactiveWorker ensures that the manifold correctly sets up the isolated worker. 145 func (s *PatchedManifoldSuite) TestIsolatedWorker(c *gc.C) { 146 delete(s.resources, "apicaller-name") 147 var called bool 148 s.manifoldConfig.NewIsolatedStatusWorker = func(cfg meterstatus.IsolatedConfig) (worker.Worker, error) { 149 called = true 150 return meterstatus.NewIsolatedStatusWorker(cfg) 151 } 152 manifold := meterstatus.Manifold(s.manifoldConfig) 153 worker, err := manifold.Start(s.resources.Context()) 154 c.Assert(called, jc.IsTrue) 155 c.Assert(err, jc.ErrorIsNil) 156 c.Assert(worker, gc.NotNil) 157 worker.Kill() 158 err = worker.Wait() 159 c.Assert(err, jc.ErrorIsNil) 160 s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus") 161 } 162 163 type dummyAgent struct { 164 agent.Agent 165 dataDir string 166 } 167 168 func (a dummyAgent) CurrentConfig() agent.Config { 169 return &dummyAgentConfig{dataDir: a.dataDir} 170 } 171 172 type dummyAgentConfig struct { 173 agent.Config 174 dataDir string 175 } 176 177 // Tag implements agent.AgentConfig. 178 func (ac dummyAgentConfig) Tag() names.Tag { 179 return names.NewUnitTag("u/0") 180 } 181 182 // DataDir implements agent.AgentConfig. 183 func (ac dummyAgentConfig) DataDir() string { 184 return ac.dataDir 185 } 186 187 type dummyAPICaller struct { 188 base.APICaller 189 } 190 191 func (dummyAPICaller) BestFacadeVersion(facade string) int { 192 return 42 193 } 194 195 type stubMeterStatusClient struct { 196 sync.RWMutex 197 stub *testing.Stub 198 changes chan struct{} 199 code string 200 } 201 202 func newStubMeterStatusClient(stub *testing.Stub) *stubMeterStatusClient { 203 changes := make(chan struct{}) 204 return &stubMeterStatusClient{stub: stub, changes: changes} 205 } 206 207 func (s *stubMeterStatusClient) SignalStatus(codes ...string) { 208 if len(codes) == 0 { 209 codes = []string{s.code} 210 } 211 for _, code := range codes { 212 s.SetStatus(code) 213 select { 214 case s.changes <- struct{}{}: 215 case <-time.After(coretesting.LongWait): 216 panic("timed out signaling meter status change") 217 } 218 } 219 } 220 221 func (s *stubMeterStatusClient) SetStatus(code string) { 222 s.Lock() 223 defer s.Unlock() 224 s.code = code 225 } 226 227 func (s *stubMeterStatusClient) MeterStatus() (string, string, error) { 228 s.RLock() 229 defer s.RUnlock() 230 s.stub.MethodCall(s, "MeterStatus") 231 if s.code == "" { 232 return "GREEN", "", nil 233 } else { 234 return s.code, "", nil 235 } 236 237 } 238 239 func (s *stubMeterStatusClient) WatchMeterStatus() (watcher.NotifyWatcher, error) { 240 s.stub.MethodCall(s, "WatchMeterStatus") 241 return s, nil 242 } 243 244 func (s *stubMeterStatusClient) Changes() watcher.NotifyChannel { 245 return s.changes 246 } 247 248 func (s *stubMeterStatusClient) Kill() { 249 } 250 251 func (s *stubMeterStatusClient) Wait() error { 252 return nil 253 } 254 255 type stubRunner struct { 256 runner.Runner 257 stub *testing.Stub 258 ran chan struct{} 259 } 260 261 func (r *stubRunner) RunHook(code, info string, abort <-chan struct{}) error { 262 r.stub.MethodCall(r, "RunHook", code, info) 263 if r.ran != nil { 264 select { 265 case r.ran <- struct{}{}: 266 case <-time.After(coretesting.LongWait): 267 panic("timed out signaling hook run") 268 } 269 } 270 return r.stub.NextErr() 271 }