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