github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/addresser/mock_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package addresser_test 5 6 import ( 7 "sort" 8 "sync" 9 10 gc "gopkg.in/check.v1" 11 12 "github.com/juju/errors" 13 "github.com/juju/names" 14 "github.com/juju/testing" 15 jujutxn "github.com/juju/txn" 16 17 "github.com/juju/juju/apiserver/addresser" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/network" 22 "github.com/juju/juju/provider/dummy" 23 "github.com/juju/juju/state" 24 coretesting "github.com/juju/juju/testing" 25 ) 26 27 // mockState implements StateInterface and allows inspection of called 28 // methods. 29 type mockState struct { 30 mu sync.Mutex 31 stub *testing.Stub 32 33 config *config.Config 34 ipAddresses map[string]*mockIPAddress 35 36 ipAddressWatchers []*mockIPAddressWatcher 37 } 38 39 func newMockState() *mockState { 40 mst := &mockState{ 41 stub: &testing.Stub{}, 42 ipAddresses: make(map[string]*mockIPAddress), 43 } 44 mst.setUpState() 45 return mst 46 } 47 48 func (mst *mockState) setUpState() { 49 mst.mu.Lock() 50 defer mst.mu.Unlock() 51 52 ips := []struct { 53 value string 54 uuid string 55 life state.Life 56 subnetId string 57 instanceId string 58 macaddr string 59 }{ 60 {"0.1.2.3", "00000000-1111-2222-3333-0123456789ab", state.Alive, "a", "a3", "fff3"}, 61 {"0.1.2.4", "00000000-1111-2222-4444-0123456789ab", state.Alive, "b", "b4", "fff4"}, 62 {"0.1.2.5", "00000000-1111-2222-5555-0123456789ab", state.Alive, "b", "b5", "fff5"}, 63 {"0.1.2.6", "00000000-1111-2222-6666-0123456789ab", state.Dead, "c", "c6", "fff6"}, 64 {"0.1.2.7", "00000000-1111-2222-7777-0123456789ab", state.Dead, "c", "c7", "fff7"}, 65 } 66 for _, ip := range ips { 67 mst.ipAddresses[ip.value] = &mockIPAddress{ 68 stub: mst.stub, 69 st: mst, 70 value: ip.value, 71 tag: names.NewIPAddressTag(ip.uuid), 72 life: ip.life, 73 subnetId: ip.subnetId, 74 instanceId: instance.Id(ip.instanceId), 75 addr: network.NewAddress(ip.value), 76 macaddr: ip.macaddr, 77 } 78 } 79 } 80 81 var _ addresser.StateInterface = (*mockState)(nil) 82 83 // EnvironConfig implements StateInterface. 84 func (mst *mockState) EnvironConfig() (*config.Config, error) { 85 mst.mu.Lock() 86 defer mst.mu.Unlock() 87 88 mst.stub.MethodCall(mst, "EnvironConfig") 89 90 if err := mst.stub.NextErr(); err != nil { 91 return nil, err 92 } 93 return mst.config, nil 94 } 95 96 // setConfig updates the environ config stored internally. Triggers a 97 // change event for all created config watchers. 98 func (mst *mockState) setConfig(c *gc.C, newConfig *config.Config) { 99 mst.mu.Lock() 100 defer mst.mu.Unlock() 101 102 mst.config = newConfig 103 } 104 105 // IPAddress implements StateInterface. 106 func (mst *mockState) IPAddress(value string) (addresser.StateIPAddress, error) { 107 mst.mu.Lock() 108 defer mst.mu.Unlock() 109 110 mst.stub.MethodCall(mst, "IPAddress", value) 111 if err := mst.stub.NextErr(); err != nil { 112 return nil, err 113 } 114 ipAddress, found := mst.ipAddresses[value] 115 if !found { 116 return nil, errors.NotFoundf("IP address %s", value) 117 } 118 return ipAddress, nil 119 } 120 121 // setDead sets a mock IP address in state to dead. 122 func (mst *mockState) setDead(c *gc.C, value string) { 123 mst.mu.Lock() 124 defer mst.mu.Unlock() 125 126 ipAddress, found := mst.ipAddresses[value] 127 c.Assert(found, gc.Equals, true) 128 129 ipAddress.life = state.Dead 130 } 131 132 // DeadIPAddresses implements StateInterface. 133 func (mst *mockState) DeadIPAddresses() ([]addresser.StateIPAddress, error) { 134 mst.mu.Lock() 135 defer mst.mu.Unlock() 136 137 mst.stub.MethodCall(mst, "DeadIPAddresses") 138 if err := mst.stub.NextErr(); err != nil { 139 return nil, err 140 } 141 var deadIPAddresses []addresser.StateIPAddress 142 for _, ipAddress := range mst.ipAddresses { 143 if ipAddress.life == state.Dead { 144 deadIPAddresses = append(deadIPAddresses, ipAddress) 145 } 146 } 147 return deadIPAddresses, nil 148 } 149 150 // WatchIPAddresses implements StateInterface. 151 func (mst *mockState) WatchIPAddresses() state.StringsWatcher { 152 mst.mu.Lock() 153 defer mst.mu.Unlock() 154 155 mst.stub.MethodCall(mst, "WatchIPAddresses") 156 mst.stub.NextErr() 157 return mst.nextIPAddressWatcher() 158 } 159 160 // addIPAddressWatcher adds an IP address watcher with the given IDs. 161 func (mst *mockState) addIPAddressWatcher(ids ...string) *mockIPAddressWatcher { 162 w := newMockIPAddressWatcher(ids) 163 mst.ipAddressWatchers = append(mst.ipAddressWatchers, w) 164 return w 165 } 166 167 // nextIPAddressWatcher returns an IP address watcher . 168 func (mst *mockState) nextIPAddressWatcher() *mockIPAddressWatcher { 169 if len(mst.ipAddressWatchers) == 0 { 170 panic("ran out of watchers") 171 } 172 173 w := mst.ipAddressWatchers[0] 174 mst.ipAddressWatchers = mst.ipAddressWatchers[1:] 175 if len(w.initial) == 0 { 176 ids := make([]string, 0, len(mst.ipAddresses)) 177 // Initial event - all IP address ids, sorted. 178 for id := range mst.ipAddresses { 179 ids = append(ids, id) 180 } 181 sort.Strings(ids) 182 w.initial = ids 183 } 184 w.start() 185 return w 186 } 187 188 // removeIPAddress is used by mockIPAddress.Remove() 189 func (mst *mockState) removeIPAddress(value string) error { 190 mst.mu.Lock() 191 defer mst.mu.Unlock() 192 193 ipAddr, ok := mst.ipAddresses[value] 194 if !ok { 195 return jujutxn.ErrNoOperations 196 } 197 if ipAddr.life != state.Dead { 198 return errors.Errorf("cannot remove IP address %q: IP address is not dead", ipAddr.value) 199 } 200 delete(mst.ipAddresses, value) 201 return nil 202 } 203 204 // StartSync implements statetesting.SyncStarter, so mockState can be 205 // used with watcher helpers/checkers. 206 func (mst *mockState) StartSync() {} 207 208 // mockIPAddress implements StateIPAddress for testing. 209 type mockIPAddress struct { 210 addresser.StateIPAddress 211 212 stub *testing.Stub 213 st *mockState 214 value string 215 tag names.Tag 216 life state.Life 217 subnetId string 218 instanceId instance.Id 219 addr network.Address 220 macaddr string 221 } 222 223 var _ addresser.StateIPAddress = (*mockIPAddress)(nil) 224 225 // Value implements StateIPAddress. 226 func (mip *mockIPAddress) Value() string { 227 mip.stub.MethodCall(mip, "Value") 228 mip.stub.NextErr() // Consume the unused error. 229 return mip.value 230 } 231 232 // Tag implements StateIPAddress. 233 func (mip *mockIPAddress) Tag() names.Tag { 234 mip.stub.MethodCall(mip, "Tag") 235 mip.stub.NextErr() // Consume the unused error. 236 return mip.tag 237 } 238 239 // Life implements StateIPAddress. 240 func (mip *mockIPAddress) Life() state.Life { 241 mip.stub.MethodCall(mip, "Life") 242 mip.stub.NextErr() // Consume the unused error. 243 return mip.life 244 } 245 246 // Remove implements StateIPAddress. 247 func (mip *mockIPAddress) Remove() error { 248 mip.stub.MethodCall(mip, "Remove") 249 if err := mip.stub.NextErr(); err != nil { 250 return err 251 } 252 return mip.st.removeIPAddress(mip.value) 253 } 254 255 // SubnetId implements StateIPAddress. 256 func (mip *mockIPAddress) SubnetId() string { 257 mip.stub.MethodCall(mip, "SubnetId") 258 mip.stub.NextErr() // Consume the unused error. 259 return mip.subnetId 260 } 261 262 // InstanceId implements StateIPAddress. 263 func (mip *mockIPAddress) InstanceId() instance.Id { 264 mip.stub.MethodCall(mip, "InstanceId") 265 mip.stub.NextErr() // Consume the unused error. 266 return mip.instanceId 267 } 268 269 // Address implements StateIPAddress. 270 func (mip *mockIPAddress) Address() network.Address { 271 mip.stub.MethodCall(mip, "Address") 272 mip.stub.NextErr() // Consume the unused error. 273 return mip.addr 274 } 275 276 // MACAddress implements StateIPAddress. 277 func (mip *mockIPAddress) MACAddress() string { 278 mip.stub.MethodCall(mip, "MACAddress") 279 mip.stub.NextErr() // Consume the unused error. 280 return mip.macaddr 281 } 282 283 // mockIPAddressWatcher notifies about IP address changes. 284 type mockIPAddressWatcher struct { 285 err error 286 initial []string 287 wontStart bool 288 incoming chan []string 289 changes chan []string 290 done chan struct{} 291 } 292 293 var _ state.StringsWatcher = (*mockIPAddressWatcher)(nil) 294 295 func newMockIPAddressWatcher(initial []string) *mockIPAddressWatcher { 296 mipw := &mockIPAddressWatcher{ 297 initial: initial, 298 wontStart: false, 299 incoming: make(chan []string), 300 changes: make(chan []string), 301 done: make(chan struct{}), 302 } 303 return mipw 304 } 305 306 // Kill implements state.Watcher. 307 func (mipw *mockIPAddressWatcher) Kill() {} 308 309 // Stop implements state.Watcher. 310 func (mipw *mockIPAddressWatcher) Stop() error { 311 select { 312 case <-mipw.done: 313 // Closed. 314 default: 315 // Signal the loop we want to stop. 316 close(mipw.done) 317 // Signal the clients we've closed. 318 close(mipw.changes) 319 } 320 return mipw.err 321 } 322 323 // Wait implements state.Watcher. 324 func (mipw *mockIPAddressWatcher) Wait() error { 325 return mipw.Stop() 326 } 327 328 // Err implements state.Watcher. 329 func (mipw *mockIPAddressWatcher) Err() error { 330 return mipw.err 331 } 332 333 // start starts the backend loop depending on a field setting. 334 func (mipw *mockIPAddressWatcher) start() { 335 if mipw.wontStart { 336 // Set manually by tests that need it. 337 mipw.Stop() 338 return 339 } 340 go mipw.loop() 341 } 342 343 func (mipw *mockIPAddressWatcher) loop() { 344 // Prepare initial event. 345 unsent := mipw.initial 346 outChanges := mipw.changes 347 // Forward any incoming changes until stopped. 348 for { 349 select { 350 case <-mipw.done: 351 return 352 case outChanges <- unsent: 353 outChanges = nil 354 unsent = nil 355 case ids := <-mipw.incoming: 356 unsent = append(unsent, ids...) 357 outChanges = mipw.changes 358 } 359 } 360 } 361 362 // Changes implements state.StringsWatcher. 363 func (mipw *mockIPAddressWatcher) Changes() <-chan []string { 364 return mipw.changes 365 } 366 367 // mockConfig returns a configuration for the usage of the 368 // mock provider below. 369 func mockConfig() coretesting.Attrs { 370 return dummy.SampleConfig().Merge(coretesting.Attrs{ 371 "type": "mock", 372 }) 373 } 374 375 // mockEnviron is an environment without networking support. 376 type mockEnviron struct { 377 environs.Environ 378 } 379 380 func (e mockEnviron) Config() *config.Config { 381 cfg, err := config.New(config.NoDefaults, mockConfig()) 382 if err != nil { 383 panic("invalid configuration for testing") 384 } 385 return cfg 386 } 387 388 // mockEnvironProvider is the smallest possible provider to 389 // test the addresses without networking support. 390 type mockEnvironProvider struct { 391 environs.EnvironProvider 392 } 393 394 func (p mockEnvironProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { 395 return &mockEnviron{}, nil 396 } 397 398 func (p mockEnvironProvider) Open(*config.Config) (environs.Environ, error) { 399 return &mockEnviron{}, nil 400 }