github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 package instancepoller_test 5 6 import ( 7 "sort" 8 "sync" 9 10 "github.com/juju/errors" 11 "github.com/juju/testing" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/facades/controller/instancepoller" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/core/status" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/network" 20 "github.com/juju/juju/state" 21 ) 22 23 // mockState implements StateInterface and allows inspection of called 24 // methods. 25 type mockState struct { 26 *testing.Stub 27 28 mu sync.Mutex 29 30 configWatchers []*mockConfigWatcher 31 machinesWatchers []*mockMachinesWatcher 32 33 config *config.Config 34 machines map[string]*mockMachine 35 } 36 37 func NewMockState() *mockState { 38 return &mockState{ 39 Stub: &testing.Stub{}, 40 machines: make(map[string]*mockMachine), 41 } 42 } 43 44 var _ instancepoller.StateInterface = (*mockState)(nil) 45 46 // CheckFindEntityCall is a helper wrapper around 47 // testing.Stub.CheckCall for FindEntity. 48 func (m *mockState) CheckFindEntityCall(c *gc.C, index int, machineId string) { 49 m.CheckCall(c, index, "FindEntity", interface{}(names.NewMachineTag(machineId))) 50 } 51 52 // CheckSetProviderAddressesCall is a helper wrapper around 53 // testing.Stub.CheckCall for SetProviderAddresses. 54 func (m *mockState) CheckSetProviderAddressesCall(c *gc.C, index int, addrs []network.Address) { 55 args := make([]interface{}, len(addrs)) 56 for i, addr := range addrs { 57 args[i] = addr 58 } 59 m.CheckCall(c, index, "SetProviderAddresses", args...) 60 } 61 62 // WatchForModelConfigChanges implements StateInterface. 63 func (m *mockState) WatchForModelConfigChanges() state.NotifyWatcher { 64 m.mu.Lock() 65 defer m.mu.Unlock() 66 67 m.MethodCall(m, "WatchForModelConfigChanges") 68 69 w := NewMockConfigWatcher(m.NextErr()) 70 m.configWatchers = append(m.configWatchers, w) 71 return w 72 } 73 74 // ModelConfig implements StateInterface. 75 func (m *mockState) ModelConfig() (*config.Config, error) { 76 m.mu.Lock() 77 defer m.mu.Unlock() 78 79 m.MethodCall(m, "ModelConfig") 80 81 if err := m.NextErr(); err != nil { 82 return nil, err 83 } 84 return m.config, nil 85 } 86 87 // SetConfig updates the model config stored internally. Triggers a 88 // change event for all created config watchers. 89 func (m *mockState) SetConfig(c *gc.C, newConfig *config.Config) { 90 m.mu.Lock() 91 defer m.mu.Unlock() 92 93 m.config = newConfig 94 95 // Notify any watchers for the changes. 96 for _, w := range m.configWatchers { 97 w.incoming <- struct{}{} 98 } 99 } 100 101 // WatchModelMachines implements StateInterface. 102 func (m *mockState) WatchModelMachines() state.StringsWatcher { 103 m.mu.Lock() 104 defer m.mu.Unlock() 105 106 m.MethodCall(m, "WatchModelMachines") 107 108 ids := make([]string, 0, len(m.machines)) 109 // Initial event - all machine ids, sorted. 110 for id := range m.machines { 111 ids = append(ids, id) 112 } 113 sort.Strings(ids) 114 115 w := NewMockMachinesWatcher(ids, m.NextErr()) 116 m.machinesWatchers = append(m.machinesWatchers, w) 117 return w 118 } 119 120 // FindEntity implements StateInterface. 121 func (m *mockState) FindEntity(tag names.Tag) (state.Entity, error) { 122 m.mu.Lock() 123 defer m.mu.Unlock() 124 125 m.MethodCall(m, "FindEntity", tag) 126 127 if err := m.NextErr(); err != nil { 128 return nil, err 129 } 130 if tag == nil { 131 return nil, errors.NotValidf("nil tag is") // +" not valid" 132 } 133 machine, found := m.machines[tag.Id()] 134 if !found { 135 return nil, errors.NotFoundf("machine %s", tag.Id()) 136 } 137 return machine, nil 138 } 139 140 // SetMachineInfo adds a new or updates existing mockMachine info. 141 // Triggers any created mock machines watchers to return a change. 142 func (m *mockState) SetMachineInfo(c *gc.C, args machineInfo) { 143 m.mu.Lock() 144 defer m.mu.Unlock() 145 146 c.Assert(args.id, gc.Not(gc.Equals), "") 147 148 machine, found := m.machines[args.id] 149 if !found { 150 machine = &mockMachine{ 151 Stub: m.Stub, // reuse parent stub. 152 machineInfo: args, 153 } 154 } else { 155 machine.machineInfo = args 156 } 157 m.machines[args.id] = machine 158 159 // Notify any watchers for the changes. 160 ids := []string{args.id} 161 for _, w := range m.machinesWatchers { 162 w.incoming <- ids 163 } 164 } 165 166 // RemoveMachine removes an existing mockMachine with the given id. 167 // Triggers the machines watchers on success. If the id is not found 168 // no error occurs and no change is reported by the watchers. 169 func (m *mockState) RemoveMachine(c *gc.C, id string) { 170 m.mu.Lock() 171 defer m.mu.Unlock() 172 173 if _, found := m.machines[id]; !found { 174 return 175 } 176 177 delete(m.machines, id) 178 179 // Notify any watchers for the changes. 180 ids := []string{id} 181 for _, w := range m.machinesWatchers { 182 w.incoming <- ids 183 } 184 } 185 186 // Machine implements StateInterface. 187 func (m *mockState) Machine(id string) (instancepoller.StateMachine, error) { 188 m.mu.Lock() 189 defer m.mu.Unlock() 190 191 m.MethodCall(m, "Machine", id) 192 193 if err := m.NextErr(); err != nil { 194 return nil, err 195 } 196 machine, found := m.machines[id] 197 if !found { 198 return nil, errors.NotFoundf("machine %s", id) 199 } 200 return machine, nil 201 } 202 203 // StartSync implements statetesting.SyncStarter, so mockState can be 204 // used with watcher helpers/checkers. 205 func (m *mockState) StartSync() {} 206 207 type machineInfo struct { 208 id string 209 instanceId instance.Id 210 status status.StatusInfo 211 instanceStatus status.StatusInfo 212 providerAddresses []network.Address 213 life state.Life 214 isManual bool 215 } 216 217 type mockMachine struct { 218 *testing.Stub 219 instancepoller.StateMachine 220 221 mu sync.Mutex 222 223 machineInfo 224 } 225 226 var _ instancepoller.StateMachine = (*mockMachine)(nil) 227 228 // InstanceId implements StateMachine. 229 func (m *mockMachine) InstanceId() (instance.Id, error) { 230 m.mu.Lock() 231 defer m.mu.Unlock() 232 233 m.MethodCall(m, "InstanceId") 234 if err := m.NextErr(); err != nil { 235 return "", err 236 } 237 return m.instanceId, nil 238 } 239 240 // ProviderAddresses implements StateMachine. 241 func (m *mockMachine) ProviderAddresses() []network.Address { 242 m.mu.Lock() 243 defer m.mu.Unlock() 244 245 m.MethodCall(m, "ProviderAddresses") 246 m.NextErr() // consume the unused error 247 return m.providerAddresses 248 } 249 250 // SetProviderAddresses implements StateMachine. 251 func (m *mockMachine) SetProviderAddresses(addrs ...network.Address) error { 252 m.mu.Lock() 253 defer m.mu.Unlock() 254 255 args := make([]interface{}, len(addrs)) 256 for i, addr := range addrs { 257 args[i] = addr 258 } 259 m.MethodCall(m, "SetProviderAddresses", args...) 260 if err := m.NextErr(); err != nil { 261 return err 262 } 263 m.providerAddresses = addrs 264 return nil 265 } 266 267 // InstanceStatus implements StateMachine. 268 func (m *mockMachine) InstanceStatus() (status.StatusInfo, error) { 269 m.mu.Lock() 270 defer m.mu.Unlock() 271 272 m.MethodCall(m, "InstanceStatus") 273 if err := m.NextErr(); err != nil { 274 return status.StatusInfo{}, err 275 } 276 return m.instanceStatus, nil 277 } 278 279 // SetInstanceStatus implements StateMachine. 280 func (m *mockMachine) SetInstanceStatus(instanceStatus status.StatusInfo) error { 281 m.mu.Lock() 282 defer m.mu.Unlock() 283 284 m.MethodCall(m, "SetInstanceStatus", instanceStatus) 285 if err := m.NextErr(); err != nil { 286 return err 287 } 288 m.instanceStatus = instanceStatus 289 return nil 290 } 291 292 // Life implements StateMachine. 293 func (m *mockMachine) Life() state.Life { 294 m.mu.Lock() 295 defer m.mu.Unlock() 296 297 m.MethodCall(m, "Life") 298 m.NextErr() // consume the unused error 299 return m.life 300 } 301 302 // IsManual implements StateMachine. 303 func (m *mockMachine) IsManual() (bool, error) { 304 m.mu.Lock() 305 defer m.mu.Unlock() 306 307 m.MethodCall(m, "IsManual") 308 return m.isManual, m.NextErr() 309 } 310 311 // Status implements StateMachine. 312 func (m *mockMachine) Status() (status.StatusInfo, error) { 313 m.mu.Lock() 314 defer m.mu.Unlock() 315 316 m.MethodCall(m, "Status") 317 return m.status, m.NextErr() 318 } 319 320 type mockBaseWatcher struct { 321 err error 322 323 closeChanges func() 324 done chan struct{} 325 } 326 327 var _ state.Watcher = (*mockBaseWatcher)(nil) 328 329 func NewMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher { 330 w := &mockBaseWatcher{ 331 err: err, 332 closeChanges: closeChanges, 333 done: make(chan struct{}), 334 } 335 if err != nil { 336 // Don't start the loop if we should fail. 337 w.Stop() 338 } 339 return w 340 } 341 342 // Kill implements state.Watcher. 343 func (m *mockBaseWatcher) Kill() {} 344 345 // Stop implements state.Watcher. 346 func (m *mockBaseWatcher) Stop() error { 347 select { 348 case <-m.done: 349 // already closed 350 default: 351 // Signal the loop we want to stop. 352 close(m.done) 353 // Signal the clients we've closed. 354 m.closeChanges() 355 } 356 return m.err 357 } 358 359 // Wait implements state.Watcher. 360 func (m *mockBaseWatcher) Wait() error { 361 return m.Stop() 362 } 363 364 // Err implements state.Watcher. 365 func (m *mockBaseWatcher) Err() error { 366 return m.err 367 } 368 369 type mockConfigWatcher struct { 370 *mockBaseWatcher 371 372 incoming chan struct{} 373 changes chan struct{} 374 } 375 376 var _ state.NotifyWatcher = (*mockConfigWatcher)(nil) 377 378 func NewMockConfigWatcher(err error) *mockConfigWatcher { 379 changes := make(chan struct{}) 380 w := &mockConfigWatcher{ 381 changes: changes, 382 incoming: make(chan struct{}), 383 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 384 } 385 if err == nil { 386 go w.loop() 387 } 388 return w 389 } 390 391 func (m *mockConfigWatcher) loop() { 392 // Prepare initial event. 393 outChanges := m.changes 394 // Forward any incoming changes until stopped. 395 for { 396 select { 397 case <-m.done: 398 // We're about to quit. 399 return 400 case outChanges <- struct{}{}: 401 outChanges = nil 402 case <-m.incoming: 403 outChanges = m.changes 404 } 405 } 406 } 407 408 // Changes implements state.NotifyWatcher. 409 func (m *mockConfigWatcher) Changes() <-chan struct{} { 410 return m.changes 411 } 412 413 type mockMachinesWatcher struct { 414 *mockBaseWatcher 415 416 initial []string 417 incoming chan []string 418 changes chan []string 419 } 420 421 func NewMockMachinesWatcher(initial []string, err error) *mockMachinesWatcher { 422 changes := make(chan []string) 423 w := &mockMachinesWatcher{ 424 initial: initial, 425 changes: changes, 426 incoming: make(chan []string), 427 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 428 } 429 if err == nil { 430 go w.loop() 431 } 432 return w 433 } 434 435 func (m *mockMachinesWatcher) loop() { 436 // Prepare initial event. 437 unsent := m.initial 438 outChanges := m.changes 439 // Forward any incoming changes until stopped. 440 for { 441 select { 442 case <-m.done: 443 // We're about to quit. 444 return 445 case outChanges <- unsent: 446 outChanges = nil 447 unsent = nil 448 case ids := <-m.incoming: 449 unsent = append(unsent, ids...) 450 outChanges = m.changes 451 } 452 } 453 } 454 455 // Changes implements state.StringsWatcher. 456 func (m *mockMachinesWatcher) Changes() <-chan []string { 457 return m.changes 458 } 459 460 var _ state.StringsWatcher = (*mockMachinesWatcher)(nil)