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