github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/reboot/reboot.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Copyright 2014 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package reboot 6 7 import ( 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/agent" 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/api/reboot" 20 "github.com/juju/juju/apiserver/params" 21 "github.com/juju/juju/container" 22 "github.com/juju/juju/container/factory" 23 "github.com/juju/juju/instance" 24 ) 25 26 var logger = loggo.GetLogger("juju.cmd.jujud.reboot") 27 var timeout = time.Duration(10 * time.Minute) 28 var rebootAfter = 15 29 30 func runCommand(args []string) error { 31 err := exec.Command(args[0], args[1:]...).Run() 32 return errors.Trace(err) 33 } 34 35 var tmpFile = func() (*os.File, error) { 36 f, err := ioutil.TempFile(os.TempDir(), "juju-reboot") 37 return f, errors.Trace(err) 38 } 39 40 // Reboot implements the ExecuteReboot command which will reboot a machine 41 // once all containers have shut down, or a timeout is reached 42 type Reboot struct { 43 acfg agent.Config 44 apistate api.Connection 45 tag names.MachineTag 46 st reboot.State 47 } 48 49 func NewRebootWaiter(apistate api.Connection, acfg agent.Config) (*Reboot, error) { 50 rebootState, err := apistate.Reboot() 51 if err != nil { 52 return nil, errors.Trace(err) 53 } 54 tag, ok := acfg.Tag().(names.MachineTag) 55 if !ok { 56 return nil, errors.Errorf("Expected names.MachineTag, got: %T --> %v", acfg.Tag(), acfg.Tag()) 57 } 58 return &Reboot{ 59 acfg: acfg, 60 st: rebootState, 61 tag: tag, 62 apistate: apistate, 63 }, nil 64 } 65 66 // ExecuteReboot will wait for all running containers to stop, and then execute 67 // a shutdown or a reboot (based on the action param) 68 func (r *Reboot) ExecuteReboot(action params.RebootAction) error { 69 if err := r.waitForContainersOrTimeout(); err != nil { 70 return errors.Trace(err) 71 } 72 73 if err := scheduleAction(action, rebootAfter); err != nil { 74 return errors.Trace(err) 75 } 76 77 err := r.st.ClearReboot() 78 return errors.Trace(err) 79 } 80 81 func (r *Reboot) runningContainers() ([]instance.Instance, error) { 82 var runningInstances []instance.Instance 83 modelUUID := r.acfg.Model().Id() 84 for _, val := range instance.ContainerTypes { 85 managerConfig := container.ManagerConfig{ 86 container.ConfigModelUUID: modelUUID} 87 cfg := container.ManagerConfig(managerConfig) 88 manager, err := factory.NewContainerManager(val, cfg) 89 if err != nil { 90 return nil, errors.Annotatef(err, "failed to get manager for container type %v", val) 91 } 92 if !manager.IsInitialized() { 93 logger.Infof("container type %q not supported", val) 94 continue 95 } 96 instances, err := manager.ListContainers() 97 if err != nil { 98 return nil, errors.Annotate(err, "failed to list containers") 99 } 100 runningInstances = append(runningInstances, instances...) 101 } 102 return runningInstances, nil 103 } 104 105 func (r *Reboot) waitForContainersOrTimeout() error { 106 c := make(chan error, 1) 107 quit := make(chan bool, 1) 108 go func() { 109 for { 110 select { 111 case <-quit: 112 c <- nil 113 return 114 default: 115 containers, err := r.runningContainers() 116 if err != nil { 117 c <- err 118 return 119 } 120 if len(containers) == 0 { 121 c <- nil 122 return 123 } 124 logger.Warningf("Waiting for containers to shutdown: %v", containers) 125 time.Sleep(1 * time.Second) 126 } 127 } 128 }() 129 130 select { 131 case <-time.After(timeout): 132 // TODO(fwereade): 2016-03-17 lp:1558657 133 // Containers are still up after timeout. C'est la vie 134 quit <- true 135 return errors.New("Timeout reached waiting for containers to shutdown") 136 case err := <-c: 137 return errors.Trace(err) 138 } 139 }