github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/core/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 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/core/presence" 14 ) 15 16 type suite struct{} 17 18 var _ = gc.Suite(&suite{}) 19 20 func (*suite) assertEmptyConnections(c *gc.C, connections presence.Connections) { 21 c.Assert(connections.Count(), gc.Equals, 0) 22 c.Assert(connections.Models(), gc.HasLen, 0) 23 c.Assert(connections.Servers(), gc.HasLen, 0) 24 c.Assert(connections.Agents(), gc.HasLen, 0) 25 c.Assert(connections.Values(), gc.HasLen, 0) 26 } 27 28 func (*suite) assertConnections(c *gc.C, connections presence.Connections, expected []presence.Value) { 29 c.Assert(connections.Values(), jc.SameContents, expected) 30 } 31 32 func (s *suite) TestEmptyRecorder(c *gc.C) { 33 r := presence.New(testclock.NewClock(time.Time{})) 34 c.Assert(r.IsEnabled(), jc.IsFalse) 35 r.Enable() 36 s.assertEmptyConnections(c, r.Connections()) 37 } 38 39 func (s *suite) TestBootstrapCase(c *gc.C) { 40 r, _ := bootstrap() 41 42 c.Assert(r.IsEnabled(), jc.IsTrue) 43 44 connections := r.Connections() 45 expected := []presence.Value{alive(ha0)} 46 47 s.assertConnections(c, connections, expected) 48 s.assertConnections(c, connections.ForModel(bootstrapUUID), expected) 49 s.assertEmptyConnections(c, connections.ForModel(modelUUID)) 50 } 51 52 func (s *suite) TestHAController(c *gc.C) { 53 r, _ := bootstrap() 54 enableHA(r) 55 56 connections := r.Connections() 57 expected := []presence.Value{alive(ha0), alive(ha1), alive(ha2)} 58 59 c.Assert(connections.Values(), jc.DeepEquals, expected) 60 s.assertConnections(c, connections.ForModel(bootstrapUUID), expected) 61 s.assertEmptyConnections(c, connections.ForModel(modelUUID)) 62 63 s.assertConnections(c, connections.ForServer(ha0.Server), values(alive(ha0))) 64 s.assertConnections(c, connections.ForServer(ha1.Server), values(alive(ha1))) 65 s.assertConnections(c, connections.ForServer(ha2.Server), values(alive(ha2))) 66 } 67 68 func (s *suite) TestModels(c *gc.C) { 69 r, _ := bootstrap() 70 enableHA(r) 71 deployModel(r) 72 73 connections := r.Connections() 74 s.assertConnections(c, connections.ForModel(bootstrapUUID), 75 values(alive(ha0), alive(ha1), alive(ha2))) 76 s.assertConnections(c, connections.ForModel(modelUUID), 77 values(alive(modelMachine0), alive(modelMachine1), 78 alive(modelUnit1), alive(modelUnit2))) 79 80 s.assertConnections(c, connections.ForServer(ha0.Server), 81 values(alive(ha0), alive(modelUnit1), alive(modelUnit2))) 82 s.assertConnections(c, connections.ForServer(ha1.Server), 83 values(alive(ha1), alive(modelMachine0))) 84 s.assertConnections(c, connections.ForServer(ha2.Server), 85 values(alive(ha2), alive(modelMachine1))) 86 } 87 88 func (s *suite) TestTimeRecording(c *gc.C) { 89 now := time.Now() 90 r, clock := bootstrap(now) 91 92 m0 := lastSeen(alive(ha0), now) 93 clock.Advance(time.Minute) 94 now = clock.Now() 95 enableHA(r) 96 97 m1 := lastSeen(alive(ha1), now) 98 m2 := lastSeen(alive(ha2), now) 99 100 connections := r.Connections() 101 s.assertConnections(c, connections.ForModel(bootstrapUUID), 102 values(m0, m1, m2)) 103 } 104 105 func (s *suite) TestActivity(c *gc.C) { 106 r, clock := bootstrap() 107 enableHA(r) 108 deployModel(r) 109 110 clock.Advance(5 * time.Minute) 111 112 // Register activity for model machine 0. 113 r.Activity("machine-1", 1237) 114 115 // These connections are all the same except for modelMachine0 116 // which shows an updated time. 117 mm0 := lastSeen(alive(modelMachine0), clock.Now()) 118 119 connections := r.Connections() 120 s.assertConnections(c, connections.ForModel(bootstrapUUID), 121 values(alive(ha0), alive(ha1), alive(ha2))) 122 s.assertConnections(c, connections.ForModel(modelUUID), 123 values(mm0, alive(modelMachine1), 124 alive(modelUnit1), alive(modelUnit2))) 125 126 s.assertConnections(c, connections.ForServer(ha0.Server), 127 values(alive(ha0), alive(modelUnit1), alive(modelUnit2))) 128 s.assertConnections(c, connections.ForServer(ha1.Server), 129 values(alive(ha1), mm0)) 130 s.assertConnections(c, connections.ForServer(ha2.Server), 131 values(alive(ha2), alive(modelMachine1))) 132 } 133 134 func (s *suite) TestDisconnect(c *gc.C) { 135 r, _ := bootstrap() 136 enableHA(r) 137 deployModel(r) 138 139 r.Disconnect(ha0.Server, ha0.ConnectionID) 140 r.Disconnect(modelUnit1.Server, modelUnit1.ConnectionID) 141 142 connections := r.Connections() 143 144 c.Assert(connections.Count(), gc.Equals, 5) 145 s.assertConnections(c, connections.ForModel(bootstrapUUID), 146 values(alive(ha1), alive(ha2))) 147 s.assertConnections(c, connections.ForModel(modelUUID), 148 values(alive(modelMachine0), alive(modelMachine1), alive(modelUnit2))) 149 } 150 151 func (s *suite) TestDisableClears(c *gc.C) { 152 r, _ := bootstrap() 153 r.Disable() 154 155 s.assertEmptyConnections(c, r.Connections()) 156 } 157 158 func (s *suite) TestServerDown(c *gc.C) { 159 r, _ := bootstrap() 160 enableHA(r) 161 deployModel(r) 162 163 r.ServerDown("machine-0") 164 165 connections := r.Connections() 166 s.assertConnections(c, connections.ForModel(bootstrapUUID), 167 values(missing(ha0), alive(ha1), alive(ha2))) 168 s.assertConnections(c, connections.ForModel(modelUUID), 169 values(alive(modelMachine0), alive(modelMachine1), 170 missing(modelUnit1), missing(modelUnit2))) 171 172 s.assertConnections(c, connections.ForServer(ha0.Server), 173 values(missing(ha0), missing(modelUnit1), missing(modelUnit2))) 174 s.assertConnections(c, connections.ForServer(ha1.Server), 175 values(alive(ha1), alive(modelMachine0))) 176 s.assertConnections(c, connections.ForServer(ha2.Server), 177 values(alive(ha2), alive(modelMachine1))) 178 } 179 180 func (s *suite) TestServerDownFollowedByConnections(c *gc.C) { 181 r, _ := bootstrap() 182 enableHA(r) 183 deployModel(r) 184 185 r.ServerDown("machine-0") 186 connect(r, ha0) 187 connect(r, modelUnit1) 188 connect(r, modelUnit2) 189 190 connections := r.Connections() 191 s.assertConnections(c, connections.ForModel(bootstrapUUID), 192 values(alive(ha0), alive(ha1), alive(ha2))) 193 s.assertConnections(c, connections.ForModel(modelUUID), 194 values(alive(modelMachine0), alive(modelMachine1), 195 alive(modelUnit1), alive(modelUnit2))) 196 197 s.assertConnections(c, connections.ForServer(ha0.Server), 198 values(alive(ha0), alive(modelUnit1), alive(modelUnit2))) 199 s.assertConnections(c, connections.ForServer(ha1.Server), 200 values(alive(ha1), alive(modelMachine0))) 201 s.assertConnections(c, connections.ForServer(ha2.Server), 202 values(alive(ha2), alive(modelMachine1))) 203 } 204 205 func (s *suite) TestServerDownRaceConnections(c *gc.C) { 206 r, _ := bootstrap() 207 enableHA(r) 208 deployModel(r) 209 210 // Deal with the situation where machine-0 was marked as 211 // down, but before an update for the server comes it, some 212 // connections are updated, but the update from the server 213 // hadn't processed all the connections yet. 214 r.ServerDown("machine-0") 215 connect(r, ha0) 216 connect(r, modelUnit1) 217 err := r.UpdateServer("machine-0", values(ha0)) 218 c.Assert(err, jc.ErrorIsNil) 219 220 connections := r.Connections() 221 s.assertConnections(c, connections.ForServer(ha0.Server), 222 values(alive(ha0), alive(modelUnit1))) 223 } 224 225 func (s *suite) TestUpdateServer(c *gc.C) { 226 r, _ := bootstrap() 227 enableHA(r) 228 deployModel(r) 229 230 // Replace machine-0 values with just the ha node. 231 // The values need to include the status of the connection. 232 r.ServerDown("machine-0") 233 err := r.UpdateServer("machine-0", values(ha0)) 234 c.Assert(err, jc.ErrorIsNil) 235 236 connections := r.Connections() 237 s.assertConnections(c, connections.ForModel(bootstrapUUID), 238 values(alive(ha0), alive(ha1), alive(ha2))) 239 s.assertConnections(c, connections.ForModel(modelUUID), 240 values(alive(modelMachine0), alive(modelMachine1))) 241 242 s.assertConnections(c, connections.ForServer(ha0.Server), 243 values(alive(ha0))) 244 s.assertConnections(c, connections.ForServer(ha1.Server), 245 values(alive(ha1), alive(modelMachine0))) 246 s.assertConnections(c, connections.ForServer(ha2.Server), 247 values(alive(ha2), alive(modelMachine1))) 248 } 249 250 func (s *suite) TestUpdateServerError(c *gc.C) { 251 r, _ := bootstrap() 252 enableHA(r) 253 deployModel(r) 254 255 err := r.UpdateServer("machine-0", values(alive(ha1))) 256 c.Assert(err, gc.ErrorMatches, `connection server mismatch, got "machine-1" expected "machine-0"`) 257 } 258 259 func (s *suite) TestConnections(c *gc.C) { 260 r, _ := bootstrap() 261 enableHA(r) 262 deployModel(r) 263 264 r.ServerDown("machine-0") 265 266 connections := r.Connections() 267 268 c.Assert(connections.Count(), gc.Equals, 7) 269 status, err := connections.AgentStatus("machine-0") 270 c.Assert(status, gc.Equals, presence.Unknown) 271 c.Assert(err, gc.ErrorMatches, "connections not limited to a model, agent ambiguous") 272 273 controllerConnections := connections.ForModel(bootstrapUUID) 274 c.Assert(controllerConnections.Count(), gc.Equals, 3) 275 276 status, err = controllerConnections.AgentStatus("machine-0") 277 c.Assert(status, gc.Equals, presence.Missing) 278 c.Assert(err, jc.ErrorIsNil) 279 status, err = controllerConnections.AgentStatus("machine-1") 280 c.Assert(status, gc.Equals, presence.Alive) 281 c.Assert(err, jc.ErrorIsNil) 282 status, err = controllerConnections.AgentStatus("machine-4") 283 c.Assert(status, gc.Equals, presence.Unknown) 284 c.Assert(err, jc.ErrorIsNil) 285 } 286 287 func bootstrap(initialTime ...time.Time) (presence.Recorder, *testclock.Clock) { 288 if len(initialTime) > 1 { 289 panic("initialTime should be zero or one values") 290 } 291 var t time.Time 292 if len(initialTime) > 0 { 293 t = initialTime[0] 294 } 295 // By using a testing clock with a zero time, the times are always empty. 296 clock := testclock.NewClock(t) 297 r := presence.New(clock) 298 r.Enable() 299 connect(r, ha0) 300 return r, clock 301 } 302 303 func enableHA(r presence.Recorder) { 304 connect(r, ha1) 305 connect(r, ha2) 306 } 307 308 func deployModel(r presence.Recorder) { 309 connect(r, modelMachine0) 310 connect(r, modelMachine1) 311 connect(r, modelUnit1) 312 connect(r, modelUnit2) 313 } 314 315 func values(v ...presence.Value) []presence.Value { 316 return v 317 } 318 319 func alive(v presence.Value) presence.Value { 320 v.Status = presence.Alive 321 return v 322 } 323 324 func missing(v presence.Value) presence.Value { 325 v.Status = presence.Missing 326 return v 327 } 328 329 func lastSeen(v presence.Value, when time.Time) presence.Value { 330 v.LastSeen = when 331 return v 332 } 333 334 func connect(r presence.Recorder, info presence.Value) { 335 r.Connect(info.Server, info.Model, info.Agent, info.ConnectionID, info.ControllerAgent, info.UserData) 336 } 337 338 const bootstrapUUID = "bootstrap-uuid" 339 const modelUUID = "model-uuid" 340 341 var ha0 = presence.Value{ 342 Model: bootstrapUUID, 343 Server: "machine-0", 344 Agent: "machine-0", 345 ConnectionID: 1234, 346 } 347 var ha1 = presence.Value{ 348 Model: bootstrapUUID, 349 Server: "machine-1", 350 Agent: "machine-1", 351 ConnectionID: 1235, 352 } 353 var ha2 = presence.Value{ 354 Model: bootstrapUUID, 355 Server: "machine-2", 356 Agent: "machine-2", 357 ConnectionID: 1236, 358 } 359 360 var modelMachine0 = presence.Value{ 361 Model: modelUUID, 362 Server: "machine-1", 363 Agent: "machine-0", 364 ConnectionID: 1237, 365 } 366 var modelMachine1 = presence.Value{ 367 Model: modelUUID, 368 Server: "machine-2", 369 Agent: "machine-1", 370 ConnectionID: 1238, 371 } 372 var modelUnit1 = presence.Value{ 373 Model: modelUUID, 374 Server: "machine-0", 375 Agent: "unit-wordpress-0", 376 ConnectionID: 1239, 377 } 378 var modelUnit2 = presence.Value{ 379 Model: modelUUID, 380 Server: "machine-0", 381 Agent: "unit-mysql-0", 382 ConnectionID: 12409, 383 }