github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "gopkg.in/juju/names.v2" 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 applicationids map[names.ApplicationTag]*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 applicationids: make(map[names.ApplicationTag]*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 253 // register the machined with the firewaller's catacomb. 254 return fw.catacomb.Add(machined) 255 } 256 257 // startUnit creates a new data value for tracking details of the unit 258 // The provided machineTag must be the tag for the machine the unit was last 259 // observed to be assigned to. 260 func (fw *Firewaller) startUnit(unit *firewaller.Unit, machineTag names.MachineTag) error { 261 application, err := unit.Application() 262 if err != nil { 263 return err 264 } 265 applicationTag := application.Tag() 266 unitTag := unit.Tag() 267 if err != nil { 268 return err 269 } 270 unitd := &unitData{ 271 fw: fw, 272 unit: unit, 273 tag: unitTag, 274 } 275 fw.unitds[unitTag] = unitd 276 277 unitd.machined = fw.machineds[machineTag] 278 unitd.machined.unitds[unitTag] = unitd 279 if fw.applicationids[applicationTag] == nil { 280 err := fw.startService(application) 281 if err != nil { 282 delete(fw.unitds, unitTag) 283 return err 284 } 285 } 286 unitd.serviced = fw.applicationids[applicationTag] 287 unitd.serviced.unitds[unitTag] = unitd 288 289 m, err := unitd.machined.machine() 290 if err != nil { 291 return err 292 } 293 294 // check if the machine has ports open on any subnets 295 subnetTags, err := m.ActiveSubnets() 296 if err != nil { 297 return errors.Annotatef(err, "failed getting %q active subnets", machineTag) 298 } 299 for _, subnetTag := range subnetTags { 300 err := fw.openedPortsChanged(machineTag, subnetTag) 301 if err != nil { 302 return err 303 } 304 } 305 306 return nil 307 } 308 309 // startService creates a new data value for tracking details of the 310 // service and starts watching the service for exposure changes. 311 func (fw *Firewaller) startService(service *firewaller.Application) error { 312 exposed, err := service.IsExposed() 313 if err != nil { 314 return err 315 } 316 serviced := &serviceData{ 317 fw: fw, 318 application: service, 319 exposed: exposed, 320 unitds: make(map[names.UnitTag]*unitData), 321 } 322 err = catacomb.Invoke(catacomb.Plan{ 323 Site: &serviced.catacomb, 324 Work: func() error { 325 return serviced.watchLoop(exposed) 326 }, 327 }) 328 if err != nil { 329 return errors.Trace(err) 330 } 331 if err := fw.catacomb.Add(serviced); err != nil { 332 return errors.Trace(err) 333 } 334 fw.applicationids[service.Tag()] = serviced 335 return nil 336 } 337 338 // reconcileGlobal compares the initially started watcher for machines, 339 // units and services with the opened and closed ports globally and 340 // opens and closes the appropriate ports for the whole environment. 341 func (fw *Firewaller) reconcileGlobal() error { 342 initialPortRanges, err := fw.environ.Ports() 343 if err != nil { 344 return err 345 } 346 collector := make(map[network.PortRange]bool) 347 for _, machined := range fw.machineds { 348 for portRange, unitTag := range machined.definedPorts { 349 unitd, known := machined.unitds[unitTag] 350 if !known { 351 delete(machined.unitds, unitTag) 352 continue 353 } 354 if unitd.serviced.exposed { 355 collector[portRange] = true 356 } 357 } 358 } 359 wantedPorts := []network.PortRange{} 360 for port := range collector { 361 wantedPorts = append(wantedPorts, port) 362 } 363 // Check which ports to open or to close. 364 toOpen := diffRanges(wantedPorts, initialPortRanges) 365 toClose := diffRanges(initialPortRanges, wantedPorts) 366 if len(toOpen) > 0 { 367 logger.Infof("opening global ports %v", toOpen) 368 if err := fw.environ.OpenPorts(toOpen); err != nil { 369 return err 370 } 371 network.SortPortRanges(toOpen) 372 } 373 if len(toClose) > 0 { 374 logger.Infof("closing global ports %v", toClose) 375 if err := fw.environ.ClosePorts(toClose); err != nil { 376 return err 377 } 378 network.SortPortRanges(toClose) 379 } 380 return nil 381 } 382 383 // reconcileInstances compares the initially started watcher for machines, 384 // units and services with the opened and closed ports of the instances and 385 // opens and closes the appropriate ports for each instance. 386 func (fw *Firewaller) reconcileInstances() error { 387 for _, machined := range fw.machineds { 388 m, err := machined.machine() 389 if params.IsCodeNotFound(err) { 390 if err := fw.forgetMachine(machined); err != nil { 391 return err 392 } 393 continue 394 } 395 if err != nil { 396 return err 397 } 398 instanceId, err := m.InstanceId() 399 if errors.IsNotProvisioned(err) { 400 logger.Errorf("Machine not yet provisioned: %v", err) 401 continue 402 } 403 if err != nil { 404 return err 405 } 406 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 407 if err == environs.ErrNoInstances { 408 return nil 409 } 410 if err != nil { 411 return err 412 } 413 machineId := machined.tag.Id() 414 initialPortRanges, err := instances[0].Ports(machineId) 415 if err != nil { 416 return err 417 } 418 419 // Check which ports to open or to close. 420 toOpen := diffRanges(machined.openedPorts, initialPortRanges) 421 toClose := diffRanges(initialPortRanges, machined.openedPorts) 422 if len(toOpen) > 0 { 423 logger.Infof("opening instance port ranges %v for %q", 424 toOpen, machined.tag) 425 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 426 // TODO(mue) Add local retry logic. 427 return err 428 } 429 network.SortPortRanges(toOpen) 430 } 431 if len(toClose) > 0 { 432 logger.Infof("closing instance port ranges %v for %q", 433 toClose, machined.tag) 434 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 435 // TODO(mue) Add local retry logic. 436 return err 437 } 438 network.SortPortRanges(toClose) 439 } 440 } 441 return nil 442 } 443 444 // unitsChanged responds to changes to the assigned units. 445 func (fw *Firewaller) unitsChanged(change *unitsChange) error { 446 changed := []*unitData{} 447 for _, name := range change.units { 448 unitTag := names.NewUnitTag(name) 449 unit, err := fw.st.Unit(unitTag) 450 if err != nil && !params.IsCodeNotFound(err) { 451 return err 452 } 453 var machineTag names.MachineTag 454 if unit != nil { 455 machineTag, err = unit.AssignedMachine() 456 if params.IsCodeNotFound(err) { 457 continue 458 } else if err != nil && !params.IsCodeNotAssigned(err) { 459 return err 460 } 461 } 462 if unitd, known := fw.unitds[unitTag]; known { 463 knownMachineTag := fw.unitds[unitTag].machined.tag 464 if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { 465 fw.forgetUnit(unitd) 466 changed = append(changed, unitd) 467 logger.Debugf("stopped watching unit %s", name) 468 } 469 // TODO(dfc) fw.machineds should be map[names.Tag] 470 } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { 471 err = fw.startUnit(unit, machineTag) 472 if err != nil { 473 return err 474 } 475 changed = append(changed, fw.unitds[unitTag]) 476 logger.Debugf("started watching %q", unitTag) 477 } 478 } 479 if err := fw.flushUnits(changed); err != nil { 480 return errors.Annotate(err, "cannot change firewall ports") 481 } 482 return nil 483 } 484 485 // openedPortsChanged handles port change notifications 486 func (fw *Firewaller) openedPortsChanged(machineTag names.MachineTag, subnetTag names.SubnetTag) error { 487 488 machined, ok := fw.machineds[machineTag] 489 if !ok { 490 // It is common to receive a port change notification before 491 // registering the machine, so if a machine is not found in 492 // firewaller's list, just skip the change. 493 logger.Errorf("failed to lookup %q, skipping port change", machineTag) 494 return nil 495 } 496 497 m, err := machined.machine() 498 if err != nil { 499 return err 500 } 501 502 ports, err := m.OpenedPorts(subnetTag) 503 if err != nil { 504 return err 505 } 506 507 newPortRanges := make(map[network.PortRange]names.UnitTag) 508 for portRange, unitTag := range ports { 509 unitd, ok := machined.unitds[unitTag] 510 if !ok { 511 // It is common to receive port change notification before 512 // registering a unit. Skip handling the port change - it will 513 // be handled when the unit is registered. 514 logger.Errorf("failed to lookup %q, skipping port change", unitTag) 515 return nil 516 } 517 newPortRanges[portRange] = unitd.tag 518 } 519 520 if !portMapsEqual(machined.definedPorts, newPortRanges) { 521 machined.definedPorts = newPortRanges 522 return fw.flushMachine(machined) 523 } 524 return nil 525 } 526 527 func portMapsEqual(a, b map[network.PortRange]names.UnitTag) bool { 528 if len(a) != len(b) { 529 return false 530 } 531 for key, valueA := range a { 532 valueB, exists := b[key] 533 if !exists { 534 return false 535 } 536 if valueA != valueB { 537 return false 538 } 539 } 540 return true 541 } 542 543 // flushUnits opens and closes ports for the passed unit data. 544 func (fw *Firewaller) flushUnits(unitds []*unitData) error { 545 machineds := map[names.MachineTag]*machineData{} 546 for _, unitd := range unitds { 547 machineds[unitd.machined.tag] = unitd.machined 548 } 549 for _, machined := range machineds { 550 if err := fw.flushMachine(machined); err != nil { 551 return err 552 } 553 } 554 return nil 555 } 556 557 // flushMachine opens and closes ports for the passed machine. 558 func (fw *Firewaller) flushMachine(machined *machineData) error { 559 // Gather ports to open and close. 560 want := []network.PortRange{} 561 for portRange, unitTag := range machined.definedPorts { 562 unitd, known := machined.unitds[unitTag] 563 if !known { 564 delete(machined.unitds, unitTag) 565 continue 566 } 567 if unitd.serviced.exposed { 568 want = append(want, portRange) 569 } 570 } 571 toOpen := diffRanges(want, machined.openedPorts) 572 toClose := diffRanges(machined.openedPorts, want) 573 machined.openedPorts = want 574 if fw.globalMode { 575 return fw.flushGlobalPorts(toOpen, toClose) 576 } 577 return fw.flushInstancePorts(machined, toOpen, toClose) 578 } 579 580 // flushGlobalPorts opens and closes global ports in the environment. 581 // It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events 582 // modify the environment. 583 func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []network.PortRange) error { 584 // Filter which ports are really to open or close. 585 var toOpen, toClose []network.PortRange 586 for _, portRange := range rawOpen { 587 if fw.globalPortRef[portRange] == 0 { 588 toOpen = append(toOpen, portRange) 589 } 590 fw.globalPortRef[portRange]++ 591 } 592 for _, portRange := range rawClose { 593 fw.globalPortRef[portRange]-- 594 if fw.globalPortRef[portRange] == 0 { 595 toClose = append(toClose, portRange) 596 delete(fw.globalPortRef, portRange) 597 } 598 } 599 // Open and close the ports. 600 if len(toOpen) > 0 { 601 if err := fw.environ.OpenPorts(toOpen); err != nil { 602 // TODO(mue) Add local retry logic. 603 return err 604 } 605 network.SortPortRanges(toOpen) 606 logger.Infof("opened port ranges %v in environment", toOpen) 607 } 608 if len(toClose) > 0 { 609 if err := fw.environ.ClosePorts(toClose); err != nil { 610 // TODO(mue) Add local retry logic. 611 return err 612 } 613 network.SortPortRanges(toClose) 614 logger.Infof("closed port ranges %v in environment", toClose) 615 } 616 return nil 617 } 618 619 // flushInstancePorts opens and closes ports global on the machine. 620 func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []network.PortRange) error { 621 // If there's nothing to do, do nothing. 622 // This is important because when a machine is first created, 623 // it will have no instance id but also no open ports - 624 // InstanceId will fail but we don't care. 625 if len(toOpen) == 0 && len(toClose) == 0 { 626 return nil 627 } 628 m, err := machined.machine() 629 if params.IsCodeNotFound(err) { 630 return nil 631 } 632 if err != nil { 633 return err 634 } 635 machineId := machined.tag.Id() 636 instanceId, err := m.InstanceId() 637 if err != nil { 638 return err 639 } 640 instances, err := fw.environ.Instances([]instance.Id{instanceId}) 641 if err != nil { 642 return err 643 } 644 // Open and close the ports. 645 if len(toOpen) > 0 { 646 if err := instances[0].OpenPorts(machineId, toOpen); err != nil { 647 // TODO(mue) Add local retry logic. 648 return err 649 } 650 network.SortPortRanges(toOpen) 651 logger.Infof("opened port ranges %v on %q", toOpen, machined.tag) 652 } 653 if len(toClose) > 0 { 654 if err := instances[0].ClosePorts(machineId, toClose); err != nil { 655 // TODO(mue) Add local retry logic. 656 return err 657 } 658 network.SortPortRanges(toClose) 659 logger.Infof("closed port ranges %v on %q", toClose, machined.tag) 660 } 661 return nil 662 } 663 664 // machineLifeChanged starts watching new machines when the firewaller 665 // is starting, or when new machines come to life, and stops watching 666 // machines that are dying. 667 func (fw *Firewaller) machineLifeChanged(tag names.MachineTag) error { 668 m, err := fw.st.Machine(tag) 669 found := !params.IsCodeNotFound(err) 670 if found && err != nil { 671 return err 672 } 673 dead := !found || m.Life() == params.Dead 674 machined, known := fw.machineds[tag] 675 if known && dead { 676 return fw.forgetMachine(machined) 677 } 678 if !known && !dead { 679 err = fw.startMachine(tag) 680 if err != nil { 681 return err 682 } 683 logger.Debugf("started watching %q", tag) 684 } 685 return nil 686 } 687 688 // forgetMachine cleans the machine data after the machine is removed. 689 func (fw *Firewaller) forgetMachine(machined *machineData) error { 690 for _, unitd := range machined.unitds { 691 fw.forgetUnit(unitd) 692 } 693 if err := fw.flushMachine(machined); err != nil { 694 return errors.Trace(err) 695 } 696 697 // Unusually, it's fine to ignore this error, because we know the machined 698 // is being tracked in fw.catacomb. But we do still want to wait until the 699 // watch loop has stopped before we nuke the last data and return. 700 worker.Stop(machined) 701 delete(fw.machineds, machined.tag) 702 logger.Debugf("stopped watching %q", machined.tag) 703 return nil 704 } 705 706 // forgetUnit cleans the unit data after the unit is removed. 707 func (fw *Firewaller) forgetUnit(unitd *unitData) { 708 serviced := unitd.serviced 709 machined := unitd.machined 710 711 // If it's the last unit in the service, we'll need to stop the serviced. 712 stoppedService := false 713 if len(serviced.unitds) == 1 { 714 if _, found := serviced.unitds[unitd.tag]; found { 715 // Unusually, it's fine to ignore this error, because we know the 716 // serviced is being tracked in fw.catacomb. But we do still want 717 // to wait until the watch loop has stopped before we nuke the last 718 // data and return. 719 worker.Stop(serviced) 720 stoppedService = true 721 } 722 } 723 724 // Clean up after stopping. 725 delete(fw.unitds, unitd.tag) 726 delete(machined.unitds, unitd.tag) 727 delete(serviced.unitds, unitd.tag) 728 logger.Debugf("stopped watching %q", unitd.tag) 729 if stoppedService { 730 applicationTag := serviced.application.Tag() 731 delete(fw.applicationids, applicationTag) 732 logger.Debugf("stopped watching %q", applicationTag) 733 } 734 } 735 736 // Kill is part of the worker.Worker interface. 737 func (fw *Firewaller) Kill() { 738 fw.catacomb.Kill(nil) 739 } 740 741 // Wait is part of the worker.Worker interface. 742 func (fw *Firewaller) Wait() error { 743 return fw.catacomb.Wait() 744 } 745 746 // unitsChange contains the changed units for one specific machine. 747 type unitsChange struct { 748 machined *machineData 749 units []string 750 } 751 752 // machineData holds machine details and watches units added or removed. 753 type machineData struct { 754 catacomb catacomb.Catacomb 755 fw *Firewaller 756 tag names.MachineTag 757 unitds map[names.UnitTag]*unitData 758 openedPorts []network.PortRange 759 // ports defined by units on this machine 760 definedPorts map[network.PortRange]names.UnitTag 761 } 762 763 func (md *machineData) machine() (*firewaller.Machine, error) { 764 return md.fw.st.Machine(md.tag) 765 } 766 767 // watchLoop watches the machine for units added or removed. 768 func (md *machineData) watchLoop(unitw watcher.StringsWatcher) error { 769 if err := md.catacomb.Add(unitw); err != nil { 770 return errors.Trace(err) 771 } 772 for { 773 select { 774 case <-md.catacomb.Dying(): 775 return md.catacomb.ErrDying() 776 case change, ok := <-unitw.Changes(): 777 if !ok { 778 return errors.New("machine units watcher closed") 779 } 780 select { 781 case md.fw.unitsChange <- &unitsChange{md, change}: 782 case <-md.catacomb.Dying(): 783 return md.catacomb.ErrDying() 784 } 785 } 786 } 787 } 788 789 // Kill is part of the worker.Worker interface. 790 func (md *machineData) Kill() { 791 md.catacomb.Kill(nil) 792 } 793 794 // Wait is part of the worker.Worker interface. 795 func (md *machineData) Wait() error { 796 return md.catacomb.Wait() 797 } 798 799 // unitData holds unit details. 800 type unitData struct { 801 fw *Firewaller 802 tag names.UnitTag 803 unit *firewaller.Unit 804 serviced *serviceData 805 machined *machineData 806 } 807 808 // exposedChange contains the changed exposed flag for one specific service. 809 type exposedChange struct { 810 serviced *serviceData 811 exposed bool 812 } 813 814 // serviceData holds service details and watches exposure changes. 815 type serviceData struct { 816 catacomb catacomb.Catacomb 817 fw *Firewaller 818 application *firewaller.Application 819 exposed bool 820 unitds map[names.UnitTag]*unitData 821 } 822 823 // watchLoop watches the service's exposed flag for changes. 824 func (sd *serviceData) watchLoop(exposed bool) error { 825 serviceWatcher, err := sd.application.Watch() 826 if err != nil { 827 return errors.Trace(err) 828 } 829 if err := sd.catacomb.Add(serviceWatcher); err != nil { 830 return errors.Trace(err) 831 } 832 for { 833 select { 834 case <-sd.catacomb.Dying(): 835 return sd.catacomb.ErrDying() 836 case _, ok := <-serviceWatcher.Changes(): 837 if !ok { 838 return errors.New("service watcher closed") 839 } 840 if err := sd.application.Refresh(); err != nil { 841 if !params.IsCodeNotFound(err) { 842 return errors.Trace(err) 843 } 844 return nil 845 } 846 change, err := sd.application.IsExposed() 847 if err != nil { 848 return errors.Trace(err) 849 } 850 if change == exposed { 851 continue 852 } 853 854 exposed = change 855 select { 856 case sd.fw.exposedChange <- &exposedChange{sd, change}: 857 case <-sd.catacomb.Dying(): 858 return sd.catacomb.ErrDying() 859 } 860 } 861 } 862 } 863 864 // Kill is part of the worker.Worker interface. 865 func (sd *serviceData) Kill() { 866 sd.catacomb.Kill(nil) 867 } 868 869 // Wait is part of the worker.Worker interface. 870 func (sd *serviceData) Wait() error { 871 return sd.catacomb.Wait() 872 } 873 874 // diffRanges returns all the port rangess that exist in A but not B. 875 func diffRanges(A, B []network.PortRange) (missing []network.PortRange) { 876 next: 877 for _, a := range A { 878 for _, b := range B { 879 if a == b { 880 continue next 881 } 882 } 883 missing = append(missing, a) 884 } 885 return 886 } 887 888 // parsePortsKey parses a ports document global key coming from the ports 889 // watcher (e.g. "42:0.1.2.0/24") and returns the machine and subnet tags from 890 // its components (in the last example "machine-42" and "subnet-0.1.2.0/24"). 891 func parsePortsKey(change string) (machineTag names.MachineTag, subnetTag names.SubnetTag, err error) { 892 defer errors.DeferredAnnotatef(&err, "invalid ports change %q", change) 893 894 parts := strings.SplitN(change, ":", 2) 895 if len(parts) != 2 { 896 return names.MachineTag{}, names.SubnetTag{}, errors.Errorf("unexpected format") 897 } 898 machineID, subnetID := parts[0], parts[1] 899 900 machineTag = names.NewMachineTag(machineID) 901 if subnetID != "" { 902 subnetTag = names.NewSubnetTag(subnetID) 903 } 904 return machineTag, subnetTag, nil 905 }