github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/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/collections/set" 8 "github.com/juju/errors" 9 "github.com/juju/names/v5" 10 jc "github.com/juju/testing/checkers" 11 "go.uber.org/mock/gomock" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/apiserver/common" 15 "github.com/juju/juju/apiserver/common/networkingcommon" 16 networkcommonmocks "github.com/juju/juju/apiserver/common/networkingcommon/mocks" 17 apiservererrors "github.com/juju/juju/apiserver/errors" 18 facademocks "github.com/juju/juju/apiserver/facade/mocks" 19 "github.com/juju/juju/apiserver/facades/client/subnets" 20 "github.com/juju/juju/apiserver/facades/client/subnets/mocks" 21 apiservertesting "github.com/juju/juju/apiserver/testing" 22 "github.com/juju/juju/core/life" 23 "github.com/juju/juju/core/network" 24 "github.com/juju/juju/environs/context" 25 "github.com/juju/juju/rpc/params" 26 "github.com/juju/juju/state" 27 coretesting "github.com/juju/juju/testing" 28 ) 29 30 // SubnetSuite uses mocks for testing. 31 // All future facade tests should be added to this suite. 32 type SubnetSuite struct { 33 mockBacking *mocks.MockBacking 34 mockResource *facademocks.MockResources 35 mockCloudCallContext *context.CloudCallContext 36 mockAuthorizer *facademocks.MockAuthorizer 37 38 api *subnets.API 39 } 40 41 var _ = gc.Suite(&SubnetSuite{}) 42 43 func (s *SubnetSuite) TearDownTest(c *gc.C) { 44 s.api = nil 45 } 46 47 func (s *SubnetSuite) TestSubnetsByCIDR(c *gc.C) { 48 ctrl := s.setupSubnetsAPI(c) 49 defer ctrl.Finish() 50 51 cidrs := []string{"10.10.10.0/24", "10.10.20.0/24", "not-a-cidr"} 52 53 subnet := networkcommonmocks.NewMockBackingSubnet(ctrl) 54 sExp := subnet.EXPECT() 55 sExp.ID().Return("1") 56 sExp.CIDR().Return("10.10.20.0/24") 57 sExp.SpaceName().Return("space") 58 sExp.VLANTag().Return(0) 59 sExp.ProviderId().Return(network.Id("0")) 60 sExp.ProviderNetworkId().Return(network.Id("1")) 61 sExp.AvailabilityZones().Return([]string{"bar", "bam"}) 62 sExp.Life().Return(state.Alive) 63 64 bExp := s.mockBacking.EXPECT() 65 gomock.InOrder( 66 bExp.SubnetsByCIDR(cidrs[0]).Return(nil, errors.New("bad-mongo")), 67 bExp.SubnetsByCIDR(cidrs[1]).Return([]networkingcommon.BackingSubnet{subnet}, nil), 68 // No call for cidrs[2]; the input is invalidated. 69 ) 70 71 arg := params.CIDRParams{CIDRS: cidrs} 72 res, err := s.api.SubnetsByCIDR(arg) 73 c.Assert(err, jc.ErrorIsNil) 74 75 results := res.Results 76 c.Assert(results, gc.HasLen, 3) 77 78 c.Check(results[0].Error.Message, gc.Equals, "bad-mongo") 79 c.Check(results[1].Subnets, gc.HasLen, 1) 80 c.Check(results[2].Error.Message, gc.Equals, `CIDR "not-a-cidr" not valid`) 81 } 82 83 func (s *SubnetSuite) setupSubnetsAPI(c *gc.C) *gomock.Controller { 84 ctrl := gomock.NewController(c) 85 s.mockResource = facademocks.NewMockResources(ctrl) 86 s.mockCloudCallContext = context.NewEmptyCloudCallContext() 87 s.mockBacking = mocks.NewMockBacking(ctrl) 88 89 s.mockAuthorizer = facademocks.NewMockAuthorizer(ctrl) 90 s.mockAuthorizer.EXPECT().HasPermission(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 91 s.mockAuthorizer.EXPECT().AuthClient().Return(true) 92 93 s.mockBacking.EXPECT().ModelTag().Return(names.NewModelTag("123")) 94 95 var err error 96 s.api, err = subnets.NewAPIWithBacking(s.mockBacking, s.mockCloudCallContext, s.mockResource, s.mockAuthorizer) 97 c.Assert(err, jc.ErrorIsNil) 98 return ctrl 99 } 100 101 // SubnetsSuite is the old testing suite based on testing stubs. 102 // This should be phased out in favour of mock-based tests. 103 // The testing network infrastructure should also be removed as soon as can be 104 // managed. 105 type SubnetsSuite struct { 106 coretesting.BaseSuite 107 apiservertesting.StubNetwork 108 109 resources *common.Resources 110 authorizer apiservertesting.FakeAuthorizer 111 facade *subnets.API 112 113 callContext context.ProviderCallContext 114 } 115 116 type stubBacking struct { 117 *apiservertesting.StubBacking 118 } 119 120 func (sb *stubBacking) SubnetsByCIDR(_ string) ([]networkingcommon.BackingSubnet, error) { 121 panic("should not be called") 122 } 123 124 var _ = gc.Suite(&SubnetsSuite{}) 125 126 func (s *SubnetsSuite) SetUpSuite(c *gc.C) { 127 s.StubNetwork.SetUpSuite(c) 128 s.BaseSuite.SetUpSuite(c) 129 } 130 131 func (s *SubnetsSuite) TearDownSuite(c *gc.C) { 132 s.BaseSuite.TearDownSuite(c) 133 } 134 135 func (s *SubnetsSuite) SetUpTest(c *gc.C) { 136 s.BaseSuite.SetUpTest(c) 137 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 138 139 s.resources = common.NewResources() 140 s.authorizer = apiservertesting.FakeAuthorizer{ 141 Tag: names.NewUserTag("admin"), 142 Controller: false, 143 } 144 145 s.callContext = context.NewEmptyCloudCallContext() 146 var err error 147 s.facade, err = subnets.NewAPIWithBacking( 148 &stubBacking{apiservertesting.BackingInstance}, 149 s.callContext, 150 s.resources, s.authorizer, 151 ) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(s.facade, gc.NotNil) 154 } 155 156 func (s *SubnetsSuite) TearDownTest(c *gc.C) { 157 if s.resources != nil { 158 s.resources.StopAll() 159 } 160 s.BaseSuite.TearDownTest(c) 161 } 162 163 // AssertAllZonesResult makes it easier to verify AllZones results. 164 func (s *SubnetsSuite) AssertAllZonesResult(c *gc.C, got params.ZoneResults, expected network.AvailabilityZones) { 165 results := make([]params.ZoneResult, len(expected)) 166 for i, zone := range expected { 167 results[i].Name = zone.Name() 168 results[i].Available = zone.Available() 169 } 170 c.Assert(got, jc.DeepEquals, params.ZoneResults{Results: results}) 171 } 172 173 // AssertAllSpacesResult makes it easier to verify AllSpaces results. 174 func (s *SubnetsSuite) AssertAllSpacesResult(c *gc.C, got params.SpaceResults, expected []networkingcommon.BackingSpace) { 175 seen := set.Strings{} 176 results := []params.SpaceResult{} 177 for _, space := range expected { 178 if seen.Contains(space.Name()) { 179 continue 180 } 181 seen.Add(space.Name()) 182 result := params.SpaceResult{} 183 result.Tag = names.NewSpaceTag(space.Name()).String() 184 results = append(results, result) 185 } 186 c.Assert(got, jc.DeepEquals, params.SpaceResults{Results: results}) 187 } 188 189 func (s *SubnetsSuite) TestNewAPIWithBacking(c *gc.C) { 190 // Clients are allowed. 191 facade, err := subnets.NewAPIWithBacking( 192 &stubBacking{apiservertesting.BackingInstance}, 193 s.callContext, 194 s.resources, s.authorizer, 195 ) 196 c.Assert(err, jc.ErrorIsNil) 197 c.Assert(facade, gc.NotNil) 198 // No calls so far. 199 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub) 200 201 // Agents are not allowed 202 agentAuthorizer := s.authorizer 203 agentAuthorizer.Tag = names.NewMachineTag("42") 204 facade, err = subnets.NewAPIWithBacking( 205 &stubBacking{apiservertesting.BackingInstance}, 206 s.callContext, 207 s.resources, agentAuthorizer, 208 ) 209 c.Assert(err, jc.DeepEquals, apiservererrors.ErrPerm) 210 c.Assert(facade, gc.IsNil) 211 // No calls so far. 212 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub) 213 } 214 215 func (s *SubnetsSuite) TestAllZonesWhenBackingAvailabilityZonesFails(c *gc.C) { 216 apiservertesting.SharedStub.SetErrors(errors.NotSupportedf("zones")) 217 218 results, err := s.facade.AllZones() 219 c.Assert(err, gc.ErrorMatches, "zones not supported") 220 // Verify the cause is not obscured. 221 c.Assert(err, jc.Satisfies, errors.IsNotSupported) 222 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 223 224 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 225 apiservertesting.BackingCall("AvailabilityZones"), 226 ) 227 } 228 229 func (s *SubnetsSuite) TestAllZonesUsesBackingZonesWhenAvailable(c *gc.C) { 230 results, err := s.facade.AllZones() 231 c.Assert(err, jc.ErrorIsNil) 232 s.AssertAllZonesResult(c, results, apiservertesting.BackingInstance.Zones) 233 234 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 235 apiservertesting.BackingCall("AvailabilityZones"), 236 ) 237 } 238 239 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesUpdates(c *gc.C) { 240 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 241 242 results, err := s.facade.AllZones() 243 c.Assert(err, jc.ErrorIsNil) 244 s.AssertAllZonesResult(c, results, apiservertesting.ProviderInstance.Zones) 245 246 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 247 apiservertesting.BackingCall("AvailabilityZones"), 248 apiservertesting.BackingCall("ModelConfig"), 249 apiservertesting.BackingCall("CloudSpec"), 250 apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig), 251 apiservertesting.ZonedEnvironCall("AvailabilityZones", s.callContext), 252 apiservertesting.BackingCall("SetAvailabilityZones", apiservertesting.ProviderInstance.Zones), 253 ) 254 } 255 256 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndSetFails(c *gc.C) { 257 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 258 apiservertesting.SharedStub.SetErrors( 259 nil, // Backing.AvailabilityZones 260 nil, // Backing.ModelConfig 261 nil, // Backing.CloudSpec 262 nil, // Provider.Open 263 nil, // ZonedEnviron.AvailabilityZones 264 errors.NotSupportedf("setting"), // Backing.SetAvailabilityZones 265 ) 266 267 results, err := s.facade.AllZones() 268 c.Assert(err, gc.ErrorMatches, 269 `cannot update known zones: setting not supported`, 270 ) 271 // Verify the cause is not obscured. 272 c.Assert(err, jc.Satisfies, errors.IsNotSupported) 273 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 274 275 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 276 apiservertesting.BackingCall("AvailabilityZones"), 277 apiservertesting.BackingCall("ModelConfig"), 278 apiservertesting.BackingCall("CloudSpec"), 279 apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig), 280 apiservertesting.ZonedEnvironCall("AvailabilityZones", s.callContext), 281 apiservertesting.BackingCall("SetAvailabilityZones", apiservertesting.ProviderInstance.Zones), 282 ) 283 } 284 285 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndFetchingZonesFails(c *gc.C) { 286 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 287 apiservertesting.SharedStub.SetErrors( 288 nil, // Backing.AvailabilityZones 289 nil, // Backing.ModelConfig 290 nil, // Backing.CloudSpec 291 nil, // Provider.Open 292 errors.NotValidf("foo"), // ZonedEnviron.AvailabilityZones 293 ) 294 295 results, err := s.facade.AllZones() 296 c.Assert(err, gc.ErrorMatches, 297 `cannot update known zones: foo not valid`, 298 ) 299 // Verify the cause is not obscured. 300 c.Assert(err, jc.Satisfies, errors.IsNotValid) 301 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 302 303 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 304 apiservertesting.BackingCall("AvailabilityZones"), 305 apiservertesting.BackingCall("ModelConfig"), 306 apiservertesting.BackingCall("CloudSpec"), 307 apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig), 308 apiservertesting.ZonedEnvironCall("AvailabilityZones", s.callContext), 309 ) 310 } 311 312 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndModelConfigFails(c *gc.C) { 313 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 314 apiservertesting.SharedStub.SetErrors( 315 nil, // Backing.AvailabilityZones 316 errors.NotFoundf("config"), // Backing.ModelConfig 317 ) 318 319 results, err := s.facade.AllZones() 320 c.Assert(err, gc.ErrorMatches, 321 `cannot update known zones: opening environment: retrieving model config: config not found`, 322 ) 323 // Verify the cause is not obscured. 324 c.Assert(err, jc.Satisfies, errors.IsNotFound) 325 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 326 327 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 328 apiservertesting.BackingCall("AvailabilityZones"), 329 apiservertesting.BackingCall("ModelConfig"), 330 ) 331 } 332 333 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndOpenFails(c *gc.C) { 334 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubZonedEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 335 apiservertesting.SharedStub.SetErrors( 336 nil, // Backing.AvailabilityZones 337 nil, // Backing.ModelConfig 338 nil, // Backing.CloudSpec 339 errors.NotValidf("config"), // Provider.Open 340 ) 341 342 results, err := s.facade.AllZones() 343 c.Assert(err, gc.ErrorMatches, 344 `cannot update known zones: opening environment: creating environ for model \"stub-zoned-environ\" \(.*\): config not valid`, 345 ) 346 // Verify the cause is not obscured. 347 c.Assert(err, jc.Satisfies, errors.IsNotValid) 348 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 349 350 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 351 apiservertesting.BackingCall("AvailabilityZones"), 352 apiservertesting.BackingCall("ModelConfig"), 353 apiservertesting.BackingCall("CloudSpec"), 354 apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig), 355 ) 356 } 357 358 func (s *SubnetsSuite) TestAllZonesWithNoBackingZonesAndZonesNotSupported(c *gc.C) { 359 apiservertesting.BackingInstance.SetUp(c, apiservertesting.StubEnvironName, apiservertesting.WithoutZones, apiservertesting.WithSpaces, apiservertesting.WithSubnets) 360 // ZonedEnviron not supported 361 362 results, err := s.facade.AllZones() 363 c.Assert(err, gc.ErrorMatches, 364 `cannot update known zones: availability zones not supported`, 365 ) 366 // Verify the cause is not obscured. 367 c.Assert(err, jc.Satisfies, errors.IsNotSupported) 368 c.Assert(results, jc.DeepEquals, params.ZoneResults{}) 369 370 apiservertesting.CheckMethodCalls(c, apiservertesting.SharedStub, 371 apiservertesting.BackingCall("AvailabilityZones"), 372 apiservertesting.BackingCall("ModelConfig"), 373 apiservertesting.BackingCall("CloudSpec"), 374 apiservertesting.ProviderCall("Open", apiservertesting.BackingInstance.EnvConfig), 375 ) 376 } 377 378 func (s *SubnetsSuite) TestListSubnetsAndFiltering(c *gc.C) { 379 expected := []params.Subnet{{ 380 CIDR: "10.10.0.0/24", 381 ProviderId: "sn-zadf00d", 382 ProviderNetworkId: "godspeed", 383 VLANTag: 0, 384 Life: life.Alive, 385 SpaceTag: "space-private", 386 Zones: []string{"zone1"}, 387 }, { 388 CIDR: "2001:db8::/32", 389 ProviderId: "sn-ipv6", 390 ProviderNetworkId: "", 391 VLANTag: 0, 392 Life: life.Alive, 393 SpaceTag: "space-dmz", 394 Zones: []string{"zone1", "zone3"}, 395 }} 396 // No filtering. 397 args := params.SubnetsFilters{} 398 subnets, err := s.facade.ListSubnets(args) 399 c.Assert(err, jc.ErrorIsNil) 400 c.Assert(subnets.Results, jc.DeepEquals, expected) 401 402 // Filter by space only. 403 args.SpaceTag = "space-dmz" 404 subnets, err = s.facade.ListSubnets(args) 405 c.Assert(err, jc.ErrorIsNil) 406 c.Assert(subnets.Results, jc.DeepEquals, expected[1:]) 407 408 // Filter by zone only. 409 args.SpaceTag = "" 410 args.Zone = "zone3" 411 subnets, err = s.facade.ListSubnets(args) 412 c.Assert(err, jc.ErrorIsNil) 413 c.Assert(subnets.Results, jc.DeepEquals, expected[1:]) 414 415 // Filter by both space and zone. 416 args.SpaceTag = "space-private" 417 args.Zone = "zone1" 418 subnets, err = s.facade.ListSubnets(args) 419 c.Assert(err, jc.ErrorIsNil) 420 c.Assert(subnets.Results, jc.DeepEquals, expected[:1]) 421 } 422 423 func (s *SubnetsSuite) TestListSubnetsInvalidSpaceTag(c *gc.C) { 424 args := params.SubnetsFilters{SpaceTag: "invalid"} 425 _, err := s.facade.ListSubnets(args) 426 c.Assert(err, gc.ErrorMatches, `"invalid" is not a valid tag`) 427 } 428 429 func (s *SubnetsSuite) TestListSubnetsAllSubnetError(c *gc.C) { 430 boom := errors.New("no subnets for you") 431 apiservertesting.BackingInstance.SetErrors(boom) 432 _, err := s.facade.ListSubnets(params.SubnetsFilters{}) 433 c.Assert(err, gc.ErrorMatches, "no subnets for you") 434 }