github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/subnet_test.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network_test 5 6 import ( 7 "net" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/core/network" 15 ) 16 17 type subnetSuite struct { 18 testing.IsolationSuite 19 } 20 21 var _ = gc.Suite(&subnetSuite{}) 22 23 func (*subnetSuite) TestFindSubnetIDsForAZ(c *gc.C) { 24 testCases := []struct { 25 name string 26 zoneName string 27 subnetsToZones map[network.Id][]string 28 expected []network.Id 29 expectedErr func(error) bool 30 }{ 31 { 32 name: "empty", 33 zoneName: "", 34 subnetsToZones: make(map[network.Id][]string), 35 expected: make([]network.Id, 0), 36 expectedErr: errors.IsNotFound, 37 }, 38 { 39 name: "no match", 40 zoneName: "fuzz", 41 subnetsToZones: map[network.Id][]string{ 42 "bar": {"foo", "baz"}, 43 }, 44 expected: make([]network.Id, 0), 45 expectedErr: errors.IsNotFound, 46 }, 47 { 48 name: "match", 49 zoneName: "foo", 50 subnetsToZones: map[network.Id][]string{ 51 "bar": {"foo", "baz"}, 52 }, 53 expected: []network.Id{"bar"}, 54 }, 55 { 56 name: "multi-match", 57 zoneName: "foo", 58 subnetsToZones: map[network.Id][]string{ 59 "bar": {"foo", "baz"}, 60 "other": {"aaa", "foo", "xxx"}, 61 }, 62 expected: []network.Id{"bar", "other"}, 63 }, 64 { 65 name: "empty zone match", 66 zoneName: "", 67 subnetsToZones: map[network.Id][]string{ 68 "bar": {}, 69 "other": {}, 70 }, 71 expected: []network.Id{"bar", "other"}, 72 }, 73 } 74 75 for i, t := range testCases { 76 c.Logf("test %d: %s", i, t.name) 77 78 res, err := network.FindSubnetIDsForAvailabilityZone(t.zoneName, t.subnetsToZones) 79 if t.expectedErr != nil { 80 c.Check(t.expectedErr(err), jc.IsTrue) 81 } else { 82 c.Assert(err, gc.IsNil) 83 c.Check(res, gc.DeepEquals, t.expected) 84 } 85 } 86 } 87 88 func (*subnetSuite) TestFilterInFanNetwork(c *gc.C) { 89 testCases := []struct { 90 name string 91 subnets []network.Id 92 expected []network.Id 93 }{ 94 { 95 name: "empty", 96 subnets: make([]network.Id, 0), 97 expected: []network.Id(nil), 98 }, 99 { 100 name: "no match", 101 subnets: []network.Id{ 102 "aaa-bbb-ccc", 103 "xxx-yyy-zzz", 104 }, 105 expected: []network.Id{ 106 "aaa-bbb-ccc", 107 "xxx-yyy-zzz", 108 }, 109 }, 110 { 111 name: "match", 112 subnets: []network.Id{ 113 "aaa-bbb-ccc", 114 "foo-INFAN-bar", 115 "xxx-yyy-zzz", 116 }, 117 expected: []network.Id{ 118 "aaa-bbb-ccc", 119 "xxx-yyy-zzz", 120 }, 121 }, 122 } 123 124 for i, t := range testCases { 125 c.Logf("test %d: %s", i, t.name) 126 127 res := network.FilterInFanNetwork(t.subnets) 128 c.Check(res, gc.DeepEquals, t.expected) 129 } 130 } 131 132 func (*subnetSuite) TestIsInFanNetwork(c *gc.C) { 133 testCases := []struct { 134 name string 135 subnet network.Id 136 expected bool 137 }{ 138 { 139 name: "empty", 140 subnet: network.Id(""), 141 expected: false, 142 }, 143 { 144 name: "no match", 145 subnet: network.Id("foo-1asd-fan-network"), 146 expected: false, 147 }, 148 { 149 name: "match", 150 subnet: network.Id("foo-1asd-INFAN-network"), 151 expected: true, 152 }, 153 } 154 155 for i, t := range testCases { 156 c.Logf("test %d: %s", i, t.name) 157 158 res := network.IsInFanNetwork(t.subnet) 159 c.Check(res, gc.Equals, t.expected) 160 } 161 } 162 163 func (*subnetSuite) TestSubnetInfosEquality(c *gc.C) { 164 s1 := network.SubnetInfos{ 165 {ID: "1"}, 166 {ID: "2"}, 167 } 168 169 s2 := network.SubnetInfos{ 170 {ID: "2"}, 171 {ID: "1"}, 172 } 173 174 s3 := append(s2, network.SubnetInfo{ID: "3"}) 175 176 c.Check(s1.EqualTo(s2), jc.IsTrue) 177 c.Check(s1.EqualTo(s3), jc.IsFalse) 178 } 179 180 func (*subnetSuite) TestSubnetInfosSpaceIDs(c *gc.C) { 181 s := network.SubnetInfos{ 182 {ID: "1", SpaceID: network.AlphaSpaceId}, 183 {ID: "2", SpaceID: network.AlphaSpaceId}, 184 {ID: "3", SpaceID: "666"}, 185 } 186 187 c.Check(s.SpaceIDs().SortedValues(), jc.DeepEquals, []string{network.AlphaSpaceId, "666"}) 188 } 189 190 func (*subnetSuite) TestSubnetInfosGetByUnderLayCIDR(c *gc.C) { 191 s := network.SubnetInfos{ 192 { 193 ID: "1", 194 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"}, 195 }, 196 { 197 ID: "2", 198 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"}, 199 }, 200 { 201 ID: "3", 202 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"}, 203 }, 204 } 205 206 _, err := s.GetByUnderlayCIDR("invalid") 207 c.Check(err, jc.Satisfies, errors.IsNotValid) 208 209 overlays, err := s.GetByUnderlayCIDR(s[0].FanLocalUnderlay()) 210 c.Assert(err, jc.ErrorIsNil) 211 c.Check(overlays, gc.DeepEquals, network.SubnetInfos{s[0]}) 212 213 overlays, err = s.GetByUnderlayCIDR(s[1].FanLocalUnderlay()) 214 c.Assert(err, jc.ErrorIsNil) 215 c.Check(overlays, gc.DeepEquals, network.SubnetInfos{s[1], s[2]}) 216 217 overlays, err = s.GetByUnderlayCIDR("30.30.30.0/24") 218 c.Assert(err, jc.ErrorIsNil) 219 c.Check(overlays, gc.HasLen, 0) 220 } 221 222 func (*subnetSuite) TestSubnetInfosGetByCIDR(c *gc.C) { 223 s := network.SubnetInfos{ 224 {ID: "1", CIDR: "10.10.10.0/25", ProviderId: "1"}, 225 {ID: "2", CIDR: "10.10.10.0/25", ProviderId: "2"}, 226 {ID: "4", CIDR: "20.20.20.0/25"}, 227 } 228 229 _, err := s.GetByCIDR("invalid") 230 c.Check(err, jc.Satisfies, errors.IsNotValid) 231 232 subs, err := s.GetByCIDR("30.30.30.0/25") 233 c.Assert(err, jc.ErrorIsNil) 234 c.Check(subs, gc.HasLen, 0) 235 236 subs, err = s.GetByCIDR("10.10.10.0/25") 237 c.Assert(err, jc.ErrorIsNil) 238 c.Assert(subs.EqualTo(s[:2]), jc.IsTrue) 239 240 // Check fallback CIDR-in-CIDR matching when CIDR is carved out of a 241 // subnet CIDR. 242 subs, err = s.GetByCIDR("10.10.10.0/31") 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(subs.EqualTo(s[:2]), jc.IsTrue, gc.Commentf("expected input that is a subset of the subnet CIDRs to be matched to a subnet")) 245 246 // Same check as above but using a different network IP which is still 247 // contained within the 10.10.10.0/25 subnets from the SubnetInfos list. 248 subs, err = s.GetByCIDR("10.10.10.8/31") 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(subs.EqualTo(s[:2]), jc.IsTrue, gc.Commentf("expected input that is a subset of the subnet CIDRs to be matched to a subnet")) 251 252 subs, err = s.GetByCIDR("10.10.0.0/24") 253 c.Assert(err, jc.ErrorIsNil) 254 c.Assert(subs, gc.HasLen, 0, gc.Commentf("expected input that is a superset of the subnet CIDRs not to be matched to any subnet")) 255 } 256 257 func (*subnetSuite) TestSubnetInfosGetByID(c *gc.C) { 258 s := network.SubnetInfos{ 259 {ID: "1"}, 260 {ID: "2"}, 261 {ID: "3"}, 262 } 263 264 c.Check(s.GetByID("1"), gc.NotNil) 265 c.Check(s.ContainsID("1"), jc.IsTrue) 266 267 c.Check(s.GetByID("9"), gc.IsNil) 268 c.Check(s.ContainsID("9"), jc.IsFalse) 269 } 270 271 func (*subnetSuite) TestSubnetInfosGetByAddress(c *gc.C) { 272 s := network.SubnetInfos{ 273 {ID: "1", CIDR: "10.10.10.0/24", ProviderId: "1"}, 274 {ID: "2", CIDR: "10.10.10.0/24", ProviderId: "2"}, 275 {ID: "3", CIDR: "20.20.20.0/24"}, 276 } 277 278 _, err := s.GetByAddress("invalid") 279 c.Check(err, jc.Satisfies, errors.IsNotValid) 280 281 subs, err := s.GetByAddress("10.10.10.5") 282 c.Assert(err, jc.ErrorIsNil) 283 284 // We need to check these explicitly, because the IPNets of the original 285 // members will now be populated, making them differ. 286 c.Assert(subs, gc.HasLen, 2) 287 c.Check(subs[0].ProviderId, gc.Equals, network.Id("1")) 288 c.Check(subs[1].ProviderId, gc.Equals, network.Id("2")) 289 290 subs, err = s.GetByAddress("30.30.30.5") 291 c.Assert(err, jc.ErrorIsNil) 292 c.Check(subs, gc.HasLen, 0) 293 } 294 295 func (*subnetSuite) TestSubnetInfosGetBySpaceID(c *gc.C) { 296 s := network.SubnetInfos{ 297 { 298 ID: "1", 299 CIDR: "10.10.10.0/24", 300 SpaceID: "666", 301 }, 302 { 303 ID: "2", 304 CIDR: "222.0.0.0/8", 305 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"}, 306 }, 307 { 308 ID: "3", 309 CIDR: "20.20.20.0/24", 310 SpaceID: "999", 311 }, 312 { 313 ID: "4", 314 CIDR: "223.0.0.0/8", 315 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"}, 316 // This is to check that we don't get duplicates when retrieving 317 // by the underlay CIDR. 318 SpaceID: "999", 319 }, 320 } 321 322 subs, err := s.GetBySpaceID("666") 323 c.Assert(err, jc.ErrorIsNil) 324 c.Check(subs, gc.DeepEquals, network.SubnetInfos{ 325 { 326 ID: "1", 327 CIDR: "10.10.10.0/24", 328 SpaceID: "666", 329 }, 330 { 331 ID: "2", 332 CIDR: "222.0.0.0/8", 333 FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"}, 334 SpaceID: "666", 335 }, 336 }) 337 338 subs, err = s.GetBySpaceID("999") 339 c.Assert(err, jc.ErrorIsNil) 340 c.Check(subs, gc.DeepEquals, s[2:]) 341 } 342 343 func (*subnetSuite) TestSubnetInfosAllSubnetInfos(c *gc.C) { 344 s := network.SubnetInfos{ 345 {ID: "1", CIDR: "10.10.10.0/24", ProviderId: "1"}, 346 {ID: "2", CIDR: "10.10.10.0/24", ProviderId: "2"}, 347 {ID: "3", CIDR: "20.20.20.0/24"}, 348 } 349 350 allSubs, err := s.AllSubnetInfos() 351 c.Assert(err, jc.ErrorIsNil) 352 c.Check(allSubs, gc.DeepEquals, s) 353 } 354 355 func (*subnetSuite) TestIPRangeForCIDR(c *gc.C) { 356 specs := []struct { 357 cidr string 358 expFirst net.IP 359 expLast net.IP 360 }{ 361 { 362 cidr: "10.20.30.0/24", 363 expFirst: net.ParseIP("10.20.30.0"), 364 expLast: net.ParseIP("10.20.30.255"), 365 }, 366 { 367 cidr: "10.20.28.0/22", 368 expFirst: net.ParseIP("10.20.28.0"), 369 expLast: net.ParseIP("10.20.31.255"), 370 }, 371 { 372 cidr: "10.1.2.42/29", 373 expFirst: net.ParseIP("10.1.2.40"), 374 expLast: net.ParseIP("10.1.2.47"), 375 }, 376 { 377 cidr: "10.1.2.42/32", 378 expFirst: net.ParseIP("10.1.2.42"), 379 expLast: net.ParseIP("10.1.2.42"), 380 }, 381 { 382 cidr: "2002::1234:abcd:ffff:c0a8:101/64", 383 expFirst: net.ParseIP("2002:0000:0000:1234:0000:0000:0000:0000"), 384 expLast: net.ParseIP("2002::1234:ffff:ffff:ffff:ffff"), 385 }, 386 { 387 cidr: "2001:db8:85a3::8a2e:370:7334/128", 388 expFirst: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 389 expLast: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 390 }, 391 } 392 393 for i, spec := range specs { 394 c.Logf("%d. check that range for %q is [%s, %s]", i, spec.cidr, spec.expFirst, spec.expLast) 395 gotFirst, gotLast, err := network.IPRangeForCIDR(spec.cidr) 396 c.Assert(err, jc.ErrorIsNil) 397 c.Assert(gotFirst.String(), gc.Equals, spec.expFirst.String()) 398 c.Assert(gotLast.String(), gc.Equals, spec.expLast.String()) 399 } 400 }