github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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.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 }