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