github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/peergrouper/manifold_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package peergrouper_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/worker/v3" 15 "github.com/juju/worker/v3/dependency" 16 dt "github.com/juju/worker/v3/dependency/testing" 17 "github.com/juju/worker/v3/workertest" 18 "github.com/prometheus/client_golang/prometheus" 19 gc "gopkg.in/check.v1" 20 21 "github.com/juju/juju/agent" 22 "github.com/juju/juju/controller" 23 "github.com/juju/juju/state" 24 statetesting "github.com/juju/juju/state/testing" 25 "github.com/juju/juju/worker/peergrouper" 26 ) 27 28 type ManifoldSuite struct { 29 statetesting.StateSuite 30 31 manifold dependency.Manifold 32 context dependency.Context 33 clock *testclock.Clock 34 agent *mockAgent 35 hub *mockHub 36 registerer *fakeRegisterer 37 stateTracker stubStateTracker 38 39 stub testing.Stub 40 } 41 42 var _ = gc.Suite(&ManifoldSuite{}) 43 44 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 45 s.StateSuite.SetUpTest(c) 46 47 s.clock = testclock.NewClock(time.Time{}) 48 s.agent = &mockAgent{conf: mockAgentConfig{ 49 info: &controller.StateServingInfo{ 50 StatePort: 1234, 51 APIPort: 5678, 52 }, 53 }} 54 s.hub = &mockHub{} 55 s.registerer = &fakeRegisterer{} 56 s.stateTracker = stubStateTracker{pool: s.StatePool} 57 s.stub.ResetCalls() 58 59 s.context = s.newContext(nil) 60 s.manifold = peergrouper.Manifold(peergrouper.ManifoldConfig{ 61 AgentName: "agent", 62 ClockName: "clock", 63 ControllerPortName: "controller-port", 64 StateName: "state", 65 Hub: s.hub, 66 NewWorker: s.newWorker, 67 PrometheusRegisterer: s.registerer, 68 }) 69 } 70 71 func (s *ManifoldSuite) newContext(overlay map[string]interface{}) dependency.Context { 72 resources := map[string]interface{}{ 73 "agent": s.agent, 74 "clock": s.clock, 75 "controller-port": nil, 76 "state": &s.stateTracker, 77 } 78 for k, v := range overlay { 79 resources[k] = v 80 } 81 return dt.StubContext(nil, resources) 82 } 83 84 func (s *ManifoldSuite) newWorker(config peergrouper.Config) (worker.Worker, error) { 85 s.stub.MethodCall(s, "NewWorker", config) 86 if err := s.stub.NextErr(); err != nil { 87 return nil, err 88 } 89 w := worker.NewRunner(worker.RunnerParams{}) 90 s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, w) }) 91 return w, nil 92 } 93 94 var expectedInputs = []string{"agent", "clock", "controller-port", "state"} 95 96 func (s *ManifoldSuite) TestInputs(c *gc.C) { 97 c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs) 98 } 99 100 func (s *ManifoldSuite) TestMissingInputs(c *gc.C) { 101 for _, input := range expectedInputs { 102 context := s.newContext(map[string]interface{}{ 103 input: dependency.ErrMissing, 104 }) 105 _, err := s.manifold.Start(context) 106 c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) 107 } 108 } 109 110 func (s *ManifoldSuite) TestStart(c *gc.C) { 111 w := s.startWorkerClean(c) 112 workertest.CleanKill(c, w) 113 114 s.stub.CheckCallNames(c, "NewWorker") 115 args := s.stub.Calls()[0].Args 116 c.Assert(args, gc.HasLen, 1) 117 c.Assert(args[0], gc.FitsTypeOf, peergrouper.Config{}) 118 config := args[0].(peergrouper.Config) 119 120 c.Assert(config.ControllerId(), gc.Equals, "10") 121 config.ControllerId = nil 122 c.Assert(config, jc.DeepEquals, peergrouper.Config{ 123 State: peergrouper.StateShim{State: s.State}, 124 MongoSession: peergrouper.MongoSessionShim{Session: s.State.MongoSession()}, 125 APIHostPortsSetter: &peergrouper.CachingAPIHostPortsSetter{ 126 APIHostPortsSetter: s.State, 127 }, 128 Clock: s.clock, 129 Hub: s.hub, 130 MongoPort: 1234, 131 APIPort: 5678, 132 SupportsHA: true, 133 PrometheusRegisterer: s.registerer, 134 }) 135 } 136 137 func (s *ManifoldSuite) TestStopWorkerClosesState(c *gc.C) { 138 w := s.startWorkerClean(c) 139 defer workertest.CleanKill(c, w) 140 141 s.stateTracker.CheckCallNames(c, "Use") 142 143 workertest.CleanKill(c, w) 144 s.stateTracker.CheckCallNames(c, "Use", "Done") 145 } 146 147 func (s *ManifoldSuite) startWorkerClean(c *gc.C) worker.Worker { 148 w, err := s.manifold.Start(s.context) 149 c.Assert(err, jc.ErrorIsNil) 150 workertest.CheckAlive(c, w) 151 return w 152 } 153 154 func (s *ManifoldSuite) TestNoStateServingInfoClosesState(c *gc.C) { 155 s.agent.conf.info = nil 156 157 _, err := s.manifold.Start(s.context) 158 c.Assert(err, gc.ErrorMatches, "state serving info missing from agent config") 159 160 s.stateTracker.CheckCallNames(c, "Use", "Done") 161 } 162 163 type stubStateTracker struct { 164 testing.Stub 165 pool *state.StatePool 166 } 167 168 func (s *stubStateTracker) Use() (*state.StatePool, error) { 169 s.MethodCall(s, "Use") 170 return s.pool, s.NextErr() 171 } 172 173 func (s *stubStateTracker) Done() error { 174 s.MethodCall(s, "Done") 175 return s.NextErr() 176 } 177 178 func (s *stubStateTracker) Report() map[string]interface{} { 179 s.MethodCall(s, "Report") 180 return nil 181 } 182 183 type mockAgent struct { 184 agent.Agent 185 conf mockAgentConfig 186 } 187 188 func (ma *mockAgent) CurrentConfig() agent.Config { 189 return &ma.conf 190 } 191 192 type mockAgentConfig struct { 193 agent.Config 194 info *controller.StateServingInfo 195 } 196 197 func (c *mockAgentConfig) Tag() names.Tag { 198 return names.NewMachineTag("10") 199 } 200 201 func (c *mockAgentConfig) StateServingInfo() (controller.StateServingInfo, bool) { 202 if c.info != nil { 203 return *c.info, true 204 } 205 return controller.StateServingInfo{}, false 206 } 207 208 type mockHub struct { 209 peergrouper.Hub 210 } 211 212 type fakeRegisterer struct { 213 prometheus.Registerer 214 }