github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/presence/presence_test.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package presence_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/pubsub" 13 "github.com/juju/testing" 14 jc "github.com/juju/testing/checkers" 15 "github.com/kr/pretty" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/names.v2" 18 "gopkg.in/juju/worker.v1" 19 "gopkg.in/juju/worker.v1/workertest" 20 21 corepresence "github.com/juju/juju/core/presence" 22 "github.com/juju/juju/pubsub/apiserver" 23 "github.com/juju/juju/pubsub/centralhub" 24 "github.com/juju/juju/pubsub/forwarder" 25 coretesting "github.com/juju/juju/testing" 26 "github.com/juju/juju/worker/presence" 27 ) 28 29 type PresenceSuite struct { 30 testing.IsolationSuite 31 server string 32 hub *pubsub.StructuredHub 33 clock *testclock.Clock 34 recorder corepresence.Recorder 35 config presence.WorkerConfig 36 } 37 38 var _ = gc.Suite(&PresenceSuite{}) 39 40 func (s *PresenceSuite) SetUpTest(c *gc.C) { 41 s.IsolationSuite.SetUpTest(c) 42 s.hub = centralhub.New(ourTag) 43 s.clock = testclock.NewClock(time.Time{}) 44 s.recorder = corepresence.New(s.clock) 45 s.recorder.Enable() 46 s.config = presence.WorkerConfig{ 47 Origin: ourServer, 48 Hub: s.hub, 49 Recorder: s.recorder, 50 Logger: loggo.GetLogger("test"), 51 } 52 loggo.ConfigureLoggers("<root>=trace") 53 } 54 55 func (s *PresenceSuite) worker(c *gc.C) worker.Worker { 56 w, err := presence.NewWorker(s.config) 57 c.Assert(err, jc.ErrorIsNil) 58 return w 59 } 60 61 func (s *PresenceSuite) TestWorkerConfigMissingOrigin(c *gc.C) { 62 s.config.Origin = "" 63 err := s.config.Validate() 64 c.Check(err, jc.Satisfies, errors.IsNotValid) 65 c.Check(err, gc.ErrorMatches, "missing origin not valid") 66 } 67 68 func (s *PresenceSuite) TestWorkerConfigMissingHub(c *gc.C) { 69 s.config.Hub = nil 70 err := s.config.Validate() 71 c.Check(err, jc.Satisfies, errors.IsNotValid) 72 c.Check(err, gc.ErrorMatches, "missing hub not valid") 73 } 74 75 func (s *PresenceSuite) TestWorkerConfigMissingRecorder(c *gc.C) { 76 s.config.Recorder = nil 77 err := s.config.Validate() 78 c.Check(err, jc.Satisfies, errors.IsNotValid) 79 c.Check(err, gc.ErrorMatches, "missing recorder not valid") 80 } 81 82 func (s *PresenceSuite) TestWorkerConfigMissingLogger(c *gc.C) { 83 s.config.Logger = nil 84 err := s.config.Validate() 85 c.Check(err, jc.Satisfies, errors.IsNotValid) 86 c.Check(err, gc.ErrorMatches, "missing logger not valid") 87 } 88 89 func (s *PresenceSuite) TestNewWorkerValidatesConfig(c *gc.C) { 90 w, err := presence.NewWorker(presence.WorkerConfig{}) 91 c.Check(err, gc.ErrorMatches, "missing origin not valid") 92 c.Check(w, gc.IsNil) 93 } 94 95 func (s *PresenceSuite) TestWorkerDies(c *gc.C) { 96 w := s.worker(c) 97 workertest.CleanKill(c, w) 98 } 99 100 func (s *PresenceSuite) TestReport(c *gc.C) { 101 w := s.worker(c) 102 defer workertest.CleanKill(c, w) 103 104 s.recorder.Connect("machine-0", "model-uuid", "agent", 1, false, "") 105 s.recorder.Connect("machine-0", "model-uuid", "agent", 2, false, "") 106 s.recorder.Connect("machine-0", "model-uuid", "agent", 3, false, "") 107 s.recorder.Connect("machine-1", "model-uuid", "agent", 4, false, "") 108 s.recorder.Connect("machine-1", "model-uuid", "agent", 5, false, "") 109 s.recorder.Connect("machine-2", "model-uuid", "agent", 6, false, "") 110 111 reporter, ok := w.(worker.Reporter) 112 c.Assert(ok, jc.IsTrue) 113 c.Assert(reporter.Report(), jc.DeepEquals, map[string]interface{}{ 114 "machine-0": 3, 115 "machine-1": 2, 116 "machine-2": 1, 117 }) 118 } 119 120 func (s *PresenceSuite) TestForwarderConnectToOther(c *gc.C) { 121 w := s.worker(c) 122 defer workertest.CleanKill(c, w) 123 124 done := make(chan struct{}) 125 126 unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) { 127 c.Logf("handler called for %q", topic) 128 c.Check(err, jc.ErrorIsNil) 129 c.Check(data.Target, gc.Equals, otherServer) 130 c.Check(data.Origin, gc.Equals, ourServer) 131 close(done) 132 }) 133 c.Assert(err, jc.ErrorIsNil) 134 defer unsub() 135 136 // When connections are established from us to them, we ask for their presence info. 137 _, err = s.hub.Publish( 138 forwarder.ConnectedTopic, 139 apiserver.OriginTarget{Origin: ourServer, Target: otherServer}) 140 c.Assert(err, jc.ErrorIsNil) 141 s.AssertDone(c, done) 142 } 143 144 func (s *PresenceSuite) TestForwarderConnectFromOther(c *gc.C) { 145 w := s.worker(c) 146 defer workertest.CleanKill(c, w) 147 148 done := make(chan struct{}) 149 150 unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) { 151 c.Logf("handler called for %q", topic) 152 c.Check(err, jc.ErrorIsNil) 153 c.Check(data.Target, gc.Equals, otherServer) 154 c.Check(data.Origin, gc.Equals, ourServer) 155 close(done) 156 }) 157 c.Assert(err, jc.ErrorIsNil) 158 defer unsub() 159 160 // When connections are established from them to us, we ask for their presence info. 161 _, err = s.hub.Publish( 162 forwarder.ConnectedTopic, 163 apiserver.OriginTarget{Origin: otherServer, Target: ourServer}) 164 c.Assert(err, jc.ErrorIsNil) 165 s.AssertDone(c, done) 166 } 167 168 func (s *PresenceSuite) TestForwarderConnectOtherIgnored(c *gc.C) { 169 w := s.worker(c) 170 defer workertest.CleanKill(c, w) 171 172 called := make(chan struct{}) 173 174 unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) { 175 c.Logf("handler called for %q", topic) 176 close(called) 177 }) 178 c.Assert(err, jc.ErrorIsNil) 179 defer unsub() 180 181 _, err = s.hub.Publish( 182 forwarder.ConnectedTopic, 183 apiserver.OriginTarget{Origin: otherServer, Target: "machine-8"}) 184 c.Assert(err, jc.ErrorIsNil) 185 s.AssertNotCalled(c, called) 186 } 187 188 func (s *PresenceSuite) TestForwarderDisconnectConnectFromOther(c *gc.C) { 189 w := s.worker(c) 190 defer workertest.CleanKill(c, w) 191 192 connect(s.recorder, agent1, agent2) 193 194 done, err := s.hub.Publish( 195 forwarder.DisconnectedTopic, 196 apiserver.OriginTarget{Origin: ourServer, Target: otherServer}) 197 c.Assert(err, jc.ErrorIsNil) 198 s.AssertDone(c, done) 199 s.AssertConnections(c, alive(agent1), missing(agent2)) 200 } 201 202 func (s *PresenceSuite) TestForwarderDisconnectOthersIgnored(c *gc.C) { 203 w := s.worker(c) 204 defer workertest.CleanKill(c, w) 205 206 connect(s.recorder, agent1, agent2) 207 208 done, err := s.hub.Publish( 209 forwarder.DisconnectedTopic, 210 apiserver.OriginTarget{Origin: "machine-7", Target: otherServer}) 211 c.Assert(err, jc.ErrorIsNil) 212 s.AssertDone(c, done) 213 s.AssertConnections(c, alive(agent1), alive(agent2)) 214 } 215 216 func (s *PresenceSuite) TestConnectTopic(c *gc.C) { 217 w := s.worker(c) 218 defer workertest.CleanKill(c, w) 219 220 done, err := s.hub.Publish( 221 apiserver.ConnectTopic, 222 apiserver.APIConnection{ 223 Origin: "machine-5", 224 ModelUUID: "model-uuid", 225 AgentTag: "agent-2", 226 ConnectionID: 42, 227 ControllerAgent: true, 228 UserData: "test", 229 }) 230 c.Assert(err, jc.ErrorIsNil) 231 s.AssertDone(c, done) 232 s.AssertConnections(c, corepresence.Value{ 233 Model: "model-uuid", 234 Server: "machine-5", 235 Agent: "agent-2", 236 ConnectionID: 42, 237 Status: corepresence.Alive, 238 ControllerAgent: true, 239 UserData: "test", 240 }) 241 } 242 243 func (s *PresenceSuite) TestDisconnectTopic(c *gc.C) { 244 w := s.worker(c) 245 defer workertest.CleanKill(c, w) 246 247 connect(s.recorder, agent1, agent2) 248 249 done, err := s.hub.Publish( 250 apiserver.DisconnectTopic, 251 apiserver.APIConnection{ 252 Origin: agent2.Server, 253 ConnectionID: agent2.ConnectionID, 254 }) 255 c.Assert(err, jc.ErrorIsNil) 256 s.AssertDone(c, done) 257 s.AssertConnections(c, alive(agent1)) 258 } 259 260 func (s *PresenceSuite) TestPresenceRequest(c *gc.C) { 261 w := s.worker(c) 262 defer workertest.CleanKill(c, w) 263 264 connect(s.recorder, agent1, agent2, agent3, agent4) 265 266 done := make(chan struct{}) 267 unsub, err := s.hub.Subscribe(apiserver.PresenceResponseTopic, func(topic string, data apiserver.PresenceResponse, err error) { 268 c.Logf("handler called for %q", topic) 269 c.Check(err, jc.ErrorIsNil) 270 c.Check(data.Origin, gc.Equals, ourServer) 271 272 c.Check(data.Connections, gc.HasLen, 2) 273 s.CheckConnection(c, data.Connections[0], agent1) 274 s.CheckConnection(c, data.Connections[1], agent3) 275 276 close(done) 277 }) 278 c.Assert(err, jc.ErrorIsNil) 279 defer unsub() 280 281 // When asked for our presence, we respond with the agents connected to us. 282 _, err = s.hub.Publish( 283 apiserver.PresenceRequestTopic, 284 apiserver.OriginTarget{Origin: otherServer, Target: ourServer}) 285 c.Assert(err, jc.ErrorIsNil) 286 s.AssertDone(c, done) 287 } 288 289 func (s *PresenceSuite) TestPresenceRequestOtherServer(c *gc.C) { 290 w := s.worker(c) 291 defer workertest.CleanKill(c, w) 292 293 called := make(chan struct{}) 294 unsub, err := s.hub.Subscribe(apiserver.PresenceResponseTopic, func(topic string, data apiserver.PresenceResponse, err error) { 295 c.Logf("handler called for %q", topic) 296 close(called) 297 }) 298 c.Assert(err, jc.ErrorIsNil) 299 defer unsub() 300 301 // When presence requests come in for other servers, we ignore them. 302 _, err = s.hub.Publish( 303 apiserver.PresenceRequestTopic, 304 apiserver.OriginTarget{Origin: otherServer, Target: "another"}) 305 c.Assert(err, jc.ErrorIsNil) 306 s.AssertNotCalled(c, called) 307 } 308 309 func (s *PresenceSuite) TestPresenceResponse(c *gc.C) { 310 w := s.worker(c) 311 defer workertest.CleanKill(c, w) 312 313 connect(s.recorder, agent1, agent2, agent3, agent4) 314 s.recorder.ServerDown(otherServer) 315 316 // When connections information comes from other servers, we update our recorder. 317 done, err := s.hub.Publish( 318 apiserver.PresenceResponseTopic, 319 apiserver.PresenceResponse{ 320 Origin: otherServer, 321 Connections: []apiserver.APIConnection{ 322 apiConn(agent2), apiConn(agent4), 323 }, 324 }) 325 c.Assert(err, jc.ErrorIsNil) 326 s.AssertDone(c, done) 327 328 s.AssertConnections(c, alive(agent1), alive(agent2), alive(agent3), alive(agent4)) 329 } 330 331 func (s *PresenceSuite) AssertDone(c *gc.C, called <-chan struct{}) { 332 select { 333 case <-called: 334 case <-time.After(coretesting.LongWait): 335 c.Fatal("event not handled") 336 } 337 } 338 339 func (s *PresenceSuite) AssertNotCalled(c *gc.C, called <-chan struct{}) { 340 select { 341 case <-called: 342 c.Fatal("event called unexpectedly") 343 case <-time.After(coretesting.ShortWait): 344 } 345 } 346 347 func (s *PresenceSuite) AssertConnections(c *gc.C, values ...corepresence.Value) { 348 connections := s.recorder.Connections() 349 c.Log(pretty.Sprint(connections)) 350 c.Assert(connections.Values(), jc.SameContents, values) 351 } 352 353 func (s *PresenceSuite) CheckConnection(c *gc.C, conn apiserver.APIConnection, agent corepresence.Value) { 354 c.Check(conn.AgentTag, gc.Equals, agent.Agent) 355 c.Check(conn.ControllerAgent, gc.Equals, agent.ControllerAgent) 356 c.Check(conn.ModelUUID, gc.Equals, agent.Model) 357 c.Check(conn.ConnectionID, gc.Equals, agent.ConnectionID) 358 c.Check(conn.Origin, gc.Equals, agent.Server) 359 c.Check(conn.UserData, gc.Equals, agent.UserData) 360 } 361 362 func apiConn(value corepresence.Value) apiserver.APIConnection { 363 return apiserver.APIConnection{ 364 AgentTag: value.Agent, 365 ControllerAgent: value.ControllerAgent, 366 ModelUUID: value.Model, 367 ConnectionID: value.ConnectionID, 368 Origin: value.Server, 369 UserData: value.UserData, 370 } 371 } 372 373 func alive(v corepresence.Value) corepresence.Value { 374 v.Status = corepresence.Alive 375 return v 376 } 377 378 func missing(v corepresence.Value) corepresence.Value { 379 v.Status = corepresence.Missing 380 return v 381 } 382 383 func connect(r corepresence.Recorder, values ...corepresence.Value) { 384 for _, info := range values { 385 r.Connect(info.Server, info.Model, info.Agent, info.ConnectionID, info.ControllerAgent, info.UserData) 386 } 387 } 388 389 const modelUUID = "model-uuid" 390 391 var ( 392 ourTag = names.NewMachineTag("1") 393 ourServer = ourTag.String() 394 otherServer = "machine-2" 395 agent1 = corepresence.Value{ 396 Model: modelUUID, 397 Server: ourServer, 398 Agent: "machine-0", 399 ConnectionID: 1237, 400 UserData: "foo", 401 } 402 agent2 = corepresence.Value{ 403 Model: modelUUID, 404 Server: otherServer, 405 Agent: "machine-1", 406 ConnectionID: 1238, 407 UserData: "bar", 408 } 409 agent3 = corepresence.Value{ 410 Model: modelUUID, 411 Server: ourServer, 412 Agent: "unit-ubuntu-0", 413 ConnectionID: 1239, 414 UserData: "baz", 415 } 416 agent4 = corepresence.Value{ 417 Model: modelUUID, 418 Server: otherServer, 419 Agent: "unit-ubuntu-1", 420 ConnectionID: 1240, 421 UserData: "splat", 422 } 423 )