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