| 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} |
| } |