github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/testing/stub_network.go (about) 1 package testing 2 3 import ( 4 "fmt" 5 "net" 6 "strconv" 7 "strings" 8 9 gc "gopkg.in/check.v1" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/network" 17 providercommon "github.com/juju/juju/provider/common" 18 coretesting "github.com/juju/juju/testing" 19 "github.com/juju/testing" 20 "github.com/juju/utils" 21 "github.com/juju/utils/set" 22 ) 23 24 type StubNetwork struct { 25 } 26 27 var ( 28 // SharedStub records all method calls to any of the stubs. 29 SharedStub = &testing.Stub{} 30 31 BackingInstance = &StubBacking{Stub: SharedStub} 32 ProviderInstance = &StubProvider{Stub: SharedStub} 33 EnvironInstance = &StubEnviron{Stub: SharedStub} 34 ZonedEnvironInstance = &StubZonedEnviron{Stub: SharedStub} 35 NetworkingEnvironInstance = &StubNetworkingEnviron{Stub: SharedStub} 36 ZonedNetworkingEnvironInstance = &StubZonedNetworkingEnviron{Stub: SharedStub} 37 ) 38 39 const ( 40 StubProviderType = "stub-provider" 41 StubEnvironName = "stub-environ" 42 StubZonedEnvironName = "stub-zoned-environ" 43 StubNetworkingEnvironName = "stub-networking-environ" 44 StubZonedNetworkingEnvironName = "stub-zoned-networking-environ" 45 ) 46 47 func (s StubNetwork) SetUpSuite(c *gc.C) { 48 ProviderInstance.Zones = []providercommon.AvailabilityZone{ 49 &FakeZone{"zone1", true}, 50 &FakeZone{"zone2", false}, 51 &FakeZone{"zone3", true}, 52 &FakeZone{"zone4", false}, 53 &FakeZone{"zone4", false}, // duplicates are ignored 54 } 55 ProviderInstance.Subnets = []network.SubnetInfo{{ 56 CIDR: "10.10.0.0/24", 57 ProviderId: "sn-zadf00d", 58 AvailabilityZones: []string{"zone1"}, 59 AllocatableIPLow: net.ParseIP("10.10.0.10"), 60 AllocatableIPHigh: net.ParseIP("10.10.0.100"), 61 }, { 62 CIDR: "2001:db8::/32", 63 ProviderId: "sn-ipv6", 64 AvailabilityZones: []string{"zone1", "zone3"}, 65 }, { 66 // no CIDR or provider id -> cached, but cannot be added 67 CIDR: "", 68 ProviderId: "", 69 }, { 70 // no CIDR, just provider id -> cached, but can only be added by id 71 CIDR: "", 72 ProviderId: "sn-empty", 73 }, { 74 // invalid CIDR and provider id -> cannot be added, but is cached 75 CIDR: "invalid", 76 ProviderId: "sn-invalid", 77 }, { 78 // incorrectly specified CIDR, with provider id -> cached, cannot be added 79 CIDR: "0.1.2.3/4", 80 ProviderId: "sn-awesome", 81 }, { 82 // no zones, no provider-id -> cached, but can only be added by CIDR 83 CIDR: "10.20.0.0/16", 84 }, { 85 // with zones, duplicate provider-id -> overwritten by the last 86 // subnet with the same provider id when caching. 87 CIDR: "10.99.88.0/24", 88 ProviderId: "sn-deadbeef", 89 AvailabilityZones: []string{"zone1", "zone2"}, 90 }, { 91 // no zones 92 CIDR: "10.42.0.0/16", 93 ProviderId: "sn-42", 94 }, { 95 // in an unavailable zone, duplicate CIDR -> cannot be added, but is cached 96 CIDR: "10.10.0.0/24", 97 ProviderId: "sn-deadbeef", 98 AvailabilityZones: []string{"zone2"}, 99 }, { 100 CIDR: "10.30.1.0/24", 101 ProviderId: "vlan-42", 102 VLANTag: 42, 103 AvailabilityZones: []string{"zone3"}, 104 }} 105 106 environs.RegisterProvider(StubProviderType, ProviderInstance) 107 } 108 109 type errReturner func() error 110 111 // FakeSpace implements common.BackingSpace for testing. 112 type FakeSpace struct { 113 SpaceName string 114 SubnetIds []string 115 Public bool 116 NextErr errReturner 117 } 118 119 var _ common.BackingSpace = (*FakeSpace)(nil) 120 121 func (f *FakeSpace) Name() string { 122 return f.SpaceName 123 } 124 125 func (f *FakeSpace) Subnets() (bs []common.BackingSubnet, err error) { 126 outputSubnets := []common.BackingSubnet{} 127 128 if err = f.NextErr(); err != nil { 129 return outputSubnets, err 130 } 131 132 for _, subnetId := range f.SubnetIds { 133 providerId := "provider-" + subnetId 134 135 // Pick the third element of the IP address and use this to 136 // decide how we construct the Subnet. It provides variation of 137 // test data. 138 first, err := strconv.Atoi(strings.Split(subnetId, ".")[2]) 139 if err != nil { 140 return outputSubnets, err 141 } 142 vlantag := 0 143 zones := []string{"foo"} 144 status := "in-use" 145 if first%2 == 1 { 146 vlantag = 23 147 zones = []string{"bar", "bam"} 148 status = "" 149 } 150 151 backing := common.BackingSubnetInfo{ 152 CIDR: subnetId, 153 SpaceName: f.SpaceName, 154 ProviderId: providerId, 155 VLANTag: vlantag, 156 AvailabilityZones: zones, 157 Status: status, 158 } 159 outputSubnets = append(outputSubnets, &FakeSubnet{info: backing}) 160 } 161 162 return outputSubnets, nil 163 } 164 165 func (f *FakeSpace) ProviderId() (netID network.Id) { 166 return 167 } 168 169 func (f *FakeSpace) Zones() []string { 170 return []string{""} 171 } 172 173 func (f *FakeSpace) Life() (life params.Life) { 174 return 175 } 176 177 // GoString implements fmt.GoStringer. 178 func (f *FakeSpace) GoString() string { 179 return fmt.Sprintf("&FakeSpace{%q}", f.SpaceName) 180 } 181 182 // StubMethodCall is like testing.StubCall, but includes the receiver 183 // as well. 184 type StubMethodCall struct { 185 Receiver interface{} 186 FuncName string 187 Args []interface{} 188 } 189 190 // BackingCall makes it easy to check method calls on BackingInstance. 191 func BackingCall(name string, args ...interface{}) StubMethodCall { 192 return StubMethodCall{ 193 Receiver: BackingInstance, 194 FuncName: name, 195 Args: args, 196 } 197 } 198 199 // ProviderCall makes it easy to check method calls on ProviderInstance. 200 func ProviderCall(name string, args ...interface{}) StubMethodCall { 201 return StubMethodCall{ 202 Receiver: ProviderInstance, 203 FuncName: name, 204 Args: args, 205 } 206 } 207 208 // EnvironCall makes it easy to check method calls on EnvironInstance. 209 func EnvironCall(name string, args ...interface{}) StubMethodCall { 210 return StubMethodCall{ 211 Receiver: EnvironInstance, 212 FuncName: name, 213 Args: args, 214 } 215 } 216 217 // ZonedEnvironCall makes it easy to check method calls on 218 // ZonedEnvironInstance. 219 func ZonedEnvironCall(name string, args ...interface{}) StubMethodCall { 220 return StubMethodCall{ 221 Receiver: ZonedEnvironInstance, 222 FuncName: name, 223 Args: args, 224 } 225 } 226 227 // NetworkingEnvironCall makes it easy to check method calls on 228 // NetworkingEnvironInstance. 229 func NetworkingEnvironCall(name string, args ...interface{}) StubMethodCall { 230 return StubMethodCall{ 231 Receiver: NetworkingEnvironInstance, 232 FuncName: name, 233 Args: args, 234 } 235 } 236 237 // ZonedNetworkingEnvironCall makes it easy to check method calls on 238 // ZonedNetworkingEnvironInstance. 239 func ZonedNetworkingEnvironCall(name string, args ...interface{}) StubMethodCall { 240 return StubMethodCall{ 241 Receiver: ZonedNetworkingEnvironInstance, 242 FuncName: name, 243 Args: args, 244 } 245 } 246 247 // CheckMethodCalls works like testing.Stub.CheckCalls, but also 248 // checks the receivers. 249 func CheckMethodCalls(c *gc.C, stub *testing.Stub, calls ...StubMethodCall) { 250 receivers := make([]interface{}, len(calls)) 251 for i, call := range calls { 252 receivers[i] = call.Receiver 253 } 254 stub.CheckReceivers(c, receivers...) 255 c.Check(stub.Calls(), gc.HasLen, len(calls)) 256 for i, call := range calls { 257 stub.CheckCall(c, i, call.FuncName, call.Args...) 258 } 259 } 260 261 // FakeZone implements providercommon.AvailabilityZone for testing. 262 type FakeZone struct { 263 ZoneName string 264 ZoneAvailable bool 265 } 266 267 var _ providercommon.AvailabilityZone = (*FakeZone)(nil) 268 269 func (f *FakeZone) Name() string { 270 return f.ZoneName 271 } 272 273 func (f *FakeZone) Available() bool { 274 return f.ZoneAvailable 275 } 276 277 // GoString implements fmt.GoStringer. 278 func (f *FakeZone) GoString() string { 279 return fmt.Sprintf("&FakeZone{%q, %v}", f.ZoneName, f.ZoneAvailable) 280 } 281 282 // FakeSubnet implements common.BackingSubnet for testing. 283 type FakeSubnet struct { 284 info common.BackingSubnetInfo 285 } 286 287 var _ common.BackingSubnet = (*FakeSubnet)(nil) 288 289 // GoString implements fmt.GoStringer. 290 func (f *FakeSubnet) GoString() string { 291 return fmt.Sprintf("&FakeSubnet{%#v}", f.info) 292 } 293 294 func (f *FakeSubnet) Status() string { 295 return f.info.Status 296 } 297 298 func (f *FakeSubnet) CIDR() string { 299 return f.info.CIDR 300 } 301 302 func (f *FakeSubnet) AvailabilityZones() []string { 303 return f.info.AvailabilityZones 304 } 305 306 func (f *FakeSubnet) ProviderId() string { 307 return f.info.ProviderId 308 } 309 310 func (f *FakeSubnet) VLANTag() int { 311 return f.info.VLANTag 312 } 313 314 func (f *FakeSubnet) SpaceName() string { 315 return f.info.SpaceName 316 } 317 318 func (f *FakeSubnet) Life() params.Life { 319 return f.info.Life 320 } 321 322 // ResetStub resets all recorded calls and errors of the given stub. 323 func ResetStub(stub *testing.Stub) { 324 *stub = testing.Stub{} 325 } 326 327 // StubBacking implements common.NetworkBacking and records calls to its 328 // methods. 329 type StubBacking struct { 330 *testing.Stub 331 332 EnvConfig *config.Config 333 334 Zones []providercommon.AvailabilityZone 335 Spaces []common.BackingSpace 336 Subnets []common.BackingSubnet 337 } 338 339 var _ common.NetworkBacking = (*StubBacking)(nil) 340 341 type SetUpFlag bool 342 343 const ( 344 WithZones SetUpFlag = true 345 WithoutZones SetUpFlag = false 346 WithSpaces SetUpFlag = true 347 WithoutSpaces SetUpFlag = false 348 WithSubnets SetUpFlag = true 349 WithoutSubnets SetUpFlag = false 350 ) 351 352 func (sb *StubBacking) SetUp(c *gc.C, envName string, withZones, withSpaces, withSubnets SetUpFlag) { 353 // This method must be called at the beginning of each test, which 354 // needs access to any of the mocks, to reset the recorded calls 355 // and errors, as well as to initialize the mocks as needed. 356 ResetStub(sb.Stub) 357 358 // Make sure we use the stub provider. 359 extraAttrs := coretesting.Attrs{ 360 "uuid": utils.MustNewUUID().String(), 361 "type": StubProviderType, 362 "name": envName, 363 } 364 sb.EnvConfig = coretesting.CustomEnvironConfig(c, extraAttrs) 365 sb.Zones = []providercommon.AvailabilityZone{} 366 if withZones { 367 sb.Zones = make([]providercommon.AvailabilityZone, len(ProviderInstance.Zones)) 368 copy(sb.Zones, ProviderInstance.Zones) 369 } 370 sb.Spaces = []common.BackingSpace{} 371 if withSpaces { 372 // Note that full subnet data is generated from the SubnetIds in 373 // FakeSpace.Subnets(). 374 sb.Spaces = []common.BackingSpace{ 375 &FakeSpace{ 376 SpaceName: "default", 377 SubnetIds: []string{"192.168.0.0/24", "192.168.3.0/24"}, 378 NextErr: sb.NextErr}, 379 &FakeSpace{ 380 SpaceName: "dmz", 381 SubnetIds: []string{"192.168.1.0/24"}, 382 NextErr: sb.NextErr}, 383 &FakeSpace{ 384 SpaceName: "private", 385 SubnetIds: []string{"192.168.2.0/24"}, 386 NextErr: sb.NextErr}, 387 &FakeSpace{ 388 SpaceName: "private", 389 SubnetIds: []string{"192.168.2.0/24"}, 390 NextErr: sb.NextErr}, // duplicates are ignored when caching spaces. 391 } 392 } 393 sb.Subnets = []common.BackingSubnet{} 394 if withSubnets { 395 info0 := common.BackingSubnetInfo{ 396 CIDR: ProviderInstance.Subnets[0].CIDR, 397 ProviderId: string(ProviderInstance.Subnets[0].ProviderId), 398 AllocatableIPLow: ProviderInstance.Subnets[0].AllocatableIPLow.String(), 399 AllocatableIPHigh: ProviderInstance.Subnets[0].AllocatableIPHigh.String(), 400 AvailabilityZones: ProviderInstance.Subnets[0].AvailabilityZones, 401 SpaceName: "private", 402 } 403 info1 := common.BackingSubnetInfo{ 404 CIDR: ProviderInstance.Subnets[1].CIDR, 405 ProviderId: string(ProviderInstance.Subnets[1].ProviderId), 406 AvailabilityZones: ProviderInstance.Subnets[1].AvailabilityZones, 407 SpaceName: "dmz", 408 } 409 410 sb.Subnets = []common.BackingSubnet{ 411 &FakeSubnet{info0}, 412 &FakeSubnet{info1}, 413 } 414 } 415 } 416 417 func (sb *StubBacking) EnvironConfig() (*config.Config, error) { 418 sb.MethodCall(sb, "EnvironConfig") 419 if err := sb.NextErr(); err != nil { 420 return nil, err 421 } 422 return sb.EnvConfig, nil 423 } 424 425 func (sb *StubBacking) AvailabilityZones() ([]providercommon.AvailabilityZone, error) { 426 sb.MethodCall(sb, "AvailabilityZones") 427 if err := sb.NextErr(); err != nil { 428 return nil, err 429 } 430 return sb.Zones, nil 431 } 432 433 func (sb *StubBacking) SetAvailabilityZones(zones []providercommon.AvailabilityZone) error { 434 sb.MethodCall(sb, "SetAvailabilityZones", zones) 435 return sb.NextErr() 436 } 437 438 func (sb *StubBacking) AllSpaces() ([]common.BackingSpace, error) { 439 sb.MethodCall(sb, "AllSpaces") 440 if err := sb.NextErr(); err != nil { 441 return nil, err 442 } 443 444 // Filter duplicates. 445 seen := set.Strings{} 446 output := []common.BackingSpace{} 447 for _, space := range sb.Spaces { 448 if seen.Contains(space.Name()) { 449 continue 450 } 451 seen.Add(space.Name()) 452 output = append(output, space) 453 } 454 return output, nil 455 } 456 457 func (sb *StubBacking) AllSubnets() ([]common.BackingSubnet, error) { 458 sb.MethodCall(sb, "AllSubnets") 459 if err := sb.NextErr(); err != nil { 460 return nil, err 461 } 462 463 // Filter duplicates. 464 seen := set.Strings{} 465 output := []common.BackingSubnet{} 466 for _, subnet := range sb.Subnets { 467 if seen.Contains(subnet.CIDR()) { 468 continue 469 } 470 seen.Add(subnet.CIDR()) 471 output = append(output, subnet) 472 } 473 return output, nil 474 } 475 476 func (sb *StubBacking) AddSubnet(subnetInfo common.BackingSubnetInfo) (common.BackingSubnet, error) { 477 sb.MethodCall(sb, "AddSubnet", subnetInfo) 478 if err := sb.NextErr(); err != nil { 479 return nil, err 480 } 481 fs := &FakeSubnet{info: subnetInfo} 482 sb.Subnets = append(sb.Subnets, fs) 483 return fs, nil 484 } 485 486 func (sb *StubBacking) AddSpace(name string, subnets []string, public bool) error { 487 sb.MethodCall(sb, "AddSpace", name, subnets, public) 488 if err := sb.NextErr(); err != nil { 489 return err 490 } 491 fs := &FakeSpace{SpaceName: name, SubnetIds: subnets, Public: public} 492 sb.Spaces = append(sb.Spaces, fs) 493 return nil 494 } 495 496 // GoString implements fmt.GoStringer. 497 func (se *StubBacking) GoString() string { 498 return "&StubBacking{}" 499 } 500 501 // StubProvider implements a subset of environs.EnvironProvider 502 // methods used in tests. 503 type StubProvider struct { 504 *testing.Stub 505 506 Zones []providercommon.AvailabilityZone 507 Subnets []network.SubnetInfo 508 509 environs.EnvironProvider // panic on any not implemented method call. 510 } 511 512 var _ environs.EnvironProvider = (*StubProvider)(nil) 513 514 func (sp *StubProvider) Open(cfg *config.Config) (environs.Environ, error) { 515 sp.MethodCall(sp, "Open", cfg) 516 if err := sp.NextErr(); err != nil { 517 return nil, err 518 } 519 switch cfg.Name() { 520 case StubEnvironName: 521 return EnvironInstance, nil 522 case StubZonedEnvironName: 523 return ZonedEnvironInstance, nil 524 case StubNetworkingEnvironName: 525 return NetworkingEnvironInstance, nil 526 case StubZonedNetworkingEnvironName: 527 return ZonedNetworkingEnvironInstance, nil 528 } 529 panic("unexpected environment name: " + cfg.Name()) 530 } 531 532 // GoString implements fmt.GoStringer. 533 func (se *StubProvider) GoString() string { 534 return "&StubProvider{}" 535 } 536 537 // StubEnviron is used in tests where environs.Environ is needed. 538 type StubEnviron struct { 539 *testing.Stub 540 541 environs.Environ // panic on any not implemented method call 542 } 543 544 var _ environs.Environ = (*StubEnviron)(nil) 545 546 // GoString implements fmt.GoStringer. 547 func (se *StubEnviron) GoString() string { 548 return "&StubEnviron{}" 549 } 550 551 // StubZonedEnviron is used in tests where providercommon.ZonedEnviron 552 // is needed. 553 type StubZonedEnviron struct { 554 *testing.Stub 555 556 providercommon.ZonedEnviron // panic on any not implemented method call 557 } 558 559 var _ providercommon.ZonedEnviron = (*StubZonedEnviron)(nil) 560 561 func (se *StubZonedEnviron) AvailabilityZones() ([]providercommon.AvailabilityZone, error) { 562 se.MethodCall(se, "AvailabilityZones") 563 if err := se.NextErr(); err != nil { 564 return nil, err 565 } 566 return ProviderInstance.Zones, nil 567 } 568 569 // GoString implements fmt.GoStringer. 570 func (se *StubZonedEnviron) GoString() string { 571 return "&StubZonedEnviron{}" 572 } 573 574 // StubNetworkingEnviron is used in tests where 575 // environs.NetworkingEnviron is needed. 576 type StubNetworkingEnviron struct { 577 *testing.Stub 578 579 environs.NetworkingEnviron // panic on any not implemented method call 580 } 581 582 var _ environs.NetworkingEnviron = (*StubNetworkingEnviron)(nil) 583 584 func (se *StubNetworkingEnviron) Subnets(instId instance.Id, subIds []network.Id) ([]network.SubnetInfo, error) { 585 se.MethodCall(se, "Subnets", instId, subIds) 586 if err := se.NextErr(); err != nil { 587 return nil, err 588 } 589 return ProviderInstance.Subnets, nil 590 } 591 592 // GoString implements fmt.GoStringer. 593 func (se *StubNetworkingEnviron) GoString() string { 594 return "&StubNetworkingEnviron{}" 595 } 596 597 // StubZonedNetworkingEnviron is used in tests where features from 598 // both environs.Networking and providercommon.ZonedEnviron are 599 // needed. 600 type StubZonedNetworkingEnviron struct { 601 *testing.Stub 602 603 // panic on any not implemented method call 604 providercommon.ZonedEnviron 605 environs.Networking 606 } 607 608 // GoString implements fmt.GoStringer. 609 func (se *StubZonedNetworkingEnviron) GoString() string { 610 return "&StubZonedNetworkingEnviron{}" 611 } 612 613 func (se *StubZonedNetworkingEnviron) SupportsSpaces() (bool, error) { 614 se.MethodCall(se, "SupportsSpaces") 615 if err := se.NextErr(); err != nil { 616 return false, err 617 } 618 return true, nil 619 } 620 621 func (se *StubZonedNetworkingEnviron) Subnets(instId instance.Id, subIds []network.Id) ([]network.SubnetInfo, error) { 622 se.MethodCall(se, "Subnets", instId, subIds) 623 if err := se.NextErr(); err != nil { 624 return nil, err 625 } 626 return ProviderInstance.Subnets, nil 627 } 628 629 func (se *StubZonedNetworkingEnviron) AvailabilityZones() ([]providercommon.AvailabilityZone, error) { 630 se.MethodCall(se, "AvailabilityZones") 631 if err := se.NextErr(); err != nil { 632 return nil, err 633 } 634 return ProviderInstance.Zones, nil 635 }