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