github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/machiner/machiner.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 package machiner 4 5 import ( 6 "net" 7 8 "github.com/juju/errors" 9 "github.com/juju/loggo" 10 "gopkg.in/juju/names.v2" 11 "gopkg.in/juju/worker.v1" 12 13 "github.com/juju/juju/api/common" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/core/status" 16 "github.com/juju/juju/core/watcher" 17 "github.com/juju/juju/network" 18 jworker "github.com/juju/juju/worker" 19 ) 20 21 var logger = loggo.GetLogger("juju.worker.machiner") 22 23 // Config defines the configuration for a machiner worker. 24 type Config struct { 25 // MachineAccessor provides a means of observing and updating the 26 // machine's state. 27 MachineAccessor MachineAccessor 28 29 // Tag is the machine's tag. 30 Tag names.MachineTag 31 32 // ClearMachineAddressesOnStart indicates whether or not to clear 33 // the machine's machine addresses when the worker starts. 34 ClearMachineAddressesOnStart bool 35 36 // NotifyMachineDead will, if non-nil, be called after the machine 37 // is transitioned to the Dead lifecycle state. 38 NotifyMachineDead func() error 39 } 40 41 // Validate reports whether or not the configuration is valid. 42 func (cfg *Config) Validate() error { 43 if cfg.MachineAccessor == nil { 44 return errors.NotValidf("unspecified MachineAccessor") 45 } 46 if cfg.Tag == (names.MachineTag{}) { 47 return errors.NotValidf("unspecified Tag") 48 } 49 return nil 50 } 51 52 // Machiner is responsible for a machine agent's lifecycle. 53 type Machiner struct { 54 config Config 55 machine Machine 56 } 57 58 // NewMachiner returns a Worker that will wait for the identified machine 59 // to become Dying and make it Dead; or until the machine becomes Dead by 60 // other means. 61 // 62 // The machineDead function will be called immediately after the machine's 63 // lifecycle is updated to Dead. 64 var NewMachiner = func(cfg Config) (worker.Worker, error) { 65 if err := cfg.Validate(); err != nil { 66 return nil, errors.Annotate(err, "validating config") 67 } 68 handler := &Machiner{config: cfg} 69 w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{ 70 Handler: handler, 71 }) 72 if err != nil { 73 return nil, errors.Trace(err) 74 } 75 return w, nil 76 } 77 78 var getObservedNetworkConfig = common.GetObservedNetworkConfig 79 80 func (mr *Machiner) SetUp() (watcher.NotifyWatcher, error) { 81 // Find which machine we're responsible for. 82 m, err := mr.config.MachineAccessor.Machine(mr.config.Tag) 83 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 84 return nil, jworker.ErrTerminateAgent 85 } else if err != nil { 86 return nil, errors.Trace(err) 87 } 88 mr.machine = m 89 90 if mr.config.ClearMachineAddressesOnStart { 91 logger.Debugf("machiner configured to reset machine %q addresses to empty", mr.config.Tag) 92 if err := m.SetMachineAddresses(nil); err != nil { 93 return nil, errors.Annotate(err, "reseting machine addresses") 94 } 95 } else { 96 // Set the addresses in state to the host's addresses. 97 if err := setMachineAddresses(mr.config.Tag, m); err != nil { 98 return nil, errors.Annotate(err, "setting machine addresses") 99 } 100 } 101 102 // Mark the machine as started and log it. 103 if err := m.SetStatus(status.Started, "", nil); err != nil { 104 return nil, errors.Annotatef(err, "%s failed to set status started", mr.config.Tag) 105 } 106 logger.Infof("%q started", mr.config.Tag) 107 108 return m.Watch() 109 } 110 111 var interfaceAddrs = net.InterfaceAddrs 112 113 // setMachineAddresses sets the addresses for this machine to all of the 114 // host's non-loopback interface IP addresses. 115 func setMachineAddresses(tag names.MachineTag, m Machine) error { 116 addrs, err := interfaceAddrs() 117 if err != nil { 118 return err 119 } 120 var hostAddresses []network.Address 121 for _, addr := range addrs { 122 var ip net.IP 123 switch addr := addr.(type) { 124 case *net.IPAddr: 125 ip = addr.IP 126 case *net.IPNet: 127 ip = addr.IP 128 default: 129 continue 130 } 131 address := network.NewAddress(ip.String()) 132 // Filter out link-local addresses as we cannot reliably use them. 133 if address.Scope == network.ScopeLinkLocal { 134 continue 135 } 136 hostAddresses = append(hostAddresses, address) 137 } 138 if len(hostAddresses) == 0 { 139 return nil 140 } 141 // Filter out any LXC or LXD bridge addresses. 142 hostAddresses = network.FilterBridgeAddresses(hostAddresses) 143 logger.Infof("setting addresses for %q to %v", tag, hostAddresses) 144 return m.SetMachineAddresses(hostAddresses) 145 } 146 147 func (mr *Machiner) Handle(_ <-chan struct{}) error { 148 if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) { 149 // NOTE(axw) we can distinguish between NotFound and CodeUnauthorized, 150 // so we could call NotifyMachineDead here in case the agent failed to 151 // call NotifyMachineDead directly after setting the machine Dead in 152 // the first place. We're not doing that to be cautious: the machine 153 // could be missing from state due to invalid global state. 154 return jworker.ErrTerminateAgent 155 } else if err != nil { 156 return err 157 } 158 159 life := mr.machine.Life() 160 if life == params.Alive { 161 observedConfig, err := getObservedNetworkConfig(common.DefaultNetworkConfigSource()) 162 if err != nil { 163 return errors.Annotate(err, "cannot discover observed network config") 164 } else if len(observedConfig) == 0 { 165 logger.Warningf("not updating network config: no observed config found to update") 166 } 167 if len(observedConfig) > 0 { 168 if err := mr.machine.SetObservedNetworkConfig(observedConfig); err != nil { 169 return errors.Annotate(err, "cannot update observed network config") 170 } 171 } 172 logger.Debugf("observed network config updated for %q to %+v", mr.config.Tag, observedConfig) 173 174 return nil 175 } 176 logger.Debugf("%q is now %s", mr.config.Tag, life) 177 if err := mr.machine.SetStatus(status.Stopped, "", nil); err != nil { 178 return errors.Annotatef(err, "%s failed to set status stopped", mr.config.Tag) 179 } 180 181 // Attempt to mark the machine Dead. If the machine still has units 182 // assigned, or storage attached, this will fail with 183 // CodeHasAssignedUnits or CodeMachineHasAttachedStorage respectively. 184 // Once units or storage are removed, the watcher will trigger again 185 // and we'll reattempt. 186 if err := mr.machine.EnsureDead(); err != nil { 187 if params.IsCodeHasAssignedUnits(err) { 188 return nil 189 } 190 if params.IsCodeMachineHasAttachedStorage(err) { 191 logger.Tracef("machine still has storage attached") 192 return nil 193 } 194 err = errors.Annotatef(err, "%s failed to set machine to dead", mr.config.Tag) 195 if e := mr.machine.SetStatus(status.Error, errors.Annotate(err, "destroying machine").Error(), nil); e != nil { 196 logger.Errorf("failed to set status for error %v ", err) 197 } 198 return errors.Trace(err) 199 } 200 // Report on the machine's death. It is important that we do this after 201 // the machine is Dead, because this is the mechanism we use to clean up 202 // the machine (uninstall). If we were to report before marking the machine 203 // as Dead, then we would risk uninstalling prematurely. 204 if mr.config.NotifyMachineDead != nil { 205 if err := mr.config.NotifyMachineDead(); err != nil { 206 return errors.Annotate(err, "reporting machine death") 207 } 208 } 209 return jworker.ErrTerminateAgent 210 } 211 212 func (mr *Machiner) TearDown() error { 213 // Nothing to do here. 214 return nil 215 }