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