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