github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/machine_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 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 jujutxn "github.com/juju/txn/v3" 10 gc "gopkg.in/check.v1" 11 12 "github.com/juju/juju/core/network" 13 "github.com/juju/juju/state" 14 statetesting "github.com/juju/juju/state/testing" 15 "github.com/juju/juju/testing/factory" 16 ) 17 18 type MachinePortsDocSuite struct { 19 ConnSuite 20 charm *state.Charm 21 application *state.Application 22 unit1 *state.Unit 23 unit2 *state.Unit 24 machine *state.Machine 25 subnet *state.Subnet 26 machPortRanges state.MachinePortRanges 27 } 28 29 var _ = gc.Suite(&MachinePortsDocSuite{}) 30 31 const allEndpoints = "" 32 33 func (s *MachinePortsDocSuite) SetUpTest(c *gc.C) { 34 s.ConnSuite.SetUpTest(c) 35 36 s.charm = s.Factory.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 37 s.application = s.Factory.MakeApplication(c, &factory.ApplicationParams{Name: "wordpress", Charm: s.charm}) 38 s.machine = s.Factory.MakeMachine(c, &factory.MachineParams{Base: state.UbuntuBase("12.10")}) 39 s.unit1 = s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application, Machine: s.machine}) 40 s.unit2 = s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application, Machine: s.machine}) 41 42 var err error 43 s.subnet, err = s.State.AddSubnet(network.SubnetInfo{CIDR: "0.1.2.0/24"}) 44 c.Assert(err, jc.ErrorIsNil) 45 46 s.machPortRanges, err = s.machine.OpenedPortRanges() 47 c.Assert(err, jc.ErrorIsNil) 48 c.Assert(s.machPortRanges.UniquePortRanges(), gc.HasLen, 0, gc.Commentf("expected no port ranges to be open for machine")) 49 } 50 51 func assertRefreshMachinePortsDoc(c *gc.C, p state.MachinePortRanges, errSatisfier func(error) bool) { 52 type refresher interface { 53 Refresh() error 54 } 55 56 portRefresher, supports := p.(refresher) 57 c.Assert(supports, jc.IsTrue, gc.Commentf("machine ports interface does not implement Refresh()")) 58 59 err := portRefresher.Refresh() 60 if errSatisfier != nil { 61 c.Assert(err, jc.Satisfies, errSatisfier) 62 } else { 63 c.Assert(err, jc.ErrorIsNil) 64 } 65 } 66 67 func assertRemoveMachinePortsDoc(c *gc.C, p state.MachinePortRanges) { 68 type remover interface { 69 Remove() error 70 } 71 72 portRemover, supports := p.(remover) 73 c.Assert(supports, jc.IsTrue, gc.Commentf("port document does not implement Remove()")) 74 c.Assert(portRemover.Remove(), jc.ErrorIsNil) 75 } 76 77 func assertMachinePortsPersisted(c *gc.C, p state.MachinePortRanges, persisted bool) { 78 type persistChecker interface { 79 Persisted() bool 80 } 81 82 checker, supports := p.(persistChecker) 83 c.Assert(supports, jc.IsTrue, gc.Commentf("machine ports interface does not implement Persisted()")) 84 c.Assert(checker.Persisted(), gc.Equals, persisted) 85 } 86 87 func (s *MachinePortsDocSuite) mustOpenCloseMachinePorts(c *gc.C, machPorts state.MachinePortRanges, unitName, endpointName string, openRanges, closeRanges []network.PortRange) { 88 err := s.openCloseMachinePorts(machPorts, unitName, endpointName, openRanges, closeRanges) 89 c.Assert(err, jc.ErrorIsNil) 90 } 91 92 func (s *MachinePortsDocSuite) openCloseMachinePorts(machPorts state.MachinePortRanges, unitName, endpointName string, openRanges, closeRanges []network.PortRange) error { 93 unitPorts := machPorts.ForUnit(unitName) 94 for _, pr := range openRanges { 95 unitPorts.Open(endpointName, pr) 96 } 97 for _, pr := range closeRanges { 98 unitPorts.Close(endpointName, pr) 99 } 100 return s.State.ApplyOperation(machPorts.Changes()) 101 } 102 103 func (s *MachinePortsDocSuite) TestModelAllOpenPortRanges(c *gc.C) { 104 toOpen := []network.PortRange{ 105 network.MustParsePortRange("100-200/tcp"), 106 network.MustParsePortRange("300-400/tcp"), 107 network.MustParsePortRange("500-600/tcp"), 108 } 109 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen[0:1], nil) 110 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit2.Name(), allEndpoints, toOpen[1:2], nil) 111 112 // Add a second machine with another unit and open the last port range 113 mach2 := s.Factory.MakeMachine(c, &factory.MachineParams{Base: state.UbuntuBase("12.10")}) 114 unit3 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application, Machine: mach2}) 115 mach2Ports, err := mach2.OpenedPortRanges() 116 c.Assert(err, jc.ErrorIsNil) 117 s.mustOpenCloseMachinePorts(c, mach2Ports, unit3.Name(), allEndpoints, toOpen[2:3], nil) 118 119 allMachinePortRanges, err := s.Model.OpenedPortRangesForAllMachines() 120 c.Assert(err, jc.ErrorIsNil) 121 c.Assert(allMachinePortRanges, gc.HasLen, 2) 122 123 c.Assert(allMachinePortRanges[0].UniquePortRanges(), gc.DeepEquals, toOpen[0:2]) 124 c.Assert(allMachinePortRanges[1].UniquePortRanges(), gc.DeepEquals, toOpen[2:]) 125 } 126 127 func (s *MachinePortsDocSuite) TestOpenMachinePortsForWildcardEndpoint(c *gc.C) { 128 s.testOpenPortsForEndpoint(c, allEndpoints) 129 } 130 131 func (s *MachinePortsDocSuite) TestOpenMachinePortsForEndpoint(c *gc.C) { 132 s.testOpenPortsForEndpoint(c, "monitoring-port") 133 } 134 135 func (s *MachinePortsDocSuite) testOpenPortsForEndpoint(c *gc.C, endpoint string) { 136 toOpen := []network.PortRange{network.MustParsePortRange("100-200/tcp")} 137 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), endpoint, toOpen, nil) 138 139 machPorts, err := s.machine.OpenedPortRanges() 140 c.Assert(err, jc.ErrorIsNil) 141 c.Assert(machPorts.MachineID(), gc.Equals, s.machine.Id()) 142 c.Assert(machPorts.UniquePortRanges(), gc.DeepEquals, toOpen) 143 } 144 145 func (s *MachinePortsDocSuite) TestOpenAndCloseMachinePorts(c *gc.C) { 146 type unitPortRange struct { 147 UnitName string 148 PortRange network.PortRange 149 } 150 testCases := []struct { 151 about string 152 existing []unitPortRange 153 toOpen *unitPortRange 154 toClose *unitPortRange 155 expected string 156 }{{ 157 about: "open and close same port range", 158 existing: nil, 159 toOpen: &unitPortRange{ 160 PortRange: network.MustParsePortRange("100-200/tcp"), 161 UnitName: s.unit1.Name(), 162 }, 163 toClose: &unitPortRange{ 164 PortRange: network.MustParsePortRange("100-200/tcp"), 165 UnitName: s.unit1.Name(), 166 }, 167 expected: "", 168 }, { 169 about: "open and close icmp", 170 existing: nil, 171 toOpen: &unitPortRange{ 172 PortRange: network.MustParsePortRange("icmp"), 173 UnitName: s.unit1.Name(), 174 }, 175 toClose: &unitPortRange{ 176 PortRange: network.MustParsePortRange("icmp"), 177 UnitName: s.unit1.Name(), 178 }, 179 expected: "", 180 }, { 181 about: "try to close part of a port range", 182 existing: []unitPortRange{{ 183 PortRange: network.MustParsePortRange("100-200/tcp"), 184 UnitName: s.unit1.Name(), 185 }}, 186 toOpen: nil, 187 toClose: &unitPortRange{ 188 PortRange: network.MustParsePortRange("100-150/tcp"), 189 UnitName: s.unit1.Name(), 190 }, 191 expected: `cannot close ports 100-150/tcp: port ranges 100-200/tcp \("wordpress/0"\) and 100-150/tcp \("wordpress/0"\) conflict`, 192 }, { 193 about: "close a port range opened by another unit", 194 existing: []unitPortRange{{ 195 PortRange: network.MustParsePortRange("100-150/tcp"), 196 UnitName: s.unit2.Name(), 197 }}, 198 toOpen: nil, 199 toClose: &unitPortRange{ 200 PortRange: network.MustParsePortRange("100-150/tcp"), 201 UnitName: s.unit1.Name(), 202 }, 203 expected: `cannot close ports 100-150/tcp: port ranges 100-150/tcp \("wordpress/1"\) and 100-150/tcp \("wordpress/0"\) conflict`, 204 }, { 205 about: "open twice the same port range", 206 existing: []unitPortRange{{ 207 PortRange: network.MustParsePortRange("100-150/tcp"), 208 UnitName: s.unit1.Name(), 209 }}, 210 toOpen: &unitPortRange{ 211 PortRange: network.MustParsePortRange("100-150/tcp"), 212 UnitName: s.unit1.Name(), 213 }, 214 toClose: nil, 215 expected: "", 216 }, { 217 about: "close an unopened port range", 218 existing: nil, 219 toOpen: nil, 220 toClose: &unitPortRange{ 221 PortRange: network.MustParsePortRange("100-150/tcp"), 222 UnitName: s.unit1.Name(), 223 }, 224 expected: "", 225 }, { 226 about: "try to close an overlapping port range", 227 existing: []unitPortRange{{ 228 PortRange: network.MustParsePortRange("100-200/tcp"), 229 UnitName: s.unit1.Name(), 230 }}, 231 toOpen: nil, 232 toClose: &unitPortRange{ 233 PortRange: network.MustParsePortRange("100-300/tcp"), 234 UnitName: s.unit1.Name(), 235 }, 236 expected: `cannot close ports 100-300/tcp: port ranges 100-200/tcp \("wordpress/0"\) and 100-300/tcp \("wordpress/0"\) conflict`, 237 }, { 238 about: "try to open an overlapping port range with different unit", 239 existing: []unitPortRange{{ 240 PortRange: network.MustParsePortRange("100-200/tcp"), 241 UnitName: s.unit1.Name(), 242 }}, 243 toOpen: &unitPortRange{ 244 PortRange: network.MustParsePortRange("100-300/tcp"), 245 UnitName: s.unit2.Name(), 246 }, 247 expected: `cannot open ports 100-300/tcp: port ranges 100-200/tcp \("wordpress/0"\) and 100-300/tcp \("wordpress/1"\) conflict`, 248 }, { 249 about: "try to open an identical port range with different unit", 250 existing: []unitPortRange{{ 251 PortRange: network.MustParsePortRange("100-200/tcp"), 252 UnitName: s.unit1.Name(), 253 }}, 254 toOpen: &unitPortRange{ 255 PortRange: network.MustParsePortRange("100-200/tcp"), 256 UnitName: s.unit2.Name(), 257 }, 258 expected: `cannot open ports 100-200/tcp: port ranges 100-200/tcp \("wordpress/0"\) and 100-200/tcp \("wordpress/1"\) conflict`, 259 }, { 260 about: "try to open a port range with different protocol with different unit", 261 existing: []unitPortRange{{ 262 PortRange: network.MustParsePortRange("100-200/tcp"), 263 UnitName: s.unit1.Name(), 264 }}, 265 toOpen: &unitPortRange{ 266 PortRange: network.MustParsePortRange("100-200/udp"), 267 UnitName: s.unit2.Name(), 268 }, 269 expected: "", 270 }, { 271 about: "try to open a non-overlapping port range with different unit", 272 existing: []unitPortRange{{ 273 PortRange: network.MustParsePortRange("100-200/tcp"), UnitName: s.unit1.Name(), 274 }}, 275 toOpen: &unitPortRange{ 276 PortRange: network.MustParsePortRange("300-400/tcp"), 277 UnitName: s.unit2.Name(), 278 }, 279 expected: "", 280 }} 281 282 for i, t := range testCases { 283 c.Logf("test %d: %s", i, t.about) 284 285 ports, err := s.machine.OpenedPortRanges() 286 c.Assert(err, jc.ErrorIsNil) 287 288 // open ports that should exist for the test case 289 for _, pr := range t.existing { 290 s.mustOpenCloseMachinePorts(c, ports, pr.UnitName, allEndpoints, []network.PortRange{pr.PortRange}, nil) 291 } 292 if len(t.existing) != 0 { 293 assertRefreshMachinePortsDoc(c, ports, nil) 294 } 295 if t.toOpen != nil { 296 err := s.openCloseMachinePorts(ports, t.toOpen.UnitName, allEndpoints, []network.PortRange{t.toOpen.PortRange}, nil) 297 if t.expected == "" { 298 c.Check(err, jc.ErrorIsNil) 299 } else { 300 c.Check(err, gc.ErrorMatches, t.expected) 301 } 302 assertRefreshMachinePortsDoc(c, ports, nil) 303 } 304 305 if t.toClose != nil { 306 err := s.openCloseMachinePorts(ports, t.toClose.UnitName, allEndpoints, nil, []network.PortRange{t.toClose.PortRange}) 307 if t.expected == "" { 308 c.Check(err, jc.ErrorIsNil) 309 } else { 310 c.Check(err, gc.ErrorMatches, t.expected) 311 } 312 } 313 assertRemoveMachinePortsDoc(c, ports) 314 } 315 } 316 317 func (s *MachinePortsDocSuite) TestClosePortRangeOperationSucceedsForDyingModel(c *gc.C) { 318 // Open initial port range 319 toOpen := []network.PortRange{network.MustParsePortRange("200-210/tcp")} 320 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 321 322 defer state.SetBeforeHooks(c, s.State, func() { 323 c.Assert(s.Model.Destroy(state.DestroyModelParams{}), jc.ErrorIsNil) 324 }).Check() 325 326 // Close the initially opened ports 327 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, 328 nil, 329 []network.PortRange{network.MustParsePortRange("200-210/tcp")}, 330 ) 331 } 332 333 func (s *MachinePortsDocSuite) TestComposedOpenCloseOperation(c *gc.C) { 334 // Open initial port range 335 toOpen := []network.PortRange{network.MustParsePortRange("200-210/tcp")} 336 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 337 338 // Run a composed open/close operation 339 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, 340 []network.PortRange{network.MustParsePortRange("400-500/tcp")}, 341 []network.PortRange{network.MustParsePortRange("200-210/tcp")}, 342 ) 343 344 // Enumerate ports 345 assertRefreshMachinePortsDoc(c, s.machPortRanges, nil) 346 unitRanges := s.machPortRanges.ForUnit(s.unit1.Name()).ForEndpoint(allEndpoints) 347 c.Assert(unitRanges, gc.DeepEquals, []network.PortRange{network.MustParsePortRange("400-500/tcp")}) 348 349 // If we open and close the same set of ports the port doc should be deleted. 350 assertRefreshMachinePortsDoc(c, s.machPortRanges, nil) 351 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, 352 []network.PortRange{network.MustParsePortRange("400-500/tcp")}, 353 []network.PortRange{network.MustParsePortRange("400-500/tcp")}, 354 ) 355 356 // The next refresh should fail with ErrNotFound as the document has been removed. 357 assertRefreshMachinePortsDoc(c, s.machPortRanges, errors.IsNotFound) 358 } 359 360 func (s *MachinePortsDocSuite) TestComposedOpenCloseOperationNoEffectiveOps(c *gc.C) { 361 // Run a composed open/close operation 362 unitPortRanges := s.machPortRanges.ForUnit(s.unit1.Name()) 363 364 // Duplicate range should be skipped 365 unitPortRanges.Open("monitoring-port", network.MustParsePortRange("400-500/tcp")) 366 unitPortRanges.Open("monitoring-port", network.MustParsePortRange("400-500/tcp")) 367 368 unitPortRanges.Close("monitoring-port", network.MustParsePortRange("400-500/tcp")) 369 370 // As the doc does not exist and the end result is still an empty port range 371 // this should return ErrNoOperations 372 _, err := s.machPortRanges.Changes().Build(0) 373 c.Assert(err, gc.Equals, jujutxn.ErrNoOperations) 374 } 375 376 func (s *MachinePortsDocSuite) TestICMP(c *gc.C) { 377 // Open initial port range 378 toOpen := []network.PortRange{network.MustParsePortRange("icmp")} 379 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 380 381 ranges := s.machPortRanges.ForUnit(s.unit1.Name()).UniquePortRanges() 382 c.Assert(ranges, gc.HasLen, 1) 383 } 384 385 func (s *MachinePortsDocSuite) TestOpenInvalidRange(c *gc.C) { 386 toOpen := []network.PortRange{{FromPort: 400, ToPort: 200, Protocol: "tcp"}} 387 err := s.openCloseMachinePorts(s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 388 c.Assert(err, gc.ErrorMatches, `cannot open ports 400-200/tcp: invalid port range 400-200/tcp`) 389 } 390 391 func (s *MachinePortsDocSuite) TestCloseInvalidRange(c *gc.C) { 392 // Open initial port range 393 toOpen := []network.PortRange{network.MustParsePortRange("100-200/tcp")} 394 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 395 396 assertRefreshMachinePortsDoc(c, s.machPortRanges, nil) 397 398 toClose := []network.PortRange{{FromPort: 150, ToPort: 200, Protocol: "tcp"}} 399 err := s.openCloseMachinePorts(s.machPortRanges, s.unit1.Name(), allEndpoints, nil, toClose) 400 c.Assert(err, gc.ErrorMatches, `cannot close ports 150-200/tcp: port ranges 100-200/tcp \("wordpress/0"\) and 150-200/tcp \("wordpress/0"\) conflict`) 401 } 402 403 func (s *MachinePortsDocSuite) TestRemoveMachinePortsDoc(c *gc.C) { 404 // Open initial port range 405 toOpen := []network.PortRange{network.MustParsePortRange("100-200/tcp")} 406 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, toOpen, nil) 407 408 // Remove document 409 assertRemoveMachinePortsDoc(c, s.machPortRanges) 410 411 // If we lookup the opened ports for the machine we should now get a blank doc. 412 machPorts, err := s.machine.OpenedPortRanges() 413 c.Assert(err, jc.ErrorIsNil) 414 assertMachinePortsPersisted(c, machPorts, false) 415 } 416 417 func (s *MachinePortsDocSuite) TestWatchMachinePorts(c *gc.C) { 418 // No port ranges open initially, no changes. 419 w := s.State.WatchOpenedPorts() 420 c.Assert(w, gc.NotNil) 421 422 defer statetesting.AssertStop(c, w) 423 wc := statetesting.NewStringsWatcherC(c, w) 424 // The first change we get is an empty one, as there are no ports 425 // opened yet and we need an initial event for the API watcher to 426 // work. 427 wc.AssertChange() 428 wc.AssertNoChange() 429 430 portRange := network.MustParsePortRange("100-200/tcp") 431 432 // Open a port range, detect a change. 433 expectChange := s.machine.Id() 434 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, []network.PortRange{portRange}, nil) 435 wc.AssertChange(expectChange) 436 wc.AssertNoChange() 437 438 // Close the port range, detect a change. 439 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, nil, []network.PortRange{portRange}) 440 wc.AssertChange(expectChange) 441 wc.AssertNoChange() 442 443 // Close the port range again, no changes. 444 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, nil, []network.PortRange{portRange}) 445 wc.AssertNoChange() 446 447 // Open another range, detect a change. 448 portRange = network.MustParsePortRange("999-1999/udp") 449 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), "monitoring-port", []network.PortRange{portRange}, nil) 450 wc.AssertChange(expectChange) 451 wc.AssertNoChange() 452 453 // Open the same range again, no changes. 454 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), "monitoring-port", []network.PortRange{portRange}, nil) 455 wc.AssertNoChange() 456 457 // Open another range, detect a change. 458 otherRange := network.MustParsePortRange("1-100/tcp") 459 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, []network.PortRange{otherRange}, nil) 460 wc.AssertChange(expectChange) 461 wc.AssertNoChange() 462 463 // Close the other range, detect a change. 464 s.mustOpenCloseMachinePorts(c, s.machPortRanges, s.unit1.Name(), allEndpoints, nil, []network.PortRange{otherRange}) 465 wc.AssertChange(expectChange) 466 wc.AssertNoChange() 467 468 // Remove the ports document, detect a change. 469 assertRemoveMachinePortsDoc(c, s.machPortRanges) 470 wc.AssertChange(expectChange) 471 wc.AssertNoChange() 472 473 // And again - no change. 474 assertRemoveMachinePortsDoc(c, s.machPortRanges) 475 wc.AssertNoChange() 476 } 477 478 func (s *MachinePortsDocSuite) TestChangesForIndividualUnits(c *gc.C) { 479 unit1PortRanges := s.machPortRanges.ForUnit(s.unit1.Name()) 480 unit1PortRanges.Open(allEndpoints, network.MustParsePortRange("100-200/tcp")) 481 482 unit2PortRanges := s.machPortRanges.ForUnit(s.unit2.Name()) 483 unit2PortRanges.Open(allEndpoints, network.MustParsePortRange("8080/tcp")) 484 485 // Apply changes scoped to unit 1. The recorded changes for unit 2 486 // in the machine port ranges instance should remain intact. 487 c.Assert(s.State.ApplyOperation(unit1PortRanges.Changes()), jc.ErrorIsNil) 488 489 // Check that the existing machine port ranges instance reflects the 490 // unit 1 changes we just applied. 491 c.Assert(s.machPortRanges.UniquePortRanges(), gc.DeepEquals, []network.PortRange{ 492 network.MustParsePortRange("100-200/tcp"), 493 }, gc.Commentf("machine port ranges instance not updated correctly after unit-scoped port change application")) 494 495 // Grab a fresh copy of the machine ranges and verify the expected ports 496 // have been correctly persisted. 497 freshMachPortRanges, err := s.machine.OpenedPortRanges() 498 c.Assert(err, jc.ErrorIsNil) 499 c.Assert(freshMachPortRanges.UniquePortRanges(), gc.DeepEquals, []network.PortRange{ 500 network.MustParsePortRange("100-200/tcp"), 501 }, gc.Commentf("unit 1 changes were not correctly persisted to DB")) 502 503 // Apply pending changes scoped to unit 2. 504 c.Assert(s.State.ApplyOperation(unit2PortRanges.Changes()), jc.ErrorIsNil) 505 506 // Check that the existing machine port ranges instance reflects both 507 // unit 1 and unit 2 changes 508 c.Assert(s.machPortRanges.UniquePortRanges(), gc.DeepEquals, []network.PortRange{ 509 network.MustParsePortRange("100-200/tcp"), 510 network.MustParsePortRange("8080/tcp"), 511 }, gc.Commentf("machine port ranges instance not updated correctly after unit-scoped port change application")) 512 513 // Grab a fresh copy of the machine ranges and verify the expected ports 514 // have been correctly persisted. 515 freshMachPortRanges, err = s.machine.OpenedPortRanges() 516 c.Assert(err, jc.ErrorIsNil) 517 c.Assert(freshMachPortRanges.UniquePortRanges(), gc.DeepEquals, []network.PortRange{ 518 network.MustParsePortRange("100-200/tcp"), 519 network.MustParsePortRange("8080/tcp"), 520 }, gc.Commentf("unit changes were not correctly persisted to DB")) 521 522 // Verify that if we call changes on the machine ports instance we 523 // get no ops as everything has been committed. 524 _, err = s.machPortRanges.Changes().Build(0) 525 c.Assert(err, gc.Equals, jujutxn.ErrNoOperations, gc.Commentf("machine port range was not synced correctly after applying changes")) 526 }