@@ -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
1612type 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)
9291type 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
9898type 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
105107type 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-
340191func mergeTestResults (a , b TestResult ) TestResult {
341192 a .Runs += b .Runs
342193 a .Durations = append (a .Durations , b .Durations ... )
0 commit comments