github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/cmd/jujud/reboot/rebootshim.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package reboot 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/juju/errors" 12 13 "github.com/juju/juju/agent" 14 "github.com/juju/juju/container" 15 "github.com/juju/juju/container/factory" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/rpc/params" 18 "github.com/juju/juju/service" 19 ) 20 21 // rebootWaiterShim wraps the functions required by RebootWaiter 22 // to facilitate mock testing. 23 type rebootWaiterShim struct { 24 } 25 26 // ListServices returns a list of names of services running 27 // on the current host. 28 func (r rebootWaiterShim) ListServices() ([]string, error) { 29 return service.ListServices() 30 } 31 32 // NewServiceReference returns a new juju service object. 33 func (r rebootWaiterShim) NewServiceReference(name string) (Service, error) { 34 return service.NewServiceReference(name) 35 } 36 37 // NewContainerManager return an object implementing Manager. 38 func (r rebootWaiterShim) NewContainerManager(containerType instance.ContainerType, conf container.ManagerConfig) (Manager, error) { 39 return factory.NewContainerManager(containerType, conf) 40 } 41 42 // ScheduleAction schedules the reboot action based on the 43 // current operating system. 44 func (r rebootWaiterShim) ScheduleAction(action params.RebootAction, after int) error { 45 return scheduleAction(action, after) 46 } 47 48 // agentConfigShim wraps the method required by a Model in 49 // the RebootWaiter. 50 type agentConfigShim struct { 51 aCfg agent.Config 52 } 53 54 // Model return an object implementing Model. 55 func (a *agentConfigShim) Model() Model { 56 return a.aCfg.Model() 57 } 58 59 // TODO (tlm): This code has been moved across in the move to 3.0 removing 60 // Windows. However there are a number of things that can be fixed here for some 61 // easy wins. 62 // - Don't write out a script file. It introduces another failure point that we 63 // don't need to take on. We can just run the commands directly from the 64 // interpreter 65 // 66 // If we do decided to keep the script file: 67 // - Don't set the executable bit as we are giving the file directly to the 68 // interpreter. 69 // - Align the shabang line and the interpreter we use. 70 71 // scheduleAction will do a reboot or shutdown after given number of seconds 72 // this function executes the operating system's reboot binary with appropriate 73 // parameters to schedule the reboot 74 // If action is params.ShouldDoNothing, it will return immediately. 75 func scheduleAction(action params.RebootAction, after int) error { 76 if action == params.ShouldDoNothing { 77 return nil 78 } 79 args := []string{"shutdown"} 80 switch action { 81 case params.ShouldReboot: 82 args = append(args, "-r") 83 case params.ShouldShutdown: 84 args = append(args, "-h") 85 } 86 args = append(args, "now") 87 88 script, err := writeScript(args, after) 89 if err != nil { 90 return err 91 } 92 // Use the "nohup" command to run the reboot script without blocking. 93 scheduled := []string{ 94 "nohup", 95 "sh", 96 script, 97 "&", 98 } 99 return runCommand(scheduled) 100 } 101 102 func writeScript(args []string, after int) (string, error) { 103 tpl := `#!/bin/bash 104 sleep %d 105 %s` 106 script := fmt.Sprintf(tpl, after, strings.Join(args, " ")) 107 108 f, err := tmpFile() 109 if err != nil { 110 return "", errors.Trace(err) 111 } 112 defer func() { _ = f.Close() }() 113 114 _, err = f.WriteString(script) 115 if err != nil { 116 return "", errors.Trace(err) 117 } 118 name := f.Name() 119 err = os.Chmod(name, 0755) 120 if err != nil { 121 return "", errors.Trace(err) 122 } 123 return name, nil 124 }