github.com/mfpierre/corectl@v0.5.6/halt.go (about)

     1  // Copyright 2015 - António Meireles  <antonio.meireles@reformi.st>
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package main
    17  
    18  import (
    19  	"fmt"
    20  	"log"
    21  	"os"
    22  	"path/filepath"
    23  	"time"
    24  
    25  	"github.com/spf13/cobra"
    26  )
    27  
    28  var (
    29  	killCmd = &cobra.Command{
    30  		Use:     "kill [VMids]",
    31  		Aliases: []string{"stop", "halt"},
    32  		Short:   "Halts one or more running CoreOS instances",
    33  		PreRunE: func(cmd *cobra.Command, args []string) (err error) {
    34  			engine.rawArgs.BindPFlags(cmd.Flags())
    35  			if len(args) < 1 && !engine.rawArgs.GetBool("all") {
    36  				err = fmt.Errorf("This command requires either at least " +
    37  					"one argument to work or --all.")
    38  			}
    39  			return
    40  		},
    41  		RunE: killCommand,
    42  	}
    43  )
    44  
    45  func killCommand(cmd *cobra.Command, args []string) (err error) {
    46  	var (
    47  		up []VMInfo
    48  		vm VMInfo
    49  	)
    50  	if up, err = allRunningInstances(); err != nil {
    51  		return
    52  	}
    53  	if engine.rawArgs.GetBool("all") {
    54  		for _, vm := range up {
    55  			if err = vm.halt(); err != nil {
    56  				return
    57  			}
    58  		}
    59  		return
    60  	}
    61  	for _, arg := range args {
    62  		if vm, err = vmInfo(arg); err != nil {
    63  			return
    64  		} else if err = vm.halt(); err != nil {
    65  			return
    66  		}
    67  	}
    68  	return
    69  }
    70  
    71  func (vm VMInfo) halt() (err error) {
    72  	var (
    73  		sshSession *sshClient
    74  		command    = "sudo sync;sudo halt"
    75  		hardKill   = func(e error) (err error) {
    76  			if e != nil {
    77  				// ssh messed up for some reason or target has no IP
    78  				log.Printf("couldn't ssh to %v (%v)...\n", vm.Name, e)
    79  				if canKill := engine.allowedToRun(); canKill != nil {
    80  					return canKill
    81  				}
    82  				if p, ee := os.FindProcess(vm.Pid); ee == nil {
    83  					log.Println("hard kill...")
    84  					if err = p.Signal(os.Interrupt); err != nil {
    85  						return
    86  					}
    87  				}
    88  			}
    89  			return
    90  		}
    91  	)
    92  	if sshSession, err = vm.startSSHsession(); err != nil {
    93  		if err = hardKill(err); err != nil {
    94  			return
    95  		}
    96  	} else {
    97  		defer sshSession.close()
    98  		if err =
    99  			hardKill(sshSession.executeRemoteCommand(command)); err != nil {
   100  			return
   101  		}
   102  	}
   103  	// wait until it's _really_ dead, but not forever
   104  	for {
   105  		timeout := time.After(30 * time.Second)
   106  		select {
   107  		// unmounts may take a bit in slow drives...
   108  		case <-timeout:
   109  			return fmt.Errorf(fmt.Sprintf("'%s' didn't shutdown normally "+
   110  				"after 30s (!)... ", vm.Name))
   111  		case <-time.Tick(100 * time.Millisecond):
   112  			if _, ee := os.FindProcess(vm.Pid); ee == nil {
   113  				if e :=
   114  					os.RemoveAll(filepath.Join(engine.runDir,
   115  						vm.UUID)); e != nil {
   116  					log.Println(e.Error())
   117  				}
   118  				log.Printf("successfully halted '%s'\n", vm.Name)
   119  				return
   120  			}
   121  		}
   122  	}
   123  }
   124  
   125  func init() {
   126  	killCmd.Flags().BoolP("all", "a", false, "halts all running instances")
   127  	RootCmd.AddCommand(killCmd)
   128  }