Update hack/jenkins/test-flake-chart/report_flakes/report_flake.go
Co-authored-by: Steven Powell <44844360+spowelljr@users.noreply.github.com>pull/19217/head
parent
60ab0cdad8
commit
c2422d7337
|
@ -25,7 +25,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MAX_ITEM_ENV = 10
|
MaxItemEnv = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// This program requires three arguments
|
// This program requires three arguments
|
||||||
|
@ -33,6 +33,7 @@ const (
|
||||||
// $2 is the ROOT_JOB
|
// $2 is the ROOT_JOB
|
||||||
// $3 is the file containing a list of finished environments, one item per line
|
// $3 is the file containing a list of finished environments, one item per line
|
||||||
func main() {
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
client, err := storage.NewClient(context.Background())
|
client, err := storage.NewClient(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to connect to gcp: %v\n", err)
|
fmt.Printf("failed to connect to gcp: %v\n", err)
|
||||||
|
@ -53,15 +54,15 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
// fetch the test results
|
// fetch the test results
|
||||||
testSummaries, err := GetTestSummariesFromGcp(pr, rootJob, envList, client)
|
testSummaries, err := TestSummariesFromGCP(ctx, pr, rootJob, envList, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to load summaries: %v\n", err)
|
fmt.Printf("failed to load summaries: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
// fetch the pre-calculated flake rates
|
// fetch the pre-calculated flake rates
|
||||||
flakeRates, err := GetFlakeRate(client)
|
flakeRates, err := GetFlakeRate(ctx, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("failed to load flake rates: %v", err)
|
fmt.Printf("failed to load flake rates: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
// generate and send the message
|
// generate and send the message
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -59,10 +60,10 @@ func ParseEnvironmentList(listFile string) ([]string, error) {
|
||||||
return strings.Split(strings.TrimSpace(string(data)), "\n"), nil
|
return strings.Split(strings.TrimSpace(string(data)), "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSummariesFromGCP(pr, rootJob string, envList []string, client *storage.Client) (map[string]*ShortSummary, error) {
|
func TestSummariesFromGCP(ctx context.Context, pr, rootJob string, envList []string, client *storage.Client) (map[string]*ShortSummary, error) {
|
||||||
envToSummaries := map[string]*ShortSummary{}
|
envToSummaries := map[string]*ShortSummary{}
|
||||||
for _, env := range envList {
|
for _, env := range envList {
|
||||||
if summary, err := getTestSummaryFromGCP(pr, rootJob, env, client); err == nil {
|
if summary, err := testSummaryFromGCP(ctx, pr, rootJob, env, client); err == nil {
|
||||||
if summary != nil {
|
if summary != nil {
|
||||||
// if the summary is nil(missing) we just skip it
|
// if the summary is nil(missing) we just skip it
|
||||||
envToSummaries[env] = summary
|
envToSummaries[env] = summary
|
||||||
|
@ -74,19 +75,18 @@ func TestSummariesFromGCP(pr, rootJob string, envList []string, client *storage.
|
||||||
return envToSummaries, nil
|
return envToSummaries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFromSummary get the summary of a test on the specified env from the specified summary.
|
// testFromSummary get the summary of a test on the specified env from the specified summary.
|
||||||
func getTestSummaryFromGCP(pr, rootJob, env string, client *storage.Client) (*ShortSummary, error) {
|
func testSummaryFromGCP(ctx context.Context, pr, rootJob, env string, client *storage.Client) (*ShortSummary, error) {
|
||||||
ctx := context.TODO()
|
|
||||||
|
|
||||||
btk := client.Bucket("minikube-builds")
|
btk := client.Bucket("minikube-builds")
|
||||||
obj := btk.Object(fmt.Sprintf("logs/%s/%s/%s_summary.json", pr, rootJob, env))
|
obj := btk.Object(fmt.Sprintf("logs/%s/%s/%s_summary.json", pr, rootJob, env))
|
||||||
|
|
||||||
reader, err := obj.NewReader(ctx)
|
reader, err := obj.NewReader(ctx)
|
||||||
|
if errors.Is(err, storage.ErrObjectNotExist) {
|
||||||
|
// if this file does not exist, just skip it
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == storage.ErrObjectNotExist {
|
|
||||||
// if this file does not exist, just skip it
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// read the file
|
// read the file
|
||||||
|
@ -104,10 +104,10 @@ func getTestSummaryFromGCP(pr, rootJob, env string, client *storage.Client) (*Sh
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFlakeRate downloaded recent flake rate from gcs, and return the map{env->map{testname->flake rate}}
|
// GetFlakeRate downloaded recent flake rate from gcs, and return the map{env->map{testname->flake rate}}
|
||||||
func GetFlakeRate(client *storage.Client) (map[string]map[string]float64, error) {
|
func GetFlakeRate(ctx context.Context, client *storage.Client) (map[string]map[string]float64, error) {
|
||||||
btk := client.Bucket("minikube-flake-rate")
|
btk := client.Bucket("minikube-flake-rate")
|
||||||
obj := btk.Object("flake_rates.csv")
|
obj := btk.Object("flake_rates.csv")
|
||||||
reader, err := obj.NewReader(context.Background())
|
reader, err := obj.NewReader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read the flake rate file: %v", err)
|
return nil, fmt.Errorf("failed to read the flake rate file: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,10 @@ func GetFlakeRate(client *storage.Client) (map[string]map[string]float64, error)
|
||||||
result := map[string]map[string]float64{}
|
result := map[string]map[string]float64{}
|
||||||
for i := 1; i < len(records); i++ {
|
for i := 1; i < len(records); i++ {
|
||||||
// for each line in csv we extract env, test name and flake rate
|
// for each line in csv we extract env, test name and flake rate
|
||||||
|
if len(records[i]) < 2 {
|
||||||
|
// the csv must have at least 2 columns
|
||||||
|
continue
|
||||||
|
}
|
||||||
env := records[i][0]
|
env := records[i][0]
|
||||||
test := records[i][1]
|
test := records[i][1]
|
||||||
flakeRate, err := strconv.ParseFloat(records[i][2], 64)
|
flakeRate, err := strconv.ParseFloat(records[i][2], 64)
|
||||||
|
@ -134,7 +138,6 @@ func GetFlakeRate(client *storage.Client) (map[string]map[string]float64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCommentMessage(summaries map[string]*ShortSummary, flakeRates map[string]map[string]float64, pr, rootJob string) string {
|
func GenerateCommentMessage(summaries map[string]*ShortSummary, flakeRates map[string]map[string]float64, pr, rootJob string) string {
|
||||||
//builder := strings.Builder{}
|
|
||||||
type failedTest struct {
|
type failedTest struct {
|
||||||
flakeRate float64
|
flakeRate float64
|
||||||
env string
|
env string
|
||||||
|
@ -172,10 +175,10 @@ func GenerateCommentMessage(summaries map[string]*ShortSummary, flakeRates map[s
|
||||||
{"Environment", "Test Name", "Flake Rate"},
|
{"Environment", "Test Name", "Flake Rate"},
|
||||||
}
|
}
|
||||||
// if an env has too much failures we will just skip it and print a message in the end
|
// if an env has too much failures we will just skip it and print a message in the end
|
||||||
tooMuchFailure := []string{}
|
tooManyFailures := []string{}
|
||||||
for env, list := range envFailedTestList {
|
for env, list := range envFailedTestList {
|
||||||
if len(list) > MAX_ITEM_ENV {
|
if len(list) > MaxItemEnv {
|
||||||
tooMuchFailure = append(tooMuchFailure, env)
|
tooManyFailures = append(tooManyFailures, env)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, item := range list {
|
for _, item := range list {
|
||||||
|
@ -193,13 +196,13 @@ func GenerateCommentMessage(summaries map[string]*ShortSummary, flakeRates map[s
|
||||||
|
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
builder.WriteString(
|
builder.WriteString(
|
||||||
fmt.Sprintf("Here are the number of top %d failed tests in each environments with lowest flake rate.\n\n", MAX_ITEM_ENV))
|
fmt.Sprintf("Here are the number of top %d failed tests in each environments with lowest flake rate.\n\n", MaxItemEnv))
|
||||||
builder.WriteString(generateMarkdownTable(table))
|
builder.WriteString(generateMarkdownTable(table))
|
||||||
if len(tooMuchFailure) > 0 {
|
if len(tooManyFailures) > 0 {
|
||||||
|
|
||||||
builder.WriteString("\n\n Besides the following environments have too much failed tests:")
|
builder.WriteString("\n\n Besides the following environments have too much failed tests:")
|
||||||
for _, env := range tooMuchFailure {
|
for _, env := range tooManyFailures {
|
||||||
builder.WriteString(fmt.Sprintf("\n\n - %s: %d failed", env, len(envFailedTestList[env])))
|
builder.WriteString(fmt.Sprintf("\n\n - %s: %d failed %s ", env, len(envFailedTestList[env]), gopoghMDLink(pr, rootJob, env, "")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.WriteString("\n\nTo see the flake rates of all tests by environment, click [here](https://minikube.sigs.k8s.io/docs/contrib/test_flakes/).")
|
builder.WriteString("\n\nTo see the flake rates of all tests by environment, click [here](https://minikube.sigs.k8s.io/docs/contrib/test_flakes/).")
|
||||||
|
@ -231,7 +234,7 @@ func generateMarkdownTable(table [][]string) string {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// generate the hyphens seperator
|
// generate the hyphens separator
|
||||||
builder.WriteString("|")
|
builder.WriteString("|")
|
||||||
for j := 0; j < len(group); j++ {
|
for j := 0; j < len(group); j++ {
|
||||||
builder.WriteString(" ---- |")
|
builder.WriteString(" ---- |")
|
||||||
|
|
Loading…
Reference in New Issue