1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
26
type StubNetwork struct {
30
// SharedStub records all method calls to any of the stubs.
31
SharedStub = &testing.Stub{}
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}
42
StubProviderType = "stub-provider"
43
StubEnvironName = "stub-environ"
44
StubZonedEnvironName = "stub-zoned-environ"
45
StubNetworkingEnvironName = "stub-networking-environ"
46
StubZonedNetworkingEnvironName = "stub-zoned-networking-environ"
49
func (s StubNetwork) SetUpSuite(c *gc.C) {
50
providers := environs.RegisteredProviders()
51
for _, name := range providers {
52
if name == StubProviderType {
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
64
ProviderInstance.Subnets = []network.SubnetInfo{{
66
ProviderId: "sn-zadf00d",
67
AvailabilityZones: []string{"zone1"},
69
CIDR: "2001:db8::/32",
70
ProviderId: "sn-ipv6",
71
AvailabilityZones: []string{"zone1", "zone3"},
73
// no CIDR or provider id -> cached, but cannot be added
77
// no CIDR, just provider id -> cached, but can only be added by id
79
ProviderId: "sn-empty",
81
// invalid CIDR and provider id -> cannot be added, but is cached
83
ProviderId: "sn-invalid",
85
// incorrectly specified CIDR, with provider id -> cached, cannot be added
87
ProviderId: "sn-awesome",
89
// no zones, no provider-id -> cached, but can only be added by CIDR
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"},
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"},
107
CIDR: "10.30.1.0/24",
108
ProviderId: "vlan-42",
110
AvailabilityZones: []string{"zone3"},
113
environs.RegisterProvider(StubProviderType, ProviderInstance)
116
type errReturner func() error
118
// FakeSpace implements networkingcommon.BackingSpace for testing.
119
type FakeSpace struct {
126
var _ networkingcommon.BackingSpace = (*FakeSpace)(nil)
128
func (f *FakeSpace) Name() string {
132
func (f *FakeSpace) Subnets() (bs []networkingcommon.BackingSubnet, err error) {
133
outputSubnets := []networkingcommon.BackingSubnet{}
135
if err = f.NextErr(); err != nil {
136
return outputSubnets, err
139
for _, subnetId := range f.SubnetIds {
140
providerId := network.Id("provider-" + subnetId)
142
// Pick the third element of the IP address and use this to
143
// decide how we construct the Subnet. It provides variation of
145
first, err := strconv.Atoi(strings.Split(subnetId, ".")[2])
147
return outputSubnets, err
150
zones := []string{"foo"}
154
zones = []string{"bar", "bam"}
158
backing := networkingcommon.BackingSubnetInfo{
160
SpaceName: f.SpaceName,
161
ProviderId: providerId,
163
AvailabilityZones: zones,
166
outputSubnets = append(outputSubnets, &FakeSubnet{info: backing})
169
return outputSubnets, nil
172
func (f *FakeSpace) ProviderId() (netID network.Id) {
176
func (f *FakeSpace) Zones() []string {
180
func (f *FakeSpace) Life() (life params.Life) {
184
// GoString implements fmt.GoStringer.
185
func (f *FakeSpace) GoString() string {
186
return fmt.Sprintf("&FakeSpace{%q}", f.SpaceName)
189
// StubMethodCall is like testing.StubCall, but includes the receiver
191
type StubMethodCall struct {
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,
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,
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,
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,
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,
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,
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
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...)
268
// FakeZone implements providercommon.AvailabilityZone for testing.
269
type FakeZone struct {
274
var _ providercommon.AvailabilityZone = (*FakeZone)(nil)
276
func (f *FakeZone) Name() string {
280
func (f *FakeZone) Available() bool {
281
return f.ZoneAvailable
284
// GoString implements fmt.GoStringer.
285
func (f *FakeZone) GoString() string {
286
return fmt.Sprintf("&FakeZone{%q, %v}", f.ZoneName, f.ZoneAvailable)
289
// FakeSubnet implements networkingcommon.BackingSubnet for testing.
290
type FakeSubnet struct {
291
info networkingcommon.BackingSubnetInfo
294
var _ networkingcommon.BackingSubnet = (*FakeSubnet)(nil)
296
// GoString implements fmt.GoStringer.
297
func (f *FakeSubnet) GoString() string {
298
return fmt.Sprintf("&FakeSubnet{%#v}", f.info)
301
func (f *FakeSubnet) Status() string {
305
func (f *FakeSubnet) CIDR() string {
309
func (f *FakeSubnet) AvailabilityZones() []string {
310
return f.info.AvailabilityZones
313
func (f *FakeSubnet) ProviderId() network.Id {
314
return f.info.ProviderId
317
func (f *FakeSubnet) VLANTag() int {
318
return f.info.VLANTag
321
func (f *FakeSubnet) SpaceName() string {
322
return f.info.SpaceName
325
func (f *FakeSubnet) Life() params.Life {
329
// ResetStub resets all recorded calls and errors of the given stub.
330
func ResetStub(stub *testing.Stub) {
331
*stub = testing.Stub{}
334
// StubBacking implements networkingcommon.NetworkBacking and records calls to its
336
type StubBacking struct {
339
EnvConfig *config.Config
340
Cloud environs.CloudSpec
342
Zones []providercommon.AvailabilityZone
343
Spaces []networkingcommon.BackingSpace
344
Subnets []networkingcommon.BackingSubnet
347
var _ networkingcommon.NetworkBacking = (*StubBacking)(nil)
352
WithZones SetUpFlag = true
353
WithoutZones SetUpFlag = false
354
WithSpaces SetUpFlag = true
355
WithoutSpaces SetUpFlag = false
356
WithSubnets SetUpFlag = true
357
WithoutSubnets SetUpFlag = false
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.
366
// Make sure we use the stub provider.
367
extraAttrs := coretesting.Attrs{
368
"uuid": utils.MustNewUUID().String(),
369
"type": StubProviderType,
372
sb.EnvConfig = coretesting.CustomModelConfig(c, extraAttrs)
373
sb.Cloud = environs.CloudSpec{
374
Type: StubProviderType,
376
Endpoint: "endpoint",
377
StorageEndpoint: "storage-endpoint",
379
sb.Zones = []providercommon.AvailabilityZone{}
381
sb.Zones = make([]providercommon.AvailabilityZone, len(ProviderInstance.Zones))
382
copy(sb.Zones, ProviderInstance.Zones)
384
sb.Spaces = []networkingcommon.BackingSpace{}
386
// Note that full subnet data is generated from the SubnetIds in
387
// FakeSpace.Subnets().
388
sb.Spaces = []networkingcommon.BackingSpace{
390
SpaceName: "default",
391
SubnetIds: []string{"192.168.0.0/24", "192.168.3.0/24"},
392
NextErr: sb.NextErr},
395
SubnetIds: []string{"192.168.1.0/24"},
396
NextErr: sb.NextErr},
398
SpaceName: "private",
399
SubnetIds: []string{"192.168.2.0/24"},
400
NextErr: sb.NextErr},
402
SpaceName: "private",
403
SubnetIds: []string{"192.168.2.0/24"},
404
NextErr: sb.NextErr}, // duplicates are ignored when caching spaces.
407
sb.Subnets = []networkingcommon.BackingSubnet{}
409
info0 := networkingcommon.BackingSubnetInfo{
410
CIDR: ProviderInstance.Subnets[0].CIDR,
411
ProviderId: ProviderInstance.Subnets[0].ProviderId,
412
AvailabilityZones: ProviderInstance.Subnets[0].AvailabilityZones,
413
SpaceName: "private",
415
info1 := networkingcommon.BackingSubnetInfo{
416
CIDR: ProviderInstance.Subnets[1].CIDR,
417
ProviderId: ProviderInstance.Subnets[1].ProviderId,
418
AvailabilityZones: ProviderInstance.Subnets[1].AvailabilityZones,
422
sb.Subnets = []networkingcommon.BackingSubnet{
429
func (sb *StubBacking) ModelConfig() (*config.Config, error) {
430
sb.MethodCall(sb, "ModelConfig")
431
if err := sb.NextErr(); err != nil {
434
return sb.EnvConfig, nil
437
func (sb *StubBacking) CloudSpec(names.ModelTag) (environs.CloudSpec, error) {
438
sb.MethodCall(sb, "CloudSpec")
439
if err := sb.NextErr(); err != nil {
440
return environs.CloudSpec{}, err
445
func (sb *StubBacking) AvailabilityZones() ([]providercommon.AvailabilityZone, error) {
446
sb.MethodCall(sb, "AvailabilityZones")
447
if err := sb.NextErr(); err != nil {
453
func (sb *StubBacking) SetAvailabilityZones(zones []providercommon.AvailabilityZone) error {
454
sb.MethodCall(sb, "SetAvailabilityZones", zones)
458
func (sb *StubBacking) AllSpaces() ([]networkingcommon.BackingSpace, error) {
459
sb.MethodCall(sb, "AllSpaces")
460
if err := sb.NextErr(); err != nil {
464
// Filter duplicates.
465
seen := set.Strings{}
466
output := []networkingcommon.BackingSpace{}
467
for _, space := range sb.Spaces {
468
if seen.Contains(space.Name()) {
471
seen.Add(space.Name())
472
output = append(output, space)
477
func (sb *StubBacking) AllSubnets() ([]networkingcommon.BackingSubnet, error) {
478
sb.MethodCall(sb, "AllSubnets")
479
if err := sb.NextErr(); err != nil {
483
// Filter duplicates.
484
seen := set.Strings{}
485
output := []networkingcommon.BackingSubnet{}
486
for _, subnet := range sb.Subnets {
487
if seen.Contains(subnet.CIDR()) {
490
seen.Add(subnet.CIDR())
491
output = append(output, subnet)
496
func (sb *StubBacking) AddSubnet(subnetInfo networkingcommon.BackingSubnetInfo) (networkingcommon.BackingSubnet, error) {
497
sb.MethodCall(sb, "AddSubnet", subnetInfo)
498
if err := sb.NextErr(); err != nil {
501
fs := &FakeSubnet{info: subnetInfo}
502
sb.Subnets = append(sb.Subnets, fs)
506
func (sb *StubBacking) AddSpace(name string, providerId network.Id, subnets []string, public bool) error {
507
sb.MethodCall(sb, "AddSpace", name, providerId, subnets, public)
508
if err := sb.NextErr(); err != nil {
511
fs := &FakeSpace{SpaceName: name, SubnetIds: subnets, Public: public}
512
sb.Spaces = append(sb.Spaces, fs)
516
// GoString implements fmt.GoStringer.
517
func (se *StubBacking) GoString() string {
518
return "&StubBacking{}"
521
// StubProvider implements a subset of environs.EnvironProvider
522
// methods used in tests.
523
type StubProvider struct {
526
Zones []providercommon.AvailabilityZone
527
Subnets []network.SubnetInfo
529
environs.EnvironProvider // panic on any not implemented method call.
532
var _ environs.EnvironProvider = (*StubProvider)(nil)
534
func (sp *StubProvider) Open(args environs.OpenParams) (environs.Environ, error) {
535
sp.MethodCall(sp, "Open", args.Config)
536
if err := sp.NextErr(); err != nil {
539
switch args.Config.Name() {
540
case StubEnvironName:
541
return EnvironInstance, nil
542
case StubZonedEnvironName:
543
return ZonedEnvironInstance, nil
544
case StubNetworkingEnvironName:
545
return NetworkingEnvironInstance, nil
546
case StubZonedNetworkingEnvironName:
547
return ZonedNetworkingEnvironInstance, nil
549
panic("unexpected model name: " + args.Config.Name())
552
// GoString implements fmt.GoStringer.
553
func (se *StubProvider) GoString() string {
554
return "&StubProvider{}"
557
// StubEnviron is used in tests where environs.Environ is needed.
558
type StubEnviron struct {
561
environs.Environ // panic on any not implemented method call
564
var _ environs.Environ = (*StubEnviron)(nil)
566
// GoString implements fmt.GoStringer.
567
func (se *StubEnviron) GoString() string {
568
return "&StubEnviron{}"
571
// StubZonedEnviron is used in tests where providercommon.ZonedEnviron
573
type StubZonedEnviron struct {
576
providercommon.ZonedEnviron // panic on any not implemented method call
579
var _ providercommon.ZonedEnviron = (*StubZonedEnviron)(nil)
581
func (se *StubZonedEnviron) AvailabilityZones() ([]providercommon.AvailabilityZone, error) {
582
se.MethodCall(se, "AvailabilityZones")
583
if err := se.NextErr(); err != nil {
586
return ProviderInstance.Zones, nil
589
// GoString implements fmt.GoStringer.
590
func (se *StubZonedEnviron) GoString() string {
591
return "&StubZonedEnviron{}"
594
// StubNetworkingEnviron is used in tests where
595
// environs.NetworkingEnviron is needed.
596
type StubNetworkingEnviron struct {
599
environs.NetworkingEnviron // panic on any not implemented method call
602
var _ environs.NetworkingEnviron = (*StubNetworkingEnviron)(nil)
604
func (se *StubNetworkingEnviron) Subnets(instId instance.Id, subIds []network.Id) ([]network.SubnetInfo, error) {
605
se.MethodCall(se, "Subnets", instId, subIds)
606
if err := se.NextErr(); err != nil {
609
return ProviderInstance.Subnets, nil
612
func (se *StubNetworkingEnviron) SupportsSpaces() (bool, error) {
613
se.MethodCall(se, "SupportsSpaces")
614
if err := se.NextErr(); err != nil {
620
// GoString implements fmt.GoStringer.
621
func (se *StubNetworkingEnviron) GoString() string {
622
return "&StubNetworkingEnviron{}"
625
// StubZonedNetworkingEnviron is used in tests where features from
626
// both environs.Networking and providercommon.ZonedEnviron are
628
type StubZonedNetworkingEnviron struct {
631
// panic on any not implemented method call
632
providercommon.ZonedEnviron
636
// GoString implements fmt.GoStringer.
637
func (se *StubZonedNetworkingEnviron) GoString() string {
638
return "&StubZonedNetworkingEnviron{}"
641
func (se *StubZonedNetworkingEnviron) SupportsSpaces() (bool, error) {
642
se.MethodCall(se, "SupportsSpaces")
643
if err := se.NextErr(); err != nil {
649
func (se *StubZonedNetworkingEnviron) Subnets(instId instance.Id, subIds []network.Id) ([]network.SubnetInfo, error) {
650
se.MethodCall(se, "Subnets", instId, subIds)
651
if err := se.NextErr(); err != nil {
654
return ProviderInstance.Subnets, nil
657
func (se *StubZonedNetworkingEnviron) AvailabilityZones() ([]providercommon.AvailabilityZone, error) {
658
se.MethodCall(se, "AvailabilityZones")
659
if err := se.NextErr(); err != nil {
662
return ProviderInstance.Zones, nil