github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/firewaller/firewaller.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package firewaller 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 "launchpad.net/tomb" 12 13 apifirewaller "github.com/juju/juju/api/firewaller" 14 apiwatcher "github.com/juju/juju/api/watcher" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/network" 20 "github.com/juju/juju/state/watcher" 21 "github.com/juju/juju/worker" 22 ) 23 24 type machineRanges map[network.PortRange]bool 25 26 // Firewaller watches the state for port ranges opened or closed on 27 // machines and reflects those changes onto the backing environment. 28 // Uses Firewaller API V1. 29 type Firewaller struct { 30 tomb tomb.Tomb 31 st *apifirewaller.State 32 environ environs.Environ 33 environWatcher apiwatcher.NotifyWatcher 34 machinesWatcher apiwatcher.StringsWatcher 35 portsWatcher apiwatcher.StringsWatcher 36 machineds map[names.MachineTag]*machineData 37 unitsChange chan *unitsChange 38 unitds map[names.UnitTag]*unitData 39 serviceds map[names.ServiceTag]*serviceData 40 exposedChange chan *exposedChange 41 globalMode bool 42 globalPortRef map[network.PortRange]int 43 machinePorts map[names.MachineTag]machineRanges 44 } 45 46 // NewFirewaller returns a new Firewaller or a new FirewallerV0, 47 // depending on what the API supports. 48 func NewFirewaller(st *apifirewaller.State) (_ worker.Worker, err error) { 49 fw := &Firewaller{ 50 st: st, 51 machineds: make(map[names.MachineTag]*machineData), 52 unitsChange: make(chan *unitsChange), 53 unitds: make(map[names.UnitTag]*unitData), 54 serviceds: make(map[names.ServiceTag]*serviceData), 55 exposedChange: make(chan *exposedChange), 56 machinePorts: make(map[names.MachineTag]machineRanges), 57 } 58 defer func() { 59 if err != nil { 60 fw.stopWatchers() 61 } 62 }() 63 64 fw.environWatcher, err = st.WatchForEnvironConfigChanges() 65 if err != nil { 66 return nil, err 67 } 68 69 fw.machinesWatcher, err = st.WatchEnvironMachines() 70 if err != nil { 71 return nil, err 72 } 73 74 fw.portsWatcher, err = st.WatchOpenedPorts() 75 if err != nil { 76 return nil, errors.Annotatef(err, "failed to start ports watcher") 77 } 78 logger.Debugf("started watching opened port ranges for the environment") 79 80 // We won't "wait" actually, because the environ is already 81 // available and has a guaranteed valid config, but until 82 // WaitForEnviron goes away, this code needs to stay. 83 fw.environ, err = worker.WaitForEnviron(fw.environWatcher, fw.st, fw.tomb.Dying()) 84 if err != nil { 85 return nil, err 86 } 87 88 switch fw.environ.Config().FirewallMode() { 89 case config.FwGlobal: 90 fw.globalMode = true 91 fw.globalPortRef = make(map[network.PortRange]int) 92 case config.FwNone: 93 logger.Warningf("stopping firewaller - firewall-mode is %q", config.FwNone) 94 return nil, errors.Errorf("firewaller is disabled when firewall-mode is %q", config.FwNone) 95 } 96 97 go func() { 98 defer fw.tomb.Done() 99 fw.tomb.Kill(fw.loop()) 100 }() 101 return fw, nil 102 } 103 104 var _ worker.Worker = (*Firewaller)(nil) 105 106 func (fw *Firewaller) loop() error { 107 defer fw.stopWatchers() 108 109 var reconciled bool 110 111 portsChange := fw.portsWatcher.Changes() 112 for { 113 select { 114 case <-fw.tomb.Dying(): 115 return tomb.ErrDying 116 case _, ok := <-fw.environWatcher.Changes(): 117 logger.Debugf("got environ config changes") 118 if !ok { 119 return watcher.EnsureErr(fw.environWatcher) 120 } 121 config, err := fw.st.EnvironConfig() 122 if err != nil { 123 return err 124 } 125 if err := fw.environ.SetConfig(config); err != nil { 126 logger.Errorf("loaded invalid environment configuration: %v", err) 127 } 128 case change, ok := <-fw.machinesWatcher.Changes(): 129 if !ok { 130 return watcher.EnsureErr(fw.machinesWatcher) 131 } 132 for _, machineId := range change { 133 fw.machineLifeChanged(names.NewMachineTag(machineId)) 134 } 135 if !reconciled { 136 reconciled = true 137 var err error 138 if fw.globalMode { 139 err = fw.reconcileGlobal() 140 } else { 141 err = fw.reconcileInstances() 142 } 143 if err != nil { 144 return err 145 } 146 } 147 case change, ok := <-portsChange: 148 if !ok { 149 return watcher.EnsureErr(fw.portsWatcher) 150 } 151 for _, portsGlobalKey := range change { 152 machineTag, networkTag, err := parsePortsKey(portsGlobalKey) 153 if err != nil { 154 return errors.Trace(err) 155 } 156 if err := fw.openedPortsChanged(machineTag, networkTag); err != nil { 157 return errors.Trace(err) 158 } 159 } 160 case change := <-fw.unitsChange: 161 if err := fw.unitsChanged(change); err != nil { 162 return err 163 } 164 case change := <-fw.exposedChange: 165 change.serviced.exposed = change.exposed 166 unitds := []*unitData{} 167 for _, unitd := range change.serviced.unitds { 168 unitds = append(unitds, unitd) 169 } 170 if err := fw.flushUnits(unitds); err != nil { 171 return errors.Annotate(err, "cannot change firewall ports") 172 } 173 } 174 } 175 } 176 177 // startMachine creates a new data value for tracking details of the 178 // machine and starts watching the machine for units added or removed. 179 func (fw *Firewaller) startMachine(tag names.MachineTag) error { 180 machined := &machineData{ 181 fw: fw, 182 tag: tag, 183 unitds: make(map[names.UnitTag]*unitData), 184 openedPorts: make([]network.PortRange, 0), 185 definedPorts: make(map[network.PortRange]names.UnitTag), 186 } 187 m, err := machined.machine() 188 if params.IsCodeNotFound(err) { 189 return nil 190 } else if err != nil { 191 return errors.Annotate(err, "cannot watch machine units") 192 } 193 unitw, err := m.WatchUnits() 194 if err != nil { 195 return err 196 } 197 select { 198 case <-fw.tomb.Dying(): 199 return tomb.ErrDying 200 case change, ok := <-unitw.Changes(): 201 if !ok { 202 return watcher.EnsureErr(unitw) 203 } 204 fw.machineds[tag] = machined 205 err = fw.unitsChanged(&unitsChange{machined, change}) 206 if err != nil { 207 delete(fw.machineds, tag) 208 return errors.Annotatef(err, "cannot respond to units changes for %q", tag) 209 } 210 } 211 go machined.watchLoop(unitw) 212 return nil 213 } 214 215 // startUnit creates a new data value for tracking details of the unit 216 // and starts watching the unit for port changes. The provided 217 // machineTag must be the tag for the machine the unit was last 218 // observed to be assigned to. 219 func (fw *Firewaller) startUnit(unit *apifirewaller.Unit, machineTag names.MachineTag) error { 220 service, err := unit.Service() 221 if err != nil { 222 return err 223 } 224 serviceTag := service.Tag() 225 unitTag := unit.Tag() 226 if err != nil { 227 return err 228 } 229 unitd := &unitData{ 230 fw: fw, 231 unit: unit, 232 tag: unitTag, 233 } 234 fw.unitds[unitTag] = unitd 235 236 unitd.machined = fw.machineds[machineTag] 237 unitd.machined.unitds[unitTag] = unitd 238 if fw.serviceds[serviceTag] == nil { 239 err := fw.startService(service) 240 if err != nil { 241 delete(fw.unitds, unitTag) 242 return err 243 } 244 } 245 unitd.serviced = fw.serviceds[serviceTag] 246 unitd.serviced.unitds[unitTag] = unitd 247 248 m, err := unitd.machined.machine() 249 if err != nil { 250 return err 251 } 252 253 // check if the machine has ports open on any networks 254 networkTags, err := m.ActiveNetworks() 255 if err != nil { 256 return errors.Annotatef(err, "failed getting %q active networks", machineTag) 257 } 258 for _, networkTag := range networkTags { 259 err := fw.openedPortsChanged(machineTag, networkTag) 260 if err != nil { 261 return err 262 } 263 } 264 265 return nil 266 } 267 268 // startService creates a new data value for tracking details of the 269 // service and starts watching the service for exposure changes. 270 func (fw *Firewaller) startService(service *apifirewaller.Service) error { 271 exposed, err := service.IsExposed() 272 if err != nil { 273 return err 274 } 275 serviced := &serviceData{ 276 fw: fw, 277 service: service, 278 exposed: exposed, 279 unitds: make(map[names.UnitTag]*unitData), 280 } 281 fw.serviceds[service.Tag()] = serviced 282 go serviced.watchLoop(serviced.exposed) 283 return nil 284 } 285 286 // reconcileGlobal compares the initially started watcher for machines, 287 // units and services with the opened and closed ports globally and 288 // opens and closes the appropriate ports for the whole environment. 289 func (fw *Firewaller) reconcileGlobal() error { 290 initialPortRanges, err := fw.environ.Ports() 291 if err != nil { 292 return err 293 } 294 collector := make(map[network.PortRange]bool) 295 for _, machined := range fw.machineds { 296 for portRange, unitTag := range machined.definedPorts { 297 unitd, known := machined.unitds[unitTag] 298 if !known { 299 delete(machined.unitds, unitTag) 300 continue 301 } 302 if unitd.serviced.exposed { 303 collector[portRange] = true 304 } 305 } 306 } 307 wantedPorts := []network.PortRange{} 308 for port := range collector { 309 wantedPorts = append(wantedPorts, port) 310 } 311 // Check which ports to open or to close. 312 toOpen := diffRanges(wantedPorts, initialPortRanges) 313 toClose := diffRanges(initialPortRanges, wantedPorts) 314 if len(toOpen) > 0 { 315 logger.Infof("opening global ports %v", toOpen) 316 if err := fw.environ.OpenPorts(toOpen); err != nil { 317 return err 318 } 319 network.SortPortRanges(toOpen) 320 } 321 if len(toClose) > 0 { 322 logger.Infof("closing global ports %v", toClose) 323 if err := fw.environ.ClosePorts(toClose); err != nil { 324 return err 325 } 326 network.SortPortRanges(toClose) 327 } 328 return nil 329 } 330 331 // reconcileInstances compares the initially started watcher for machines, 332 // units and services with the opened and closed ports of the instances and 333 // opens and closes the appropriate ports for each instance. 334 func (fw *Firewaller) reconcileInstances() error { 335 for _, machined := range fw.machineds { 336 m, err := machined.machine() 337 if params.IsCodeNotFound(err) { 338 if err := fw.forgetMachine(machined); err != nil { 339 return err 340 } 341 continue 342 } else if err != nil { 343 return err 344 } 345 instanceId, err := m.InstanceId() 346 if err != nil { 347 return err 348 } 349 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 350 if err == environs.ErrNoInstances { 351 return nil 352 } else if err != nil { 353 return err 354 } 355 machineId := machined.tag.Id() 356 initialPortRanges, err := instances[0].Ports(machineId) 357 if err != nil { 358 return err 359 } 360 361 // Check which ports to open or to close. 362 toOpen := diffRanges(machined.openedPorts, initialPortRanges) 363 toClose := diffRanges(initialPortRanges, machined.openedPorts) 364 if len(toOpen) > 0 { 365 logger.Infof("opening instance port ranges %v for %q", 366 toOpen, machined.tag) 367 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 368 // TODO(mue) Add local retry logic. 369 return err 370 } 371 network.SortPortRanges(toOpen) 372 } 373 if len(toClose) > 0 { 374 logger.Infof("closing instance port ranges %v for %q", 375 toClose, machined.tag) 376 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 377 // TODO(mue) Add local retry logic. 378 return err 379 } 380 network.SortPortRanges(toClose) 381 } 382 } 383 return nil 384 } 385 386 // unitsChanged responds to changes to the assigned units. 387 func (fw *Firewaller) unitsChanged(change *unitsChange) error { 388 changed := []*unitData{} 389 for _, name := range change.units { 390 unitTag := names.NewUnitTag(name) 391 unit, err := fw.st.Unit(unitTag) 392 if err != nil && !params.IsCodeNotFound(err) { 393 return err 394 } 395 var machineTag names.MachineTag 396 if unit != nil { 397 machineTag, err = unit.AssignedMachine() 398 if params.IsCodeNotFound(err) { 399 continue 400 } else if err != nil && !params.IsCodeNotAssigned(err) { 401 return err 402 } 403 } 404 if unitd, known := fw.unitds[unitTag]; known { 405 knownMachineTag := fw.unitds[unitTag].machined.tag 406 if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { 407 fw.forgetUnit(unitd) 408 changed = append(changed, unitd) 409 logger.Debugf("stopped watching unit %s", name) 410 } 411 // TODO(dfc) fw.machineds should be map[names.Tag] 412 } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { 413 err = fw.startUnit(unit, machineTag) 414 if err != nil { 415 return err 416 } 417 changed = append(changed, fw.unitds[unitTag]) 418 logger.Debugf("started watching %q", unitTag) 419 } 420 } 421 if err := fw.flushUnits(changed); err != nil { 422 return errors.Annotate(err, "cannot change firewall ports") 423 } 424 return nil 425 } 426 427 // openedPortsChanged handles port change notifications 428 func (fw *Firewaller) openedPortsChanged(machineTag names.MachineTag, networkTag names.NetworkTag) error { 429 430 machined, ok := fw.machineds[machineTag] 431 if !ok { 432 // It is common to receive a port change notification before 433 // registering the machine, so if a machine is not found in 434 // firewaller's list, just skip the change. 435 logger.Errorf("failed to lookup %q, skipping port change", machineTag) 436 return nil 437 } 438 439 m, err := machined.machine() 440 if err != nil { 441 return err 442 } 443 444 ports, err := m.OpenedPorts(networkTag) 445 if err != nil { 446 return err 447 } 448 449 newPortRanges := make(map[network.PortRange]names.UnitTag) 450 for portRange, unitTag := range ports { 451 unitd, ok := machined.unitds[unitTag] 452 if !ok { 453 // It is common to receive port change notification before 454 // registering a unit. Skip handling the port change - it will 455 // be handled when the unit is registered. 456 logger.Errorf("failed to lookup %q, skipping port change", unitTag) 457 return nil 458 } 459 newPortRanges[portRange] = unitd.tag 460 } 461 462 if !portMapsEqual(machined.definedPorts, newPortRanges) { 463 machined.definedPorts = newPortRanges 464 return fw.flushMachine(machined) 465 } 466 return nil 467 } 468 469 func portMapsEqual(a, b map[network.PortRange]names.UnitTag) bool { 470 if len(a) != len(b) { 471 return false 472 } 473 for key, valueA := range a { 474 valueB, exists := b[key] 475 if !exists { 476 return false 477 } 478 if valueA != valueB { 479 return false 480 } 481 } 482 return true 483 } 484 485 // flushUnits opens and closes ports for the passed unit data. 486 func (fw *Firewaller) flushUnits(unitds []*unitData) error { 487 machineds := map[names.MachineTag]*machineData{} 488 for _, unitd := range unitds { 489 machineds[unitd.machined.tag] = unitd.machined 490 } 491 for _, machined := range machineds { 492 if err := fw.flushMachine(machined); err != nil { 493 return err 494 } 495 } 496 return nil 497 } 498 499 // flushMachine opens and closes ports for the passed machine. 500 func (fw *Firewaller) flushMachine(machined *machineData) error { 501 // Gather ports to open and close. 502 want := []network.PortRange{} 503 for portRange, unitTag := range machined.definedPorts { 504 unitd, known := machined.unitds[unitTag] 505 if !known { 506 delete(machined.unitds, unitTag) 507 continue 508 } 509 if unitd.serviced.exposed { 510 want = append(want, portRange) 511 } 512 } 513 toOpen := diffRanges(want, machined.openedPorts) 514 toClose := diffRanges(machined.openedPorts, want) 515 machined.openedPorts = want 516 if fw.globalMode { 517 return fw.flushGlobalPorts(toOpen, toClose) 518 } 519 return fw.flushInstancePorts(machined, toOpen, toClose) 520 } 521 522 // flushGlobalPorts opens and closes global ports in the environment. 523 // It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events 524 // modify the environment. 525 func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []network.PortRange) error { 526 // Filter which ports are really to open or close. 527 var toOpen, toClose []network.PortRange 528 for _, portRange := range rawOpen { 529 if fw.globalPortRef[portRange] == 0 { 530 toOpen = append(toOpen, portRange) 531 } 532 fw.globalPortRef[portRange]++ 533 } 534 for _, portRange := range rawClose { 535 fw.globalPortRef[portRange]-- 536 if fw.globalPortRef[portRange] == 0 { 537 toClose = append(toClose, portRange) 538 delete(fw.globalPortRef, portRange) 539 } 540 } 541 // Open and close the ports. 542 if len(toOpen) > 0 { 543 if err := fw.environ.OpenPorts(toOpen); err != nil { 544 // TODO(mue) Add local retry logic. 545 return err 546 } 547 network.SortPortRanges(toOpen) 548 logger.Infof("opened port ranges %v in environment", toOpen) 549 } 550 if len(toClose) > 0 { 551 if err := fw.environ.ClosePorts(toClose); err != nil { 552 // TODO(mue) Add local retry logic. 553 return err 554 } 555 network.SortPortRanges(toClose) 556 logger.Infof("closed port ranges %v in environment", toClose) 557 } 558 return nil 559 } 560 561 // flushInstancePorts opens and closes ports global on the machine. 562 func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []network.PortRange) error { 563 // If there's nothing to do, do nothing. 564 // This is important because when a machine is first created, 565 // it will have no instance id but also no open ports - 566 // InstanceId will fail but we don't care. 567 if len(toOpen) == 0 && len(toClose) == 0 { 568 return nil 569 } 570 m, err := machined.machine() 571 if params.IsCodeNotFound(err) { 572 return nil 573 } 574 if err != nil { 575 return err 576 } 577 machineId := machined.tag.Id() 578 instanceId, err := m.InstanceId() 579 if err != nil { 580 return err 581 } 582 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 583 if err != nil { 584 return err 585 } 586 // Open and close the ports. 587 if len(toOpen) > 0 { 588 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 589 // TODO(mue) Add local retry logic. 590 return err 591 } 592 network.SortPortRanges(toOpen) 593 logger.Infof("opened port ranges %v on %q", toOpen, machined.tag) 594 } 595 if len(toClose) > 0 { 596 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 597 // TODO(mue) Add local retry logic. 598 return err 599 } 600 network.SortPortRanges(toClose) 601 logger.Infof("closed port ranges %v on %q", toClose, machined.tag) 602 } 603 return nil 604 } 605 606 // machineLifeChanged starts watching new machines when the firewaller 607 // is starting, or when new machines come to life, and stops watching 608 // machines that are dying. 609 func (fw *Firewaller) machineLifeChanged(tag names.MachineTag) error { 610 m, err := fw.st.Machine(tag) 611 found := !params.IsCodeNotFound(err) 612 if found && err != nil { 613 return err 614 } 615 dead := !found || m.Life() == params.Dead 616 machined, known := fw.machineds[tag] 617 if known && dead { 618 return fw.forgetMachine(machined) 619 } 620 if !known && !dead { 621 err = fw.startMachine(tag) 622 if err != nil { 623 return err 624 } 625 logger.Debugf("started watching %q", tag) 626 } 627 return nil 628 } 629 630 // forgetMachine cleans the machine data after the machine is removed. 631 func (fw *Firewaller) forgetMachine(machined *machineData) error { 632 for _, unitd := range machined.unitds { 633 fw.forgetUnit(unitd) 634 } 635 if err := fw.flushMachine(machined); err != nil { 636 return err 637 } 638 delete(fw.machineds, machined.tag) 639 if err := machined.Stop(); err != nil { 640 return err 641 } 642 logger.Debugf("stopped watching %q", machined.tag) 643 return nil 644 } 645 646 // forgetUnit cleans the unit data after the unit is removed. 647 func (fw *Firewaller) forgetUnit(unitd *unitData) { 648 serviced := unitd.serviced 649 machined := unitd.machined 650 651 // Clean up after stopping. 652 delete(fw.unitds, unitd.tag) 653 delete(machined.unitds, unitd.tag) 654 delete(serviced.unitds, unitd.tag) 655 if len(serviced.unitds) == 0 { 656 // Stop service data after all units are removed. 657 if err := serviced.Stop(); err != nil { 658 logger.Errorf("service watcher %q returned error when stopping: %v", serviced.service.Name(), err) 659 } 660 delete(fw.serviceds, serviced.service.Tag()) 661 } 662 } 663 664 // stopWatchers stops all the firewaller's watchers. 665 func (fw *Firewaller) stopWatchers() { 666 if fw.environWatcher != nil { 667 watcher.Stop(fw.environWatcher, &fw.tomb) 668 } 669 if fw.machinesWatcher != nil { 670 watcher.Stop(fw.machinesWatcher, &fw.tomb) 671 } 672 if fw.portsWatcher != nil { 673 watcher.Stop(fw.portsWatcher, &fw.tomb) 674 } 675 for _, serviced := range fw.serviceds { 676 if serviced != nil { 677 watcher.Stop(serviced, &fw.tomb) 678 } 679 } 680 for _, machined := range fw.machineds { 681 if machined != nil { 682 watcher.Stop(machined, &fw.tomb) 683 } 684 } 685 } 686 687 // Err returns the reason why the firewaller has stopped or tomb.ErrStillAlive 688 // when it is still alive. 689 func (fw *Firewaller) Err() (reason error) { 690 return fw.tomb.Err() 691 } 692 693 // Kill implements worker.Worker.Kill. 694 func (fw *Firewaller) Kill() { 695 fw.tomb.Kill(nil) 696 } 697 698 // Wait implements worker.Worker.Wait. 699 func (fw *Firewaller) Wait() error { 700 return fw.tomb.Wait() 701 } 702 703 // unitsChange contains the changed units for one specific machine. 704 type unitsChange struct { 705 machined *machineData 706 units []string 707 } 708 709 // machineData holds machine details and watches units added or removed. 710 type machineData struct { 711 tomb tomb.Tomb 712 fw *Firewaller 713 tag names.MachineTag 714 unitds map[names.UnitTag]*unitData 715 openedPorts []network.PortRange 716 // ports defined by units on this machine 717 definedPorts map[network.PortRange]names.UnitTag 718 } 719 720 func (md *machineData) machine() (*apifirewaller.Machine, error) { 721 return md.fw.st.Machine(md.tag) 722 } 723 724 // watchLoop watches the machine for units added or removed. 725 func (md *machineData) watchLoop(unitw apiwatcher.StringsWatcher) { 726 defer md.tomb.Done() 727 defer watcher.Stop(unitw, &md.tomb) 728 for { 729 select { 730 case <-md.tomb.Dying(): 731 return 732 case change, ok := <-unitw.Changes(): 733 if !ok { 734 _, err := md.machine() 735 if !params.IsCodeNotFound(err) { 736 md.fw.tomb.Kill(watcher.EnsureErr(unitw)) 737 } 738 return 739 } 740 select { 741 case md.fw.unitsChange <- &unitsChange{md, change}: 742 case <-md.tomb.Dying(): 743 return 744 } 745 } 746 } 747 } 748 749 // Stop stops the machine watching. 750 func (md *machineData) Stop() error { 751 md.tomb.Kill(nil) 752 return md.tomb.Wait() 753 } 754 755 // unitData holds unit details and watches port changes. 756 type unitData struct { 757 tomb tomb.Tomb 758 fw *Firewaller 759 tag names.UnitTag 760 unit *apifirewaller.Unit 761 serviced *serviceData 762 machined *machineData 763 } 764 765 // exposedChange contains the changed exposed flag for one specific service. 766 type exposedChange struct { 767 serviced *serviceData 768 exposed bool 769 } 770 771 // serviceData holds service details and watches exposure changes. 772 type serviceData struct { 773 tomb tomb.Tomb 774 fw *Firewaller 775 service *apifirewaller.Service 776 exposed bool 777 unitds map[names.UnitTag]*unitData 778 } 779 780 // watchLoop watches the service's exposed flag for changes. 781 func (sd *serviceData) watchLoop(exposed bool) { 782 defer sd.tomb.Done() 783 w, err := sd.service.Watch() 784 if err != nil { 785 sd.fw.tomb.Kill(err) 786 return 787 } 788 defer watcher.Stop(w, &sd.tomb) 789 for { 790 select { 791 case <-sd.tomb.Dying(): 792 return 793 case _, ok := <-w.Changes(): 794 if !ok { 795 sd.fw.tomb.Kill(watcher.EnsureErr(w)) 796 return 797 } 798 if err := sd.service.Refresh(); err != nil { 799 if !params.IsCodeNotFound(err) { 800 sd.fw.tomb.Kill(err) 801 } 802 return 803 } 804 change, err := sd.service.IsExposed() 805 if err != nil { 806 sd.fw.tomb.Kill(err) 807 return 808 } 809 if change == exposed { 810 continue 811 } 812 exposed = change 813 select { 814 case sd.fw.exposedChange <- &exposedChange{sd, change}: 815 case <-sd.tomb.Dying(): 816 return 817 } 818 } 819 } 820 } 821 822 // Stop stops the service watching. 823 func (sd *serviceData) Stop() error { 824 sd.tomb.Kill(nil) 825 return sd.tomb.Wait() 826 } 827 828 // diffRanges returns all the port rangess that exist in A but not B. 829 func diffRanges(A, B []network.PortRange) (missing []network.PortRange) { 830 next: 831 for _, a := range A { 832 for _, b := range B { 833 if a == b { 834 continue next 835 } 836 } 837 missing = append(missing, a) 838 } 839 return 840 } 841 842 // parsePortsKey parses a ports document global key coming from the 843 // ports watcher (e.g. "42:juju-public") and returns the machine and 844 // network tags from its components (in the last example "machine-42" 845 // and "network-juju-public"). 846 func parsePortsKey(change string) (machineTag names.MachineTag, networkTag names.NetworkTag, err error) { 847 defer errors.DeferredAnnotatef(&err, "invalid ports change %q", change) 848 849 parts := strings.SplitN(change, ":", 2) 850 if len(parts) != 2 { 851 return names.MachineTag{}, names.NetworkTag{}, errors.Errorf("unexpected format") 852 } 853 machineId, networkName := parts[0], parts[1] 854 return names.NewMachineTag(machineId), names.NewNetworkTag(networkName), nil 855 }