-
Notifications
You must be signed in to change notification settings - Fork 24
/
script_checker.go
112 lines (96 loc) · 2.41 KB
/
script_checker.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package healthchecker
import (
"bytes"
"fmt"
"os/exec"
"time"
"code.cloudfoundry.org/lager/v3"
"code.cloudfoundry.org/route-registrar/commandrunner"
)
//go:generate counterfeiter . HealthChecker
type HealthChecker interface {
Check(runner commandrunner.Runner, scriptPath string, timeout time.Duration) (bool, error)
}
type healthChecker struct {
logger lager.Logger
}
func NewHealthChecker(logger lager.Logger) HealthChecker {
return &healthChecker{
logger: logger,
}
}
func (h healthChecker) Check(runner commandrunner.Runner, scriptPath string, timeout time.Duration) (bool, error) {
h.logger.Info(
"Executing script",
lager.Data{"scriptPath": scriptPath},
)
var outbuf, errbuf bytes.Buffer
err := runner.Run(&outbuf, &errbuf)
if err != nil {
h.logger.Info(
"Error starting script",
lager.Data{
"script": scriptPath,
"error": err.Error(),
"stdout": outbuf.String(),
"stderr": errbuf.String(),
},
)
return false, err
}
if timeout <= 0 {
err := runner.Wait()
return h.handleOutput(scriptPath, err, outbuf, errbuf)
}
commandErrChan := make(chan error)
go func() {
commandErrChan <- runner.Wait()
}()
select {
case <-time.After(timeout):
h.logger.Info(
"Script failed to exit within timeout",
lager.Data{
"script": scriptPath,
"stdout": outbuf.String(),
"stderr": errbuf.String(),
"timeout": timeout,
},
)
runner.Kill()
return false, fmt.Errorf("Script failed to exit within %v", timeout)
case err := <-commandErrChan:
return h.handleOutput(scriptPath, err, outbuf, errbuf)
}
}
func (h healthChecker) handleOutput(scriptPath string, err error, outbuf, errbuf bytes.Buffer) (bool, error) {
if err != nil {
h.logger.Info(
"Script exited with error",
lager.Data{
"script": scriptPath,
"error": err.Error(),
"stdout": outbuf.String(),
"stderr": errbuf.String(),
},
)
// If the script exited non-zero then we do not consider that an error
_, ok := err.(*exec.ExitError)
if ok {
return false, nil
}
// Untested due to difficulty of reproducing this case under test
// E.g. this path would be encountered for I/O errors between the script
// and the golang parent process which we cannot force in a test.
return false, err
}
h.logger.Info(
"Script exited without error",
lager.Data{
"script": scriptPath,
"stdout": outbuf.String(),
"stderr": errbuf.String(),
},
)
return true, nil
}