github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/reboot/reboot.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package reboot 5 6 import ( 7 "github.com/juju/clock" 8 "github.com/juju/errors" 9 "github.com/juju/loggo" 10 "gopkg.in/juju/names.v2" 11 "gopkg.in/juju/worker.v1" 12 "gopkg.in/tomb.v2" 13 14 "github.com/juju/juju/agent" 15 "github.com/juju/juju/api/reboot" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/core/machinelock" 18 "github.com/juju/juju/core/watcher" 19 jworker "github.com/juju/juju/worker" 20 ) 21 22 var logger = loggo.GetLogger("juju.worker.reboot") 23 24 // The reboot worker listens for changes to the reboot flag and 25 // exists with worker.ErrRebootMachine if the machine should reboot or 26 // with worker.ErrShutdownMachine if it should shutdown. This will be picked 27 // up by the machine agent as a fatal error and will do the 28 // right thing (reboot or shutdown) 29 type Reboot struct { 30 tomb tomb.Tomb 31 st reboot.State 32 tag names.MachineTag 33 machineLock machinelock.Lock 34 clock clock.Clock 35 } 36 37 func NewReboot(st reboot.State, agentConfig agent.Config, machineLock machinelock.Lock, clock clock.Clock) (worker.Worker, error) { 38 tag, ok := agentConfig.Tag().(names.MachineTag) 39 if !ok { 40 return nil, errors.Errorf("Expected names.MachineTag, got %T: %v", agentConfig.Tag(), agentConfig.Tag()) 41 } 42 r := &Reboot{ 43 st: st, 44 tag: tag, 45 machineLock: machineLock, 46 clock: clock, 47 } 48 w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{ 49 Handler: r, 50 }) 51 return w, errors.Trace(err) 52 } 53 54 func (r *Reboot) SetUp() (watcher.NotifyWatcher, error) { 55 watcher, err := r.st.WatchForRebootEvent() 56 return watcher, errors.Trace(err) 57 } 58 59 func (r *Reboot) Handle(_ <-chan struct{}) error { 60 rAction, err := r.st.GetRebootAction() 61 if err != nil { 62 return errors.Trace(err) 63 } 64 logger.Debugf("Reboot worker got action: %v", rAction) 65 66 // NOTE: Here we explicitly avoid stopping on the abort channel as we are 67 // wanting to make sure that we grab the lock and return an error 68 // sufficiently heavyweight to get the agent to restart. 69 spec := machinelock.Spec{ 70 Worker: "reboot", 71 NoCancel: true, 72 } 73 74 switch rAction { 75 case params.ShouldReboot: 76 spec.Comment = "reboot" 77 if _, err := r.machineLock.Acquire(spec); err != nil { 78 return errors.Trace(err) 79 } 80 logger.Debugf("machine lock will not be released manually") 81 err = jworker.ErrRebootMachine 82 case params.ShouldShutdown: 83 spec.Comment = "shutdown" 84 if _, err := r.machineLock.Acquire(spec); err != nil { 85 return errors.Trace(err) 86 } 87 logger.Debugf("machine lock will not be released manually") 88 err = jworker.ErrShutdownMachine 89 } 90 91 if err != nil { 92 // We clear the reboot flag here rather than when we are attempting to 93 // handle the reboot error in the machine agent code as it is possible 94 // that the machine agent is also a controller, and the apiserver has been 95 // shut down. It is better to clear the flag and not reboot on a weird 96 // error rather than get into a reboot loop because we can't shutdown. 97 if err := r.st.ClearReboot(); err != nil { 98 logger.Infof("unable to clear reboot flag: %v", err) 99 } 100 } 101 return err 102 } 103 104 func (r *Reboot) TearDown() error { 105 // nothing to teardown. 106 return nil 107 }