github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/apiserver/manifold_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "net/http" 8 "time" 9 10 "github.com/juju/clock/testclock" 11 "github.com/juju/errors" 12 "github.com/juju/pubsub" 13 "github.com/juju/testing" 14 jc "github.com/juju/testing/checkers" 15 "github.com/prometheus/client_golang/prometheus" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/names.v2" 18 "gopkg.in/juju/worker.v1" 19 "gopkg.in/juju/worker.v1/dependency" 20 dt "gopkg.in/juju/worker.v1/dependency/testing" 21 "gopkg.in/juju/worker.v1/workertest" 22 23 "github.com/juju/juju/agent" 24 coreapiserver "github.com/juju/juju/apiserver" 25 "github.com/juju/juju/apiserver/apiserverhttp" 26 "github.com/juju/juju/apiserver/httpcontext" 27 "github.com/juju/juju/apiserver/params" 28 "github.com/juju/juju/core/auditlog" 29 "github.com/juju/juju/core/cache" 30 "github.com/juju/juju/core/presence" 31 "github.com/juju/juju/state" 32 coretesting "github.com/juju/juju/testing" 33 "github.com/juju/juju/worker/apiserver" 34 "github.com/juju/juju/worker/gate" 35 "github.com/juju/juju/worker/lease" 36 ) 37 38 type ManifoldSuite struct { 39 testing.IsolationSuite 40 41 manifold dependency.Manifold 42 context dependency.Context 43 agent *mockAgent 44 authenticator *mockAuthenticator 45 clock *testclock.Clock 46 controller *cache.Controller 47 mux *apiserverhttp.Mux 48 state stubStateTracker 49 prometheusRegisterer stubPrometheusRegisterer 50 hub pubsub.StructuredHub 51 upgradeGate stubGateWaiter 52 auditConfig stubAuditConfig 53 leaseManager *lease.Manager 54 metricsCollector *coreapiserver.Collector 55 56 stub testing.Stub 57 } 58 59 var _ = gc.Suite(&ManifoldSuite{}) 60 61 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 62 s.IsolationSuite.SetUpTest(c) 63 64 s.agent = &mockAgent{} 65 s.authenticator = &mockAuthenticator{} 66 s.clock = testclock.NewClock(time.Time{}) 67 controller, err := cache.NewController(cache.ControllerConfig{ 68 Changes: make(chan interface{}), 69 }) 70 c.Assert(err, jc.ErrorIsNil) 71 s.controller = controller 72 s.mux = apiserverhttp.NewMux() 73 s.state = stubStateTracker{} 74 s.metricsCollector = coreapiserver.NewMetricsCollector() 75 s.upgradeGate = stubGateWaiter{} 76 s.auditConfig = stubAuditConfig{} 77 s.leaseManager = &lease.Manager{} 78 s.stub.ResetCalls() 79 80 s.context = s.newContext(nil) 81 s.manifold = apiserver.Manifold(apiserver.ManifoldConfig{ 82 AgentName: "agent", 83 AuthenticatorName: "authenticator", 84 ClockName: "clock", 85 MuxName: "mux", 86 ModelCacheName: "modelcache", 87 RestoreStatusName: "restore-status", 88 StateName: "state", 89 UpgradeGateName: "upgrade", 90 AuditConfigUpdaterName: "auditconfig-updater", 91 LeaseManagerName: "lease-manager", 92 RaftTransportName: "raft-transport", 93 PrometheusRegisterer: &s.prometheusRegisterer, 94 RegisterIntrospectionHTTPHandlers: func(func(string, http.Handler)) {}, 95 Hub: &s.hub, 96 Presence: presence.New(s.clock), 97 NewWorker: s.newWorker, 98 NewMetricsCollector: s.newMetricsCollector, 99 }) 100 } 101 102 func (s *ManifoldSuite) newContext(overlay map[string]interface{}) dependency.Context { 103 resources := map[string]interface{}{ 104 "agent": s.agent, 105 "authenticator": s.authenticator, 106 "clock": s.clock, 107 "mux": s.mux, 108 "modelcache": s.controller, 109 "restore-status": s.RestoreStatus, 110 "state": &s.state, 111 "upgrade": &s.upgradeGate, 112 "auditconfig-updater": s.auditConfig.get, 113 "lease-manager": s.leaseManager, 114 "raft-transport": nil, 115 } 116 for k, v := range overlay { 117 resources[k] = v 118 } 119 return dt.StubContext(nil, resources) 120 } 121 122 func (s *ManifoldSuite) RestoreStatus() state.RestoreStatus { 123 s.stub.MethodCall(s, "RestoreStatus") 124 return "" 125 } 126 127 func (s *ManifoldSuite) newWorker(config apiserver.Config) (worker.Worker, error) { 128 s.stub.MethodCall(s, "NewWorker", config) 129 if err := s.stub.NextErr(); err != nil { 130 return nil, err 131 } 132 return worker.NewRunner(worker.RunnerParams{}), nil 133 } 134 135 func (s *ManifoldSuite) newMetricsCollector() *coreapiserver.Collector { 136 return s.metricsCollector 137 } 138 139 var expectedInputs = []string{ 140 "agent", "authenticator", "clock", "modelcache", "mux", "restore-status", "state", "upgrade", "auditconfig-updater", "lease-manager", "raft-transport", 141 } 142 143 func (s *ManifoldSuite) TestInputs(c *gc.C) { 144 c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs) 145 } 146 147 func (s *ManifoldSuite) TestMissingInputs(c *gc.C) { 148 for _, input := range expectedInputs { 149 context := s.newContext(map[string]interface{}{ 150 input: dependency.ErrMissing, 151 }) 152 _, err := s.manifold.Start(context) 153 c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) 154 155 // The state tracker must have either no calls, or a Use and a Done. 156 if len(s.state.Calls()) > 0 { 157 s.state.CheckCallNames(c, "Use", "Done") 158 } 159 s.state.ResetCalls() 160 } 161 } 162 163 func (s *ManifoldSuite) TestStart(c *gc.C) { 164 w := s.startWorkerClean(c) 165 workertest.CleanKill(c, w) 166 167 s.stub.CheckCallNames(c, "NewWorker") 168 args := s.stub.Calls()[0].Args 169 c.Assert(args, gc.HasLen, 1) 170 c.Assert(args[0], gc.FitsTypeOf, apiserver.Config{}) 171 config := args[0].(apiserver.Config) 172 173 c.Assert(config.GetAuditConfig, gc.NotNil) 174 c.Assert(config.GetAuditConfig(), gc.DeepEquals, s.auditConfig.config) 175 config.GetAuditConfig = nil 176 177 c.Assert(config.UpgradeComplete, gc.NotNil) 178 config.UpgradeComplete() 179 config.UpgradeComplete = nil 180 s.upgradeGate.CheckCallNames(c, "IsUnlocked") 181 182 c.Assert(config.RestoreStatus, gc.NotNil) 183 config.RestoreStatus() 184 config.RestoreStatus = nil 185 s.stub.CheckCallNames(c, "NewWorker", "RestoreStatus") 186 187 c.Assert(config.RegisterIntrospectionHTTPHandlers, gc.NotNil) 188 config.RegisterIntrospectionHTTPHandlers = nil 189 190 c.Assert(config.Presence, gc.NotNil) 191 config.Presence = nil 192 193 // NewServer is hard-coded by the manifold to an internal shim. 194 c.Assert(config.NewServer, gc.NotNil) 195 config.NewServer = nil 196 197 c.Assert(config, jc.DeepEquals, apiserver.Config{ 198 AgentConfig: &s.agent.conf, 199 Authenticator: s.authenticator, 200 Clock: s.clock, 201 Controller: s.controller, 202 Mux: s.mux, 203 StatePool: &s.state.pool, 204 LeaseManager: s.leaseManager, 205 MetricsCollector: s.metricsCollector, 206 Hub: &s.hub, 207 }) 208 } 209 210 func (s *ManifoldSuite) TestStopWorkerClosesState(c *gc.C) { 211 w := s.startWorkerClean(c) 212 defer workertest.CleanKill(c, w) 213 214 s.state.CheckCallNames(c, "Use") 215 216 workertest.CleanKill(c, w) 217 s.state.CheckCallNames(c, "Use", "Done") 218 } 219 220 func (s *ManifoldSuite) startWorkerClean(c *gc.C) worker.Worker { 221 w, err := s.manifold.Start(s.context) 222 c.Assert(err, jc.ErrorIsNil) 223 workertest.CheckAlive(c, w) 224 return w 225 } 226 227 func (s *ManifoldSuite) TestAddsAndRemovesMuxClients(c *gc.C) { 228 waitFinished := make(chan struct{}) 229 w := s.startWorkerClean(c) 230 go func() { 231 defer close(waitFinished) 232 s.mux.Wait() 233 }() 234 235 select { 236 case <-waitFinished: 237 c.Fatalf("didn't add clients to the mux") 238 case <-time.After(coretesting.ShortWait): 239 } 240 241 workertest.CleanKill(c, w) 242 243 select { 244 case <-waitFinished: 245 case <-time.After(coretesting.LongWait): 246 c.Fatalf("didn't tell the mux we were finished") 247 } 248 } 249 250 type mockAgent struct { 251 agent.Agent 252 conf mockAgentConfig 253 } 254 255 func (ma *mockAgent) CurrentConfig() agent.Config { 256 return &ma.conf 257 } 258 259 type mockAgentConfig struct { 260 agent.Config 261 dataDir string 262 logDir string 263 info *params.StateServingInfo 264 values map[string]string 265 } 266 267 func (c *mockAgentConfig) Tag() names.Tag { 268 return names.NewMachineTag("123") 269 } 270 271 func (c *mockAgentConfig) LogDir() string { 272 return c.logDir 273 } 274 275 func (c *mockAgentConfig) DataDir() string { 276 return c.dataDir 277 } 278 279 func (c *mockAgentConfig) StateServingInfo() (params.StateServingInfo, bool) { 280 if c.info != nil { 281 return *c.info, true 282 } 283 return params.StateServingInfo{}, false 284 } 285 286 func (c *mockAgentConfig) Value(key string) string { 287 return c.values[key] 288 } 289 290 type stubStateTracker struct { 291 testing.Stub 292 pool state.StatePool 293 } 294 295 func (s *stubStateTracker) Use() (*state.StatePool, error) { 296 s.MethodCall(s, "Use") 297 return &s.pool, s.NextErr() 298 } 299 300 func (s *stubStateTracker) Done() error { 301 s.MethodCall(s, "Done") 302 return s.NextErr() 303 } 304 305 func (s *stubStateTracker) Report() map[string]interface{} { 306 s.MethodCall(s, "Report") 307 return nil 308 } 309 310 type stubPrometheusRegisterer struct { 311 testing.Stub 312 } 313 314 func (s *stubPrometheusRegisterer) MustRegister(...prometheus.Collector) { 315 panic("should not be called") 316 } 317 318 func (s *stubPrometheusRegisterer) Register(c prometheus.Collector) error { 319 s.MethodCall(s, "Register", c) 320 return s.NextErr() 321 } 322 323 func (s *stubPrometheusRegisterer) Unregister(c prometheus.Collector) bool { 324 s.MethodCall(s, "Unregister", c) 325 return false 326 } 327 328 type stubGateWaiter struct { 329 testing.Stub 330 gate.Waiter 331 } 332 333 func (w *stubGateWaiter) IsUnlocked() bool { 334 w.MethodCall(w, "IsUnlocked") 335 return true 336 } 337 338 type stubAuditConfig struct { 339 testing.Stub 340 config auditlog.Config 341 } 342 343 func (c *stubAuditConfig) get() auditlog.Config { 344 c.MethodCall(c, "get") 345 return c.config 346 } 347 348 type mockAuthenticator struct { 349 httpcontext.LocalMacaroonAuthenticator 350 }