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