github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 errors.IsNotProvisioned(err) { 347 logger.Warningf("Machine not yet provisioned: %v", err) 348 continue 349 } 350 if err != nil { 351 return err 352 } 353 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 354 if err == environs.ErrNoInstances { 355 return nil 356 } else if err != nil { 357 return err 358 } 359 machineId := machined.tag.Id() 360 initialPortRanges, err := instances[0].Ports(machineId) 361 if err != nil { 362 return err 363 } 364 365 // Check which ports to open or to close. 366 toOpen := diffRanges(machined.openedPorts, initialPortRanges) 367 toClose := diffRanges(initialPortRanges, machined.openedPorts) 368 if len(toOpen) > 0 { 369 logger.Infof("opening instance port ranges %v for %q", 370 toOpen, machined.tag) 371 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 372 // TODO(mue) Add local retry logic. 373 return err 374 } 375 network.SortPortRanges(toOpen) 376 } 377 if len(toClose) > 0 { 378 logger.Infof("closing instance port ranges %v for %q", 379 toClose, machined.tag) 380 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 381 // TODO(mue) Add local retry logic. 382 return err 383 } 384 network.SortPortRanges(toClose) 385 } 386 } 387 return nil 388 } 389 390 // unitsChanged responds to changes to the assigned units. 391 func (fw *Firewaller) unitsChanged(change *unitsChange) error { 392 changed := []*unitData{} 393 for _, name := range change.units { 394 unitTag := names.NewUnitTag(name) 395 unit, err := fw.st.Unit(unitTag) 396 if err != nil && !params.IsCodeNotFound(err) { 397 return err 398 } 399 var machineTag names.MachineTag 400 if unit != nil { 401 machineTag, err = unit.AssignedMachine() 402 if params.IsCodeNotFound(err) { 403 continue 404 } else if err != nil && !params.IsCodeNotAssigned(err) { 405 return err 406 } 407 } 408 if unitd, known := fw.unitds[unitTag]; known { 409 knownMachineTag := fw.unitds[unitTag].machined.tag 410 if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { 411 fw.forgetUnit(unitd) 412 changed = append(changed, unitd) 413 logger.Debugf("stopped watching unit %s", name) 414 } 415 // TODO(dfc) fw.machineds should be map[names.Tag] 416 } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { 417 err = fw.startUnit(unit, machineTag) 418 if err != nil { 419 return err 420 } 421 changed = append(changed, fw.unitds[unitTag]) 422 logger.Debugf("started watching %q", unitTag) 423 } 424 } 425 if err := fw.flushUnits(changed); err != nil { 426 return errors.Annotate(err, "cannot change firewall ports") 427 } 428 return nil 429 } 430 431 // openedPortsChanged handles port change notifications 432 func (fw *Firewaller) openedPortsChanged(machineTag names.MachineTag, networkTag names.NetworkTag) error { 433 434 machined, ok := fw.machineds[machineTag] 435 if !ok { 436 // It is common to receive a port change notification before 437 // registering the machine, so if a machine is not found in 438 // firewaller's list, just skip the change. 439 logger.Errorf("failed to lookup %q, skipping port change", machineTag) 440 return nil 441 } 442 443 m, err := machined.machine() 444 if err != nil { 445 return err 446 } 447 448 ports, err := m.OpenedPorts(networkTag) 449 if err != nil { 450 return err 451 } 452 453 newPortRanges := make(map[network.PortRange]names.UnitTag) 454 for portRange, unitTag := range ports { 455 unitd, ok := machined.unitds[unitTag] 456 if !ok { 457 // It is common to receive port change notification before 458 // registering a unit. Skip handling the port change - it will 459 // be handled when the unit is registered. 460 logger.Errorf("failed to lookup %q, skipping port change", unitTag) 461 return nil 462 } 463 newPortRanges[portRange] = unitd.tag 464 } 465 466 if !portMapsEqual(machined.definedPorts, newPortRanges) { 467 machined.definedPorts = newPortRanges 468 return fw.flushMachine(machined) 469 } 470 return nil 471 } 472 473 func portMapsEqual(a, b map[network.PortRange]names.UnitTag) bool { 474 if len(a) != len(b) { 475 return false 476 } 477 for key, valueA := range a { 478 valueB, exists := b[key] 479 if !exists { 480 return false 481 } 482 if valueA != valueB { 483 return false 484 } 485 } 486 return true 487 } 488 489 // flushUnits opens and closes ports for the passed unit data. 490 func (fw *Firewaller) flushUnits(unitds []*unitData) error { 491 machineds := map[names.MachineTag]*machineData{} 492 for _, unitd := range unitds { 493 machineds[unitd.machined.tag] = unitd.machined 494 } 495 for _, machined := range machineds { 496 if err := fw.flushMachine(machined); err != nil { 497 return err 498 } 499 } 500 return nil 501 } 502 503 // flushMachine opens and closes ports for the passed machine. 504 func (fw *Firewaller) flushMachine(machined *machineData) error { 505 // Gather ports to open and close. 506 want := []network.PortRange{} 507 for portRange, unitTag := range machined.definedPorts { 508 unitd, known := machined.unitds[unitTag] 509 if !known { 510 delete(machined.unitds, unitTag) 511 continue 512 } 513 if unitd.serviced.exposed { 514 want = append(want, portRange) 515 } 516 } 517 toOpen := diffRanges(want, machined.openedPorts) 518 toClose := diffRanges(machined.openedPorts, want) 519 machined.openedPorts = want 520 if fw.globalMode { 521 return fw.flushGlobalPorts(toOpen, toClose) 522 } 523 return fw.flushInstancePorts(machined, toOpen, toClose) 524 } 525 526 // flushGlobalPorts opens and closes global ports in the environment. 527 // It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events 528 // modify the environment. 529 func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []network.PortRange) error { 530 // Filter which ports are really to open or close. 531 var toOpen, toClose []network.PortRange 532 for _, portRange := range rawOpen { 533 if fw.globalPortRef[portRange] == 0 { 534 toOpen = append(toOpen, portRange) 535 } 536 fw.globalPortRef[portRange]++ 537 } 538 for _, portRange := range rawClose { 539 fw.globalPortRef[portRange]-- 540 if fw.globalPortRef[portRange] == 0 { 541 toClose = append(toClose, portRange) 542 delete(fw.globalPortRef, portRange) 543 } 544 } 545 // Open and close the ports. 546 if len(toOpen) > 0 { 547 if err := fw.environ.OpenPorts(toOpen); err != nil { 548 // TODO(mue) Add local retry logic. 549 return err 550 } 551 network.SortPortRanges(toOpen) 552 logger.Infof("opened port ranges %v in environment", toOpen) 553 } 554 if len(toClose) > 0 { 555 if err := fw.environ.ClosePorts(toClose); err != nil { 556 // TODO(mue) Add local retry logic. 557 return err 558 } 559 network.SortPortRanges(toClose) 560 logger.Infof("closed port ranges %v in environment", toClose) 561 } 562 return nil 563 } 564 565 // flushInstancePorts opens and closes ports global on the machine. 566 func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []network.PortRange) error { 567 // If there's nothing to do, do nothing. 568 // This is important because when a machine is first created, 569 // it will have no instance id but also no open ports - 570 // InstanceId will fail but we don't care. 571 if len(toOpen) == 0 && len(toClose) == 0 { 572 return nil 573 } 574 m, err := machined.machine() 575 if params.IsCodeNotFound(err) { 576 return nil 577 } 578 if err != nil { 579 return err 580 } 581 machineId := machined.tag.Id() 582 instanceId, err := m.InstanceId() 583 if err != nil { 584 return err 585 } 586 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 587 if err != nil { 588 return err 589 } 590 // Open and close the ports. 591 if len(toOpen) > 0 { 592 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 593 // TODO(mue) Add local retry logic. 594 return err 595 } 596 network.SortPortRanges(toOpen) 597 logger.Infof("opened port ranges %v on %q", toOpen, machined.tag) 598 } 599 if len(toClose) > 0 { 600 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 601 // TODO(mue) Add local retry logic. 602 return err 603 } 604 network.SortPortRanges(toClose) 605 logger.Infof("closed port ranges %v on %q", toClose, machined.tag) 606 } 607 return nil 608 } 609 610 // machineLifeChanged starts watching new machines when the firewaller 611 // is starting, or when new machines come to life, and stops watching 612 // machines that are dying. 613 func (fw *Firewaller) machineLifeChanged(tag names.MachineTag) error { 614 m, err := fw.st.Machine(tag) 615 found := !params.IsCodeNotFound(err) 616 if found && err != nil { 617 return err 618 } 619 dead := !found || m.Life() == params.Dead 620 machined, known := fw.machineds[tag] 621 if known && dead { 622 return fw.forgetMachine(machined) 623 } 624 if !known && !dead { 625 err = fw.startMachine(tag) 626 if err != nil { 627 return err 628 } 629 logger.Debugf("started watching %q", tag) 630 } 631 return nil 632 } 633 634 // forgetMachine cleans the machine data after the machine is removed. 635 func (fw *Firewaller) forgetMachine(machined *machineData) error { 636 for _, unitd := range machined.unitds { 637 fw.forgetUnit(unitd) 638 } 639 if err := fw.flushMachine(machined); err != nil { 640 return err 641 } 642 delete(fw.machineds, machined.tag) 643 if err := machined.Stop(); err != nil { 644 return err 645 } 646 logger.Debugf("stopped watching %q", machined.tag) 647 return nil 648 } 649 650 // forgetUnit cleans the unit data after the unit is removed. 651 func (fw *Firewaller) forgetUnit(unitd *unitData) { 652 serviced := unitd.serviced 653 machined := unitd.machined 654 655 // Clean up after stopping. 656 delete(fw.unitds, unitd.tag) 657 delete(machined.unitds, unitd.tag) 658 delete(serviced.unitds, unitd.tag) 659 if len(serviced.unitds) == 0 { 660 // Stop service data after all units are removed. 661 if err := serviced.Stop(); err != nil { 662 logger.Errorf("service watcher %q returned error when stopping: %v", serviced.service.Name(), err) 663 } 664 delete(fw.serviceds, serviced.service.Tag()) 665 } 666 } 667 668 // stopWatchers stops all the firewaller's watchers. 669 func (fw *Firewaller) stopWatchers() { 670 if fw.environWatcher != nil { 671 watcher.Stop(fw.environWatcher, &fw.tomb) 672 } 673 if fw.machinesWatcher != nil { 674 watcher.Stop(fw.machinesWatcher, &fw.tomb) 675 } 676 if fw.portsWatcher != nil { 677 watcher.Stop(fw.portsWatcher, &fw.tomb) 678 } 679 for _, serviced := range fw.serviceds { 680 if serviced != nil { 681 watcher.Stop(serviced, &fw.tomb) 682 } 683 } 684 for _, machined := range fw.machineds { 685 if machined != nil { 686 watcher.Stop(machined, &fw.tomb) 687 } 688 } 689 } 690 691 // Err returns the reason why the firewaller has stopped or tomb.ErrStillAlive 692 // when it is still alive. 693 func (fw *Firewaller) Err() (reason error) { 694 return fw.tomb.Err() 695 } 696 697 // Kill implements worker.Worker.Kill. 698 func (fw *Firewaller) Kill() { 699 fw.tomb.Kill(nil) 700 } 701 702 // Wait implements worker.Worker.Wait. 703 func (fw *Firewaller) Wait() error { 704 return fw.tomb.Wait() 705 } 706 707 // unitsChange contains the changed units for one specific machine. 708 type unitsChange struct { 709 machined *machineData 710 units []string 711 } 712 713 // machineData holds machine details and watches units added or removed. 714 type machineData struct { 715 tomb tomb.Tomb 716 fw *Firewaller 717 tag names.MachineTag 718 unitds map[names.UnitTag]*unitData 719 openedPorts []network.PortRange 720 // ports defined by units on this machine 721 definedPorts map[network.PortRange]names.UnitTag 722 } 723 724 func (md *machineData) machine() (*apifirewaller.Machine, error) { 725 return md.fw.st.Machine(md.tag) 726 } 727 728 // watchLoop watches the machine for units added or removed. 729 func (md *machineData) watchLoop(unitw apiwatcher.StringsWatcher) { 730 defer md.tomb.Done() 731 defer watcher.Stop(unitw, &md.tomb) 732 for { 733 select { 734 case <-md.tomb.Dying(): 735 return 736 case change, ok := <-unitw.Changes(): 737 if !ok { 738 _, err := md.machine() 739 if !params.IsCodeNotFound(err) { 740 md.fw.tomb.Kill(watcher.EnsureErr(unitw)) 741 } 742 return 743 } 744 select { 745 case md.fw.unitsChange <- &unitsChange{md, change}: 746 case <-md.tomb.Dying(): 747 return 748 } 749 } 750 } 751 } 752 753 // Stop stops the machine watching. 754 func (md *machineData) Stop() error { 755 md.tomb.Kill(nil) 756 return md.tomb.Wait() 757 } 758 759 // unitData holds unit details and watches port changes. 760 type unitData struct { 761 tomb tomb.Tomb 762 fw *Firewaller 763 tag names.UnitTag 764 unit *apifirewaller.Unit 765 serviced *serviceData 766 machined *machineData 767 } 768 769 // exposedChange contains the changed exposed flag for one specific service. 770 type exposedChange struct { 771 serviced *serviceData 772 exposed bool 773 } 774 775 // serviceData holds service details and watches exposure changes. 776 type serviceData struct { 777 tomb tomb.Tomb 778 fw *Firewaller 779 service *apifirewaller.Service 780 exposed bool 781 unitds map[names.UnitTag]*unitData 782 } 783 784 // watchLoop watches the service's exposed flag for changes. 785 func (sd *serviceData) watchLoop(exposed bool) { 786 defer sd.tomb.Done() 787 w, err := sd.service.Watch() 788 if err != nil { 789 sd.fw.tomb.Kill(err) 790 return 791 } 792 defer watcher.Stop(w, &sd.tomb) 793 for { 794 select { 795 case <-sd.tomb.Dying(): 796 return 797 case _, ok := <-w.Changes(): 798 if !ok { 799 sd.fw.tomb.Kill(watcher.EnsureErr(w)) 800 return 801 } 802 if err := sd.service.Refresh(); err != nil { 803 if !params.IsCodeNotFound(err) { 804 sd.fw.tomb.Kill(err) 805 } 806 return 807 } 808 change, err := sd.service.IsExposed() 809 if err != nil { 810 sd.fw.tomb.Kill(err) 811 return 812 } 813 if change == exposed { 814 continue 815 } 816 exposed = change 817 select { 818 case sd.fw.exposedChange <- &exposedChange{sd, change}: 819 case <-sd.tomb.Dying(): 820 return 821 } 822 } 823 } 824 } 825 826 // Stop stops the service watching. 827 func (sd *serviceData) Stop() error { 828 sd.tomb.Kill(nil) 829 return sd.tomb.Wait() 830 } 831 832 // diffRanges returns all the port rangess that exist in A but not B. 833 func diffRanges(A, B []network.PortRange) (missing []network.PortRange) { 834 next: 835 for _, a := range A { 836 for _, b := range B { 837 if a == b { 838 continue next 839 } 840 } 841 missing = append(missing, a) 842 } 843 return 844 } 845 846 // parsePortsKey parses a ports document global key coming from the 847 // ports watcher (e.g. "42:juju-public") and returns the machine and 848 // network tags from its components (in the last example "machine-42" 849 // and "network-juju-public"). 850 func parsePortsKey(change string) (machineTag names.MachineTag, networkTag names.NetworkTag, err error) { 851 defer errors.DeferredAnnotatef(&err, "invalid ports change %q", change) 852 853 parts := strings.SplitN(change, ":", 2) 854 if len(parts) != 2 { 855 return names.MachineTag{}, names.NetworkTag{}, errors.Errorf("unexpected format") 856 } 857 machineId, networkName := parts[0], parts[1] 858 return names.NewMachineTag(machineId), names.NewNetworkTag(networkName), nil 859 }