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