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