Skip to content

Commit dc17490

Browse files
committed
Batches Splunk calls
1 parent 60c5371 commit dc17490

File tree

8 files changed

+425
-231
lines changed

8 files changed

+425
-231
lines changed

tools/flakeguard/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ test:
88
set -euo pipefail
99
go list ./... | grep -v 'example_test_package' | xargs go test -json -cover -coverprofile unit-test-coverage.out -v 2>&1 | tee /tmp/gotest.log | gotestfmt
1010

11+
.PHONY: test-package
12+
test-package:
13+
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
14+
set -euo pipefail
15+
go test -json -cover -coverprofile unit-test-coverage.out -v $(PKG) 2>&1 | tee /tmp/gotest.log | gotestfmt
16+
1117
.PHONY: test-race
1218
test-race:
1319
go list ./... | grep -v 'example_test_package' | xargs go test -count=1 -race

tools/flakeguard/go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ require (
1010
github.com/rs/zerolog v1.33.0
1111
github.com/spf13/cobra v1.8.1
1212
golang.org/x/oauth2 v0.24.0
13-
golang.org/x/sync v0.9.0
1413
golang.org/x/text v0.20.0
1514
)
1615

tools/flakeguard/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
4242
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
4343
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
4444
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
45-
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
46-
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
4745
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4846
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4947
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

tools/flakeguard/reports/data.go

Lines changed: 18 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,22 @@ import (
66
"sort"
77
"strings"
88
"time"
9-
10-
"github.com/go-resty/resty/v2"
11-
"github.com/rs/zerolog/log"
12-
"golang.org/x/sync/errgroup"
139
)
1410

1511
// TestReport reports on the parameters and results of one to many test runs
1612
type TestReport struct {
17-
ID string `json:"id"`
18-
GoProject string `json:"go_project"`
19-
HeadSHA string `json:"head_sha"`
20-
BaseSHA string `json:"base_sha"`
21-
RepoURL string `json:"repo_url"`
22-
GitHubWorkflowName string `json:"github_workflow_name"`
23-
TestRunCount int `json:"test_run_count"`
24-
RaceDetection bool `json:"race_detection"`
25-
ExcludedTests []string `json:"excluded_tests"`
26-
SelectedTests []string `json:"selected_tests"`
27-
Results []TestResult `json:"results,omitempty"`
13+
ID string `json:"id"`
14+
GoProject string `json:"go_project"`
15+
HeadSHA string `json:"head_sha"`
16+
BaseSHA string `json:"base_sha"`
17+
RepoURL string `json:"repo_url"`
18+
GitHubWorkflowName string `json:"github_workflow_name"`
19+
GitHubWorkflowRunURL string `json:"github_workflow_run_url"`
20+
TestRunCount int `json:"test_run_count"`
21+
RaceDetection bool `json:"race_detection"`
22+
ExcludedTests []string `json:"excluded_tests"`
23+
SelectedTests []string `json:"selected_tests"`
24+
Results []TestResult `json:"results,omitempty"`
2825
}
2926

3027
// TestResult contains the results and outputs of a single test
@@ -86,25 +83,31 @@ const (
8683

8784
// https://docs.splunk.com/Splexicon:Sourcetype
8885
SplunkSourceType = "flakeguard_json"
86+
// https://docs.splunk.com/Splexicon:Index
87+
SplunkIndex = "github_flakeguard_runs"
8988
)
9089

9190
// SplunkTestReport is the full wrapper structure sent to Splunk for the full test report (sans results)
9291
type SplunkTestReport struct {
9392
Event SplunkTestReportEvent `json:"event"` // https://docs.splunk.com/Splexicon:Event
9493
SourceType string `json:"sourcetype"` // https://docs.splunk.com/Splexicon:Sourcetype
94+
Index string `json:"index"` // https://docs.splunk.com/Splexicon:Index
9595
}
9696

9797
// SplunkTestReportEvent contains the actual meat of the Splunk test report event
9898
type SplunkTestReportEvent struct {
9999
Event SplunkEvent `json:"event"`
100100
Type SplunkType `json:"type"`
101101
Data TestReport `json:"data"`
102+
// Incomplete indicates that there were issues uploading test results and the report is incomplete
103+
Incomplete bool `json:"incomplete"`
102104
}
103105

104106
// SplunkTestResult is the full wrapper structure sent to Splunk for a single test result
105107
type SplunkTestResult struct {
106108
Event SplunkTestResultEvent `json:"event"` // https://docs.splunk.com/Splexicon:Event
107109
SourceType string `json:"sourcetype"` // https://docs.splunk.com/Splexicon:Sourcetype
110+
Index string `json:"index"` // https://docs.splunk.com/Splexicon:Index
108111
}
109112

110113
// SplunkTestResultEvent contains the actual meat of the Splunk test result event
@@ -185,158 +188,6 @@ func FilterTests(results []TestResult, predicate func(TestResult) bool) []TestRe
185188
return filtered
186189
}
187190

188-
func aggregate(reportChan <-chan *TestReport, errChan <-chan error, opts *aggregateOptions) (*TestReport, error) {
189-
var (
190-
testMap = make(map[string]TestResult)
191-
fullReport = &TestReport{}
192-
excludedTests = map[string]struct{}{}
193-
selectedTests = map[string]struct{}{}
194-
sendToSplunk = opts.splunkURL != ""
195-
)
196-
197-
fullReport.ID = opts.reportID
198-
for report := range reportChan {
199-
if fullReport.GoProject == "" {
200-
fullReport.GoProject = report.GoProject
201-
} else if fullReport.GoProject != report.GoProject {
202-
return nil, fmt.Errorf("reports with different Go projects found, expected %s, got %s", fullReport.GoProject, report.GoProject)
203-
}
204-
fullReport.TestRunCount += report.TestRunCount
205-
fullReport.RaceDetection = report.RaceDetection && fullReport.RaceDetection
206-
for _, test := range report.ExcludedTests {
207-
excludedTests[test] = struct{}{}
208-
}
209-
for _, test := range report.SelectedTests {
210-
selectedTests[test] = struct{}{}
211-
}
212-
for _, result := range report.Results {
213-
result.ReportID = opts.reportID
214-
key := result.TestName + "|" + result.TestPackage
215-
if existing, found := testMap[key]; found {
216-
existing = mergeTestResults(existing, result)
217-
testMap[key] = existing
218-
} else {
219-
testMap[key] = result
220-
}
221-
}
222-
}
223-
224-
for err := range errChan {
225-
return nil, err
226-
}
227-
228-
// Finalize excluded and selected tests
229-
for test := range excludedTests {
230-
fullReport.ExcludedTests = append(fullReport.ExcludedTests, test)
231-
}
232-
for test := range selectedTests {
233-
fullReport.SelectedTests = append(fullReport.SelectedTests, test)
234-
}
235-
236-
// Send report to Splunk before adding test results
237-
if sendToSplunk {
238-
err := sendReportToSplunk(opts.splunkURL, opts.splunkToken, opts.splunkEvent, fullReport)
239-
if err != nil {
240-
log.Error().Err(err).Msg("Error sending report to Splunk")
241-
} else {
242-
log.Debug().Str("event", string(opts.splunkEvent)).Msg("Successfully sent report sent to Splunk")
243-
}
244-
}
245-
246-
// Prepare final results
247-
eg := errgroup.Group{}
248-
var aggregatedResults []TestResult
249-
for _, r := range testMap {
250-
result := r
251-
aggregatedResults = append(aggregatedResults, result)
252-
if sendToSplunk {
253-
eg.Go(func() error {
254-
return sendResultsToSplunk(opts.splunkURL, opts.splunkToken, opts.splunkEvent, result)
255-
})
256-
}
257-
}
258-
259-
sortTestResults(aggregatedResults)
260-
fullReport.Results = aggregatedResults
261-
262-
if sendToSplunk {
263-
if splunkErr := eg.Wait(); splunkErr != nil {
264-
log.Error().Err(splunkErr).Msg("Error sending results to Splunk")
265-
} else {
266-
log.Debug().Str("event", string(opts.splunkEvent)).Msg("Successfully sent results to Splunk")
267-
}
268-
}
269-
return fullReport, nil
270-
}
271-
272-
// sendReportToSplunk sends meta test report data to Splunk
273-
func sendReportToSplunk(url, token string, event SplunkEvent, report *TestReport) error {
274-
client := resty.New()
275-
client.AddRetryAfterErrorCondition().SetRetryCount(3).SetRetryWaitTime(5 * time.Second)
276-
resp, err := client.R().
277-
SetHeader("Authorization", fmt.Sprintf("Splunk %s", token)).
278-
SetHeader("Content-Type", "application/json").
279-
SetBody(SplunkTestReport{
280-
Event: SplunkTestReportEvent{
281-
Event: event,
282-
Type: Report,
283-
Data: *report,
284-
},
285-
SourceType: SplunkSourceType,
286-
}).
287-
Post(url)
288-
if err != nil {
289-
return err
290-
}
291-
if resp.IsError() {
292-
return fmt.Errorf("error sending report to Splunk: %s", resp.String())
293-
}
294-
return nil
295-
}
296-
297-
func sendResultsToSplunk(url, token string, event SplunkEvent, results ...TestResult) error {
298-
client := resty.New()
299-
client.AddRetryAfterErrorCondition().SetRetryCount(3).SetRetryWaitTime(5 * time.Second)
300-
eg := errgroup.Group{}
301-
for _, r := range results {
302-
result := r
303-
eg.Go(func() error {
304-
resp, err := client.R().
305-
SetHeader("Authorization", fmt.Sprintf("Splunk %s", token)).
306-
SetHeader("Content-Type", "application/json").
307-
SetBody(SplunkTestResult{
308-
Event: SplunkTestResultEvent{
309-
Event: event,
310-
Type: Result,
311-
Data: result,
312-
},
313-
SourceType: SplunkSourceType,
314-
}).
315-
Post(url)
316-
if err != nil {
317-
return err
318-
}
319-
if resp.IsError() {
320-
return fmt.Errorf("error sending result to Splunk: %s", resp.String())
321-
}
322-
return nil
323-
})
324-
}
325-
326-
return eg.Wait()
327-
}
328-
329-
func aggregateFromReports(opts *aggregateOptions, reports ...*TestReport) (*TestReport, error) {
330-
reportChan := make(chan *TestReport, len(reports))
331-
errChan := make(chan error, 1)
332-
for _, report := range reports {
333-
reportChan <- report
334-
}
335-
close(reportChan)
336-
close(errChan)
337-
return aggregate(reportChan, errChan, opts)
338-
}
339-
340191
func mergeTestResults(a, b TestResult) TestResult {
341192
a.Runs += b.Runs
342193
a.Durations = append(a.Durations, b.Durations...)

0 commit comments

Comments
 (0)