-
Notifications
You must be signed in to change notification settings - Fork 251
/
errorreport.go
111 lines (99 loc) · 3.45 KB
/
errorreport.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
package errorreport
import (
"encoding/json"
"log"
"reflect"
"github.com/pkg/errors"
taskcluster "github.com/taskcluster/taskcluster/v48/clients/client-go"
"github.com/taskcluster/taskcluster/v48/clients/client-go/tcworkermanager"
"github.com/taskcluster/taskcluster/v48/tools/worker-runner/run"
"github.com/taskcluster/taskcluster/v48/tools/worker-runner/tc"
"github.com/taskcluster/taskcluster/v48/tools/workerproto"
)
type ErrorReporter struct {
state *run.State
// Factory for worker-manager clients
factory tc.WorkerManagerClientFactory
}
func (er *ErrorReporter) SetProtocol(proto *workerproto.Protocol) {
proto.Register("error-report", func(msg workerproto.Message) {
er.HandleMessage(msg)
})
proto.AddCapability("error-report")
}
func (er *ErrorReporter) HandleMessage(msg workerproto.Message) {
er.state.Lock()
defer er.state.Unlock()
validate := func(things map[string]interface{}, key, expectedType string) bool {
if _, ok := things[key]; !ok {
return false
}
if realType := reflect.TypeOf(things[key]); expectedType != realType.String() {
log.Printf("got %s with type %#v, expected %s", key, realType.String(), expectedType)
return false
}
return true
}
if !validate(msg.Properties, "description", "string") {
log.Printf("Error processing error-report message, missing description or not string")
return
}
if !validate(msg.Properties, "kind", "string") {
log.Printf("Error processing error-report message, missing kind or not string")
return
}
if !validate(msg.Properties, "title", "string") {
log.Printf("Error processing error-report message, missing title or not string")
return
}
if !validate(msg.Properties, "extra", "map[string]interface {}") {
log.Printf("Error processing error-report message, missing extra or not map[string]interface{}")
return
}
extra := msg.Properties["extra"].(map[string]interface{})
extraMsg, err := json.Marshal(extra)
if err != nil {
log.Printf("Error processing error-report message, could not marshal extra")
}
errorReport := tcworkermanager.WorkerErrorReport{
Description: msg.Properties["description"].(string),
Kind: msg.Properties["kind"].(string),
Extra: extraMsg,
Title: msg.Properties["title"].(string),
WorkerGroup: er.state.WorkerGroup,
WorkerID: er.state.WorkerID,
}
err = ReportWorkerError(er.state, er.factory, &errorReport)
if err != nil {
log.Printf("Error reporting worker error: %v\n", err)
}
}
// ReportWorkerError will send a worker error report to worker-manager
func ReportWorkerError(state *run.State, factory tc.WorkerManagerClientFactory, payload *tcworkermanager.WorkerErrorReport) error {
wc, err := factory(state.RootURL, &state.Credentials)
if err != nil {
return errors.Wrap(err, "error instanciating worker-manager client")
}
if _, err = wc.ReportWorkerError(state.WorkerPoolID, payload); err != nil {
log.Printf("Error reporting worker error: %v", err)
log.Printf("Error payload: %v", payload)
}
return err
}
// Make a new ErrorReporter object
func New(state *run.State) *ErrorReporter {
return new(state, nil)
}
// Private constructor to allow injection of a fake factory
func new(state *run.State, factory tc.WorkerManagerClientFactory) *ErrorReporter {
if factory == nil {
factory = func(rootURL string, credentials *taskcluster.Credentials) (tc.WorkerManager, error) {
prov := tcworkermanager.New(credentials, rootURL)
return prov, nil
}
}
return &ErrorReporter{
factory: factory,
state: state,
}
}