blob: d363662b36a39385eca349ccc5accf10077f4efb [file] [log] [blame] [edit]
package chromeperf
import (
"context"
"net/url"
"strings"
"go.skia.org/infra/go/metrics2"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/go/util"
)
const (
AlertGroupAPIName = "alert_group"
DetailsFuncName = "details"
MastersKey = "masters"
BotsKey = "bots"
BenchmarksKey = "benchmarks"
TestsKey = "tests"
Subtests1Key = "subtests_1"
Subtests2Key = "subtests_2"
)
// AlertGroupDetails contains data received from the alert group api.
type AlertGroupDetails struct {
GroupId string `json:"group_id"`
Anomalies map[string]string `json:"anomalies"`
StartCommitNumber int32 `json:"start_commit"`
EndCommitNumber int32 `json:"end_commit"`
// The hashes below are needed for cases where the commit numbers are
// different in chromeperf and in the perf instance. We can use these
// hashes to look up the correct commit number from the database.
StartCommitHash string `json:"start_commit_hash,omitempty"`
EndCommitHash string `json:"end_commit_hash,omitempty"`
}
// AlertGroupApiClient provides an interface to interact with the alert_group api in chromeperf.
type AlertGroupApiClient interface {
// GetAlertGroupDetails returns the alert group details for the provided group key.
GetAlertGroupDetails(ctx context.Context, groupKey string) (*AlertGroupDetails, error)
}
// alertGroupApiClientImpl implements AlertGroupApiClient.
type alertGroupApiClientImpl struct {
chromeperfClient ChromePerfClient
getAlertGroupDetailsCalled metrics2.Counter
getAlertGroupDetailsFailed metrics2.Counter
}
// GetAlertGroupDetails implements AlertGroupApiClient, returns alert group details for the provided group key.
func (client *alertGroupApiClientImpl) GetAlertGroupDetails(ctx context.Context, groupKey string) (*AlertGroupDetails, error) {
if groupKey == "" {
return nil, skerr.Fmt("Group key cannot be empty")
}
client.getAlertGroupDetailsCalled.Inc(1)
// Call Chrome Perf API to fetch alert group details
alertgroupResponse := AlertGroupDetails{}
err := client.chromeperfClient.SendGetRequest(ctx, AlertGroupAPIName, DetailsFuncName, url.Values{"key": {groupKey}}, &alertgroupResponse)
if err != nil {
client.getAlertGroupDetailsFailed.Inc(1)
return nil, skerr.Wrapf(err, "Failed to call chrome perf endpoint.")
}
return &alertgroupResponse, nil
}
// GetQueryParams returns the query parameters corresponding to the alert group data.
func (alertGroup *AlertGroupDetails) GetQueryParams(ctx context.Context) map[string][]string {
sklog.Infof("Start commit: %d, End commit: %d", alertGroup.StartCommitNumber, alertGroup.EndCommitNumber)
// We do not want duplicate params, hence create maps to use as a set datastructure for each param
masters_map := util.StringSet{}
bots_map := util.StringSet{}
benchmarks_map := util.StringSet{}
tests_map := util.StringSet{}
subtests_1_map := util.StringSet{}
subtests_2_map := util.StringSet{}
parsedInfo := map[string][]string{}
for _, test := range alertGroup.Anomalies {
splits := strings.Split(test, "/")
addToSetIfNotExists(masters_map, splits[0], parsedInfo, MastersKey)
addToSetIfNotExists(bots_map, splits[1], parsedInfo, BotsKey)
addToSetIfNotExists(benchmarks_map, splits[2], parsedInfo, BenchmarksKey)
addToSetIfNotExists(tests_map, splits[3], parsedInfo, TestsKey)
addToSetIfNotExists(subtests_1_map, splits[4], parsedInfo, Subtests1Key)
if len(splits) > 5 {
addToSetIfNotExists(subtests_2_map, splits[5], parsedInfo, Subtests2Key)
}
}
return getParamsMapFromParsedInfo(parsedInfo)
}
// GetQueryParamsPerTrace returns an array of query parameters where each element consists of query params for a specific anomaly
func (alertGroup *AlertGroupDetails) GetQueryParamsPerTrace(ctx context.Context) []map[string][]string {
traceParamsMap := []map[string][]string{}
for _, test := range alertGroup.Anomalies {
parsedInfo := map[string][]string{}
splits := strings.Split(test, "/")
parsedInfo[MastersKey] = []string{splits[0]}
parsedInfo[BotsKey] = []string{splits[1]}
parsedInfo[BenchmarksKey] = []string{splits[2]}
parsedInfo[TestsKey] = []string{splits[3]}
if len(splits) > 4 {
parsedInfo[Subtests1Key] = []string{splits[4]}
}
if len(splits) > 5 {
parsedInfo[Subtests2Key] = []string{splits[5]}
}
traceParamsMap = append(traceParamsMap, getParamsMapFromParsedInfo(parsedInfo))
}
return traceParamsMap
}
func getParamsMapFromParsedInfo(parsedInfo map[string][]string) map[string][]string {
paramsMap := map[string][]string{}
paramsMap["stat"] = []string{"value"}
paramsMap["master"] = parsedInfo[MastersKey]
paramsMap["bot"] = parsedInfo[BotsKey]
paramsMap["benchmark"] = parsedInfo[BenchmarksKey]
paramsMap["test"] = parsedInfo[TestsKey]
paramsMap["subtest_1"] = parsedInfo[Subtests1Key]
sub_2, ok := parsedInfo[Subtests2Key]
if ok && len(sub_2) > 0 {
paramsMap["subtest_2"] = parsedInfo[Subtests2Key]
}
return paramsMap
}
func addToSetIfNotExists(set util.StringSet, value string, parsedInfo map[string][]string, parsedInfoKey string) {
// Check if the parsedinfo key is present in the parsed data
if _, ok := parsedInfo[parsedInfoKey]; !ok {
parsedInfo[parsedInfoKey] = []string{}
}
// Append to the set if it isn't already present
if _, ok := set[value]; !ok {
set[value] = true
parsedInfo[parsedInfoKey] = append(parsedInfo[parsedInfoKey], value)
}
}
// NewAlertGroupApiClient returns a new instance of AlertGroupApiClient
func NewAlertGroupApiClient(ctx context.Context) (AlertGroupApiClient, error) {
cpClient, err := NewChromePerfClient(ctx, "", false)
if err != nil {
return nil, err
}
return newAlertGroupApiClient(cpClient), nil
}
// newAlertGroupApiClient returns a new instance of AlertGroupApiClient with the given chromeperf client
func newAlertGroupApiClient(cpClient ChromePerfClient) AlertGroupApiClient {
return &alertGroupApiClientImpl{
chromeperfClient: cpClient,
getAlertGroupDetailsCalled: metrics2.GetCounter("chrome_perf_get_alertgroup_details_called"),
getAlertGroupDetailsFailed: metrics2.GetCounter("chrome_perf_get_alertgroup_details_failed"),
}
}