github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/portrange_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/core/network" 11 "github.com/juju/juju/testing" 12 ) 13 14 type PortRangeSuite struct { 15 testing.BaseSuite 16 } 17 18 var _ = gc.Suite(&PortRangeSuite{}) 19 20 func (*PortRangeSuite) TestConflictsWith(c *gc.C) { 21 var testCases = []struct { 22 about string 23 first network.PortRange 24 second network.PortRange 25 expectConflict bool 26 }{{ 27 "identical ports", 28 network.PortRange{80, 80, "TCP"}, 29 network.PortRange{80, 80, "TCP"}, 30 true, 31 }, { 32 "different ports", 33 network.PortRange{80, 80, "TCP"}, 34 network.PortRange{90, 90, "TCP"}, 35 false, 36 }, { 37 "touching ranges", 38 network.PortRange{100, 200, "TCP"}, 39 network.PortRange{201, 240, "TCP"}, 40 false, 41 }, { 42 "touching ranges with overlap", 43 network.PortRange{100, 200, "TCP"}, 44 network.PortRange{200, 240, "TCP"}, 45 true, 46 }, { 47 "different protocols", 48 network.PortRange{80, 80, "UDP"}, 49 network.PortRange{80, 80, "TCP"}, 50 false, 51 }, { 52 "outside range", 53 network.PortRange{100, 200, "TCP"}, 54 network.PortRange{80, 80, "TCP"}, 55 false, 56 }, { 57 "overlap end", 58 network.PortRange{100, 200, "TCP"}, 59 network.PortRange{80, 120, "TCP"}, 60 true, 61 }, { 62 "complete overlap", 63 network.PortRange{100, 200, "TCP"}, 64 network.PortRange{120, 140, "TCP"}, 65 true, 66 }} 67 68 for i, t := range testCases { 69 c.Logf("test %d: %s", i, t.about) 70 c.Check(t.first.ConflictsWith(t.second), gc.Equals, t.expectConflict) 71 c.Check(t.second.ConflictsWith(t.first), gc.Equals, t.expectConflict) 72 } 73 } 74 75 func (*PortRangeSuite) TestStrings(c *gc.C) { 76 c.Assert( 77 network.PortRange{80, 80, "TCP"}.String(), 78 gc.Equals, 79 "80/tcp", 80 ) 81 c.Assert( 82 network.PortRange{80, 80, "TCP"}.GoString(), 83 gc.Equals, 84 "80/tcp", 85 ) 86 c.Assert( 87 network.PortRange{80, 100, "TCP"}.String(), 88 gc.Equals, 89 "80-100/tcp", 90 ) 91 c.Assert( 92 network.PortRange{80, 100, "TCP"}.GoString(), 93 gc.Equals, 94 "80-100/tcp", 95 ) 96 c.Assert( 97 network.PortRange{-1, -1, "ICMP"}.String(), 98 gc.Equals, 99 "icmp", 100 ) 101 } 102 103 func (*PortRangeSuite) TestValidate(c *gc.C) { 104 testCases := []struct { 105 about string 106 ports network.PortRange 107 expected string 108 }{{ 109 "single valid port", 110 network.PortRange{80, 80, "tcp"}, 111 "", 112 }, { 113 "valid port range", 114 network.PortRange{80, 90, "tcp"}, 115 "", 116 }, { 117 "valid udp port range", 118 network.PortRange{80, 90, "UDP"}, 119 "", 120 }, { 121 "invalid port range boundaries", 122 network.PortRange{90, 80, "tcp"}, 123 "invalid port range 90-80/tcp", 124 }, { 125 "both FromPort and ToPort too large", 126 network.PortRange{88888, 99999, "tcp"}, 127 "port range bounds must be between 0 and 65535, got 88888-99999", 128 }, { 129 "FromPort too large", 130 network.PortRange{88888, 65535, "tcp"}, 131 "invalid port range 88888-65535/tcp", 132 }, { 133 "FromPort too small", 134 network.PortRange{-1, 80, "tcp"}, 135 "port range bounds must be between 0 and 65535, got -1-80", 136 }, { 137 "ToPort too large", 138 network.PortRange{1, 99999, "tcp"}, 139 "port range bounds must be between 0 and 65535, got 1-99999", 140 }, { 141 "invalid protocol", 142 network.PortRange{80, 80, "some protocol"}, 143 `invalid protocol "some protocol", expected "tcp", "udp", or "icmp"`, 144 }, { 145 "invalid icmp port", 146 network.PortRange{1, 1, "icmp"}, 147 `protocol "icmp" doesn't support any ports; got "1"`, 148 }} 149 150 for i, t := range testCases { 151 c.Logf("test %d: %s", i, t.about) 152 if t.expected == "" { 153 c.Check(t.ports.Validate(), gc.IsNil) 154 } else { 155 c.Check(t.ports.Validate(), gc.ErrorMatches, t.expected) 156 } 157 } 158 } 159 160 func (*PortRangeSuite) TestSortPortRanges(c *gc.C) { 161 ranges := []network.PortRange{ 162 {10, 100, "udp"}, 163 {80, 90, "tcp"}, 164 {80, 80, "tcp"}, 165 } 166 expected := []network.PortRange{ 167 {80, 80, "tcp"}, 168 {80, 90, "tcp"}, 169 {10, 100, "udp"}, 170 } 171 network.SortPortRanges(ranges) 172 c.Assert(ranges, gc.DeepEquals, expected) 173 } 174 175 func (*PortRangeSuite) TestParsePortRange(c *gc.C) { 176 portRange, err := network.ParsePortRange("8000-8099/tcp") 177 c.Assert(err, jc.ErrorIsNil) 178 179 c.Check(portRange.Protocol, gc.Equals, "tcp") 180 c.Check(portRange.FromPort, gc.Equals, 8000) 181 c.Check(portRange.ToPort, gc.Equals, 8099) 182 } 183 184 func (*PortRangeSuite) TestParsePortRangeSingle(c *gc.C) { 185 portRange, err := network.ParsePortRange("80/tcp") 186 c.Assert(err, jc.ErrorIsNil) 187 188 c.Check(portRange.Protocol, gc.Equals, "tcp") 189 c.Check(portRange.FromPort, gc.Equals, 80) 190 c.Check(portRange.ToPort, gc.Equals, 80) 191 } 192 193 func (*PortRangeSuite) TestParsePortRangeDefaultProtocol(c *gc.C) { 194 portRange, err := network.ParsePortRange("80") 195 c.Assert(err, jc.ErrorIsNil) 196 197 c.Check(portRange.Protocol, gc.Equals, "tcp") 198 c.Check(portRange.FromPort, gc.Equals, 80) 199 c.Check(portRange.ToPort, gc.Equals, 80) 200 } 201 202 func (*PortRangeSuite) TestParseIcmpProtocol(c *gc.C) { 203 portRange, err := network.ParsePortRange("icmp") 204 c.Assert(err, jc.ErrorIsNil) 205 206 c.Check(portRange.Protocol, gc.Equals, "icmp") 207 c.Check(portRange.FromPort, gc.Equals, -1) 208 c.Check(portRange.ToPort, gc.Equals, -1) 209 } 210 211 func (*PortRangeSuite) TestParseIcmpProtocolRoundTrip(c *gc.C) { 212 portRange, err := network.ParsePortRange("icmp") 213 c.Assert(err, jc.ErrorIsNil) 214 portRangeStr := portRange.String() 215 216 c.Check(portRangeStr, gc.Equals, "icmp") 217 } 218 219 func (*PortRangeSuite) TestParsePortRangeRoundTrip(c *gc.C) { 220 portRange, err := network.ParsePortRange("8000-8099/tcp") 221 c.Assert(err, jc.ErrorIsNil) 222 portRangeStr := portRange.String() 223 224 c.Check(portRangeStr, gc.Equals, "8000-8099/tcp") 225 } 226 227 func (*PortRangeSuite) TestParsePortRangeMultiRange(c *gc.C) { 228 _, err := network.ParsePortRange("10-55-100") 229 230 c.Check(err, gc.ErrorMatches, `invalid port range "10-55-100".*`) 231 } 232 233 func (*PortRangeSuite) TestParsePortRangeNonIntPort(c *gc.C) { 234 _, err := network.ParsePortRange("spam-100") 235 236 c.Check(err, gc.ErrorMatches, `invalid port "spam".*`) 237 } 238 239 func (*PortRangeSuite) TestMustParsePortRange(c *gc.C) { 240 portRange := network.MustParsePortRange("8000-8099/tcp") 241 242 c.Check(portRange.Protocol, gc.Equals, "tcp") 243 c.Check(portRange.FromPort, gc.Equals, 8000) 244 c.Check(portRange.ToPort, gc.Equals, 8099) 245 } 246 247 func (*PortRangeSuite) TestMustParsePortRangeInvalid(c *gc.C) { 248 f := func() { 249 network.MustParsePortRange("10-55-100") 250 } 251 252 c.Check(f, gc.PanicMatches, `invalid port range "10-55-100".*`) 253 } 254 255 func (*PortRangeSuite) TestCombinePortRanges(c *gc.C) { 256 testCases := []struct { 257 in []network.PortRange 258 expected []network.PortRange 259 }{{ 260 []network.PortRange{{80, 80, "tcp"}}, 261 []network.PortRange{{80, 80, "tcp"}}, 262 }, { 263 []network.PortRange{{80, 82, "tcp"}, {83, 85, "tcp"}}, 264 []network.PortRange{{80, 85, "tcp"}}, 265 }, { 266 []network.PortRange{{83, 85, "tcp"}, {80, 82, "tcp"}}, 267 []network.PortRange{{80, 85, "tcp"}}, 268 }, { 269 []network.PortRange{{80, 83, "tcp"}, {85, 87, "tcp"}}, 270 []network.PortRange{{80, 83, "tcp"}, {85, 87, "tcp"}}, 271 }, { 272 []network.PortRange{{85, 87, "tcp"}, {80, 83, "tcp"}}, 273 []network.PortRange{{80, 83, "tcp"}, {85, 87, "tcp"}}, 274 }, { 275 []network.PortRange{{85, 87, "tcp"}, {80, 83, "tcp"}}, 276 []network.PortRange{{80, 83, "tcp"}, {85, 87, "tcp"}}, 277 }, { 278 []network.PortRange{{80, 83, "tcp"}, {84, 87, "udp"}}, 279 []network.PortRange{{80, 83, "tcp"}, {84, 87, "udp"}}, 280 }, { 281 []network.PortRange{{80, 82, "tcp"}, {80, 80, "udp"}, {83, 83, "tcp"}, {81, 84, "udp"}, {84, 85, "tcp"}}, 282 []network.PortRange{{80, 85, "tcp"}, {80, 84, "udp"}}, 283 }, { 284 []network.PortRange{{80, 82, "tcp"}, {81, 84, "udp"}, {84, 84, "tcp"}, {86, 87, "udp"}, {80, 80, "udp"}}, 285 []network.PortRange{{80, 82, "tcp"}, {84, 84, "tcp"}, {80, 84, "udp"}, {86, 87, "udp"}}, 286 }} 287 for i, t := range testCases { 288 c.Logf("test %d", i) 289 c.Check(network.CombinePortRanges(t.in...), jc.DeepEquals, t.expected) 290 } 291 } 292 293 func (p *PortRangeSuite) TestPortRangeLength(c *gc.C) { 294 testCases := []struct { 295 about string 296 ports network.PortRange 297 expectLength int 298 }{{ 299 "single valid port", 300 network.MustParsePortRange("80/tcp"), 301 1, 302 }, { 303 "tcp port range", 304 network.MustParsePortRange("80-90/tcp"), 305 11, 306 }, { 307 "udp port range", 308 network.MustParsePortRange("80-90/udp"), 309 11, 310 }, { 311 "ICMP range", 312 network.PortRange{Protocol: "icmp", FromPort: -1, ToPort: -1}, 313 1, 314 }, { 315 "longest valid range", 316 network.MustParsePortRange("1-65535/tcp"), 317 65535, 318 }} 319 320 for i, t := range testCases { 321 c.Logf("test %d: %s", i, t.about) 322 c.Check(t.ports.Length(), gc.Equals, t.expectLength) 323 } 324 } 325 326 func (p *PortRangeSuite) TestSanitizeBounds(c *gc.C) { 327 tests := []struct { 328 about string 329 input network.PortRange 330 output network.PortRange 331 }{{ 332 "valid range", 333 network.PortRange{FromPort: 100, ToPort: 200}, 334 network.PortRange{FromPort: 100, ToPort: 200}, 335 }, { 336 "negative lower bound", 337 network.PortRange{FromPort: -10, ToPort: 10}, 338 network.PortRange{FromPort: 1, ToPort: 10}, 339 }, { 340 "zero lower bound", 341 network.PortRange{FromPort: 0, ToPort: 10}, 342 network.PortRange{FromPort: 1, ToPort: 10}, 343 }, { 344 "negative upper bound", 345 network.PortRange{FromPort: 42, ToPort: -20}, 346 network.PortRange{FromPort: 1, ToPort: 42}, 347 }, { 348 "zero upper bound", 349 network.PortRange{FromPort: 42, ToPort: 0}, 350 network.PortRange{FromPort: 1, ToPort: 42}, 351 }, { 352 "both bounds negative", 353 network.PortRange{FromPort: -10, ToPort: -20}, 354 network.PortRange{FromPort: 1, ToPort: 1}, 355 }, { 356 "both bounds zero", 357 network.PortRange{FromPort: 0, ToPort: 0}, 358 network.PortRange{FromPort: 1, ToPort: 1}, 359 }, { 360 "swapped bounds", 361 network.PortRange{FromPort: 20, ToPort: 10}, 362 network.PortRange{FromPort: 10, ToPort: 20}, 363 }, { 364 "too large upper bound", 365 network.PortRange{FromPort: 20, ToPort: 99999}, 366 network.PortRange{FromPort: 20, ToPort: 65535}, 367 }, { 368 "too large lower bound", 369 network.PortRange{FromPort: 99999, ToPort: 10}, 370 network.PortRange{FromPort: 10, ToPort: 65535}, 371 }, { 372 "both bounds too large", 373 network.PortRange{FromPort: 88888, ToPort: 99999}, 374 network.PortRange{FromPort: 65535, ToPort: 65535}, 375 }, { 376 "lower negative, upper too large", 377 network.PortRange{FromPort: -10, ToPort: 99999}, 378 network.PortRange{FromPort: 1, ToPort: 65535}, 379 }, { 380 "lower zero, upper too large", 381 network.PortRange{FromPort: 0, ToPort: 99999}, 382 network.PortRange{FromPort: 1, ToPort: 65535}, 383 }} 384 for i, t := range tests { 385 c.Logf("test %d: %s", i, t.about) 386 c.Check(t.input.SanitizeBounds(), jc.DeepEquals, t.output) 387 } 388 } 389 390 func (p *PortRangeSuite) TestUniquePortRanges(c *gc.C) { 391 in := []network.PortRange{ 392 network.MustParsePortRange("123/tcp"), 393 network.MustParsePortRange("123/tcp"), 394 network.MustParsePortRange("123/tcp"), 395 network.MustParsePortRange("456/tcp"), 396 } 397 398 exp := []network.PortRange{ 399 network.MustParsePortRange("123/tcp"), 400 network.MustParsePortRange("456/tcp"), 401 } 402 403 got := network.UniquePortRanges(in) 404 c.Assert(got, gc.DeepEquals, exp, gc.Commentf("expected duplicate port ranges to be removed")) 405 } 406 407 func (p *PortRangeSuite) TestUniquePortRangesInGroup(c *gc.C) { 408 in := network.GroupedPortRanges{ 409 "foxtrot": []network.PortRange{ 410 network.MustParsePortRange("123/tcp"), 411 network.MustParsePortRange("123/tcp"), 412 }, 413 "unicorn": []network.PortRange{ 414 network.MustParsePortRange("123/tcp"), 415 network.MustParsePortRange("456/tcp"), 416 }, 417 } 418 419 exp := []network.PortRange{ 420 network.MustParsePortRange("123/tcp"), 421 network.MustParsePortRange("456/tcp"), 422 } 423 424 got := in.UniquePortRanges() 425 c.Assert(got, gc.DeepEquals, exp, gc.Commentf("expected duplicate port ranges to be removed")) 426 } 427 428 func (p *PortRangeSuite) TestGroupedPortRangesEquality(c *gc.C) { 429 specs := []struct { 430 descr string 431 a, b network.GroupedPortRanges 432 expEqual bool 433 }{ 434 { 435 descr: "equal port ranges in random order", 436 a: network.GroupedPortRanges{ 437 "foo": []network.PortRange{ 438 network.MustParsePortRange("123/tcp"), 439 network.MustParsePortRange("456/tcp"), 440 }, 441 "bar": []network.PortRange{ 442 network.MustParsePortRange("123/tcp"), 443 }, 444 }, 445 b: network.GroupedPortRanges{ 446 "foo": []network.PortRange{ 447 network.MustParsePortRange("456/tcp"), 448 network.MustParsePortRange("123/tcp"), 449 }, 450 "bar": []network.PortRange{ 451 network.MustParsePortRange("123/tcp"), 452 }, 453 }, 454 expEqual: true, 455 }, 456 { 457 descr: "groups with different lengths", 458 a: network.GroupedPortRanges{ 459 "foo": []network.PortRange{ 460 network.MustParsePortRange("123/tcp"), 461 network.MustParsePortRange("456/tcp"), 462 }, 463 }, 464 b: network.GroupedPortRanges{ 465 "foo": []network.PortRange{ 466 network.MustParsePortRange("123/tcp"), 467 }, 468 }, 469 expEqual: false, 470 }, 471 { 472 descr: "groups with same length but different keys", 473 a: network.GroupedPortRanges{ 474 "foo": []network.PortRange{ 475 network.MustParsePortRange("123/tcp"), 476 network.MustParsePortRange("456/tcp"), 477 }, 478 }, 479 b: network.GroupedPortRanges{ 480 "bar": []network.PortRange{ 481 network.MustParsePortRange("123/tcp"), 482 }, 483 }, 484 expEqual: false, 485 }, 486 } 487 488 for i, spec := range specs { 489 c.Logf("test %d: %s", i, spec.descr) 490 got := spec.a.EqualTo(spec.b) 491 c.Assert(got, gc.Equals, spec.expEqual) 492 } 493 }