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