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 }