github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/firewall/egressaddresswatcher.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package firewall 5 6 import ( 7 "github.com/juju/collections/set" 8 "github.com/juju/errors" 9 "gopkg.in/juju/worker.v1" 10 "gopkg.in/juju/worker.v1/catacomb" 11 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/network" 14 ) 15 16 // EgressAddressWatcher reports changes to addresses 17 // for local units in a given relation. 18 // Each event contains the entire set of addresses which 19 // are required for ingress on the remote side of the relation. 20 type EgressAddressWatcher struct { 21 catacomb catacomb.Catacomb 22 23 backend State 24 appName string 25 rel Relation 26 27 out chan []string 28 29 // Channel for machineAddressWatchers to report individual machine 30 // updates. 31 addressChanges chan string 32 33 // A map of machine id to machine data. 34 machines map[string]*machineData 35 36 // A map of machine id by unit name - this is needed because we 37 // might not be able to retrieve the machine name when a unit 38 // leaves scope if it's been completely removed by the time we look. 39 unitToMachine map[string]string 40 41 // A map of known unit addresses, keyed on unit name. 42 known map[string]string 43 44 // A set of known egress cidrs for the model. 45 knownModelEgress set.Strings 46 47 // A set of known egress cidrs for the relation. 48 knownRelationEgress set.Strings 49 } 50 51 // machineData holds the information we track at the machine level. 52 type machineData struct { 53 units set.Strings 54 worker *machineAddressWorker 55 } 56 57 // NewEgressAddressWatcher creates an EgressAddressWatcher. 58 func NewEgressAddressWatcher(backend State, rel Relation, appName string) (*EgressAddressWatcher, error) { 59 w := &EgressAddressWatcher{ 60 backend: backend, 61 appName: appName, 62 rel: rel, 63 known: make(map[string]string), 64 out: make(chan []string), 65 addressChanges: make(chan string), 66 machines: make(map[string]*machineData), 67 unitToMachine: make(map[string]string), 68 knownModelEgress: set.NewStrings(), 69 } 70 err := catacomb.Invoke(catacomb.Plan{ 71 Site: &w.catacomb, 72 Work: w.loop, 73 }) 74 return w, err 75 } 76 77 func (w *EgressAddressWatcher) loop() error { 78 defer close(w.out) 79 80 ruw, err := w.rel.WatchUnits(w.appName) 81 if errors.IsNotFound(err) { 82 return nil 83 } 84 if err != nil { 85 return errors.Trace(err) 86 } 87 if err := w.catacomb.Add(ruw); err != nil { 88 return errors.Trace(err) 89 } 90 91 // TODO(wallyworld) - we just want to watch for egress 92 // address changes but right now can only watch for 93 // any model config change. 94 mw := w.backend.WatchForModelConfigChanges() 95 if err := w.catacomb.Add(mw); err != nil { 96 return errors.Trace(err) 97 } 98 99 rw := w.rel.WatchRelationEgressNetworks() 100 if err := w.catacomb.Add(rw); err != nil { 101 return errors.Trace(err) 102 } 103 104 var ( 105 changed bool 106 sentInitial bool 107 out chan<- []string 108 addresses set.Strings 109 lastAddresses set.Strings 110 addressesCIDR []string 111 ) 112 113 // Wait for each of the watchers started above to 114 // send an initial change before sending any changes 115 // from this watcher. 116 var haveInitialRelationUnits bool 117 var haveInitialRelationEgressNetworks bool 118 var haveInitialModelConfig bool 119 120 for { 121 var ready bool 122 if !sentInitial { 123 ready = haveInitialRelationUnits && haveInitialRelationEgressNetworks && haveInitialModelConfig 124 } 125 if ready || changed { 126 addresses = nil 127 if len(w.known) > 0 { 128 // Egress CIDRs, if configured, override unit 129 // machine addresses. Relation CIDRs take 130 // precedence over those specified in model 131 // config. 132 addresses = set.NewStrings(w.knownRelationEgress.Values()...) 133 if addresses.Size() == 0 { 134 addresses = set.NewStrings(w.knownModelEgress.Values()...) 135 } 136 if addresses.Size() == 0 { 137 // No user configured egress so just use the unit addresses. 138 for _, addr := range w.known { 139 addresses.Add(addr) 140 } 141 } 142 } 143 changed = false 144 if !setEquals(addresses, lastAddresses) { 145 addressesCIDR, err = network.FormatAsCIDR(addresses.Values()) 146 if err != nil { 147 return errors.Trace(err) 148 } 149 ready = ready || sentInitial 150 } 151 } 152 if ready { 153 out = w.out 154 } 155 156 select { 157 case <-w.catacomb.Dying(): 158 return w.catacomb.ErrDying() 159 160 case out <- addressesCIDR: 161 sentInitial = true 162 lastAddresses = addresses 163 out = nil 164 165 case _, ok := <-mw.Changes(): 166 if !ok { 167 return w.catacomb.ErrDying() 168 } 169 cfg, err := w.backend.ModelConfig() 170 if err != nil { 171 return err 172 } 173 haveInitialModelConfig = true 174 egress := set.NewStrings(cfg.EgressSubnets()...) 175 if !setEquals(egress, w.knownModelEgress) { 176 logger.Debugf( 177 "model config egress subnets changed to %s (was %s)", 178 egress.SortedValues(), 179 w.knownModelEgress.SortedValues(), 180 ) 181 changed = true 182 w.knownModelEgress = egress 183 } 184 185 case changes, ok := <-rw.Changes(): 186 if !ok { 187 return w.catacomb.ErrDying() 188 } 189 haveInitialRelationEgressNetworks = true 190 egress := set.NewStrings(changes...) 191 if !setEquals(egress, w.knownRelationEgress) { 192 logger.Debugf( 193 "relation egress subnets changed to %s (was %s)", 194 egress.SortedValues(), 195 w.knownRelationEgress.SortedValues(), 196 ) 197 changed = true 198 w.knownRelationEgress = egress 199 } 200 201 case c, ok := <-ruw.Changes(): 202 if !ok { 203 return w.catacomb.ErrDying() 204 } 205 // A unit has entered or left scope. 206 // Get the new set of addresses resulting from that 207 // change, and if different to what we know, send the change. 208 haveInitialRelationUnits = true 209 addressesChanged, err := w.processUnitChanges(c) 210 if err != nil { 211 return err 212 } 213 changed = changed || addressesChanged 214 215 case machineId, ok := <-w.addressChanges: 216 if !ok { 217 continue 218 } 219 addressesChanged, err := w.processMachineAddresses(machineId) 220 if err != nil { 221 return errors.Trace(err) 222 } 223 changed = changed || addressesChanged 224 } 225 } 226 } 227 228 func (w *EgressAddressWatcher) unitAddress(unit Unit) (string, bool, error) { 229 addr, err := unit.PublicAddress() 230 if errors.IsNotAssigned(err) { 231 logger.Debugf("unit %s is not assigned to a machine, can't get address", unit.Name()) 232 return "", false, nil 233 } 234 if network.IsNoAddressError(err) { 235 logger.Debugf("unit %s has no public address", unit.Name()) 236 return "", false, nil 237 } 238 if err != nil { 239 return "", false, err 240 } 241 logger.Debugf("unit %q has public address %q", unit.Name(), addr.Value) 242 return addr.Value, true, nil 243 } 244 245 func (w *EgressAddressWatcher) processUnitChanges(c params.RelationUnitsChange) (bool, error) { 246 changed := false 247 for name := range c.Changed { 248 249 u, err := w.backend.Unit(name) 250 if errors.IsNotFound(err) { 251 continue 252 } 253 if err != nil { 254 return false, err 255 } 256 257 if err := w.trackUnit(u); err != nil { 258 return false, errors.Trace(err) 259 } 260 261 // We need to know whether to look at the public or cloud local address. 262 // For now, we'll use the public address and later if needed use a watcher 263 // parameter to look at the cloud local address. 264 addr, ok, err := w.unitAddress(u) 265 if err != nil { 266 return false, err 267 } 268 if !ok { 269 continue 270 } 271 if w.known[name] != addr { 272 w.known[name] = addr 273 changed = true 274 } 275 } 276 for _, name := range c.Departed { 277 if err := w.untrackUnit(name); err != nil { 278 return false, errors.Trace(err) 279 } 280 // If the unit is departing and we have seen its address, 281 // remove the address. 282 address, ok := w.known[name] 283 if !ok { 284 continue 285 } 286 delete(w.known, name) 287 288 // See if the address is still used by another unit. 289 inUse := false 290 for unit, addr := range w.known { 291 if name != unit && addr == address { 292 inUse = true 293 break 294 } 295 } 296 if !inUse { 297 changed = true 298 } 299 } 300 return changed, nil 301 } 302 303 func (w *EgressAddressWatcher) trackUnit(unit Unit) error { 304 machine, err := w.assignedMachine(unit) 305 if errors.IsNotAssigned(err) { 306 logger.Errorf("unit %q entered scope without a machine assigned - addresses will not be tracked", unit) 307 return nil 308 } 309 if err != nil { 310 return errors.Trace(err) 311 } 312 313 w.unitToMachine[unit.Name()] = machine.Id() 314 mData, ok := w.machines[machine.Id()] 315 if ok { 316 // We're already watching the machine, just add this unit. 317 mData.units.Add(unit.Name()) 318 return nil 319 } 320 321 addressWorker, err := newMachineAddressWorker(machine, w.addressChanges) 322 if err != nil { 323 return errors.Trace(err) 324 } 325 w.machines[machine.Id()] = &machineData{ 326 units: set.NewStrings(unit.Name()), 327 worker: addressWorker, 328 } 329 err = w.catacomb.Add(addressWorker) 330 if err != nil { 331 return errors.Trace(err) 332 } 333 return nil 334 } 335 336 func (w *EgressAddressWatcher) untrackUnit(unitName string) error { 337 machineId, ok := w.unitToMachine[unitName] 338 if !ok { 339 logger.Errorf("missing machine id for unit %q", unitName) 340 return nil 341 } 342 delete(w.unitToMachine, unitName) 343 344 mData, ok := w.machines[machineId] 345 if !ok { 346 logger.Debugf("missing machine data for machine %q (hosting unit %q)", machineId, unitName) 347 return nil 348 } 349 mData.units.Remove(unitName) 350 if mData.units.Size() > 0 { 351 // No need to stop the watcher - there are still units on the 352 // machine. 353 return nil 354 } 355 356 err := worker.Stop(mData.worker) 357 if err != nil { 358 return errors.Trace(err) 359 } 360 delete(w.machines, machineId) 361 return nil 362 } 363 364 func (w *EgressAddressWatcher) assignedMachine(unit Unit) (Machine, error) { 365 machineId, err := unit.AssignedMachineId() 366 if err != nil { 367 return nil, errors.Trace(err) 368 } 369 machine, err := w.backend.Machine(machineId) 370 if err != nil { 371 return nil, errors.Trace(err) 372 } 373 return machine, nil 374 } 375 376 func (w *EgressAddressWatcher) processMachineAddresses(machineId string) (changed bool, err error) { 377 mData, ok := w.machines[machineId] 378 if !ok { 379 return false, errors.Errorf("missing machineData for machine %q", machineId) 380 } 381 for unitName := range mData.units { 382 unit, err := w.backend.Unit(unitName) 383 if errors.IsNotFound(err) { 384 continue 385 } 386 if err != nil { 387 return false, errors.Trace(err) 388 } 389 address, _, err := w.unitAddress(unit) 390 if err != nil { 391 return false, errors.Trace(err) 392 } 393 existingAddress := w.known[unitName] 394 if existingAddress != address { 395 w.known[unitName] = address 396 changed = true 397 } 398 } 399 return changed, nil 400 } 401 402 // Changes returns the event channel for this watcher. 403 func (w *EgressAddressWatcher) Changes() <-chan []string { 404 return w.out 405 } 406 407 // Kill asks the watcher to stop without waiting for it do so. 408 func (w *EgressAddressWatcher) Kill() { 409 w.catacomb.Kill(nil) 410 } 411 412 // Wait waits for the watcher to die and returns any 413 // error encountered when it was running. 414 func (w *EgressAddressWatcher) Wait() error { 415 return w.catacomb.Wait() 416 } 417 418 // Stop kills the watcher, then waits for it to die. 419 func (w *EgressAddressWatcher) Stop() error { 420 w.Kill() 421 return w.Wait() 422 } 423 424 // Err returns any error encountered while the watcher 425 // has been running. 426 func (w *EgressAddressWatcher) Err() error { 427 return w.catacomb.Err() 428 } 429 430 func newMachineAddressWorker(machine Machine, out chan<- string) (*machineAddressWorker, error) { 431 w := &machineAddressWorker{ 432 machine: machine, 433 out: out, 434 } 435 err := catacomb.Invoke(catacomb.Plan{ 436 Site: &w.catacomb, 437 Work: w.loop, 438 }) 439 return w, errors.Trace(err) 440 } 441 442 // machineAddressWorker watches for machine address changes and 443 // notifies the dest channel when it sees them. 444 type machineAddressWorker struct { 445 catacomb catacomb.Catacomb 446 machine Machine 447 out chan<- string 448 } 449 450 func (w *machineAddressWorker) loop() error { 451 aw := w.machine.WatchAddresses() 452 if err := w.catacomb.Add(aw); err != nil { 453 return errors.Trace(err) 454 } 455 machineId := w.machine.Id() 456 var out chan<- string 457 for { 458 select { 459 case <-w.catacomb.Dying(): 460 return w.catacomb.ErrDying() 461 case <-aw.Changes(): 462 out = w.out 463 case out <- machineId: 464 out = nil 465 } 466 } 467 } 468 469 func (w *machineAddressWorker) Kill() { 470 w.catacomb.Kill(nil) 471 } 472 473 func (w *machineAddressWorker) Wait() error { 474 return w.catacomb.Wait() 475 } 476 477 func setEquals(a, b set.Strings) bool { 478 if a.Size() != b.Size() { 479 return false 480 } 481 return a.Intersection(b).Size() == a.Size() 482 }