1package process 2 3import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "github.com/sirupsen/logrus" 9) 10 11// ErrProcessNotStarted is returned when we try to manipulated/interact with a 12// process that hasn't started yet (still nil). 13var ErrProcessNotStarted = errors.New("process not started yet") 14 15// GracefulTimeout is the time a Killer should wait in general to the graceful 16// termination to timeout. 17const GracefulTimeout = 10 * time.Minute 18 19// KillTimeout is the time a killer should wait in general for the kill command 20// to finish. 21const KillTimeout = 10 * time.Second 22 23type killer interface { 24 Terminate() 25 ForceKill() 26} 27 28var newProcessKiller = newKiller 29 30type KillWaiter interface { 31 KillAndWait(command Commander, waitCh chan error) error 32} 33 34type KillProcessError struct { 35 pid int 36} 37 38func (k *KillProcessError) Error() string { 39 return fmt.Sprintf("failed to kill process PID=%d, likely process is dormant", k.pid) 40} 41 42func (k *KillProcessError) Is(err error) bool { 43 _, ok := err.(*KillProcessError) 44 45 return ok 46} 47 48type osKillWait struct { 49 logger Logger 50 51 gracefulKillTimeout time.Duration 52 forceKillTimeout time.Duration 53} 54 55func NewOSKillWait(logger Logger, gracefulKillTimeout, forceKillTimeout time.Duration) KillWaiter { 56 return &osKillWait{ 57 logger: logger, 58 gracefulKillTimeout: gracefulKillTimeout, 59 forceKillTimeout: forceKillTimeout, 60 } 61} 62 63// KillAndWait will take the specified process and terminate the process and 64// wait util the waitCh returns or the graceful kill timer runs out after which 65// a force kill on the process would be triggered. 66func (kw *osKillWait) KillAndWait(command Commander, waitCh chan error) error { 67 process := command.Process() 68 if process == nil { 69 return ErrProcessNotStarted 70 } 71 72 log := kw.logger.WithFields(logrus.Fields{ 73 "PID": process.Pid, 74 }) 75 76 processKiller := newProcessKiller(log, command) 77 processKiller.Terminate() 78 79 select { 80 case err := <-waitCh: 81 return err 82 case <-time.After(kw.gracefulKillTimeout): 83 processKiller.ForceKill() 84 85 select { 86 case err := <-waitCh: 87 return err 88 case <-time.After(kw.forceKillTimeout): 89 return &KillProcessError{pid: process.Pid} 90 } 91 } 92} 93