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