~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/common/networkingcommon/subnets_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package networkingcommon_test
 
5
 
 
6
import (
 
7
        "github.com/juju/errors"
 
8
        jc "github.com/juju/testing/checkers"
 
9
        gc "gopkg.in/check.v1"
 
10
 
 
11
        "github.com/juju/juju/apiserver/common/networkingcommon"
 
12
        "github.com/juju/juju/apiserver/params"
 
13
        apiservertesting "github.com/juju/juju/apiserver/testing"
 
14
        "github.com/juju/juju/instance"
 
15
        "github.com/juju/juju/network"
 
16
        providercommon "github.com/juju/juju/provider/common"
 
17
        coretesting "github.com/juju/juju/testing"
 
18
)
 
19
 
 
20
type SubnetsSuite struct {
 
21
        coretesting.BaseSuite
 
22
        apiservertesting.StubNetwork
 
23
}
 
24
 
 
25
var _ = gc.Suite(&SubnetsSuite{})
 
26
 
 
27
func (s *SubnetsSuite) SetUpSuite(c *gc.C) {
 
28
        s.StubNetwork.SetUpSuite(c)
 
29
        s.BaseSuite.SetUpSuite(c)
 
30
}
 
31
 
 
32
func (s *SubnetsSuite) TearDownSuite(c *gc.C) {
 
33
        s.BaseSuite.TearDownSuite(c)
 
34
}
 
35
 
 
36
func (s *SubnetsSuite) SetUpTest(c *gc.C) {
 
37
        s.BaseSuite.SetUpTest(c)
 
38
        apiservertesting.BackingInstance.SetUp(
 
39
                c,
 
40
                apiservertesting.StubZonedEnvironName,
 
41
                apiservertesting.WithZones,
 
42
                apiservertesting.WithSpaces,
 
43
                apiservertesting.WithSubnets)
 
44
}
 
45
 
 
46
func (s *SubnetsSuite) TearDownTest(c *gc.C) {
 
47
        s.BaseSuite.TearDownTest(c)
 
48
}
 
49
 
 
50
// AssertAllZonesResult makes it easier to verify AllZones results.
 
51
func (s *SubnetsSuite) AssertAllZonesResult(c *gc.C, got params.ZoneResults, expected []providercommon.AvailabilityZone) {
 
52
        results := make([]params.ZoneResult, len(expected))
 
53
        for i, zone := range expected {
 
54
                results[i].Name = zone.Name()
 
55
                results[i].Available = zone.Available()
 
56
        }
 
57
        c.Assert(got, jc.DeepEquals, params.ZoneResults{Results: results})
 
58
}
 
59
 
 
60
func (s *SubnetsSuite) TestAllZonesWhenBackingAvailabilityZonesFails(c *gc.C) {
 
61
        apiservertesting.SharedStub.SetErrors(errors.NotSupportedf("zones"))
 
62
 
 
63
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
64
        c.Assert(err, gc.ErrorMatches, "zones not supported")
 
65
        // Verify the cause is not obscured.
 
66
        c.Assert(err, jc.Satisfies, errors.IsNotSupported)
 
67
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
68
 
 
69
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
70
                apiservertesting.BackingCall("AvailabilityZones"),
 
71
        )
 
72
}
 
73
 
 
74
func (s *SubnetsSuite) TestAllZonesUsesBackingZonesWhenAvailable(c *gc.C) {
 
75
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
76
        c.Assert(err, jc.ErrorIsNil)
 
77
        s.AssertAllZonesResult(c, results, apiservertesting.BackingInstance.Zones)
 
78
 
 
79
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
80
                apiservertesting.BackingCall("AvailabilityZones"),
 
81
        )
 
82
}
 
83
 
 
84
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesUpdates(c *gc.C) {
 
85
        apiservertesting.BackingInstance.SetUp(
 
86
                c,
 
87
                apiservertesting.StubZonedEnvironName,
 
88
                apiservertesting.WithoutZones,
 
89
                apiservertesting.WithSpaces,
 
90
                apiservertesting.WithSubnets)
 
91
 
 
92
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
93
        c.Assert(err, jc.ErrorIsNil)
 
94
        s.AssertAllZonesResult(c, results, apiservertesting.ProviderInstance.Zones)
 
95
 
 
96
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
97
                apiservertesting.BackingCall("AvailabilityZones"),
 
98
                apiservertesting.BackingCall("ModelConfig"),
 
99
                apiservertesting.BackingCall("CloudSpec"),
 
100
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
101
                apiservertesting.ZonedEnvironCall("AvailabilityZones"),
 
102
                apiservertesting.BackingCall("SetAvailabilityZones", apiservertesting.ProviderInstance.Zones),
 
103
        )
 
104
}
 
105
 
 
106
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndSetFails(c *gc.C) {
 
107
        apiservertesting.BackingInstance.SetUp(
 
108
                c,
 
109
                apiservertesting.StubZonedEnvironName,
 
110
                apiservertesting.WithoutZones,
 
111
                apiservertesting.WithSpaces,
 
112
                apiservertesting.WithSubnets)
 
113
 
 
114
        apiservertesting.SharedStub.SetErrors(
 
115
                nil, // Backing.AvailabilityZones
 
116
                nil, // Backing.ModelConfig
 
117
                nil, // Backing.CloudSpec
 
118
                nil, // Provider.Open
 
119
                nil, // ZonedEnviron.AvailabilityZones
 
120
                errors.NotSupportedf("setting"), // Backing.SetAvailabilityZones
 
121
        )
 
122
 
 
123
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
124
        c.Assert(err, gc.ErrorMatches,
 
125
                `cannot update known zones: setting not supported`,
 
126
        )
 
127
        // Verify the cause is not obscured.
 
128
        c.Assert(err, jc.Satisfies, errors.IsNotSupported)
 
129
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
130
 
 
131
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
132
                apiservertesting.BackingCall("AvailabilityZones"),
 
133
                apiservertesting.BackingCall("ModelConfig"),
 
134
                apiservertesting.BackingCall("CloudSpec"),
 
135
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
136
                apiservertesting.ZonedEnvironCall("AvailabilityZones"),
 
137
                apiservertesting.BackingCall("SetAvailabilityZones", apiservertesting.ProviderInstance.Zones),
 
138
        )
 
139
}
 
140
 
 
141
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndFetchingZonesFails(c *gc.C) {
 
142
        apiservertesting.BackingInstance.SetUp(
 
143
                c,
 
144
                apiservertesting.StubZonedEnvironName,
 
145
                apiservertesting.WithoutZones,
 
146
                apiservertesting.WithSpaces,
 
147
                apiservertesting.WithSubnets)
 
148
 
 
149
        apiservertesting.SharedStub.SetErrors(
 
150
                nil, // Backing.AvailabilityZones
 
151
                nil, // Backing.ModelConfig
 
152
                nil, // Backing.CloudSpec
 
153
                nil, // Provider.Open
 
154
                errors.NotValidf("foo"), // ZonedEnviron.AvailabilityZones
 
155
        )
 
156
 
 
157
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
158
        c.Assert(err, gc.ErrorMatches,
 
159
                `cannot update known zones: foo not valid`,
 
160
        )
 
161
        // Verify the cause is not obscured.
 
162
        c.Assert(err, jc.Satisfies, errors.IsNotValid)
 
163
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
164
 
 
165
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
166
                apiservertesting.BackingCall("AvailabilityZones"),
 
167
                apiservertesting.BackingCall("ModelConfig"),
 
168
                apiservertesting.BackingCall("CloudSpec"),
 
169
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
170
                apiservertesting.ZonedEnvironCall("AvailabilityZones"),
 
171
        )
 
172
}
 
173
 
 
174
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndModelConfigFails(c *gc.C) {
 
175
        apiservertesting.BackingInstance.SetUp(
 
176
                c,
 
177
                apiservertesting.StubZonedEnvironName,
 
178
                apiservertesting.WithoutZones,
 
179
                apiservertesting.WithSpaces,
 
180
                apiservertesting.WithSubnets)
 
181
 
 
182
        apiservertesting.SharedStub.SetErrors(
 
183
                nil, // Backing.AvailabilityZones
 
184
                errors.NotFoundf("config"), // Backing.ModelConfig
 
185
        )
 
186
 
 
187
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
188
        c.Assert(err, gc.ErrorMatches,
 
189
                `cannot update known zones: opening environment: config not found`,
 
190
        )
 
191
        // Verify the cause is not obscured.
 
192
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
 
193
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
194
 
 
195
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
196
                apiservertesting.BackingCall("AvailabilityZones"),
 
197
                apiservertesting.BackingCall("ModelConfig"),
 
198
        )
 
199
}
 
200
 
 
201
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndOpenFails(c *gc.C) {
 
202
        apiservertesting.BackingInstance.SetUp(
 
203
                c,
 
204
                apiservertesting.StubZonedEnvironName,
 
205
                apiservertesting.WithoutZones,
 
206
                apiservertesting.WithSpaces,
 
207
                apiservertesting.WithSubnets)
 
208
 
 
209
        apiservertesting.SharedStub.SetErrors(
 
210
                nil, // Backing.AvailabilityZones
 
211
                nil, // Backing.ModelConfig
 
212
                nil, // Backing.CloudSpec
 
213
                errors.NotValidf("config"), // Provider.Open
 
214
        )
 
215
 
 
216
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
217
        c.Assert(err, gc.ErrorMatches,
 
218
                `cannot update known zones: opening environment: config not valid`,
 
219
        )
 
220
        // Verify the cause is not obscured.
 
221
        c.Assert(err, jc.Satisfies, errors.IsNotValid)
 
222
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
223
 
 
224
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
225
                apiservertesting.BackingCall("AvailabilityZones"),
 
226
                apiservertesting.BackingCall("ModelConfig"),
 
227
                apiservertesting.BackingCall("CloudSpec"),
 
228
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
229
        )
 
230
}
 
231
 
 
232
func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndZonesNotSupported(c *gc.C) {
 
233
        // ZonedEnviron not supported
 
234
        apiservertesting.BackingInstance.SetUp(
 
235
                c,
 
236
                apiservertesting.StubEnvironName,
 
237
                apiservertesting.WithoutZones,
 
238
                apiservertesting.WithSpaces,
 
239
                apiservertesting.WithSubnets)
 
240
 
 
241
        results, err := networkingcommon.AllZones(apiservertesting.BackingInstance)
 
242
        c.Assert(err, gc.ErrorMatches,
 
243
                `cannot update known zones: availability zones not supported`,
 
244
        )
 
245
        // Verify the cause is not obscured.
 
246
        c.Assert(err, jc.Satisfies, errors.IsNotSupported)
 
247
        c.Assert(results, jc.DeepEquals, params.ZoneResults{})
 
248
 
 
249
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
250
                apiservertesting.BackingCall("AvailabilityZones"),
 
251
                apiservertesting.BackingCall("ModelConfig"),
 
252
                apiservertesting.BackingCall("CloudSpec"),
 
253
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
254
        )
 
255
}
 
256
 
 
257
func (s *SubnetsSuite) TestAddSubnetsParamsCombinations(c *gc.C) {
 
258
        apiservertesting.BackingInstance.SetUp(
 
259
                c,
 
260
                apiservertesting.StubNetworkingEnvironName,
 
261
                apiservertesting.WithZones,
 
262
                apiservertesting.WithSpaces,
 
263
                apiservertesting.WithSubnets)
 
264
 
 
265
        args := params.AddSubnetsParams{Subnets: []params.AddSubnetParams{{
 
266
        // nothing set; early exit: no calls
 
267
        }, {
 
268
                // neither tag nor id set: the rest is ignored; same as above
 
269
                SpaceTag: "any",
 
270
                Zones:    []string{"any", "ignored"},
 
271
        }, {
 
272
                // both tag and id set; same as above
 
273
                SubnetTag:        "any",
 
274
                SubnetProviderId: "any",
 
275
        }, {
 
276
                // lookup by id needed, no cached subnets; ModelConfig(): error
 
277
                SubnetProviderId: "any",
 
278
        }, {
 
279
                // same as above, need to cache subnets; ModelConfig(): ok; Open(): error
 
280
                SubnetProviderId: "ignored",
 
281
        }, {
 
282
                // as above, caching again; ModelConfig(), Open(): ok; Subnets(): error
 
283
                SubnetProviderId: "unimportant",
 
284
        }, {
 
285
                // exactly as above, except all 3 calls ok; cached lookup: id not found
 
286
                SubnetProviderId: "missing",
 
287
        }, {
 
288
                // cached lookup by id (no calls): not found error
 
289
                SubnetProviderId: "void",
 
290
        }, {
 
291
                // cached lookup by id: ok; parsing space tag: invalid tag error
 
292
                SubnetProviderId: "sn-deadbeef",
 
293
                SpaceTag:         "invalid",
 
294
        }, {
 
295
                // as above, but slightly different error: invalid space tag error
 
296
                SubnetProviderId: "sn-zadf00d",
 
297
                SpaceTag:         "unit-foo",
 
298
        }, {
 
299
                // as above; yet another similar error (valid tag with another kind)
 
300
                SubnetProviderId: "vlan-42",
 
301
                SpaceTag:         "unit-foo-0",
 
302
        }, {
 
303
                // invalid tag (no kind): error (no calls)
 
304
                SubnetTag: "invalid",
 
305
        }, {
 
306
                // invalid subnet tag (another kind): same as above
 
307
                SubnetTag: "application-bar",
 
308
        }, {
 
309
                // cached lookup by missing CIDR: not found error
 
310
                SubnetTag: "subnet-1.2.3.0/24",
 
311
        }, {
 
312
                // cached lookup by duplicate CIDR: multiple choices error
 
313
                SubnetTag: "subnet-10.10.0.0/24",
 
314
        }, {
 
315
                // cached lookup by CIDR with empty provider id: ok; space tag is required error
 
316
                SubnetTag: "subnet-10.20.0.0/16",
 
317
        }, {
 
318
                // cached lookup by id with invalid CIDR: cannot be added error
 
319
                SubnetProviderId: "sn-invalid",
 
320
        }, {
 
321
                // cached lookup by id with empty CIDR: cannot be added error
 
322
                SubnetProviderId: "sn-empty",
 
323
        }, {
 
324
                // cached lookup by id with incorrectly specified CIDR: cannot be added error
 
325
                SubnetProviderId: "sn-awesome",
 
326
        }, {
 
327
                // cached lookup by CIDR: ok; valid tag; caching spaces: AllSpaces(): error
 
328
                SubnetTag: "subnet-10.30.1.0/24",
 
329
                SpaceTag:  "space-unverified",
 
330
        }, {
 
331
                // exactly as above, except AllSpaces(): ok; cached lookup: space not found
 
332
                SubnetTag: "subnet-2001:db8::/32",
 
333
                SpaceTag:  "space-missing",
 
334
        }, {
 
335
                // both cached lookups (CIDR, space): ok; no provider or given zones: error
 
336
                SubnetTag: "subnet-10.42.0.0/16",
 
337
                SpaceTag:  "space-dmz",
 
338
        }, {
 
339
                // like above; with provider zones, extra given: error
 
340
                SubnetProviderId: "vlan-42",
 
341
                SpaceTag:         "space-private",
 
342
                Zones: []string{
 
343
                        "zone2",   // not allowed, existing, unavailable
 
344
                        "zone3",   // allowed, existing, available
 
345
                        "missing", // not allowed, non-existing
 
346
                        "zone3",   // duplicates are ignored (should they ?)
 
347
                        "zone1",   // not allowed, existing, available
 
348
                },
 
349
        }, {
 
350
                // like above; no provider, only given zones; caching: AllZones(): error
 
351
                SubnetTag: "subnet-10.42.0.0/16",
 
352
                SpaceTag:  "space-dmz",
 
353
                Zones:     []string{"any", "ignored"},
 
354
        }, {
 
355
                // as above, but unknown zones given: cached: AllZones(): ok; unknown zones error
 
356
                SubnetTag: "subnet-10.42.0.0/16",
 
357
                SpaceTag:  "space-dmz",
 
358
                Zones:     []string{"missing", "gone"},
 
359
        }, {
 
360
                // as above, but unknown and unavailable zones given: same error (no calls)
 
361
                SubnetTag: "subnet-10.42.0.0/16",
 
362
                SpaceTag:  "space-dmz",
 
363
                Zones:     []string{"zone4", "missing", "zone2"},
 
364
        }, {
 
365
                // as above, but unavailable zones given: Zones contains unavailable error
 
366
                SubnetTag: "subnet-10.42.0.0/16",
 
367
                SpaceTag:  "space-dmz",
 
368
                Zones:     []string{"zone2", "zone4"},
 
369
        }, {
 
370
                // as above, but available and unavailable zones given: same error as above
 
371
                SubnetTag: "subnet-10.42.0.0/16",
 
372
                SpaceTag:  "space-dmz",
 
373
                Zones:     []string{"zone4", "zone3"},
 
374
        }, {
 
375
                // everything succeeds, using caches as needed, until: AddSubnet(): error
 
376
                SubnetProviderId: "sn-ipv6",
 
377
                SpaceTag:         "space-dmz",
 
378
                Zones:            []string{"zone1"},
 
379
                // restriction of provider zones [zone1, zone3]
 
380
        }, {
 
381
                // cached lookups by CIDR, space: ok; duplicated provider id: unavailable zone2
 
382
                SubnetTag: "subnet-10.99.88.0/24",
 
383
                SpaceTag:  "space-dmz",
 
384
                Zones:     []string{"zone2"},
 
385
                // due to the duplicate ProviderId provider zones from subnet
 
386
                // with the last ProviderId=sn-deadbeef are used
 
387
                // (10.10.0.0/24); [zone2], not the 10.99.88.0/24 provider
 
388
                // zones: [zone1, zone2].
 
389
        }, {
 
390
                // same as above, but AddSubnet(): ok; success (backing verified later)
 
391
                SubnetProviderId: "sn-ipv6",
 
392
                SpaceTag:         "space-dmz",
 
393
                Zones:            []string{"zone1"},
 
394
                // restriction of provider zones [zone1, zone3]
 
395
        }, {
 
396
                // success (CIDR lookup; with provider (no given) zones): AddSubnet(): ok
 
397
                SubnetTag: "subnet-10.30.1.0/24",
 
398
                SpaceTag:  "space-private",
 
399
                // Zones not given, so provider zones are used instead: [zone3]
 
400
        }, {
 
401
                // success (id lookup; given zones match provider zones) AddSubnet(): ok
 
402
                SubnetProviderId: "sn-zadf00d",
 
403
                SpaceTag:         "space-private",
 
404
                Zones:            []string{"zone1"},
 
405
        }}}
 
406
        apiservertesting.SharedStub.SetErrors(
 
407
                // caching subnets (1st attempt): fails
 
408
                errors.NotFoundf("config"), // BackingInstance.ModelConfig (1st call)
 
409
 
 
410
                // caching subnets (2nd attepmt): fails
 
411
                nil, // BackingInstance.ModelConfig (2nd call)
 
412
                nil, // BackingInstance.CloudSpec (1st call)
 
413
                errors.NotFoundf("provider"), // ProviderInstance.Open (1st call)
 
414
 
 
415
                // caching subnets (3rd attempt): fails
 
416
                nil, // BackingInstance.ModelConfig (3rd call)
 
417
                nil, // BackingInstance.CloudSpec (2nd call)
 
418
                nil, // ProviderInstance.Open (2nd call)
 
419
                errors.NotFoundf("subnets"), // NetworkingEnvironInstance.Subnets (1st call)
 
420
 
 
421
                // caching subnets (4th attempt): succeeds
 
422
                nil, // BackingInstance.ModelConfig (4th call)
 
423
                nil, // BackingInstance.CloudSpec (3rd call)
 
424
                nil, // ProviderInstance.Open (3rd call)
 
425
                nil, // NetworkingEnvironInstance.Subnets (2nd call)
 
426
 
 
427
                // caching spaces (1st and 2nd attempts)
 
428
                errors.NotFoundf("spaces"), // BackingInstance.AllSpaces (1st call)
 
429
                nil, // BackingInstance.AllSpaces (2nd call)
 
430
 
 
431
                // cacing zones (1st and 2nd attempts)
 
432
                errors.NotFoundf("zones"), // BackingInstance.AvailabilityZones (1st call)
 
433
                nil, // BackingInstance.AvailabilityZones (2nd call)
 
434
 
 
435
                // validation done; adding subnets to backing store
 
436
                errors.NotFoundf("state"), // BackingInstance.AddSubnet (1st call)
 
437
                // the next 3 BackingInstance.AddSubnet calls succeed(2nd call)
 
438
        )
 
439
 
 
440
        expectedErrors := []struct {
 
441
                message   string
 
442
                satisfier func(error) bool
 
443
        }{
 
444
                {"either SubnetTag or SubnetProviderId is required", nil},
 
445
                {"either SubnetTag or SubnetProviderId is required", nil},
 
446
                {"SubnetTag and SubnetProviderId cannot be both set", nil},
 
447
                {"opening environment: config not found", params.IsCodeNotFound},
 
448
                {"opening environment: provider not found", params.IsCodeNotFound},
 
449
                {"cannot get provider subnets: subnets not found", params.IsCodeNotFound},
 
450
                {`subnet with CIDR "" and ProviderId "missing" not found`, params.IsCodeNotFound},
 
451
                {`subnet with CIDR "" and ProviderId "void" not found`, params.IsCodeNotFound},
 
452
                {`given SpaceTag is invalid: "invalid" is not a valid tag`, nil},
 
453
                {`given SpaceTag is invalid: "unit-foo" is not a valid unit tag`, nil},
 
454
                {`given SpaceTag is invalid: "unit-foo-0" is not a valid space tag`, nil},
 
455
                {`given SubnetTag is invalid: "invalid" is not a valid tag`, nil},
 
456
                {`given SubnetTag is invalid: "application-bar" is not a valid subnet tag`, nil},
 
457
                {`subnet with CIDR "1.2.3.0/24" not found`, params.IsCodeNotFound},
 
458
                {
 
459
                        `multiple subnets with CIDR "10.10.0.0/24": ` +
 
460
                                `retry using ProviderId from: "sn-deadbeef", "sn-zadf00d"`, nil,
 
461
                },
 
462
                {"SpaceTag is required", nil},
 
463
                {`subnet with CIDR "invalid" and ProviderId "sn-invalid": invalid CIDR`, nil},
 
464
                {`subnet with ProviderId "sn-empty": empty CIDR`, nil},
 
465
                {
 
466
                        `subnet with ProviderId "sn-awesome": ` +
 
467
                                `incorrect CIDR format "0.1.2.3/4", expected "0.0.0.0/4"`, nil,
 
468
                },
 
469
                {"cannot validate given SpaceTag: spaces not found", params.IsCodeNotFound},
 
470
                {`space "missing" not found`, params.IsCodeNotFound},
 
471
                {"Zones cannot be discovered from the provider and must be set", nil},
 
472
                {`Zones contain zones not allowed by the provider: "missing", "zone1", "zone2"`, nil},
 
473
                {"given Zones cannot be validated: zones not found", params.IsCodeNotFound},
 
474
                {`Zones contain unknown zones: "gone", "missing"`, nil},
 
475
                {`Zones contain unknown zones: "missing"`, nil},
 
476
                {`Zones contain unavailable zones: "zone2", "zone4"`, nil},
 
477
                {`Zones contain unavailable zones: "zone4"`, nil},
 
478
                {"state not found", params.IsCodeNotFound},
 
479
                {`Zones contain unavailable zones: "zone2"`, nil},
 
480
                {"", nil},
 
481
                {"", nil},
 
482
                {"", nil},
 
483
        }
 
484
        expectedBackingInfos := []networkingcommon.BackingSubnetInfo{{
 
485
                ProviderId:        "sn-ipv6",
 
486
                CIDR:              "2001:db8::/32",
 
487
                VLANTag:           0,
 
488
                AvailabilityZones: []string{"zone1"},
 
489
                SpaceName:         "dmz",
 
490
        }, {
 
491
                ProviderId:        "vlan-42",
 
492
                CIDR:              "10.30.1.0/24",
 
493
                VLANTag:           42,
 
494
                AvailabilityZones: []string{"zone3"},
 
495
                SpaceName:         "private",
 
496
        }, {
 
497
                ProviderId:        "sn-zadf00d",
 
498
                CIDR:              "10.10.0.0/24",
 
499
                VLANTag:           0,
 
500
                AvailabilityZones: []string{"zone1"},
 
501
                SpaceName:         "private",
 
502
        }}
 
503
        c.Check(expectedErrors, gc.HasLen, len(args.Subnets))
 
504
        results, err := networkingcommon.AddSubnets(apiservertesting.BackingInstance, args)
 
505
        c.Assert(err, jc.ErrorIsNil)
 
506
        c.Assert(len(results.Results), gc.Equals, len(args.Subnets))
 
507
        for i, result := range results.Results {
 
508
                c.Logf("result #%d: expected: %q", i, expectedErrors[i].message)
 
509
                if expectedErrors[i].message == "" {
 
510
                        if !c.Check(result.Error, gc.IsNil) {
 
511
                                c.Logf("unexpected error: %v; args: %#v", result.Error, args.Subnets[i])
 
512
                        }
 
513
                        continue
 
514
                }
 
515
                if !c.Check(result.Error, gc.NotNil) {
 
516
                        c.Logf("unexpected success; args: %#v", args.Subnets[i])
 
517
                        continue
 
518
                }
 
519
                c.Check(result.Error.Message, gc.Equals, expectedErrors[i].message)
 
520
                if expectedErrors[i].satisfier != nil {
 
521
                        c.Check(result.Error, jc.Satisfies, expectedErrors[i].satisfier)
 
522
                } else {
 
523
                        c.Check(result.Error.Code, gc.Equals, "")
 
524
                }
 
525
        }
 
526
 
 
527
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub,
 
528
                // caching subnets (1st attempt): fails
 
529
                apiservertesting.BackingCall("ModelConfig"),
 
530
 
 
531
                // caching subnets (2nd attepmt): fails
 
532
                apiservertesting.BackingCall("ModelConfig"),
 
533
                apiservertesting.BackingCall("CloudSpec"),
 
534
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
535
 
 
536
                // caching subnets (3rd attempt): fails
 
537
                apiservertesting.BackingCall("ModelConfig"),
 
538
                apiservertesting.BackingCall("CloudSpec"),
 
539
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
540
                apiservertesting.NetworkingEnvironCall("Subnets", instance.UnknownId, []network.Id(nil)),
 
541
 
 
542
                // caching subnets (4th attempt): succeeds
 
543
                apiservertesting.BackingCall("ModelConfig"),
 
544
                apiservertesting.BackingCall("CloudSpec"),
 
545
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
546
                apiservertesting.NetworkingEnvironCall("Subnets", instance.UnknownId, []network.Id(nil)),
 
547
 
 
548
                // caching spaces (1st and 2nd attempts)
 
549
                apiservertesting.BackingCall("AllSpaces"),
 
550
                apiservertesting.BackingCall("AllSpaces"),
 
551
 
 
552
                // caching zones (1st and 2nd attempts)
 
553
                apiservertesting.BackingCall("AvailabilityZones"),
 
554
                apiservertesting.BackingCall("AvailabilityZones"),
 
555
 
 
556
                // validation done; adding subnets to backing store
 
557
                apiservertesting.BackingCall("AddSubnet", expectedBackingInfos[0]),
 
558
                apiservertesting.BackingCall("AddSubnet", expectedBackingInfos[0]),
 
559
                apiservertesting.BackingCall("AddSubnet", expectedBackingInfos[1]),
 
560
                apiservertesting.BackingCall("AddSubnet", expectedBackingInfos[2]),
 
561
        )
 
562
        apiservertesting.ResetStub(apiservertesting.SharedStub)
 
563
 
 
564
        // Finally, check that no params yields no results.
 
565
        results, err = networkingcommon.AddSubnets(apiservertesting.BackingInstance, params.AddSubnetsParams{})
 
566
        c.Assert(err, jc.ErrorIsNil)
 
567
        c.Assert(results.Results, gc.NotNil)
 
568
        c.Assert(results.Results, gc.HasLen, 0)
 
569
 
 
570
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub)
 
571
}
 
572
 
 
573
func (s *SubnetsSuite) CheckAddSubnetsFails(
 
574
        c *gc.C, envName string,
 
575
        withZones, withSpaces, withSubnets apiservertesting.SetUpFlag,
 
576
        expectedError string,
 
577
        expectedSatisfies func(error) bool,
 
578
) {
 
579
        apiservertesting.BackingInstance.SetUp(c, envName, withZones, withSpaces, withSubnets)
 
580
 
 
581
        // These calls always happen.
 
582
        expectedCalls := []apiservertesting.StubMethodCall{
 
583
                apiservertesting.BackingCall("ModelConfig"),
 
584
                apiservertesting.BackingCall("CloudSpec"),
 
585
                apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
586
        }
 
587
 
 
588
        // Subnets is also always called. but the receiver is different.
 
589
        switch envName {
 
590
        case apiservertesting.StubNetworkingEnvironName:
 
591
                expectedCalls = append(
 
592
                        expectedCalls,
 
593
                        apiservertesting.NetworkingEnvironCall("Subnets", instance.UnknownId, []network.Id(nil)),
 
594
                )
 
595
        case apiservertesting.StubZonedNetworkingEnvironName:
 
596
                expectedCalls = append(
 
597
                        expectedCalls,
 
598
                        apiservertesting.ZonedNetworkingEnvironCall("Subnets", instance.UnknownId, []network.Id(nil)),
 
599
                )
 
600
        }
 
601
 
 
602
        if !withSubnets {
 
603
                // Set provider subnets to empty for this test.
 
604
                originalSubnets := make([]network.SubnetInfo, len(apiservertesting.ProviderInstance.Subnets))
 
605
                copy(originalSubnets, apiservertesting.ProviderInstance.Subnets)
 
606
                apiservertesting.ProviderInstance.Subnets = []network.SubnetInfo{}
 
607
 
 
608
                defer func() {
 
609
                        apiservertesting.ProviderInstance.Subnets = make([]network.SubnetInfo, len(originalSubnets))
 
610
                        copy(apiservertesting.ProviderInstance.Subnets, originalSubnets)
 
611
                }()
 
612
 
 
613
                if envName == apiservertesting.StubEnvironName || envName == apiservertesting.StubNetworkingEnvironName {
 
614
                        // networking is either not supported or no subnets are
 
615
                        // defined, so expected the same calls for each of the two
 
616
                        // arguments to AddSubnets() below.
 
617
                        expectedCalls = append(expectedCalls, expectedCalls...)
 
618
                }
 
619
        } else {
 
620
                // Having subnets implies spaces will be cached as well.
 
621
                expectedCalls = append(expectedCalls, apiservertesting.BackingCall("AllSpaces"))
 
622
        }
 
623
 
 
624
        if withSpaces && withSubnets {
 
625
                // Having both subnets and spaces means we'll also cache zones.
 
626
                expectedCalls = append(expectedCalls, apiservertesting.BackingCall("AvailabilityZones"))
 
627
        }
 
628
 
 
629
        if !withZones && withSpaces {
 
630
                // Set provider zones to empty for this test.
 
631
                originalZones := make([]providercommon.AvailabilityZone, len(apiservertesting.ProviderInstance.Zones))
 
632
                copy(originalZones, apiservertesting.ProviderInstance.Zones)
 
633
                apiservertesting.ProviderInstance.Zones = []providercommon.AvailabilityZone{}
 
634
 
 
635
                defer func() {
 
636
                        apiservertesting.ProviderInstance.Zones = make([]providercommon.AvailabilityZone, len(originalZones))
 
637
                        copy(apiservertesting.ProviderInstance.Zones, originalZones)
 
638
                }()
 
639
 
 
640
                // updateZones tries to constructs a ZonedEnviron with these calls.
 
641
                zoneCalls := append([]apiservertesting.StubMethodCall{},
 
642
                        apiservertesting.BackingCall("ModelConfig"),
 
643
                        apiservertesting.BackingCall("CloudSpec"),
 
644
                        apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig),
 
645
                )
 
646
                // Receiver can differ according to envName, but
 
647
                // AvailabilityZones() will be called on either receiver.
 
648
                switch envName {
 
649
                case apiservertesting.StubZonedEnvironName:
 
650
                        zoneCalls = append(
 
651
                                zoneCalls,
 
652
                                apiservertesting.ZonedEnvironCall("AvailabilityZones"),
 
653
                        )
 
654
                case apiservertesting.StubZonedNetworkingEnvironName:
 
655
                        zoneCalls = append(
 
656
                                zoneCalls,
 
657
                                apiservertesting.ZonedNetworkingEnvironCall("AvailabilityZones"),
 
658
                        )
 
659
                }
 
660
                // Finally after caching provider zones backing zones are
 
661
                // updated.
 
662
                zoneCalls = append(
 
663
                        zoneCalls,
 
664
                        apiservertesting.BackingCall("SetAvailabilityZones", apiservertesting.ProviderInstance.Zones),
 
665
                )
 
666
 
 
667
                // Now, because we have 2 arguments to AddSubnets() below, we
 
668
                // need to expect the same zoneCalls twice, with a
 
669
                // AvailabilityZones backing lookup between them.
 
670
                expectedCalls = append(expectedCalls, zoneCalls...)
 
671
                expectedCalls = append(expectedCalls, apiservertesting.BackingCall("AvailabilityZones"))
 
672
                expectedCalls = append(expectedCalls, zoneCalls...)
 
673
        }
 
674
 
 
675
        // Pass 2 arguments covering all cases we need.
 
676
        args := params.AddSubnetsParams{
 
677
                Subnets: []params.AddSubnetParams{{
 
678
                        SubnetTag: "subnet-10.42.0.0/16",
 
679
                        SpaceTag:  "space-dmz",
 
680
                        Zones:     []string{"zone1"},
 
681
                }, {
 
682
                        SubnetProviderId: "vlan-42",
 
683
                        SpaceTag:         "space-private",
 
684
                        Zones:            []string{"zone3"},
 
685
                }},
 
686
        }
 
687
        results, err := networkingcommon.AddSubnets(apiservertesting.BackingInstance, args)
 
688
        c.Assert(err, jc.ErrorIsNil)
 
689
        c.Assert(results.Results, gc.HasLen, len(args.Subnets))
 
690
        for _, result := range results.Results {
 
691
                if !c.Check(result.Error, gc.NotNil) {
 
692
                        continue
 
693
                }
 
694
                c.Check(result.Error, gc.ErrorMatches, expectedError)
 
695
                if expectedSatisfies != nil {
 
696
                        c.Check(result.Error, jc.Satisfies, expectedSatisfies)
 
697
                } else {
 
698
                        c.Check(result.Error.Code, gc.Equals, "")
 
699
                }
 
700
        }
 
701
 
 
702
        apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, expectedCalls...)
 
703
}
 
704
 
 
705
func (s *SubnetsSuite) TestAddSubnetsWithNoProviderSubnetsFails(c *gc.C) {
 
706
        s.CheckAddSubnetsFails(
 
707
                c, apiservertesting.StubNetworkingEnvironName,
 
708
                apiservertesting.WithoutZones, apiservertesting.WithoutSpaces, apiservertesting.WithoutSubnets,
 
709
                "no subnets defined",
 
710
                nil,
 
711
        )
 
712
}
 
713
 
 
714
func (s *SubnetsSuite) TestAddSubnetsWithNoBackingSpacesFails(c *gc.C) {
 
715
        s.CheckAddSubnetsFails(
 
716
                c, apiservertesting.StubNetworkingEnvironName,
 
717
                apiservertesting.WithoutZones, apiservertesting.WithoutSpaces, apiservertesting.WithSubnets,
 
718
                "no spaces defined",
 
719
                nil,
 
720
        )
 
721
}
 
722
 
 
723
func (s *SubnetsSuite) TestAddSubnetsWithNoProviderZonesFails(c *gc.C) {
 
724
        s.CheckAddSubnetsFails(
 
725
                c, apiservertesting.StubZonedNetworkingEnvironName,
 
726
                apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets,
 
727
                "no zones defined",
 
728
                nil,
 
729
        )
 
730
}
 
731
 
 
732
func (s *SubnetsSuite) TestAddSubnetsWhenNetworkingEnvironNotSupported(c *gc.C) {
 
733
        s.CheckAddSubnetsFails(
 
734
                c, apiservertesting.StubEnvironName,
 
735
                apiservertesting.WithoutZones, apiservertesting.WithoutSpaces, apiservertesting.WithoutSubnets,
 
736
                "model networking features not supported",
 
737
                params.IsCodeNotSupported,
 
738
        )
 
739
}
 
740
 
 
741
func (s *SubnetsSuite) TestListSubnetsAndFiltering(c *gc.C) {
 
742
        expected := []params.Subnet{{
 
743
                CIDR:       "10.10.0.0/24",
 
744
                ProviderId: "sn-zadf00d",
 
745
                VLANTag:    0,
 
746
                Life:       "",
 
747
                SpaceTag:   "space-private",
 
748
                Zones:      []string{"zone1"},
 
749
                Status:     "",
 
750
        }, {
 
751
                CIDR:       "2001:db8::/32",
 
752
                ProviderId: "sn-ipv6",
 
753
                VLANTag:    0,
 
754
                Life:       "",
 
755
                SpaceTag:   "space-dmz",
 
756
                Zones:      []string{"zone1", "zone3"},
 
757
                Status:     "",
 
758
        }}
 
759
        // No filtering.
 
760
        args := params.SubnetsFilters{}
 
761
        subnets, err := networkingcommon.ListSubnets(apiservertesting.BackingInstance, args)
 
762
        c.Assert(err, jc.ErrorIsNil)
 
763
        c.Assert(subnets.Results, jc.DeepEquals, expected)
 
764
 
 
765
        // Filter by space only.
 
766
        args.SpaceTag = "space-dmz"
 
767
        subnets, err = networkingcommon.ListSubnets(apiservertesting.BackingInstance, args)
 
768
        c.Assert(err, jc.ErrorIsNil)
 
769
        c.Assert(subnets.Results, jc.DeepEquals, expected[1:])
 
770
 
 
771
        // Filter by zone only.
 
772
        args.SpaceTag = ""
 
773
        args.Zone = "zone3"
 
774
        subnets, err = networkingcommon.ListSubnets(apiservertesting.BackingInstance, args)
 
775
        c.Assert(err, jc.ErrorIsNil)
 
776
        c.Assert(subnets.Results, jc.DeepEquals, expected[1:])
 
777
 
 
778
        // Filter by both space and zone.
 
779
        args.SpaceTag = "space-private"
 
780
        args.Zone = "zone1"
 
781
        subnets, err = networkingcommon.ListSubnets(apiservertesting.BackingInstance, args)
 
782
        c.Assert(err, jc.ErrorIsNil)
 
783
        c.Assert(subnets.Results, jc.DeepEquals, expected[:1])
 
784
}
 
785
 
 
786
func (s *SubnetsSuite) TestListSubnetsInvalidSpaceTag(c *gc.C) {
 
787
        args := params.SubnetsFilters{SpaceTag: "invalid"}
 
788
        _, err := networkingcommon.ListSubnets(apiservertesting.BackingInstance, args)
 
789
        c.Assert(err, gc.ErrorMatches, `"invalid" is not a valid tag`)
 
790
}
 
791
 
 
792
func (s *SubnetsSuite) TestListSubnetsAllSubnetError(c *gc.C) {
 
793
        boom := errors.New("no subnets for you")
 
794
        apiservertesting.BackingInstance.SetErrors(boom)
 
795
        _, err := networkingcommon.ListSubnets(apiservertesting.BackingInstance, params.SubnetsFilters{})
 
796
        c.Assert(err, gc.ErrorMatches, "no subnets for you")
 
797
}