-
Notifications
You must be signed in to change notification settings - Fork 129
/
platform_significance.go
140 lines (126 loc) · 4.44 KB
/
platform_significance.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
package metrics
import (
"fmt"
"log"
"strconv"
"github.com/bazelbuild/continuous-integration/metrics/clients"
"github.com/bazelbuild/continuous-integration/metrics/data"
)
type PlatformSignificance struct {
buildSuccess *BuildSuccess
columns []Column
}
func (ps *PlatformSignificance) Name() string {
return "platform_significance"
}
func (ps *PlatformSignificance) Columns() []Column {
return ps.columns
}
type pipelineStats struct {
totalBuilds int
setupFailed int
passingBuilds int
canceledBuilds int
linuxFailures int
macosFailures int
windowsFailures int
rbeFailures int
multiPlatformFailures int
}
func (ps *PlatformSignificance) Collect() (data.DataSet, error) {
buildResult, err := ps.buildSuccess.Collect()
if err != nil {
return nil, err
}
stats, err := collectPipelineResults(buildResult)
if err != nil {
return nil, fmt.Errorf("Failed to collect pipeline results: %v", err)
}
result := data.CreateDataSet(GetColumnNames(ps.columns))
for pipeline, values := range stats {
result.AddRow(pipeline, values.totalBuilds, values.passingBuilds, values.canceledBuilds, values.setupFailed, values.linuxFailures, values.macosFailures, values.windowsFailures, values.rbeFailures, values.multiPlatformFailures)
}
return result, nil
}
func collectPipelineResults(buildResult data.DataSet) (map[string]*pipelineStats, error) {
stats := make(map[string]*pipelineStats)
for _, row := range buildResult.GetData().Data {
values, err := toString(row)
if err != nil {
return nil, fmt.Errorf("Could not process build_success results: %v", err)
}
pipeline := values[0]
if _, ok := stats[pipeline]; !ok {
stats[pipeline] = &pipelineStats{}
}
passed := true
canceled := false
failures := make([]int, 0)
missingData := 0
for i := 1; i < len(values); i += 1 {
if values[i] == "" {
// Count the columns without data. If all columns have no data, this means that the setup step failed or was canceled.
// Otherwise missing data means that the respective platform is not part of a given pipeline.
missingData += 1
} else if values[i] != passingState {
passed = false
if values[i] == canceledState {
canceled = true
break
} else if values[i] == failedState {
failures = append(failures, i)
}
}
}
stats[pipeline].totalBuilds += 1
if missingData == len(values)-1 {
stats[pipeline].setupFailed += 1
} else if canceled {
stats[pipeline].canceledBuilds += 1
} else if passed {
stats[pipeline].passingBuilds += 1
} else if len(failures) > 1 {
stats[pipeline].multiPlatformFailures += 1
} else if len(failures) == 1 {
// TODO(fweikert): improve data struct?
id := fmt.Sprintf("%s/%s/%d", row[0], row[1], row[2])
switch failures[0] {
case 1:
stats[pipeline].linuxFailures += 1
log.Printf("Linux only: %s\n", id)
case 2:
stats[pipeline].macosFailures += 1
log.Printf("MacOS only: %s\n", id)
case 3:
stats[pipeline].windowsFailures += 1
log.Printf("Windows only: %s\n", id)
case 4:
stats[pipeline].rbeFailures += 1
log.Printf("RBE only: %s\n", id)
}
}
}
return stats, nil
}
func toString(row []interface{}) ([]string, error) {
result := make([]string, 0)
for i, v := range row {
var str string
if number, ok := v.(int); ok {
str = strconv.Itoa(number)
} else {
str, ok = v.(string)
if !ok {
return nil, fmt.Errorf("Expected string in column %v: %s", i, v)
}
}
result = append(result, str)
}
return result, nil
}
// CREATE TABLE platform_significance (org VARCHAR(255), pipeline VARCHAR(255), total_builds INT, passing_builds INT, canceled_builds INT, setup_failed INT, linux_failures INT, macos_failures INT, windows_failures INT, rbe_failures INT, multi_platform_failures INT, PRIMARY KEY(org, pipeline));
func CreatePlatformSignificance(client clients.BuildkiteClient, builds int, pipelines ...*data.PipelineID) *PlatformSignificance {
buildSuccess := CreateBuildSuccess(client, builds, pipelines...)
columns := []Column{Column{"org", true}, Column{"pipeline", true}, Column{"total_builds", false}, Column{"passing_builds", false}, Column{"canceled_builds", false}, Column{"setup_failed", false}, Column{"linux_failures", false}, Column{"macos_failures", false}, Column{"windows_failures", false}, Column{"rbe_failures", false}, Column{"multi_platform_failures", false}}
return &PlatformSignificance{buildSuccess: buildSuccess, columns: columns}
}