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  }