Skip to content

Commit 5a24bd8

Browse files
authored
Fixes Splunk Aggregation Data (#1615)
1 parent 34f9bdf commit 5a24bd8

File tree

9 files changed

+188
-87
lines changed

9 files changed

+188
-87
lines changed

tools/flakeguard/Makefile

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ example:
3535
--repo-url "https://github.com/smartcontractkit/chainlink-testing-framework" \
3636
--base-sha "abc" \
3737
--head-sha "xyz" \
38-
--github-workflow-name "example"
38+
--github-workflow-name "ExampleWorkflowName" \
39+
--github-workflow-run-url "https://github.com/example/repo/actions/runs/1" \
40+
--splunk-url "https://splunk.example.com" \
41+
--splunk-token "splunk-token" \
42+
--splunk-event "example-splunk-event"
3943

4044
.PHONY: example_flaky_panic
4145
example_flaky_panic:
@@ -50,7 +54,11 @@ example_flaky_panic:
5054
--repo-url "https://github.com/smartcontractkit/chainlink-testing-framework" \
5155
--base-sha "abc" \
5256
--head-sha "xyz" \
53-
--github-workflow-name "example"
57+
--github-workflow-name "ExampleWorkflowName" \
58+
--github-workflow-run-url "https://github.com/example/repo/actions/runs/1" \
59+
--splunk-url "https://splunk.example.com" \
60+
--splunk-token "splunk-token" \
61+
--splunk-event "example-splunk-event"
5462

5563
.PHONY: example_timeout
5664
example_timeout:
@@ -65,4 +73,8 @@ example_timeout:
6573
--repo-url "https://github.com/smartcontractkit/chainlink-testing-framework" \
6674
--base-sha "abc" \
6775
--head-sha "xyz" \
68-
--github-workflow-name "example"
76+
--github-workflow-name "ExampleWorkflowName" \
77+
--github-workflow-run-url "https://github.com/example/repo/actions/runs/1" \
78+
--splunk-url "https://splunk.example.com" \
79+
--splunk-token "splunk-token" \
80+
--splunk-event "example-splunk-event"

tools/flakeguard/cmd/aggregate_results.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ var AggregateResultsCmd = &cobra.Command{
4949
aggregatedReport, err := reports.LoadAndAggregate(
5050
resultsPath,
5151
reports.WithReportID(reportID),
52-
reports.WithSplunk(splunkURL, splunkToken, reports.SplunkEvent(splunkEvent)),
52+
reports.WithSplunk(splunkURL, splunkToken, splunkEvent),
53+
reports.WithBaseSha(baseSHA),
54+
reports.WithHeadSha(headSHA),
55+
reports.WithRepoURL(repoURL),
56+
reports.WithGitHubWorkflowName(githubWorkflowName),
57+
reports.WithGitHubWorkflowRunURL(githubWorkflowRunURL),
5358
)
5459
if err != nil {
5560
s.Stop()
@@ -59,13 +64,6 @@ var AggregateResultsCmd = &cobra.Command{
5964
s.Stop()
6065
fmt.Println()
6166

62-
// Add metadata to the aggregated report
63-
aggregatedReport.HeadSHA = headSHA
64-
aggregatedReport.BaseSHA = baseSHA
65-
aggregatedReport.RepoURL = repoURL
66-
aggregatedReport.GitHubWorkflowName = githubWorkflowName
67-
aggregatedReport.GitHubWorkflowRunURL = githubWorkflowRunURL
68-
6967
if err != nil {
7068
s.Stop()
7169
return fmt.Errorf("error aggregating test reports: %w", err)

tools/flakeguard/cmd/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ var RunTestsCmd = &cobra.Command{
9797
if len(flakyTests) > 0 {
9898
log.Info().Int("count", len(flakyTests)).Str("pass ratio threshold", fmt.Sprintf("%.2f%%", maxPassRatio*100)).Msg("Found flaky tests")
9999
fmt.Printf("\nFlakeguard Summary\n")
100-
reports.RenderResults(os.Stdout, flakyTests, maxPassRatio, false)
100+
reports.RenderResults(os.Stdout, flakyTests, maxPassRatio, false, false)
101101
// Exit with error code if there are flaky tests
102102
os.Exit(1)
103103
}

tools/flakeguard/reports/data.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,11 @@ type SummaryData struct {
6464
MaxPassRatio float64 `json:"max_pass_ratio"`
6565
}
6666

67-
// SplunkEvent represents a customized splunk event string that helps us distinguish what
68-
// triggered the test to run. This is a custom field, different from the Splunk event field.
69-
type SplunkEvent string
70-
7167
// SplunkType represents what type of data is being sent to Splunk, e.g. a report or a result.
7268
// This is a custom field to help us distinguish what kind of data we're sending.
7369
type SplunkType string
7470

7571
const (
76-
Manual SplunkEvent = "manual"
77-
Scheduled SplunkEvent = "scheduled"
78-
PullRequest SplunkEvent = "pull_request"
79-
8072
Report SplunkType = "report"
8173
Result SplunkType = "result"
8274

@@ -95,9 +87,9 @@ type SplunkTestReport struct {
9587

9688
// SplunkTestReportEvent contains the actual meat of the Splunk test report event
9789
type SplunkTestReportEvent struct {
98-
Event SplunkEvent `json:"event"`
99-
Type SplunkType `json:"type"`
100-
Data TestReport `json:"data"`
90+
Event string `json:"event"`
91+
Type SplunkType `json:"type"`
92+
Data TestReport `json:"data"`
10193
// Incomplete indicates that there were issues uploading test results and the report is incomplete
10294
Incomplete bool `json:"incomplete"`
10395
}
@@ -111,9 +103,9 @@ type SplunkTestResult struct {
111103

112104
// SplunkTestResultEvent contains the actual meat of the Splunk test result event
113105
type SplunkTestResultEvent struct {
114-
Event SplunkEvent `json:"event"`
115-
Type SplunkType `json:"type"`
116-
Data TestResult `json:"data"`
106+
Event string `json:"event"`
107+
Type SplunkType `json:"type"`
108+
Data TestResult `json:"data"`
117109
}
118110

119111
// Data Processing Functions

tools/flakeguard/reports/io.go

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ func (OSFileSystem) WriteFile(filename string, data []byte, perm os.FileMode) er
3939
}
4040

4141
type aggregateOptions struct {
42-
reportID string
43-
splunkURL string
44-
splunkToken string
45-
splunkEvent SplunkEvent
42+
reportID string
43+
splunkURL string
44+
splunkToken string
45+
splunkEvent string
46+
baseSha string
47+
headSha string
48+
repoURL string
49+
gitHubWorkflowName string
50+
gitHubWorkflowRunURL string
4651
}
4752

4853
// AggregateOption is a functional option for configuring the aggregation process.
@@ -56,14 +61,49 @@ func WithReportID(reportID string) AggregateOption {
5661
}
5762

5863
// WithSplunk also sends the aggregation to a Splunk instance as events.
59-
func WithSplunk(url, token string, event SplunkEvent) AggregateOption {
64+
func WithSplunk(url, token string, event string) AggregateOption {
6065
return func(opts *aggregateOptions) {
6166
opts.splunkURL = url
6267
opts.splunkToken = token
6368
opts.splunkEvent = event
6469
}
6570
}
6671

72+
// WithHeadSha sets the head SHA for the aggregated report.
73+
func WithHeadSha(headSha string) AggregateOption {
74+
return func(opts *aggregateOptions) {
75+
opts.headSha = headSha
76+
}
77+
}
78+
79+
// WithBaseSha sets the base SHA for the aggregated report.
80+
func WithBaseSha(baseSha string) AggregateOption {
81+
return func(opts *aggregateOptions) {
82+
opts.baseSha = baseSha
83+
}
84+
}
85+
86+
// WithRepoURL sets the repository URL for the aggregated report.
87+
func WithRepoURL(repoURL string) AggregateOption {
88+
return func(opts *aggregateOptions) {
89+
opts.repoURL = repoURL
90+
}
91+
}
92+
93+
// WithGitHubWorkflowName sets the GitHub workflow name for the aggregated report.
94+
func WithGitHubWorkflowName(githubWorkflowName string) AggregateOption {
95+
return func(opts *aggregateOptions) {
96+
opts.gitHubWorkflowName = githubWorkflowName
97+
}
98+
}
99+
100+
// WithGitHubWorkflowRunURL sets the GitHub workflow run URL for the aggregated report.
101+
func WithGitHubWorkflowRunURL(githubWorkflowRunURL string) AggregateOption {
102+
return func(opts *aggregateOptions) {
103+
opts.gitHubWorkflowRunURL = githubWorkflowRunURL
104+
}
105+
}
106+
67107
// LoadAndAggregate reads all JSON files in a directory and aggregates the results into a single TestReport.
68108
func LoadAndAggregate(resultsPath string, options ...AggregateOption) (*TestReport, error) {
69109
if _, err := os.Stat(resultsPath); os.IsNotExist(err) {
@@ -115,6 +155,7 @@ func LoadAndAggregate(resultsPath string, options ...AggregateOption) (*TestRepo
115155
if err != nil {
116156
return nil, fmt.Errorf("error aggregating reports: %w", err)
117157
}
158+
118159
return aggregatedReport, nil
119160
}
120161

@@ -318,14 +359,20 @@ func SaveReport(fs FileSystem, filePath string, report TestReport) error {
318359
// aggregate aggregates multiple TestReport objects into a single TestReport as they are received
319360
func aggregate(reportChan <-chan *TestReport, errChan <-chan error, opts *aggregateOptions) (*TestReport, error) {
320361
var (
362+
fullReport = &TestReport{
363+
ID: opts.reportID,
364+
BaseSHA: opts.baseSha,
365+
HeadSHA: opts.headSha,
366+
RepoURL: opts.repoURL,
367+
GitHubWorkflowName: opts.gitHubWorkflowName,
368+
GitHubWorkflowRunURL: opts.gitHubWorkflowRunURL,
369+
}
321370
testMap = make(map[string]TestResult)
322-
fullReport = &TestReport{}
323371
excludedTests = map[string]struct{}{}
324372
selectedTests = map[string]struct{}{}
325373
sendToSplunk = opts.splunkURL != ""
326374
)
327375

328-
fullReport.ID = opts.reportID
329376
for report := range reportChan {
330377
if fullReport.GoProject == "" {
331378
fullReport.GoProject = report.GoProject
@@ -388,23 +435,25 @@ func aggregate(reportChan <-chan *TestReport, errChan <-chan error, opts *aggreg
388435
// sendDataToSplunk sends a truncated TestReport and each individual TestResults to Splunk as events
389436
func sendDataToSplunk(opts *aggregateOptions, report TestReport, results ...TestResult) error {
390437
start := time.Now()
438+
// Dry-run mode for example runs
439+
isExampleRun := strings.Contains(opts.splunkURL, "splunk.example.com")
440+
391441
client := resty.New().
392442
SetBaseURL(opts.splunkURL).
393443
SetAuthScheme("Splunk").
394444
SetAuthToken(opts.splunkToken).
395445
SetHeader("Content-Type", "application/json").
396446
SetLogger(ZerologRestyLogger{})
397-
client.AddRetryAfterErrorCondition().SetRetryCount(5).SetTimeout(time.Second * 10)
398447

399448
log.Debug().Str("report id", report.ID).Int("results", len(results)).Msg("Sending aggregated data to Splunk")
400449

401-
// Send results
402450
var (
403451
splunkErrs = []error{}
404452
resultsBatchSize = 10
405453
resultsBatch = []SplunkTestResult{}
406454
successfulResultsSent = 0
407455
)
456+
408457
for resultCount, result := range results {
409458
resultsBatch = append(resultsBatch, SplunkTestResult{
410459
Event: SplunkTestResultEvent{
@@ -415,53 +464,70 @@ func sendDataToSplunk(opts *aggregateOptions, report TestReport, results ...Test
415464
SourceType: SplunkSourceType,
416465
Index: SplunkIndex,
417466
})
418-
// Send results in batches so Splunk doesn't get mad
467+
419468
if len(resultsBatch) >= resultsBatchSize || resultCount == len(results)-1 {
420469
batchData, testNames, err := batchSplunkResults(resultsBatch)
421470
if err != nil {
422471
return fmt.Errorf("error batching results: %w", err)
423472
}
424473

425-
resp, err := client.R().
426-
SetBody(batchData.String()).
427-
Post("")
428-
if err != nil {
429-
splunkErrs = append(splunkErrs,
430-
fmt.Errorf("error sending flakeguard results for [%s] to Splunk: %w", strings.Join(testNames, ", "), err),
431-
)
432-
}
433-
if resp.IsError() {
434-
splunkErrs = append(splunkErrs,
435-
fmt.Errorf("error sending flakeguard result for [%s] to Splunk: %s", strings.Join(testNames, ", "), resp.String()),
436-
)
437-
}
438-
if err == nil && !resp.IsError() {
439-
successfulResultsSent += len(resultsBatch)
474+
if isExampleRun {
475+
log.Debug().Strs("tests", testNames).Msg("Example Run. Would send the below results to Splunk")
476+
for _, result := range resultsBatch {
477+
jsonResult, err := json.Marshal(result)
478+
if err != nil {
479+
return fmt.Errorf("error marshaling result for '%s': %w", result.Event.Data.TestName, err)
480+
}
481+
fmt.Println(string(jsonResult))
482+
}
483+
} else {
484+
resp, err := client.R().SetBody(batchData.String()).Post("")
485+
if err != nil {
486+
splunkErrs = append(splunkErrs,
487+
fmt.Errorf("error sending results for [%s] to Splunk: %w", strings.Join(testNames, ", "), err),
488+
)
489+
}
490+
if resp.IsError() {
491+
splunkErrs = append(splunkErrs,
492+
fmt.Errorf("error sending result for [%s] to Splunk: %s", strings.Join(testNames, ", "), resp.String()),
493+
)
494+
}
495+
if err == nil && !resp.IsError() {
496+
successfulResultsSent += len(resultsBatch)
497+
}
440498
}
441499
resultsBatch = []SplunkTestResult{}
442500
}
443501
}
444502

445-
// Check if errors occurred while uploading results and send report with incomplete flag
446-
resp, err := client.R().
447-
SetBody(SplunkTestReport{
448-
Event: SplunkTestReportEvent{
449-
Event: opts.splunkEvent,
450-
Type: Report,
451-
Data: report,
452-
Incomplete: len(splunkErrs) > 0,
453-
},
454-
SourceType: SplunkSourceType,
455-
Index: SplunkIndex,
456-
}).
457-
Post("")
458-
459-
if err != nil {
460-
splunkErrs = append(splunkErrs, fmt.Errorf("error sending flakeguard report '%s' to Splunk: %w", report.ID, err))
503+
reportData := SplunkTestReport{
504+
Event: SplunkTestReportEvent{
505+
Event: opts.splunkEvent,
506+
Type: Report,
507+
Data: report,
508+
Incomplete: len(splunkErrs) > 0,
509+
},
510+
SourceType: SplunkSourceType,
511+
Index: SplunkIndex,
461512
}
462-
if resp.IsError() {
463-
splunkErrs = append(splunkErrs, fmt.Errorf("error sending flakeguard report '%s' to Splunk: %s", report.ID, resp.String()))
513+
514+
if isExampleRun {
515+
log.Info().Msg("Example Run. Would send the below report to Splunk")
516+
jsonReport, err := json.Marshal(reportData)
517+
if err != nil {
518+
return fmt.Errorf("error marshaling report: %w", err)
519+
}
520+
fmt.Println(string(jsonReport))
521+
} else {
522+
resp, err := client.R().SetBody(reportData).Post("")
523+
if err != nil {
524+
splunkErrs = append(splunkErrs, fmt.Errorf("error sending report '%s' to Splunk: %w", report.ID, err))
525+
}
526+
if resp.IsError() {
527+
splunkErrs = append(splunkErrs, fmt.Errorf("error sending report '%s' to Splunk: %s", report.ID, resp.String()))
528+
}
464529
}
530+
465531
if len(splunkErrs) > 0 {
466532
log.Error().
467533
Int("successfully sent", successfulResultsSent).
@@ -476,6 +542,7 @@ func sendDataToSplunk(opts *aggregateOptions, report TestReport, results ...Test
476542
Str("duration", time.Since(start).String()).
477543
Msg("All results sent successfully")
478544
}
545+
479546
return errors.Join(splunkErrs...)
480547
}
481548

tools/flakeguard/reports/io_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import (
1414
)
1515

1616
const (
17-
splunkToken = "test-token"
18-
splunkEvent SplunkEvent = "test"
19-
reportID = "123"
20-
testRunCount = 15
21-
uniqueTests = 18
17+
splunkToken = "test-token"
18+
splunkEvent = "test"
19+
reportID = "123"
20+
testRunCount = 15
21+
uniqueTests = 18
2222
)
2323

2424
func TestAggregateResultFilesSplunk(t *testing.T) {

0 commit comments

Comments
 (0)