Skip to content

Commit

Permalink
Merge pull request #1264 from michelle192837/linktemplate
Browse files Browse the repository at this point in the history
Add links for alerts, and utilities to process link templates.
  • Loading branch information
google-oss-prow[bot] authored Nov 21, 2023
2 parents a0c6a8b + 223a62c commit 2c7f95e
Show file tree
Hide file tree
Showing 6 changed files with 701 additions and 10 deletions.
1 change: 1 addition & 0 deletions pkg/summarizer/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ go_library(
"//pkg/summarizer/analyzers:go_default_library",
"//pkg/summarizer/common:go_default_library",
"//pkg/tabulator:go_default_library",
"//util:go_default_library",
"//util/gcs:go_default_library",
"//util/metrics:go_default_library",
"//util/queue:go_default_library",
Expand Down
57 changes: 55 additions & 2 deletions pkg/summarizer/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
summarypb "github.com/GoogleCloudPlatform/testgrid/pb/summary"
statuspb "github.com/GoogleCloudPlatform/testgrid/pb/test_status"
"github.com/GoogleCloudPlatform/testgrid/pkg/tabulator"
"github.com/GoogleCloudPlatform/testgrid/util"
"github.com/GoogleCloudPlatform/testgrid/util/gcs"
"github.com/GoogleCloudPlatform/testgrid/util/metrics"
"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -720,7 +721,7 @@ func updateTab(ctx context.Context, tab *configpb.DashboardTab, group *configpb.

latest, latestSeconds := latestRun(grid.Columns)
alert := staleAlert(mod, latest, staleHours(tab), len(grid.Rows))
failures := failingTestSummaries(grid.Rows)
failures := failingTestSummaries(grid.Rows, tab.GetOpenTestTemplate(), group.GetGcsPrefix())
colsCells, brokenState := gridMetrics(len(grid.Columns), grid.Rows, recent, tab.BrokenColumnThreshold, features, tab.GetStatusCustomizationOptions())
metrics := tabMetrics(colsCells)
tabStatus := overallStatus(grid, recent, alert, brokenState, failures, features, colsCells, tab.GetStatusCustomizationOptions())
Expand Down Expand Up @@ -841,7 +842,7 @@ func staleAlert(mod, ran time.Time, stale time.Duration, rows int) string {
}

// failingTestSummaries returns details for every row with an active alert.
func failingTestSummaries(rows []*statepb.Row) []*summarypb.FailingTestSummary {
func failingTestSummaries(rows []*statepb.Row, template *configpb.LinkTemplate, gcsPrefix string) []*summarypb.FailingTestSummary {
var failures []*summarypb.FailingTestSummary
for _, row := range rows {
if row.AlertInfo == nil {
Expand Down Expand Up @@ -873,6 +874,17 @@ func failingTestSummaries(rows []*statepb.Row) []*summarypb.FailingTestSummary {
if alert.FailTime != nil {
sum.FailTimestamp = float64(alert.FailTime.Seconds)
}
// Verify what the links for alerts would be with the new method.
failLink := testResultLink(template, alert.GetProperties(), alert.GetFailTestId(), row.GetId(), alert.GetFailBuildId(), gcsPrefix)
latestFailLink := testResultLink(template, alert.GetProperties(), alert.GetLatestFailTestId(), row.GetId(), alert.GetLatestFailBuildId(), gcsPrefix)
log := logrus.WithField("failLink", failLink).WithField("latestFailLink", latestFailLink)
if failLink == "" || latestFailLink == "" {
log.Warning("Failed to create failure link.")
} else if !strings.HasPrefix(failLink, "http") || !strings.HasPrefix(latestFailLink, "http") {
log.Warning("Failure link does not include scheme.")
} else {
log.Info("Created failure links.")
}

failures = append(failures, &sum)
}
Expand All @@ -885,6 +897,47 @@ func buildFailLink(testID, target string) string {
return fmt.Sprintf("%s %s", url.PathEscape(testID), url.PathEscape(target))
}

func testResultLink(template *configpb.LinkTemplate, properties map[string]string, testID, target, buildID, gcsPrefix string) string {
// Return the result of open_test_template for the tab.
// This assumes that open_test_template uses a limited set of tokens (since it's not in the context of a browser).
// Assume that the following are valid: <gcs_prefix>, <test-name>, <workflow-id>, <workflow-name>, <test-id>, <build ID>
// TODO: Ensure workflow-id, workflow-name are added in alerts.
tokens := util.Tokens(template)
parameters := map[string]string{}
for _, token := range tokens {
switch token {
case util.GcsPrefix:
parameters[util.GcsPrefix] = gcsPrefix
case util.TestName:
parameters[util.TestName] = target
case util.WorkflowID:
if workflowID, ok := properties["workflow-id"]; ok {
parameters[util.WorkflowID] = workflowID
}
case util.WorkflowName:
if WorkflowName, ok := properties["workflow-name"]; ok {
parameters[util.WorkflowName] = WorkflowName
}
case util.TestID:
parameters[util.TestID] = testID
case util.BuildID:
parameters[util.BuildID] = buildID
default:
// Didn't match any simple tokens, check if it's a property.
trimmedToken := strings.NewReplacer("<", "", ">", "").Replace(token)
if v, ok := properties[trimmedToken]; ok {
parameters[token] = v
}
}
}
link, err := util.ExpandTemplate(template, parameters)
if err != nil {
logrus.WithError(err).WithField("template", template).WithField("parameters", parameters).Error("Error expanding link template.")
return ""
}
return link
}

// overallStatus determines whether the tab is stale, failing, flaky or healthy.
//
// Tabs are:
Expand Down
172 changes: 166 additions & 6 deletions pkg/summarizer/summary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1586,20 +1586,40 @@ func TestStaleAlert(t *testing.T) {
}

func TestFailingTestSummaries(t *testing.T) {
defaultTemplate := &configpb.LinkTemplate{
Url: "http://test.com/view/<workflow-name>/<workflow-id>",
Options: []*configpb.LinkOptionsTemplate{
{
Key: "test",
Value: "<test-name>@<test-id>",
},
{
Key: "path",
Value: "<encode:<gcs_prefix>>",
},
},
}
defaultGcsPrefix := "my-bucket/logs/cool-job"
cases := []struct {
name string
rows []*statepb.Row
expected []*summarypb.FailingTestSummary
name string
template *configpb.LinkTemplate
gcsPrefix string
rows []*statepb.Row
expected []*summarypb.FailingTestSummary
}{
{
name: "do not alert by default",
name: "do not alert by default",
template: defaultTemplate,
gcsPrefix: defaultGcsPrefix,
rows: []*statepb.Row{
{},
{},
},
},
{
name: "alert when rows have alerts",
name: "alert when rows have alerts",
template: defaultTemplate,
gcsPrefix: defaultGcsPrefix,
rows: []*statepb.Row{
{},
{
Expand Down Expand Up @@ -1692,7 +1712,7 @@ func TestFailingTestSummaries(t *testing.T) {

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
actual := failingTestSummaries(tc.rows)
actual := failingTestSummaries(tc.rows, tc.template, tc.gcsPrefix)
if diff := cmp.Diff(tc.expected, actual, protocmp.Transform()); diff != "" {
t.Errorf("failingTestSummaries() (-want, +got): %s", diff)
}
Expand Down Expand Up @@ -3281,3 +3301,143 @@ func TestSummaryPath(t *testing.T) {
})
}
}

func TestTestResultLink(t *testing.T) {
cases := []struct {
name string
template *configpb.LinkTemplate
properties map[string]string
testID string
target string
buildID string
gcsPrefix string
want string
}{
{
name: "nil",
want: "",
},
{
name: "empty",
template: &configpb.LinkTemplate{
Url: "https://test.com/<encode:<workflow-name>>/<workflow-id>/<test-id>/<encode:<test-name>>",
Options: []*configpb.LinkOptionsTemplate{
{
Key: "prefix",
Value: "<gcs-prefix>",
},
{
Key: "build",
Value: "<build-id>",
},
{
Key: "prop",
Value: "<my-prop>",
},
},
},
properties: map[string]string{},
testID: "",
target: "",
buildID: "",
gcsPrefix: "",
want: "https://test.com/%3Cencode:%3Cworkflow-name%3E%3E/%3Cworkflow-id%3E//?build=&prefix=&prop=%3Cmy-prop%3E",
},
{
name: "empty template",
template: &configpb.LinkTemplate{},
properties: map[string]string{
"workflow-id": "workflow-id-1",
"workflow-name": "//my:workflow",
"my-prop": "foo",
},
testID: "my-test-id-1",
target: "//path/to:my-test",
buildID: "build-1",
gcsPrefix: "my-bucket/has/results",
want: "",
},
{
name: "basically works",
template: &configpb.LinkTemplate{
Url: "https://test.com/<encode:<workflow-name>>/<workflow-id>/<test-id>/<encode:<test-name>>",
Options: []*configpb.LinkOptionsTemplate{
{
Key: "prefix",
Value: "<gcs-prefix>",
},
{
Key: "build",
Value: "<build-id>",
},
{
Key: "prop",
Value: "<my-prop>",
},
},
},
properties: map[string]string{
"workflow-id": "workflow-id-1",
"workflow-name": "//my:workflow",
"my-prop": "foo",
},
testID: "my-test-id-1",
target: "//path/to:my-test",
buildID: "build-1",
gcsPrefix: "my-bucket/has/results",
want: "https://test.com/%2F%2Fmy:workflow/workflow-id-1/my-test-id-1/%2F%2Fpath%2Fto:my-test?build=build-1&prefix=my-bucket%2Fhas%2Fresults&prop=foo",
},
{
name: "non-matching tokens",
template: &configpb.LinkTemplate{
Url: "https://test.com/<greeting>",
Options: []*configpb.LinkOptionsTemplate{
{
Key: "farewell",
Value: "<farewell>",
},
},
},
properties: map[string]string{
"workflow-id": "workflow-id-1",
"workflow-name": "//my:workflow",
"my-prop": "foo",
},
testID: "my-test-id-1",
target: "//path/to:my-test",
buildID: "build-1",
gcsPrefix: "my-bucket/has/results",
want: "https://test.com/%3Cgreeting%3E?farewell=%3Cfarewell%3E",
},
{
name: "basically works, nil properties",
template: &configpb.LinkTemplate{
Url: "https://test.com/<encode:<workflow-name>>/<workflow-id>/<test-id>/<encode:<test-name>>",
Options: []*configpb.LinkOptionsTemplate{
{
Key: "prefix",
Value: "<gcs-prefix>",
},
{
Key: "build",
Value: "<build-id>",
},
},
},
properties: nil,
testID: "my-test-id-1",
target: "//path/to:my-test",
buildID: "build-1",
gcsPrefix: "my-bucket/has/results",
want: "https://test.com/%3Cencode:%3Cworkflow-name%3E%3E/%3Cworkflow-id%3E/my-test-id-1///path/to:my-test?build=build-1&prefix=my-bucket%2Fhas%2Fresults",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := testResultLink(tc.template, tc.properties, tc.testID, tc.target, tc.buildID, tc.gcsPrefix); got != tc.want {
t.Errorf("testResultLink(%v, %v, %s, %s, %s, %s) = %q, want %q", tc.template, tc.properties, tc.testID, tc.target, tc.buildID, tc.gcsPrefix, got, tc.want)
}
})
}
}
19 changes: 17 additions & 2 deletions util/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = [
"links.go",
"log.go",
"strings.go",
],
importpath = "github.com/GoogleCloudPlatform/testgrid/util",
visibility = ["//visibility:public"],
deps = ["@com_github_sirupsen_logrus//:go_default_library"],
deps = [
"//pb/config:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)

filegroup(
Expand All @@ -29,3 +33,14 @@ filegroup(
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

go_test(
name = "go_default_test",
srcs = ["links_test.go"],
embed = [":go_default_library"],
deps = [
"//pb/config:go_default_library",
"@com_github_google_go_cmp//cmp:go_default_library",
"@com_github_google_go_cmp//cmp/cmpopts:go_default_library",
],
)
Loading

0 comments on commit 2c7f95e

Please sign in to comment.