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