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