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  }