diff --git a/config/openshift-customizations.yaml b/config/openshift-customizations.yaml
index 8fb55ec950..88ab2fc89d 100644
--- a/config/openshift-customizations.yaml
+++ b/config/openshift-customizations.yaml
@@ -36,6 +36,11 @@ releases:
periodic-ci-Azure-ARO-HCP-main-periodic-prod-uksouth-e2e-parallel: true
periodic-ci-Azure-ARO-HCP-main-periodic-prod-e2e-parallel-ocp-nightly: true
rosa-stage:
+ overview:
+ multiVersionInstallTests: true
+ recentFailuresPeriod: "48h"
+ recentFailuresPreviousPeriod: "168h"
+ topFailingTestsPeriod: "168h"
jobs:
# ROSA Classic/STS nightly (stage)
periodic-ci-openshift-osde2e-main-nightly-4.16-rosa-classic-sts: true
diff --git a/config/openshift.yaml b/config/openshift.yaml
index cb69a0145f..3fb4764b0e 100644
--- a/config/openshift.yaml
+++ b/config/openshift.yaml
@@ -17064,6 +17064,11 @@ releases:
rosa-integration: {}
rosa-production: {}
rosa-stage:
+ overview:
+ multiVersionInstallTests: true
+ recentFailuresPeriod: 48h
+ recentFailuresPreviousPeriod: 168h
+ topFailingTestsPeriod: 168h
jobs:
periodic-ci-openshift-osde2e-main-aws-stage-informing-default: true
periodic-ci-openshift-osde2e-main-nightly-4.16-osd-aws: true
diff --git a/pkg/api/health.go b/pkg/api/health.go
index 9a9d2a9c2e..0fe3b5ad82 100644
--- a/pkg/api/health.go
+++ b/pkg/api/health.go
@@ -11,6 +11,7 @@ import (
log "github.com/sirupsen/logrus"
apitype "github.com/openshift/sippy/pkg/apis/api"
+ configv1 "github.com/openshift/sippy/pkg/apis/config/v1"
sippyprocessingv1 "github.com/openshift/sippy/pkg/apis/sippyprocessing/v1"
"github.com/openshift/sippy/pkg/db"
"github.com/openshift/sippy/pkg/db/query"
@@ -44,7 +45,7 @@ func useNewInstallTest(release string) bool {
// PrintOverallReleaseHealthFromDB gives a summarized status of the overall health, including
// infrastructure, install, upgrade, and variant success rates.
-func PrintOverallReleaseHealthFromDB(w http.ResponseWriter, dbc *db.DB, release string, reportEnd time.Time) {
+func PrintOverallReleaseHealthFromDB(w http.ResponseWriter, dbc *db.DB, release string, reportEnd time.Time, overviewCfg *configv1.OverviewConfig) {
excludedVariants := testidentification.DefaultExcludedVariants
// Minor upgrades install a previous version and should not be counted against the current version's install stat.
excludedInstallVariants := testidentification.DefaultExcludedVariants
@@ -74,6 +75,30 @@ func PrintOverallReleaseHealthFromDB(w http.ResponseWriter, dbc *db.DB, release
if installIndicator, found := query.TestReportExcludeVariants(dbc, release, installTestName, excludedInstallVariants); found {
indicators["install"] = installIndicator
}
+
+ // Releases spanning OCP version boundaries (e.g. rosa-stage) may have jobs
+ // producing old-style synthetic tests ([sig-sippy] install should work) and
+ // jobs producing new-style tests (install should succeed: overall). Query
+ // the alternate name set and keep whichever indicator has more CurrentRuns
+ // so the overview cards use the most-populated metric.
+ if overviewCfg != nil && overviewCfg.MultiVersionInstallTests {
+ altInfra := testidentification.NewInfrastructureTestName
+ altInstall := testidentification.NewInstallTestName
+ if useNewInstallTest(release) {
+ altInfra = testidentification.InfrastructureTestName
+ altInstall = testidentification.InstallTestName
+ }
+ if altInfraIndicator, found := query.TestReportExcludeVariants(dbc, release, altInfra, excludedVariants); found {
+ if existing, exists := indicators["infrastructure"]; !exists || altInfraIndicator.CurrentRuns > existing.CurrentRuns {
+ indicators["infrastructure"] = altInfraIndicator
+ }
+ }
+ if altInstallIndicator, found := query.TestReportExcludeVariants(dbc, release, altInstall, excludedInstallVariants); found {
+ if existing, exists := indicators["install"]; !exists || altInstallIndicator.CurrentRuns > existing.CurrentRuns {
+ indicators["install"] = altInstallIndicator
+ }
+ }
+ }
if upgradeIndicator, found := query.TestReportExcludeVariants(dbc, release, testidentification.UpgradeTestName, excludedVariants); found {
indicators["upgrade"] = upgradeIndicator
}
@@ -114,13 +139,22 @@ func PrintOverallReleaseHealthFromDB(w http.ResponseWriter, dbc *db.DB, release
// TODO: use or remove this logic
var warnings []string
- RespondWithJSON(http.StatusOK, w, apitype.Health{
+ health := apitype.Health{
Indicators: indicators,
LastUpdated: lastUpdated,
Current: currStats,
Previous: prevStats,
Warnings: warnings,
- })
+ }
+ if overviewCfg != nil {
+ health.Overview = &apitype.OverviewConfig{
+ MultiVersionInstallTests: overviewCfg.MultiVersionInstallTests,
+ RecentFailuresPeriod: overviewCfg.RecentFailuresPeriod,
+ RecentFailuresPreviousPeriod: overviewCfg.RecentFailuresPreviousPeriod,
+ TopFailingTestsPeriod: overviewCfg.TopFailingTestsPeriod,
+ }
+ }
+ RespondWithJSON(http.StatusOK, w, health)
}
func calculateJobResultStatistics(results []apitype.Job) (currStats, prevStats sippyprocessingv1.Statistics) {
diff --git a/pkg/api/health_test.go b/pkg/api/health_test.go
new file mode 100644
index 0000000000..630106fa35
--- /dev/null
+++ b/pkg/api/health_test.go
@@ -0,0 +1,31 @@
+package api
+
+import (
+ "testing"
+)
+
+func TestUseNewInstallTest(t *testing.T) {
+ tests := []struct {
+ release string
+ expected bool
+ }{
+ {"4.17", true},
+ {"4.11", true},
+ {"4.10", false},
+ {"4.8", false},
+ {"3.11", false},
+ {"5.0", true},
+ // Non-numeric releases return false (use old synthetic names)
+ {"rosa-stage", false},
+ {"aro-stage", false},
+ {"Presubmits", false},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.release, func(t *testing.T) {
+ if got := useNewInstallTest(tt.release); got != tt.expected {
+ t.Errorf("useNewInstallTest(%q) = %v, want %v", tt.release, got, tt.expected)
+ }
+ })
+ }
+}
diff --git a/pkg/apis/api/types.go b/pkg/apis/api/types.go
index a0eab6f2cc..30bf506731 100644
--- a/pkg/apis/api/types.go
+++ b/pkg/apis/api/types.go
@@ -935,6 +935,15 @@ type Health struct {
Warnings []string `json:"warnings"`
Current v1.Statistics `json:"current_statistics"`
Previous v1.Statistics `json:"previous_statistics"`
+ Overview *OverviewConfig `json:"overview,omitempty"`
+}
+
+// OverviewConfig is passed to the frontend to customize release overview rendering.
+type OverviewConfig struct {
+ MultiVersionInstallTests bool `json:"multi_version_install_tests,omitempty"`
+ RecentFailuresPeriod string `json:"recent_failures_period,omitempty"`
+ RecentFailuresPreviousPeriod string `json:"recent_failures_previous_period,omitempty"`
+ TopFailingTestsPeriod string `json:"top_failing_tests_period,omitempty"`
}
type ProwJobRunRiskAnalysis struct {
diff --git a/pkg/apis/config/v1/types.go b/pkg/apis/config/v1/types.go
index 2a609730f7..e78ea76f66 100644
--- a/pkg/apis/config/v1/types.go
+++ b/pkg/apis/config/v1/types.go
@@ -24,6 +24,29 @@ type ReleaseConfig struct {
// InformingJobs is the list of informing payload jobs
InformingJobs []string `yaml:"informingJobs,omitempty"`
+
+ // Overview configures the release overview page display behavior.
+ Overview *OverviewConfig `yaml:"overview,omitempty"`
+}
+
+// OverviewConfig controls how the release overview page is rendered.
+type OverviewConfig struct {
+ // MultiVersionInstallTests queries both old-style synthetic and new-style
+ // install test names, keeping whichever has more data. Useful for releases
+ // that span multiple OCP versions.
+ MultiVersionInstallTests bool `yaml:"multiVersionInstallTests,omitempty" json:"multi_version_install_tests,omitempty"`
+
+ // RecentFailuresPeriod overrides the default "current" window for new test
+ // failure detection (default: "24h").
+ RecentFailuresPeriod string `yaml:"recentFailuresPeriod,omitempty" json:"recent_failures_period,omitempty"`
+
+ // RecentFailuresPreviousPeriod overrides the default "previous" window for
+ // new test failure comparison (default: "72h").
+ RecentFailuresPreviousPeriod string `yaml:"recentFailuresPreviousPeriod,omitempty" json:"recent_failures_previous_period,omitempty"`
+
+ // TopFailingTestsPeriod, when set, adds a "Top Failing Tests" section
+ // showing all test failures within this window (e.g. "168h").
+ TopFailingTestsPeriod string `yaml:"topFailingTestsPeriod,omitempty" json:"top_failing_tests_period,omitempty"`
}
type ComponentReadinessConfig struct {
diff --git a/pkg/sippyserver/server.go b/pkg/sippyserver/server.go
index 6a1be7efd2..2f8e00524e 100644
--- a/pkg/sippyserver/server.go
+++ b/pkg/sippyserver/server.go
@@ -1224,9 +1224,14 @@ func (s *Server) jsonTestLifecyclesFromDB(w http.ResponseWriter, req *http.Reque
func (s *Server) jsonHealthReportFromDB(w http.ResponseWriter, req *http.Request) {
release := s.getParamOrFail(w, req, "release")
- if release != "" {
- api.PrintOverallReleaseHealthFromDB(w, s.db, release, s.GetReportEnd())
+ if release == "" {
+ return
+ }
+ var overviewCfg *v1.OverviewConfig
+ if cfg, ok := s.config.Releases[release]; ok {
+ overviewCfg = cfg.Overview
}
+ api.PrintOverallReleaseHealthFromDB(w, s.db, release, s.GetReportEnd(), overviewCfg)
}
func (s *Server) jsonBuildClusterHealth(w http.ResponseWriter, req *http.Request) {
diff --git a/sippy-ng/src/releases/RecentTestFailures.js b/sippy-ng/src/releases/RecentTestFailures.js
index ba2d961b31..f706cf3736 100644
--- a/sippy-ng/src/releases/RecentTestFailures.js
+++ b/sippy-ng/src/releases/RecentTestFailures.js
@@ -357,7 +357,8 @@ export default function RecentTestFailures(props) {
const [rowsPerPage, setRowsPerPage] = React.useState(props.limit || 5)
const period = props.period || '24h'
- const previousPeriod = props.previousPeriod || '72h'
+ const previousPeriod =
+ props.previousPeriod !== undefined ? props.previousPeriod : '72h'
const includeOutputs =
props.includeOutputs !== undefined ? props.includeOutputs : true
@@ -369,7 +370,9 @@ export default function RecentTestFailures(props) {
'/api/tests/recent_failures' +
`?release=${safeEncodeURIComponent(props.release)}` +
`&period=${safeEncodeURIComponent(period)}` +
- `&previousPeriod=${safeEncodeURIComponent(previousPeriod)}` +
+ (previousPeriod
+ ? `&previousPeriod=${safeEncodeURIComponent(previousPeriod)}`
+ : '') +
`&includeOutputs=${includeOutputs}` +
`&sortField=${safeEncodeURIComponent(orderBy)}` +
`&sort=${safeEncodeURIComponent(order)}` +
@@ -427,7 +430,11 @@ export default function RecentTestFailures(props) {
{title}
@@ -456,11 +463,14 @@ export default function RecentTestFailures(props) {
- No new test failures detected
+ {previousPeriod
+ ? 'No new test failures detected'
+ : 'No test failures detected'}
- No tests started failing in the last {period} that were not already
- failing in the prior {previousPeriod}.
+ {previousPeriod
+ ? `No tests started failing in the last ${period} that were not already failing in the prior ${previousPeriod}.`
+ : `No tests failed in the last ${period}.`}
) : (
diff --git a/sippy-ng/src/releases/ReleaseOverview.js b/sippy-ng/src/releases/ReleaseOverview.js
index 1a75e8842f..6c3ad120e4 100644
--- a/sippy-ng/src/releases/ReleaseOverview.js
+++ b/sippy-ng/src/releases/ReleaseOverview.js
@@ -264,9 +264,27 @@ export default function ReleaseOverview(props) {
/>
-
+
+ {data.overview?.top_failing_tests_period && (
+
+
+
+ )}
+
{releases?.release_attrs?.[props.release]?.capabilities
?.payloadTags && (