github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/ports_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/network" 15 "github.com/juju/juju/state" 16 statetesting "github.com/juju/juju/state/testing" 17 "github.com/juju/juju/testing/factory" 18 ) 19 20 type PortsDocSuite struct { 21 ConnSuite 22 charm *state.Charm 23 service *state.Service 24 unit1 *state.Unit 25 unit2 *state.Unit 26 machine *state.Machine 27 subnet *state.Subnet 28 portsOnSubnet *state.Ports 29 portsWithoutSubnet *state.Ports 30 } 31 32 var _ = gc.Suite(&PortsDocSuite{}) 33 34 func (s *PortsDocSuite) SetUpTest(c *gc.C) { 35 s.ConnSuite.SetUpTest(c) 36 37 f := factory.NewFactory(s.State) 38 s.charm = f.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 39 s.service = f.MakeService(c, &factory.ServiceParams{Name: "wordpress", Charm: s.charm}) 40 s.machine = f.MakeMachine(c, &factory.MachineParams{Series: "quantal"}) 41 s.unit1 = f.MakeUnit(c, &factory.UnitParams{Service: s.service, Machine: s.machine}) 42 s.unit2 = f.MakeUnit(c, &factory.UnitParams{Service: s.service, Machine: s.machine}) 43 44 var err error 45 s.subnet, err = s.State.AddSubnet(state.SubnetInfo{CIDR: "0.1.2.0/24"}) 46 c.Assert(err, jc.ErrorIsNil) 47 48 s.portsOnSubnet, err = state.GetOrCreatePorts(s.State, s.machine.Id(), s.subnet.CIDR()) 49 c.Assert(err, jc.ErrorIsNil) 50 c.Assert(s.portsOnSubnet, gc.NotNil) 51 52 s.portsWithoutSubnet, err = state.GetOrCreatePorts(s.State, s.machine.Id(), "") 53 c.Assert(err, jc.ErrorIsNil) 54 c.Assert(s.portsWithoutSubnet, gc.NotNil) 55 } 56 57 func (s *PortsDocSuite) TestCreatePortsWithSubnet(c *gc.C) { 58 s.testCreatePortsWithSubnetID(c, s.subnet.CIDR()) 59 } 60 61 func (s *PortsDocSuite) testCreatePortsWithSubnetID(c *gc.C, subnetID string) { 62 ports, err := state.GetOrCreatePorts(s.State, s.machine.Id(), subnetID) 63 c.Assert(err, jc.ErrorIsNil) 64 c.Assert(ports, gc.NotNil) 65 err = ports.OpenPorts(state.PortRange{ 66 FromPort: 100, 67 ToPort: 200, 68 UnitName: s.unit1.Name(), 69 Protocol: "TCP", 70 }) 71 c.Assert(err, jc.ErrorIsNil) 72 73 ports, err = state.GetPorts(s.State, s.machine.Id(), subnetID) 74 c.Assert(err, jc.ErrorIsNil) 75 c.Assert(ports, gc.NotNil) 76 77 c.Assert(ports.PortsForUnit(s.unit1.Name()), gc.HasLen, 1) 78 } 79 80 func (s *PortsDocSuite) TestCreatePortsWithoutSubnet(c *gc.C) { 81 s.testCreatePortsWithSubnetID(c, "") 82 } 83 84 func (s *PortsDocSuite) TestOpenAndClosePorts(c *gc.C) { 85 86 testCases := []struct { 87 about string 88 existing []state.PortRange 89 open *state.PortRange 90 close *state.PortRange 91 expected string 92 }{{ 93 about: "open and close same port range", 94 existing: nil, 95 open: &state.PortRange{ 96 FromPort: 100, 97 ToPort: 200, 98 UnitName: s.unit1.Name(), 99 Protocol: "TCP", 100 }, 101 close: &state.PortRange{ 102 FromPort: 100, 103 ToPort: 200, 104 UnitName: s.unit1.Name(), 105 Protocol: "TCP", 106 }, 107 expected: "", 108 }, { 109 about: "try to close part of a port range", 110 existing: []state.PortRange{{ 111 FromPort: 100, 112 ToPort: 200, 113 UnitName: s.unit1.Name(), 114 Protocol: "TCP", 115 }}, 116 open: nil, 117 close: &state.PortRange{ 118 FromPort: 100, 119 ToPort: 150, 120 UnitName: s.unit1.Name(), 121 Protocol: "TCP", 122 }, 123 expected: `cannot close ports 100-150/tcp \("wordpress/0"\): port ranges 100-200/tcp \("wordpress/0"\) and 100-150/tcp \("wordpress/0"\) conflict`, 124 }, { 125 about: "close an unopened port range with existing clash from other unit", 126 existing: []state.PortRange{{ 127 FromPort: 100, 128 ToPort: 150, 129 UnitName: s.unit2.Name(), 130 Protocol: "TCP", 131 }}, 132 open: nil, 133 close: &state.PortRange{ 134 FromPort: 100, 135 ToPort: 150, 136 UnitName: s.unit1.Name(), 137 Protocol: "TCP", 138 }, 139 expected: "", 140 }, { 141 about: "open twice the same port range", 142 existing: []state.PortRange{{ 143 FromPort: 100, 144 ToPort: 150, 145 UnitName: s.unit1.Name(), 146 Protocol: "TCP", 147 }}, 148 open: &state.PortRange{ 149 FromPort: 100, 150 ToPort: 150, 151 UnitName: s.unit1.Name(), 152 Protocol: "TCP", 153 }, 154 close: nil, 155 expected: "", 156 }, { 157 about: "close an unopened port range", 158 existing: nil, 159 open: nil, 160 close: &state.PortRange{ 161 FromPort: 100, 162 ToPort: 150, 163 UnitName: s.unit1.Name(), 164 Protocol: "TCP", 165 }, 166 expected: "", 167 }, { 168 about: "try to close an overlapping port range", 169 existing: []state.PortRange{{ 170 FromPort: 100, 171 ToPort: 200, 172 UnitName: s.unit1.Name(), 173 Protocol: "TCP", 174 }}, 175 open: nil, 176 close: &state.PortRange{ 177 FromPort: 100, 178 ToPort: 300, 179 UnitName: s.unit1.Name(), 180 Protocol: "TCP", 181 }, 182 expected: `cannot close ports 100-300/tcp \("wordpress/0"\): port ranges 100-200/tcp \("wordpress/0"\) and 100-300/tcp \("wordpress/0"\) conflict`, 183 }, { 184 about: "try to open an overlapping port range with different unit", 185 existing: []state.PortRange{{ 186 FromPort: 100, 187 ToPort: 200, 188 UnitName: s.unit1.Name(), 189 Protocol: "TCP", 190 }}, 191 open: &state.PortRange{ 192 FromPort: 100, 193 ToPort: 300, 194 UnitName: s.unit2.Name(), 195 Protocol: "TCP", 196 }, 197 expected: `cannot open ports 100-300/tcp \("wordpress/1"\): port ranges 100-200/tcp \("wordpress/0"\) and 100-300/tcp \("wordpress/1"\) conflict`, 198 }, { 199 about: "try to open an identical port range with different unit", 200 existing: []state.PortRange{{ 201 FromPort: 100, 202 ToPort: 200, 203 UnitName: s.unit1.Name(), 204 Protocol: "TCP", 205 }}, 206 open: &state.PortRange{ 207 FromPort: 100, 208 ToPort: 200, 209 UnitName: s.unit2.Name(), 210 Protocol: "TCP", 211 }, 212 expected: `cannot open ports 100-200/tcp \("wordpress/1"\): port ranges 100-200/tcp \("wordpress/0"\) and 100-200/tcp \("wordpress/1"\) conflict`, 213 }, { 214 about: "try to open a port range with different protocol with different unit", 215 existing: []state.PortRange{{ 216 FromPort: 100, 217 ToPort: 200, 218 UnitName: s.unit1.Name(), 219 Protocol: "TCP", 220 }}, 221 open: &state.PortRange{ 222 FromPort: 100, 223 ToPort: 200, 224 UnitName: s.unit2.Name(), 225 Protocol: "UDP", 226 }, 227 expected: "", 228 }, { 229 about: "try to open a non-overlapping port range with different unit", 230 existing: []state.PortRange{{ 231 FromPort: 100, 232 ToPort: 200, 233 UnitName: s.unit1.Name(), 234 Protocol: "TCP", 235 }}, 236 open: &state.PortRange{ 237 FromPort: 300, 238 ToPort: 400, 239 UnitName: s.unit2.Name(), 240 Protocol: "TCP", 241 }, 242 expected: "", 243 }} 244 245 for i, t := range testCases { 246 c.Logf("test %d: %s", i, t.about) 247 248 ports, err := state.GetOrCreatePorts(s.State, s.machine.Id(), s.subnet.CIDR()) 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(ports, gc.NotNil) 251 252 // open ports that should exist for the test case 253 for _, portRange := range t.existing { 254 err := ports.OpenPorts(portRange) 255 c.Check(err, jc.ErrorIsNil) 256 } 257 if t.existing != nil { 258 err = ports.Refresh() 259 c.Check(err, jc.ErrorIsNil) 260 } 261 if t.open != nil { 262 err = ports.OpenPorts(*t.open) 263 if t.expected == "" { 264 c.Check(err, jc.ErrorIsNil) 265 } else { 266 c.Check(err, gc.ErrorMatches, t.expected) 267 } 268 err = ports.Refresh() 269 c.Check(err, jc.ErrorIsNil) 270 271 } 272 273 if t.close != nil { 274 err := ports.ClosePorts(*t.close) 275 if t.expected == "" { 276 c.Check(err, jc.ErrorIsNil) 277 } else { 278 c.Check(err, gc.ErrorMatches, t.expected) 279 } 280 } 281 err = ports.Remove() 282 c.Check(err, jc.ErrorIsNil) 283 } 284 } 285 286 func (s *PortsDocSuite) TestAllPortRanges(c *gc.C) { 287 portRange := state.PortRange{ 288 FromPort: 100, 289 ToPort: 200, 290 UnitName: s.unit1.Name(), 291 Protocol: "TCP", 292 } 293 err := s.portsWithoutSubnet.OpenPorts(portRange) 294 c.Assert(err, jc.ErrorIsNil) 295 296 ranges := s.portsWithoutSubnet.AllPortRanges() 297 c.Assert(ranges, gc.HasLen, 1) 298 299 c.Assert(ranges[network.PortRange{100, 200, "TCP"}], gc.Equals, s.unit1.Name()) 300 } 301 302 func (s *PortsDocSuite) TestOpenInvalidRange(c *gc.C) { 303 portRange := state.PortRange{ 304 FromPort: 400, 305 ToPort: 200, 306 UnitName: s.unit1.Name(), 307 Protocol: "TCP", 308 } 309 err := s.portsWithoutSubnet.OpenPorts(portRange) 310 c.Assert(err, gc.ErrorMatches, `cannot open ports 400-200/tcp \("wordpress/0"\): invalid port range 400-200`) 311 } 312 313 func (s *PortsDocSuite) TestCloseInvalidRange(c *gc.C) { 314 portRange := state.PortRange{ 315 FromPort: 100, 316 ToPort: 200, 317 UnitName: s.unit1.Name(), 318 Protocol: "TCP", 319 } 320 err := s.portsWithoutSubnet.OpenPorts(portRange) 321 c.Assert(err, jc.ErrorIsNil) 322 323 err = s.portsWithoutSubnet.Refresh() 324 c.Assert(err, jc.ErrorIsNil) 325 err = s.portsWithoutSubnet.ClosePorts(state.PortRange{ 326 FromPort: 150, 327 ToPort: 200, 328 UnitName: s.unit1.Name(), 329 Protocol: "TCP", 330 }) 331 c.Assert(err, gc.ErrorMatches, `cannot close ports 150-200/tcp \("wordpress/0"\): port ranges 100-200/tcp \("wordpress/0"\) and 150-200/tcp \("wordpress/0"\) conflict`) 332 } 333 334 func (s *PortsDocSuite) TestRemovePortsDoc(c *gc.C) { 335 portRange := state.PortRange{ 336 FromPort: 100, 337 ToPort: 200, 338 UnitName: s.unit1.Name(), 339 Protocol: "TCP", 340 } 341 err := s.portsOnSubnet.OpenPorts(portRange) 342 c.Assert(err, jc.ErrorIsNil) 343 344 ports, err := state.GetPorts(s.State, s.machine.Id(), s.subnet.CIDR()) 345 c.Assert(err, jc.ErrorIsNil) 346 c.Assert(ports, gc.NotNil) 347 348 allPorts, err := s.machine.AllPorts() 349 c.Assert(err, jc.ErrorIsNil) 350 351 for _, prt := range allPorts { 352 err := prt.Remove() 353 c.Assert(err, jc.ErrorIsNil) 354 } 355 356 ports, err = state.GetPorts(s.State, s.machine.Id(), s.subnet.CIDR()) 357 c.Assert(ports, gc.IsNil) 358 c.Assert(err, jc.Satisfies, errors.IsNotFound) 359 c.Assert(err, gc.ErrorMatches, `ports for machine "0", subnet "0.1.2.0/24" not found`) 360 } 361 362 func (s *PortsDocSuite) TestWatchPorts(c *gc.C) { 363 // No port ranges open initially, no changes. 364 w := s.State.WatchOpenedPorts() 365 c.Assert(w, gc.NotNil) 366 367 defer statetesting.AssertStop(c, w) 368 wc := statetesting.NewStringsWatcherC(c, s.State, w) 369 // The first change we get is an empty one, as there are no ports 370 // opened yet and we need an initial event for the API watcher to 371 // work. 372 wc.AssertChange() 373 wc.AssertNoChange() 374 375 portRange := state.PortRange{ 376 FromPort: 100, 377 ToPort: 200, 378 UnitName: s.unit1.Name(), 379 Protocol: "TCP", 380 } 381 expectChange := fmt.Sprintf("%s:%s", s.machine.Id(), s.subnet.CIDR()) 382 // Open a port range, detect a change. 383 err := s.portsOnSubnet.OpenPorts(portRange) 384 c.Assert(err, jc.ErrorIsNil) 385 wc.AssertChange(expectChange) 386 wc.AssertNoChange() 387 388 // Close the port range, detect a change. 389 err = s.portsOnSubnet.ClosePorts(portRange) 390 c.Assert(err, jc.ErrorIsNil) 391 wc.AssertChange(expectChange) 392 wc.AssertNoChange() 393 394 // Close the port range again, no changes. 395 err = s.portsOnSubnet.ClosePorts(portRange) 396 c.Assert(err, jc.ErrorIsNil) 397 wc.AssertNoChange() 398 399 // Open another range, detect a change. 400 portRange = state.PortRange{ 401 FromPort: 999, 402 ToPort: 1999, 403 UnitName: s.unit2.Name(), 404 Protocol: "udp", 405 } 406 err = s.portsOnSubnet.OpenPorts(portRange) 407 c.Assert(err, jc.ErrorIsNil) 408 wc.AssertChange(expectChange) 409 wc.AssertNoChange() 410 411 // Open the same range again, no changes. 412 err = s.portsOnSubnet.OpenPorts(portRange) 413 c.Assert(err, jc.ErrorIsNil) 414 wc.AssertNoChange() 415 416 // Open another range, detect a change. 417 otherRange := state.PortRange{ 418 FromPort: 1, 419 ToPort: 100, 420 UnitName: s.unit1.Name(), 421 Protocol: "tcp", 422 } 423 err = s.portsOnSubnet.OpenPorts(otherRange) 424 c.Assert(err, jc.ErrorIsNil) 425 wc.AssertChange(expectChange) 426 wc.AssertNoChange() 427 428 // Close the other range, detect a change. 429 err = s.portsOnSubnet.ClosePorts(otherRange) 430 c.Assert(err, jc.ErrorIsNil) 431 wc.AssertChange(expectChange) 432 wc.AssertNoChange() 433 434 // Remove the ports document, detect a change. 435 err = s.portsOnSubnet.Remove() 436 c.Assert(err, jc.ErrorIsNil) 437 wc.AssertChange(expectChange) 438 wc.AssertNoChange() 439 440 // And again - no change. 441 err = s.portsOnSubnet.Remove() 442 c.Assert(err, jc.ErrorIsNil) 443 wc.AssertNoChange() 444 } 445 446 type PortRangeSuite struct{} 447 448 var _ = gc.Suite(&PortRangeSuite{}) 449 450 // Create a port range or panic if it is invalid. 451 func MustPortRange(unitName string, fromPort, toPort int, protocol string) state.PortRange { 452 portRange, err := state.NewPortRange(unitName, fromPort, toPort, protocol) 453 if err != nil { 454 panic(err) 455 } 456 return portRange 457 } 458 459 func (p *PortRangeSuite) TestPortRangeConflicts(c *gc.C) { 460 var testCases = []struct { 461 about string 462 first state.PortRange 463 second state.PortRange 464 expected interface{} 465 }{{ 466 "identical ports", 467 MustPortRange("wordpress/0", 80, 80, "TCP"), 468 MustPortRange("wordpress/0", 80, 80, "TCP"), 469 nil, 470 }, { 471 "identical port ranges", 472 MustPortRange("wordpress/0", 80, 100, "TCP"), 473 MustPortRange("wordpress/0", 80, 100, "TCP"), 474 nil, 475 }, { 476 "different ports", 477 MustPortRange("wordpress/0", 80, 80, "TCP"), 478 MustPortRange("wordpress/0", 90, 90, "TCP"), 479 nil, 480 }, { 481 "touching ranges", 482 MustPortRange("wordpress/0", 100, 200, "TCP"), 483 MustPortRange("wordpress/0", 201, 240, "TCP"), 484 nil, 485 }, { 486 "touching ranges with overlap", 487 MustPortRange("wordpress/0", 100, 200, "TCP"), 488 MustPortRange("wordpress/0", 200, 240, "TCP"), 489 "port ranges .* conflict", 490 }, { 491 "identical ports with different protocols", 492 MustPortRange("wordpress/0", 80, 80, "UDP"), 493 MustPortRange("wordpress/0", 80, 80, "TCP"), 494 nil, 495 }, { 496 "overlapping ranges with different protocols", 497 MustPortRange("wordpress/0", 80, 200, "UDP"), 498 MustPortRange("wordpress/0", 80, 300, "TCP"), 499 nil, 500 }, { 501 "outside range", 502 MustPortRange("wordpress/0", 100, 200, "TCP"), 503 MustPortRange("wordpress/0", 80, 80, "TCP"), 504 nil, 505 }, { 506 "overlap end", 507 MustPortRange("wordpress/0", 100, 200, "TCP"), 508 MustPortRange("wordpress/0", 80, 120, "TCP"), 509 "port ranges .* conflict", 510 }, { 511 "complete overlap", 512 MustPortRange("wordpress/0", 100, 200, "TCP"), 513 MustPortRange("wordpress/0", 120, 140, "TCP"), 514 "port ranges .* conflict", 515 }, { 516 "overlap with same end", 517 MustPortRange("wordpress/0", 100, 200, "TCP"), 518 MustPortRange("wordpress/0", 120, 200, "TCP"), 519 "port ranges .* conflict", 520 }, { 521 "overlap with same start", 522 MustPortRange("wordpress/0", 100, 200, "TCP"), 523 MustPortRange("wordpress/0", 100, 120, "TCP"), 524 "port ranges .* conflict", 525 }, { 526 "invalid port range", 527 state.PortRange{"wordpress/0", 100, 80, "TCP"}, 528 MustPortRange("wordpress/0", 80, 80, "TCP"), 529 "invalid port range 100-80", 530 }, { 531 "different units, same port", 532 MustPortRange("mysql/0", 80, 80, "TCP"), 533 MustPortRange("wordpress/0", 80, 80, "TCP"), 534 "port ranges .* conflict", 535 }, { 536 "different units, different port ranges", 537 MustPortRange("mysql/0", 80, 100, "TCP"), 538 MustPortRange("wordpress/0", 180, 280, "TCP"), 539 nil, 540 }, { 541 "different units, overlapping port ranges", 542 MustPortRange("mysql/0", 80, 100, "TCP"), 543 MustPortRange("wordpress/0", 90, 280, "TCP"), 544 "port ranges .* conflict", 545 }} 546 547 for i, t := range testCases { 548 c.Logf("test %d: %s", i, t.about) 549 if t.expected == nil { 550 c.Check(t.first.CheckConflicts(t.second), gc.IsNil) 551 c.Check(t.second.CheckConflicts(t.first), gc.IsNil) 552 } else if _, isString := t.expected.(string); isString { 553 c.Check(t.first.CheckConflicts(t.second), gc.ErrorMatches, t.expected.(string)) 554 c.Check(t.second.CheckConflicts(t.first), gc.ErrorMatches, t.expected.(string)) 555 } 556 // change test case protocols and test again 557 c.Logf("test %d: %s (after protocol swap)", i, t.about) 558 t.first.Protocol = swapProtocol(t.first.Protocol) 559 t.second.Protocol = swapProtocol(t.second.Protocol) 560 c.Logf("%+v %+v %v", t.first, t.second, t.expected) 561 if t.expected == nil { 562 c.Check(t.first.CheckConflicts(t.second), gc.IsNil) 563 c.Check(t.second.CheckConflicts(t.first), gc.IsNil) 564 } else if _, isString := t.expected.(string); isString { 565 c.Check(t.first.CheckConflicts(t.second), gc.ErrorMatches, t.expected.(string)) 566 c.Check(t.second.CheckConflicts(t.first), gc.ErrorMatches, t.expected.(string)) 567 } 568 569 } 570 } 571 572 func swapProtocol(protocol string) string { 573 if strings.ToLower(protocol) == "tcp" { 574 return "udp" 575 } 576 if strings.ToLower(protocol) == "udp" { 577 return "tcp" 578 } 579 return protocol 580 } 581 582 func (p *PortRangeSuite) TestPortRangeString(c *gc.C) { 583 c.Assert(state.PortRange{"wordpress/42", 80, 80, "TCP"}.String(), 584 gc.Equals, 585 `80-80/tcp ("wordpress/42")`, 586 ) 587 c.Assert(state.PortRange{"wordpress/0", 80, 100, "TCP"}.String(), 588 gc.Equals, 589 `80-100/tcp ("wordpress/0")`, 590 ) 591 } 592 593 func (p *PortRangeSuite) TestPortRangeValidityAndLength(c *gc.C) { 594 testCases := []struct { 595 about string 596 ports state.PortRange 597 expectLength int 598 expectedErr string 599 }{{ 600 "single valid port", 601 state.PortRange{"wordpress/0", 80, 80, "tcp"}, 602 1, 603 "", 604 }, { 605 "valid tcp port range", 606 state.PortRange{"wordpress/0", 80, 90, "tcp"}, 607 11, 608 "", 609 }, { 610 "valid udp port range", 611 state.PortRange{"wordpress/0", 80, 90, "UDP"}, 612 11, 613 "", 614 }, { 615 "invalid port range boundaries", 616 state.PortRange{"wordpress/0", 90, 80, "tcp"}, 617 0, 618 "invalid port range.*", 619 }, { 620 "invalid protocol", 621 state.PortRange{"wordpress/0", 80, 80, "some protocol"}, 622 0, 623 "invalid protocol.*", 624 }, { 625 "invalid unit", 626 state.PortRange{"invalid unit", 80, 80, "tcp"}, 627 0, 628 "invalid unit.*", 629 }, { 630 "negative lower bound", 631 state.PortRange{"wordpress/0", -10, 10, "tcp"}, 632 0, 633 "port range bounds must be between 1 and 65535.*", 634 }, { 635 "zero lower bound", 636 state.PortRange{"wordpress/0", 0, 10, "tcp"}, 637 0, 638 "port range bounds must be between 1 and 65535.*", 639 }, { 640 "negative upper bound", 641 state.PortRange{"wordpress/0", 10, -10, "tcp"}, 642 0, 643 "invalid port range.*", 644 }, { 645 "zero upper bound", 646 state.PortRange{"wordpress/0", 10, 0, "tcp"}, 647 0, 648 "invalid port range.*", 649 }, { 650 "too large lower bound", 651 state.PortRange{"wordpress/0", 65540, 99999, "tcp"}, 652 0, 653 "port range bounds must be between 1 and 65535.*", 654 }, { 655 "too large upper bound", 656 state.PortRange{"wordpress/0", 10, 99999, "tcp"}, 657 0, 658 "port range bounds must be between 1 and 65535.*", 659 }, { 660 "longest valid range", 661 state.PortRange{"wordpress/0", 1, 65535, "tcp"}, 662 65535, 663 "", 664 }} 665 666 for i, t := range testCases { 667 c.Logf("test %d: %s", i, t.about) 668 c.Check(t.ports.Length(), gc.Equals, t.expectLength) 669 if t.expectedErr == "" { 670 c.Check(t.ports.Validate(), gc.IsNil) 671 } else { 672 c.Check(t.ports.Validate(), gc.ErrorMatches, t.expectedErr) 673 } 674 } 675 } 676 677 func (p *PortRangeSuite) TestSanitizeBounds(c *gc.C) { 678 tests := []struct { 679 about string 680 input state.PortRange 681 output state.PortRange 682 }{{ 683 "valid range", 684 state.PortRange{"", 100, 200, ""}, 685 state.PortRange{"", 100, 200, ""}, 686 }, { 687 "negative lower bound", 688 state.PortRange{"", -10, 10, ""}, 689 state.PortRange{"", 1, 10, ""}, 690 }, { 691 "zero lower bound", 692 state.PortRange{"", 0, 10, ""}, 693 state.PortRange{"", 1, 10, ""}, 694 }, { 695 "negative upper bound", 696 state.PortRange{"", 42, -20, ""}, 697 state.PortRange{"", 1, 42, ""}, 698 }, { 699 "zero upper bound", 700 state.PortRange{"", 42, 0, ""}, 701 state.PortRange{"", 1, 42, ""}, 702 }, { 703 "both bounds negative", 704 state.PortRange{"", -10, -20, ""}, 705 state.PortRange{"", 1, 1, ""}, 706 }, { 707 "both bounds zero", 708 state.PortRange{"", 0, 0, ""}, 709 state.PortRange{"", 1, 1, ""}, 710 }, { 711 "swapped bounds", 712 state.PortRange{"", 20, 10, ""}, 713 state.PortRange{"", 10, 20, ""}, 714 }, { 715 "too large upper bound", 716 state.PortRange{"", 20, 99999, ""}, 717 state.PortRange{"", 20, 65535, ""}, 718 }, { 719 "too large lower bound", 720 state.PortRange{"", 99999, 10, ""}, 721 state.PortRange{"", 10, 65535, ""}, 722 }, { 723 "both bounds too large", 724 state.PortRange{"", 88888, 99999, ""}, 725 state.PortRange{"", 65535, 65535, ""}, 726 }, { 727 "lower negative, upper too large", 728 state.PortRange{"", -10, 99999, ""}, 729 state.PortRange{"", 1, 65535, ""}, 730 }, { 731 "lower zero, upper too large", 732 state.PortRange{"", 0, 99999, ""}, 733 state.PortRange{"", 1, 65535, ""}, 734 }} 735 for i, t := range tests { 736 c.Logf("test %d: %s", i, t.about) 737 c.Check(t.input.SanitizeBounds(), jc.DeepEquals, t.output) 738 } 739 }