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