blob: b7c38981c9c9d92ad636c1c327965261bcb7f4a0 [file] [log] [blame]
package metrics
import (
"fmt"
"github.com/bazelbuild/continuous-integration/metrics/clients"
"github.com/bazelbuild/continuous-integration/metrics/data"
)
type BuildSuccess struct {
client clients.BuildkiteClient
pipelines []*data.PipelineID
columns []Column
builds int
}
func (bs *BuildSuccess) Name() string {
return "build_success"
}
func (bs *BuildSuccess) Columns() []Column {
return bs.columns
}
func (bs *BuildSuccess) Collect() (data.DataSet, error) {
result := data.CreateDataSet(GetColumnNames(bs.columns))
for _, pipeline := range bs.pipelines {
builds, err := bs.client.GetMostRecentBuilds(pipeline, bs.builds)
if err != nil {
return nil, fmt.Errorf("Cannot collect build success statistics for pipeline %s: %v", pipeline, err)
}
for _, build := range builds {
platformStates := make(map[string]*state)
for _, job := range build.Jobs {
platform := getPlatform(job)
if platform == "" {
continue
}
mergeState(platformStates, platform, *job.State)
}
err := result.AddRow(pipeline.Org, pipeline.Slug, *build.Number, getState(platformStates, "linux"), getState(platformStates, "macos"), getState(platformStates, "windows"), getState(platformStates, "rbe"))
if err != nil {
return nil, fmt.Errorf("Failed to add result for build %d: %v", *build.Number, err)
}
}
}
return result, nil
}
type state struct {
Name string
Priority uint
}
var allStates = getAllStates()
const canceledState = "canceled"
const failedState = "failed"
const passingState = "passed"
const runningState = "running"
func getAllStates() map[string]*state {
// Our states are different from the Buildkite states (e.g. our "canceled" states includes "canceled" and "canceling").
/// They also have a priority that defines how multiple states for a given platform are aggregated into a single state.
states := make(map[string]*state)
canceled := &state{canceledState, 3}
states["canceled"] = canceled
states["canceling"] = canceled
states["failed"] = &state{failedState, 2}
running := &state{runningState, 1}
states["running"] = running
states["scheduled"] = running
states["blocked"] = running
states["passed"] = &state{passingState, 0}
return states
}
func mergeState(platformStates map[string]*state, platform, buildkiteState string) {
newState, ok := allStates[buildkiteState]
if !ok {
return
}
oldState, ok := platformStates[platform]
if ok {
if newState.Priority > oldState.Priority {
platformStates[platform] = newState
}
} else {
platformStates[platform] = newState
}
}
func getState(platformStates map[string]*state, platform string) string {
state, ok := platformStates[platform]
if !ok || state == nil {
return ""
}
return state.Name
}
// CREATE TABLE build_success (org VARCHAR(255), pipeline VARCHAR(255), build INT, linux VARCHAR(255), macos VARCHAR(255), windows VARCHAR(255), rbe VARCHAR(255), PRIMARY KEY(org, pipeline, build));
func CreateBuildSuccess(client clients.BuildkiteClient, builds int, pipelines ...*data.PipelineID) *BuildSuccess {
columns := []Column{Column{"org", true}, Column{"pipeline", true}, Column{"build", true}, Column{"linux", false}, Column{"macos", false}, Column{"windows", false}, Column{"rbe", false}}
return &BuildSuccess{client: client, pipelines: pipelines, columns: columns, builds: builds}
}