-
Notifications
You must be signed in to change notification settings - Fork 3
/
pgroup_invoker.go
78 lines (68 loc) · 2.14 KB
/
pgroup_invoker.go
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
package invoker
import (
"context"
"os"
"os/exec"
"syscall"
"code.cloudfoundry.org/dockerdriver"
"code.cloudfoundry.org/lager/v3"
)
type pgroupInvoker struct {
}
func NewProcessGroupInvoker() Invoker {
return &pgroupInvoker{}
}
func (r *pgroupInvoker) Invoke(env dockerdriver.Env, executable string, cmdArgs []string, envVars ...string) InvokeResult {
logger := env.Logger().Session("invoking-command-pgroup", lager.Data{"executable": executable, "args": cmdArgs})
logger.Info("start")
defer logger.Info("end")
// We do not pass in the docker context to let the exec.Command handle timeout/cancel, because we want to kill the entire process group. (Mount spawns child processes, which we also want to kill)
cmdHandle := exec.CommandContext(context.Background(), executable, cmdArgs...)
cmdHandle.SysProcAttr = &syscall.SysProcAttr{}
cmdHandle.SysProcAttr.Setpgid = true
var stdOutBuffer, stdErrBuffer Buffer
cmdHandle.Stdout = &stdOutBuffer
cmdHandle.Stderr = &stdErrBuffer
if len(envVars) > 0 {
allEnvVars := os.Environ()
for _, envVar := range envVars {
allEnvVars = append(allEnvVars, envVar)
}
cmdHandle.Env = allEnvVars
}
err := cmdHandle.Start()
if err != nil {
logger.Error("command-start-failed", err, lager.Data{"exe": executable, "output": stdOutBuffer.String()})
return invokeResult{
invokeErr: err,
outputBuffer: &stdOutBuffer,
errorBuffer: &stdErrBuffer,
logger: logger,
}
}
var cmdDone = false
go func() {
select {
case <-env.Context().Done():
if cmdDone {
logger.Info("not killing process due to already finished")
return
}
logger.Info("command-sigkill", lager.Data{"exe": executable, "pid": -cmdHandle.Process.Pid})
err := syscall.Kill(-cmdHandle.Process.Pid, syscall.SIGKILL)
if err != nil {
logger.Info("command-sigkill-error", lager.Data{"desc": err.Error()})
}
err = cmdHandle.Wait()
if err != nil {
logger.Info("command-sigkill-wait-error", lager.Data{"desc": err.Error()})
}
}
}()
return invokeResult{
cmdDone: &cmdDone,
cmd: cmdHandle,
outputBuffer: &stdOutBuffer,
errorBuffer: &stdErrBuffer,
logger: logger}
}