github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/instancepoller/mock_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // TODO (manadart 2020-06-11): Replace these hand-rolled mocks over time with 5 // generated mocks. See package_test.go for those currently generated. 6 7 package instancepoller_test 8 9 import ( 10 "sort" 11 "sync" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/mgo/v3/txn" 16 "github.com/juju/names/v5" 17 "github.com/juju/testing" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/apiserver/common/networkingcommon" 21 "github.com/juju/juju/apiserver/facades/controller/instancepoller" 22 "github.com/juju/juju/core/instance" 23 "github.com/juju/juju/core/network" 24 "github.com/juju/juju/core/status" 25 "github.com/juju/juju/environs/config" 26 "github.com/juju/juju/state" 27 ) 28 29 // mockState implements StateInterface and allows inspection of called 30 // methods. 31 type mockState struct { 32 *testing.Stub 33 34 mu sync.Mutex 35 36 configWatchers []*mockConfigWatcher 37 machinesWatchers []*mockMachinesWatcher 38 39 config *config.Config 40 machines map[string]*mockMachine 41 42 spaceInfos network.SpaceInfos 43 } 44 45 func NewMockState() *mockState { 46 return &mockState{ 47 Stub: &testing.Stub{}, 48 machines: make(map[string]*mockMachine), 49 } 50 } 51 52 var _ instancepoller.StateInterface = (*mockState)(nil) 53 54 // CheckFindEntityCall is a helper wrapper around 55 // testing.Stub.CheckCall for FindEntity. 56 func (m *mockState) CheckFindEntityCall(c *gc.C, index int, machineId string) { 57 m.CheckCall(c, index, "FindEntity", interface{}(names.NewMachineTag(machineId))) 58 } 59 60 // CheckMachineCall is a helper wrapper around 61 // testing.Stub.CheckCall for Machine. 62 func (m *mockState) CheckMachineCall(c *gc.C, index int, machineId string) { 63 m.CheckCall(c, index, "Machine", machineId) 64 } 65 66 // CheckSetProviderAddressesCall is a helper wrapper around 67 // testing.Stub.CheckCall for SetProviderAddresses. 68 func (m *mockState) CheckSetProviderAddressesCall(c *gc.C, index int, addrs []network.SpaceAddress) { 69 args := make([]interface{}, len(addrs)) 70 for i, addr := range addrs { 71 args[i] = addr 72 } 73 m.CheckCall(c, index, "SetProviderAddresses", args...) 74 } 75 76 // WatchForModelConfigChanges implements StateInterface. 77 func (m *mockState) WatchForModelConfigChanges() state.NotifyWatcher { 78 m.mu.Lock() 79 defer m.mu.Unlock() 80 81 m.MethodCall(m, "WatchForModelConfigChanges") 82 83 w := NewMockConfigWatcher(m.NextErr()) 84 m.configWatchers = append(m.configWatchers, w) 85 return w 86 } 87 88 // ModelConfig implements StateInterface. 89 func (m *mockState) ModelConfig() (*config.Config, error) { 90 m.mu.Lock() 91 defer m.mu.Unlock() 92 93 m.MethodCall(m, "ModelConfig") 94 95 if err := m.NextErr(); err != nil { 96 return nil, err 97 } 98 return m.config, nil 99 } 100 101 // SetConfig updates the model config stored internally. Triggers a 102 // change event for all created config watchers. 103 func (m *mockState) SetConfig(c *gc.C, newConfig *config.Config) { 104 m.mu.Lock() 105 defer m.mu.Unlock() 106 107 m.config = newConfig 108 109 // Notify any watchers for the changes. 110 for _, w := range m.configWatchers { 111 w.incoming <- struct{}{} 112 } 113 } 114 115 // WatchModelMachines implements StateInterface. 116 func (m *mockState) WatchModelMachines() state.StringsWatcher { 117 m.mu.Lock() 118 defer m.mu.Unlock() 119 120 m.MethodCall(m, "WatchModelMachines") 121 122 ids := make([]string, 0, len(m.machines)) 123 // Initial event - all machine ids, sorted. 124 for id := range m.machines { 125 ids = append(ids, id) 126 } 127 sort.Strings(ids) 128 129 w := NewMockMachinesWatcher(ids, m.NextErr()) 130 m.machinesWatchers = append(m.machinesWatchers, w) 131 return w 132 } 133 134 // WatchModelMachineStartTimes implements StateInterface. 135 func (m *mockState) WatchModelMachineStartTimes(_ time.Duration) state.StringsWatcher { 136 m.mu.Lock() 137 defer m.mu.Unlock() 138 139 m.MethodCall(m, "WatchModelMachineStartTimes") 140 141 ids := make([]string, 0, len(m.machines)) 142 // Initial event - all machine ids, sorted. 143 for id := range m.machines { 144 ids = append(ids, id) 145 } 146 sort.Strings(ids) 147 148 w := NewMockMachinesWatcher(ids, m.NextErr()) 149 m.machinesWatchers = append(m.machinesWatchers, w) 150 return w 151 } 152 153 // FindEntity implements StateInterface. 154 func (m *mockState) FindEntity(tag names.Tag) (state.Entity, error) { 155 m.mu.Lock() 156 defer m.mu.Unlock() 157 158 m.MethodCall(m, "FindEntity", tag) 159 160 if err := m.NextErr(); err != nil { 161 return nil, err 162 } 163 if tag == nil { 164 return nil, errors.NotValidf("nil tag is") // +" not valid" 165 } 166 machine, found := m.machines[tag.Id()] 167 if !found { 168 return nil, errors.NotFoundf("machine %s", tag.Id()) 169 } 170 return machine, nil 171 } 172 173 // SetMachineInfo adds a new or updates existing mockMachine info. 174 // Triggers any created mock machines watchers to return a change. 175 func (m *mockState) SetMachineInfo(c *gc.C, args machineInfo) { 176 m.mu.Lock() 177 defer m.mu.Unlock() 178 179 c.Assert(args.id, gc.Not(gc.Equals), "") 180 181 machine, found := m.machines[args.id] 182 if !found { 183 machine = &mockMachine{ 184 Stub: m.Stub, // reuse parent stub. 185 machineInfo: args, 186 } 187 } else { 188 machine.machineInfo = args 189 } 190 m.machines[args.id] = machine 191 192 // Notify any watchers for the changes. 193 ids := []string{args.id} 194 for _, w := range m.machinesWatchers { 195 w.incoming <- ids 196 } 197 } 198 199 // RemoveMachine removes an existing mockMachine with the given id. 200 // Triggers the machines watchers on success. If the id is not found 201 // no error occurs and no change is reported by the watchers. 202 func (m *mockState) RemoveMachine(c *gc.C, id string) { 203 m.mu.Lock() 204 defer m.mu.Unlock() 205 206 if _, found := m.machines[id]; !found { 207 return 208 } 209 210 delete(m.machines, id) 211 212 // Notify any watchers for the changes. 213 ids := []string{id} 214 for _, w := range m.machinesWatchers { 215 w.incoming <- ids 216 } 217 } 218 219 // Machine implements StateInterface. 220 func (m *mockState) Machine(id string) (instancepoller.StateMachine, error) { 221 m.mu.Lock() 222 defer m.mu.Unlock() 223 224 m.MethodCall(m, "Machine", id) 225 226 if err := m.NextErr(); err != nil { 227 return nil, err 228 } 229 machine, found := m.machines[id] 230 if !found { 231 return nil, errors.NotFoundf("machine %s", id) 232 } 233 return machine, nil 234 } 235 236 // AllSpaceInfos implements network.AllSpaceInfos. 237 // This method never throws an error. 238 func (m *mockState) AllSpaceInfos() (network.SpaceInfos, error) { 239 m.MethodCall(m, "AllSpaceInfos") 240 return m.spaceInfos, nil 241 } 242 243 // SetSpaceInfo updates the mocked space infos returned by AllSpaceInfos() 244 func (m *mockState) SetSpaceInfo(infos network.SpaceInfos) { 245 m.mu.Lock() 246 defer m.mu.Unlock() 247 m.spaceInfos = infos 248 } 249 250 func (m *mockState) ApplyOperation(op state.ModelOperation) error { 251 m.MethodCall(m, "ApplyOperation", op) 252 253 ops, err := op.Build(0) 254 if err == nil { 255 // Register the generated transactions so that we can interrogate them. 256 m.MethodCall(m, "ApplyOperation.Build", ops) 257 } 258 return err 259 } 260 261 type machineInfo struct { 262 id string 263 instanceId instance.Id 264 status status.StatusInfo 265 instanceStatus status.StatusInfo 266 providerAddresses []network.SpaceAddress 267 life state.Life 268 isManual bool 269 270 linkLayerDevices []networkingcommon.LinkLayerDevice 271 addresses []networkingcommon.LinkLayerAddress 272 } 273 274 type mockMachine struct { 275 *testing.Stub 276 instancepoller.StateMachine 277 278 mu sync.Mutex 279 280 machineInfo 281 } 282 283 var _ instancepoller.StateMachine = (*mockMachine)(nil) 284 285 // Id implements StateMachine. 286 func (m *mockMachine) Id() string { 287 m.mu.Lock() 288 defer m.mu.Unlock() 289 290 m.MethodCall(m, "Id") 291 return m.id 292 } 293 294 // ModelUUID implements StateMachine. 295 func (m *mockMachine) ModelUUID() string { 296 m.mu.Lock() 297 defer m.mu.Unlock() 298 299 m.MethodCall(m, "ModelUUID") 300 return "any-model-uuid" 301 } 302 303 // InstanceId implements StateMachine. 304 func (m *mockMachine) InstanceId() (instance.Id, error) { 305 m.mu.Lock() 306 defer m.mu.Unlock() 307 308 m.MethodCall(m, "InstanceId") 309 if err := m.NextErr(); err != nil { 310 return "", err 311 } 312 return m.instanceId, nil 313 } 314 315 // ProviderAddresses implements StateMachine. 316 func (m *mockMachine) ProviderAddresses() network.SpaceAddresses { 317 m.mu.Lock() 318 defer m.mu.Unlock() 319 320 m.MethodCall(m, "ProviderAddresses") 321 m.NextErr() // consume the unused error 322 return m.providerAddresses 323 } 324 325 // SetProviderAddresses implements StateMachine. 326 func (m *mockMachine) SetProviderAddresses(addrs ...network.SpaceAddress) error { 327 m.mu.Lock() 328 defer m.mu.Unlock() 329 330 args := make([]interface{}, len(addrs)) 331 for i, addr := range addrs { 332 args[i] = addr 333 } 334 m.MethodCall(m, "SetProviderAddresses", args...) 335 if err := m.NextErr(); err != nil { 336 return err 337 } 338 m.providerAddresses = addrs 339 return nil 340 } 341 342 // AllLinkLayerDevices implements StateMachine. 343 func (m *mockMachine) AllLinkLayerDevices() ([]networkingcommon.LinkLayerDevice, error) { 344 m.mu.Lock() 345 defer m.mu.Unlock() 346 347 m.MethodCall(m, "AllLinkLayerDevices") 348 if err := m.NextErr(); err != nil { 349 return nil, err 350 } 351 return m.linkLayerDevices, nil 352 } 353 354 // AllDeviceAddresses implements StateMachine. 355 func (m *mockMachine) AllDeviceAddresses() ([]networkingcommon.LinkLayerAddress, error) { 356 m.mu.Lock() 357 defer m.mu.Unlock() 358 359 m.MethodCall(m, "AllDeviceAddresses") 360 if err := m.NextErr(); err != nil { 361 return nil, err 362 } 363 return m.addresses, nil 364 } 365 366 // InstanceStatus implements StateMachine. 367 func (m *mockMachine) InstanceStatus() (status.StatusInfo, error) { 368 m.mu.Lock() 369 defer m.mu.Unlock() 370 371 m.MethodCall(m, "InstanceStatus") 372 if err := m.NextErr(); err != nil { 373 return status.StatusInfo{}, err 374 } 375 return m.instanceStatus, nil 376 } 377 378 // SetInstanceStatus implements StateMachine. 379 func (m *mockMachine) SetInstanceStatus(instanceStatus status.StatusInfo) error { 380 m.mu.Lock() 381 defer m.mu.Unlock() 382 383 m.MethodCall(m, "SetInstanceStatus", instanceStatus) 384 if err := m.NextErr(); err != nil { 385 return err 386 } 387 m.instanceStatus = instanceStatus 388 return nil 389 } 390 391 // Life implements StateMachine. 392 func (m *mockMachine) Life() state.Life { 393 m.mu.Lock() 394 defer m.mu.Unlock() 395 396 m.MethodCall(m, "Life") 397 m.NextErr() // consume the unused error 398 return m.life 399 } 400 401 // IsManual implements StateMachine. 402 func (m *mockMachine) IsManual() (bool, error) { 403 m.mu.Lock() 404 defer m.mu.Unlock() 405 406 m.MethodCall(m, "IsManual") 407 return m.isManual, m.NextErr() 408 } 409 410 // Status implements StateMachine. 411 func (m *mockMachine) Status() (status.StatusInfo, error) { 412 m.mu.Lock() 413 defer m.mu.Unlock() 414 415 m.MethodCall(m, "Status") 416 return m.status, m.NextErr() 417 } 418 419 // AssertAliveOp implements StateMachine. 420 func (m *mockMachine) AssertAliveOp() txn.Op { 421 m.mu.Lock() 422 defer m.mu.Unlock() 423 424 m.MethodCall(m, "Status") 425 return txn.Op{C: "machine-alive"} 426 } 427 428 type mockBaseWatcher struct { 429 err error 430 431 closeChanges func() 432 done chan struct{} 433 } 434 435 var _ state.Watcher = (*mockBaseWatcher)(nil) 436 437 func NewMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher { 438 w := &mockBaseWatcher{ 439 err: err, 440 closeChanges: closeChanges, 441 done: make(chan struct{}), 442 } 443 if err != nil { 444 // Don't start the loop if we should fail. 445 w.Stop() 446 } 447 return w 448 } 449 450 // Kill implements state.Watcher. 451 func (m *mockBaseWatcher) Kill() {} 452 453 // Stop implements state.Watcher. 454 func (m *mockBaseWatcher) Stop() error { 455 select { 456 case <-m.done: 457 // already closed 458 default: 459 // Signal the loop we want to stop. 460 close(m.done) 461 // Signal the clients we've closed. 462 m.closeChanges() 463 } 464 return m.err 465 } 466 467 // Wait implements state.Watcher. 468 func (m *mockBaseWatcher) Wait() error { 469 return m.Stop() 470 } 471 472 // Err implements state.Watcher. 473 func (m *mockBaseWatcher) Err() error { 474 return m.err 475 } 476 477 type mockConfigWatcher struct { 478 *mockBaseWatcher 479 480 incoming chan struct{} 481 changes chan struct{} 482 } 483 484 var _ state.NotifyWatcher = (*mockConfigWatcher)(nil) 485 486 func NewMockConfigWatcher(err error) *mockConfigWatcher { 487 changes := make(chan struct{}) 488 w := &mockConfigWatcher{ 489 changes: changes, 490 incoming: make(chan struct{}), 491 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 492 } 493 if err == nil { 494 go w.loop() 495 } 496 return w 497 } 498 499 func (m *mockConfigWatcher) loop() { 500 // Prepare initial event. 501 outChanges := m.changes 502 // Forward any incoming changes until stopped. 503 for { 504 select { 505 case <-m.done: 506 // We're about to quit. 507 return 508 case outChanges <- struct{}{}: 509 outChanges = nil 510 case <-m.incoming: 511 outChanges = m.changes 512 } 513 } 514 } 515 516 // Changes implements state.NotifyWatcher. 517 func (m *mockConfigWatcher) Changes() <-chan struct{} { 518 return m.changes 519 } 520 521 type mockMachinesWatcher struct { 522 *mockBaseWatcher 523 524 initial []string 525 incoming chan []string 526 changes chan []string 527 } 528 529 func NewMockMachinesWatcher(initial []string, err error) *mockMachinesWatcher { 530 changes := make(chan []string) 531 w := &mockMachinesWatcher{ 532 initial: initial, 533 changes: changes, 534 incoming: make(chan []string), 535 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 536 } 537 if err == nil { 538 go w.loop() 539 } 540 return w 541 } 542 543 func (m *mockMachinesWatcher) loop() { 544 // Prepare initial event. 545 unsent := m.initial 546 outChanges := m.changes 547 // Forward any incoming changes until stopped. 548 for { 549 select { 550 case <-m.done: 551 // We're about to quit. 552 return 553 case outChanges <- unsent: 554 outChanges = nil 555 unsent = nil 556 case ids := <-m.incoming: 557 unsent = append(unsent, ids...) 558 outChanges = m.changes 559 } 560 } 561 } 562 563 // Changes implements state.StringsWatcher. 564 func (m *mockMachinesWatcher) Changes() <-chan []string { 565 return m.changes 566 } 567 568 var _ state.StringsWatcher = (*mockMachinesWatcher)(nil)