github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/apiaddressupdater/apiaddressupdater.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiaddressupdater
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/worker/v3"
    12  
    13  	corenetwork "github.com/juju/juju/core/network"
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/network"
    16  )
    17  
    18  // APIAddresser is an interface that is provided to NewAPIAddressUpdater
    19  // which can be used to watch for API address changes.
    20  type APIAddresser interface {
    21  	APIHostPorts() ([]corenetwork.ProviderHostPorts, error)
    22  	WatchAPIHostPorts() (watcher.NotifyWatcher, error)
    23  }
    24  
    25  // APIAddressSetter is an interface that is provided to NewAPIAddressUpdater
    26  // whose SetAPIHostPorts method will be invoked whenever address changes occur.
    27  type APIAddressSetter interface {
    28  	SetAPIHostPorts(servers []corenetwork.HostPorts) error
    29  }
    30  
    31  // Config defines the operation of a Worker.
    32  type Config struct {
    33  	Addresser APIAddresser
    34  	Setter    APIAddressSetter
    35  	Logger    Logger
    36  }
    37  
    38  // Validate returns an error if config cannot drive a Worker.
    39  func (config Config) Validate() error {
    40  	if config.Addresser == nil {
    41  		return errors.NotValidf("nil Addresser")
    42  	}
    43  	if config.Setter == nil {
    44  		return errors.NotValidf("nil Setter")
    45  	}
    46  	if config.Logger == nil {
    47  		return errors.NotValidf("nil Logger")
    48  	}
    49  	return nil
    50  }
    51  
    52  // APIAddressUpdater is responsible for propagating API addresses.
    53  //
    54  // In practice, APIAddressUpdater is used by a machine agent to watch
    55  // API addresses in state and write the changes to the agent's config file.
    56  type APIAddressUpdater struct {
    57  	config Config
    58  
    59  	mu      sync.Mutex
    60  	current []corenetwork.ProviderHostPorts
    61  }
    62  
    63  // NewAPIAddressUpdater returns a worker.Worker that watches for changes to
    64  // API addresses and then sets them on the APIAddressSetter.
    65  func NewAPIAddressUpdater(config Config) (worker.Worker, error) {
    66  	if err := config.Validate(); err != nil {
    67  		return nil, err
    68  	}
    69  	handler := &APIAddressUpdater{
    70  		config: config,
    71  	}
    72  	w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{
    73  		Handler: handler,
    74  	})
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	return w, nil
    79  }
    80  
    81  // SetUp is part of the watcher.NotifyHandler interface.
    82  func (c *APIAddressUpdater) SetUp() (watcher.NotifyWatcher, error) {
    83  	return c.config.Addresser.WatchAPIHostPorts()
    84  }
    85  
    86  // Handle is part of the watcher.NotifyHandler interface.
    87  func (c *APIAddressUpdater) Handle(_ <-chan struct{}) error {
    88  	hps, err := c.getAddresses()
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	// Logging to identify lp: 1888453
    94  	if len(hps) == 0 {
    95  		c.config.Logger.Warningf("empty API host ports received. Updating using existing entries.")
    96  	}
    97  
    98  	c.config.Logger.Debugf("updating API hostPorts to %+v", hps)
    99  	c.mu.Lock()
   100  	// Protection case to possible help with lp: 1888453
   101  	if len(hps) != 0 {
   102  		c.current = hps
   103  	} else {
   104  		hps = c.current
   105  	}
   106  	c.mu.Unlock()
   107  
   108  	// API host/port entries are stored in state as SpaceHostPorts.
   109  	// When retrieved, the space IDs are reconciled so that they are returned
   110  	// as ProviderHostPorts.
   111  	// Here, we indirect them because they are ultimately just stored as dial
   112  	// address strings. This could be re-evaluated in the future if the space
   113  	// information becomes worthwhile to agents.
   114  	hpsToSet := make([]corenetwork.HostPorts, len(hps))
   115  	for i, hps := range hps {
   116  		hpsToSet[i] = hps.HostPorts()
   117  	}
   118  
   119  	if err := c.config.Setter.SetAPIHostPorts(hpsToSet); err != nil {
   120  		return fmt.Errorf("error setting addresses: %v", err)
   121  	}
   122  	return nil
   123  }
   124  
   125  func (c *APIAddressUpdater) getAddresses() ([]corenetwork.ProviderHostPorts, error) {
   126  	addresses, err := c.config.Addresser.APIHostPorts()
   127  	if err != nil {
   128  		return nil, fmt.Errorf("error getting addresses: %v", err)
   129  	}
   130  
   131  	// Filter out any LXC or LXD bridge addresses.
   132  	// See LP bugs #1416928 and #1567683.
   133  	hpsToSet := make([]corenetwork.ProviderHostPorts, 0)
   134  	for _, hostPorts := range addresses {
   135  		// Strip ports, filter, then add ports again.
   136  		filtered := network.FilterBridgeAddresses(hostPorts.Addresses())
   137  		hps := make(corenetwork.ProviderHostPorts, 0, len(filtered))
   138  		for _, hostPort := range hostPorts {
   139  			for _, addr := range filtered {
   140  				if addr.Value == hostPort.Value {
   141  					hps = append(hps, hostPort)
   142  				}
   143  			}
   144  		}
   145  		if len(hps) > 0 {
   146  			hpsToSet = append(hpsToSet, hps)
   147  		}
   148  	}
   149  
   150  	// Logging to identify lp: 1888453
   151  	if len(hpsToSet) == 0 {
   152  		c.config.Logger.Warningf("get address returning zero results after filtering, non filtered list: %v", addresses)
   153  	}
   154  
   155  	return hpsToSet, nil
   156  }
   157  
   158  // TearDown is part of the watcher.NotifyHandler interface.
   159  func (c *APIAddressUpdater) TearDown() error {
   160  	return nil
   161  }
   162  
   163  // Report shows up in the dependency engine report.
   164  func (c *APIAddressUpdater) Report() map[string]interface{} {
   165  	report := make(map[string]interface{})
   166  	c.mu.Lock()
   167  	defer c.mu.Unlock()
   168  	var servers [][]string
   169  	for _, server := range c.current {
   170  		var addresses []string
   171  		for _, addr := range server {
   172  			addresses = append(addresses, corenetwork.DialAddress(addr))
   173  		}
   174  		servers = append(servers, addresses)
   175  	}
   176  	report["servers"] = servers
   177  	return report
   178  }