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 }