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  }