/
hook.go
157 lines (137 loc) · 4.26 KB
/
hook.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package agent
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/sensu/sensu-go/command"
"github.com/sensu/sensu-go/types"
"github.com/sensu/sensu-go/types/dynamic"
)
// ExecuteHooks executes all hooks contained in a check request based on
// the check status code of the check request
func (a *Agent) ExecuteHooks(request *types.CheckRequest, status int) []*types.Hook {
executedHooks := []*types.Hook{}
for _, hookList := range request.Config.CheckHooks {
// find the hookList with the corresponding type
if hookShouldExecute(hookList.Type, status) {
// run all the hooks of that type
for _, hookName := range hookList.Hooks {
hookConfig := getHookConfig(hookName, request.Hooks)
origCommand := hookConfig.Command
if ok := a.prepareHook(hookConfig); !ok {
// An error occured during the preparation of the hook and the error
// has been sent back to the server. At this point we should not
// execute the hook and wait for the next check request
continue
}
// Do not duplicate hook execution for types that fall into both an exit
// code and severity (ex. 0, ok)
in := hookInList(hookConfig.Name, executedHooks)
if !in {
hook := a.executeHook(hookConfig, request.Config.Name)
// To guard against publishing sensitive/redacted client attribute values
// the original command value is reinstated.
hook.Command = origCommand
executedHooks = append(executedHooks, hook)
}
}
}
}
return executedHooks
}
func (a *Agent) executeHook(hookConfig *types.HookConfig, check string) *types.Hook {
// Instantiate Event and Hook
event := &types.Event{
Check: &types.Check{},
}
hook := &types.Hook{
HookConfig: *hookConfig,
Executed: time.Now().Unix(),
}
// Instantiate the execution command
ex := command.ExecutionRequest{
Command: hookConfig.Command,
Timeout: int(hookConfig.Timeout),
InProgress: a.inProgress,
InProgressMu: a.inProgressMu,
Name: check,
}
// If stdin is true, add JSON event data to command execution.
if hookConfig.Stdin {
input, err := json.Marshal(event)
if err != nil {
a.sendFailure(event, fmt.Errorf("error marshaling json from event: %s", err))
return nil
}
ex.Input = string(input)
}
hookExec, err := a.executor.Execute(context.Background(), ex)
if err != nil {
hook.Output = err.Error()
} else {
hook.Output = hookExec.Output
}
hook.Duration = hookExec.Duration
hook.Status = int32(hookExec.Status)
return hook
}
func (a *Agent) prepareHook(hookConfig *types.HookConfig) bool {
if hookConfig == nil {
return false
}
// Instantiate an event in case of failure
event := &types.Event{
Check: &types.Check{},
}
// Validate that the given hook is valid.
if err := hookConfig.Validate(); err != nil {
a.sendFailure(event, fmt.Errorf("given hook is invalid: %s", err))
return false
}
// Extract the extended attributes from the entity and combine them at the
// top-level so they can be easily accessed using token substitution
synthesizedEntity := dynamic.Synthesize(a.getAgentEntity())
// Substitute tokens within the check configuration with the synthesized
// entity
hookConfigBytes, err := TokenSubstitution(synthesizedEntity, hookConfig)
if err != nil {
a.sendFailure(event, err)
return false
}
// Unmarshal the check configuration obtained after the token substitution
// back into the check config struct
if err = json.Unmarshal(hookConfigBytes, hookConfig); err != nil {
a.sendFailure(event, fmt.Errorf("could not unmarshal the hook config: %s", err))
return false
}
return true
}
func getHookConfig(hookName string, hookList []types.HookConfig) *types.HookConfig {
for _, hook := range hookList {
if hook.Name == hookName {
return &hook
}
}
return nil
}
func hookInList(hookName string, hookList []*types.Hook) bool {
for _, hook := range hookList {
if hook.Name == hookName {
return true
}
}
return false
}
func hookShouldExecute(hookType string, status int) bool {
if (hookType == strconv.Itoa(status)) ||
(hookType == "non-zero" && status != 0) ||
(hookType == "ok" && status == 0) ||
(hookType == "warning" && status == 1) ||
(hookType == "critical" && status == 2) ||
(hookType == "unknown" && (status < 0 || status > 2)) {
return true
}
return false
}