-
Notifications
You must be signed in to change notification settings - Fork 124
/
cedar_models.go
162 lines (140 loc) · 4.03 KB
/
cedar_models.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
158
159
160
161
162
package apimodels
import (
"bufio"
"context"
"fmt"
"io"
"strconv"
"strings"
"time"
"github.com/evergreen-ci/evergreen/model/log"
"github.com/evergreen-ci/gimlet"
"github.com/evergreen-ci/timber"
"github.com/evergreen-ci/timber/buildlogger"
"github.com/mongodb/grip"
"github.com/mongodb/grip/level"
"github.com/pkg/errors"
)
type CedarConfig struct {
BaseURL string `json:"base_url"`
GRPCBaseURL string `json:"grpc_base_url"`
RPCPort string `json:"rpc_port"`
Username string `json:"username"`
APIKey string `json:"api_key,omitempty"`
Insecure bool `json:"insecure"`
}
// GetBuildloggerLogsOptions represents the arguments passed into the
// GetBuildloggerLogs function.
type GetBuildloggerLogsOptions struct {
BaseURL string `json:"_"`
TaskID string `json:"-"`
Execution *int `json:"-"`
TestName string `json:"-"`
Tags []string `json:"-"`
Start int64 `json:"-"`
End int64 `json:"-"`
Limit int `json:"-"`
Tail int `json:"-"`
}
// GetBuildloggerLogs makes request to Cedar for a specifc log and returns a
// log iterator.
// TODO (DEVPROD-1681): Remove this once Cedar logs have TTL'ed.
func GetBuildloggerLogs(ctx context.Context, opts GetBuildloggerLogsOptions) (log.LogIterator, error) {
usr := gimlet.GetUser(ctx)
if usr == nil {
return nil, errors.New("error getting user from context")
}
var start, end time.Time
if opts.Start > 0 {
start = time.Unix(0, opts.Start).UTC()
}
if opts.End > 0 {
end = time.Unix(0, opts.End).UTC()
}
getOpts := buildlogger.GetOptions{
Cedar: timber.GetOptions{
BaseURL: fmt.Sprintf("https://%s", opts.BaseURL),
UserKey: usr.GetAPIKey(),
UserName: usr.Username(),
},
TaskID: opts.TaskID,
Execution: opts.Execution,
TestName: opts.TestName,
Tags: opts.Tags,
PrintTime: true,
PrintPriority: true,
Start: start,
End: end,
Limit: opts.Limit,
Tail: opts.Tail,
}
r, err := buildlogger.Get(ctx, getOpts)
if err != nil {
return nil, errors.Wrapf(err, "getting logs from Cedar Buildlogger")
}
return newBuildloggerIterator(r), nil
}
// TODO (DEVPROD-1681): Remove this once Cedar logs have TTL'ed.
type buildloggerIterator struct {
readCloser io.ReadCloser
reader *bufio.Reader
item log.LogLine
catcher grip.Catcher
exhausted bool
closed bool
}
func newBuildloggerIterator(r io.ReadCloser) *buildloggerIterator {
return &buildloggerIterator{
readCloser: r,
reader: bufio.NewReader(r),
catcher: grip.NewBasicCatcher(),
}
}
func (it *buildloggerIterator) Next() bool {
if it.closed || it.exhausted {
return false
}
line, err := it.reader.ReadString('\n')
if err != nil {
it.exhausted = err == io.EOF
it.catcher.AddWhen(err != io.EOF, errors.Wrap(err, "reading log lines"))
return false
}
// Each log line is expected to have the format:
// [P:3%d] [2006/01/02 15:04:05.000] %s
// Fail if we cannot parse the first 34 characters to avoid panicking.
if len(line) < 34 {
it.catcher.Errorf("malformed line '%s'", line)
return false
}
// Parse prefixed priority with format "[P:3%d] %s".
priority, err := strconv.Atoi(strings.TrimSpace(line[3:6]))
if err != nil {
it.catcher.Wrap(err, "parsing log line priority")
return false
}
line = line[8:]
// Parse prefixed timestamp with format "[2006/01/02 15:04:05.000] %s".
ts, err := time.Parse("2006/01/02 15:04:05.000", line[1:24])
if err != nil {
it.catcher.Wrap(err, "parsing log line timestamp")
return false
}
line = line[26:]
it.item = log.LogLine{
Priority: level.Priority(priority),
Timestamp: ts.UnixNano(),
Data: strings.TrimSuffix(line, "\n"),
}
return true
}
func (it *buildloggerIterator) Item() log.LogLine { return it.item }
func (it *buildloggerIterator) Exhausted() bool { return it.exhausted }
func (it *buildloggerIterator) Err() error { return it.catcher.Resolve() }
func (it *buildloggerIterator) Close() error {
if it.closed {
return nil
}
it.closed = true
return it.readCloser.Close()
}