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