diff --git a/features.md b/features.md
index 0746864de9d..b7d995f2b3a 100644
--- a/features.md
+++ b/features.md
@@ -67,7 +67,6 @@
| GCPDualStackInstall| | | Enabled | Enabled | | | Enabled | Enabled |
| GatewayAPIWithoutOLM| | | Enabled | Enabled | | | Enabled | Enabled |
| HyperShiftOnlyDynamicResourceAllocation| Enabled | | Enabled | | Enabled | | Enabled | |
-| ImageModeStatusReporting| | | Enabled | Enabled | | | Enabled | Enabled |
| IngressControllerDynamicConfigurationManager| | | Enabled | Enabled | | | Enabled | Enabled |
| IrreconcilableMachineConfig| | | Enabled | Enabled | | | Enabled | Enabled |
| KMSEncryption| | | Enabled | Enabled | | | Enabled | Enabled |
@@ -101,6 +100,7 @@
| ExternalOIDC| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
| ExternalOIDCWithUIDAndExtraClaimMappings| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
| GCPClusterHostedDNSInstall| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
+| ImageModeStatusReporting| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
| ImageStreamImportMode| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
| InsightsConfig| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
| InsightsOnDemandDataGather| Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled | Enabled |
diff --git a/features/features.go b/features/features.go
index 9b56faf67df..e9052c9ff3e 100644
--- a/features/features.go
+++ b/features/features.go
@@ -262,7 +262,7 @@ var (
contactPerson("ijanssen").
productScope(ocpSpecific).
enhancementPR("https://github.com/openshift/enhancements/pull/1809").
- enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()).
+ enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()).
mustRegister()
FeatureGateClusterAPIInstall = newFeatureGate("ClusterAPIInstall").
diff --git a/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml
index 1e7bbbf9722..c59e7dc4c6f 100644
--- a/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml
+++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml
@@ -173,9 +173,6 @@
{
"name": "GatewayAPIWithoutOLM"
},
- {
- "name": "ImageModeStatusReporting"
- },
{
"name": "IngressControllerDynamicConfigurationManager"
},
@@ -319,6 +316,9 @@
{
"name": "HyperShiftOnlyDynamicResourceAllocation"
},
+ {
+ "name": "ImageModeStatusReporting"
+ },
{
"name": "ImageStreamImportMode"
},
diff --git a/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml
index 80234030144..7d2baa02548 100644
--- a/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml
+++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml
@@ -175,9 +175,6 @@
{
"name": "GatewayAPIWithoutOLM"
},
- {
- "name": "ImageModeStatusReporting"
- },
{
"name": "IngressControllerDynamicConfigurationManager"
},
@@ -321,6 +318,9 @@
{
"name": "HyperShiftOnlyDynamicResourceAllocation"
},
+ {
+ "name": "ImageModeStatusReporting"
+ },
{
"name": "ImageStreamImportMode"
},
diff --git a/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml
index db208cded2d..393975d162c 100644
--- a/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml
+++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml
@@ -173,9 +173,6 @@
{
"name": "HyperShiftOnlyDynamicResourceAllocation"
},
- {
- "name": "ImageModeStatusReporting"
- },
{
"name": "IngressControllerDynamicConfigurationManager"
},
@@ -313,6 +310,9 @@
{
"name": "GCPClusterHostedDNSInstall"
},
+ {
+ "name": "ImageModeStatusReporting"
+ },
{
"name": "ImageStreamImportMode"
},
diff --git a/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml
index 3c46a9898b3..0481d1dbf09 100644
--- a/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml
+++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml
@@ -175,9 +175,6 @@
{
"name": "HyperShiftOnlyDynamicResourceAllocation"
},
- {
- "name": "ImageModeStatusReporting"
- },
{
"name": "IngressControllerDynamicConfigurationManager"
},
@@ -315,6 +312,9 @@
{
"name": "GCPClusterHostedDNSInstall"
},
+ {
+ "name": "ImageModeStatusReporting"
+ },
{
"name": "ImageStreamImportMode"
},
diff --git a/tools/codegen/cmd/featuregate-test-analyzer.go b/tools/codegen/cmd/featuregate-test-analyzer.go
index 57967a371c1..bdc12f9f105 100644
--- a/tools/codegen/cmd/featuregate-test-analyzer.go
+++ b/tools/codegen/cmd/featuregate-test-analyzer.go
@@ -618,7 +618,7 @@ type JobVariant struct {
Topology string
NetworkStack string
OS string
- JobTiers string // Comma-separated tiers (e.g., "standard,informing,blocking"). If empty, defaults to "standard,informing,blocking"
+ JobTiers string // Comma-separated tiers (e.g., "standard,informing,blocking"). If empty, defaults to "standard,informing,blocking,candidate"
Optional bool // If true, validation failures for this variant are non-blocking warnings
}
@@ -680,7 +680,7 @@ func testResultByName(results []TestResults, testName string) *TestResults {
func validateJobTiers(jobVariant JobVariant) error {
if jobVariant.JobTiers == "" {
- return nil // Empty is valid - will default to standard,informing,blocking
+ return nil // Empty is valid - will default to standard,informing,blocking,candidate
}
validTiers := map[string]bool{
diff --git a/tools/codegen/cmd/featuregate-test-analyzer_test.go b/tools/codegen/cmd/featuregate-test-analyzer_test.go
index df66edcac67..5689aacb722 100644
--- a/tools/codegen/cmd/featuregate-test-analyzer_test.go
+++ b/tools/codegen/cmd/featuregate-test-analyzer_test.go
@@ -6,6 +6,8 @@ import (
"testing"
"k8s.io/apimachinery/pkg/util/sets"
+
+ "github.com/openshift/api/tools/codegen/pkg/sippy"
)
func Test_listTestResultFor(t *testing.T) {
@@ -475,3 +477,42 @@ func Test_checkIfTestingIsSufficient_OptionalVariants(t *testing.T) {
})
}
}
+
+func Test_defaultQueriesIncludeCandidateTier(t *testing.T) {
+ // When JobTiers is empty, QueriesFor should generate queries for all tiers
+ // including candidate. This test is added to prevent regressions for candidate-tier
+ // jobs being excluded, like we had for MCO TP jobs.
+ queries := sippy.QueriesFor("vsphere", "amd64", "ha", "", "", "", "FeatureGate:TestGate]")
+
+ tierNames := sets.New[string]()
+ for _, q := range queries {
+ tierNames.Insert(q.TierName)
+ }
+
+ expectedTiers := []string{"standard", "informing", "blocking", "candidate"}
+ for _, tier := range expectedTiers {
+ if !tierNames.Has(tier) {
+ t.Errorf("default queries missing tier %q - got tiers: %v", tier, sets.List(tierNames))
+ }
+ }
+}
+
+func Test_allRequiredVariantsQueryCandidateTier(t *testing.T) {
+ // Verify that all required variant definitions will query for the candidate
+ // tier, either explicitly via JobTiers or via the default.
+ allVariants := append(append([]JobVariant{}, requiredSelfManagedJobVariants...), requiredHypershiftJobVariants...)
+
+ for _, variant := range allVariants {
+ queries := sippy.QueriesFor(variant.Cloud, variant.Architecture, variant.Topology, variant.NetworkStack, variant.OS, variant.JobTiers, "FeatureGate:Test]")
+ hasCandidateQuery := false
+ for _, q := range queries {
+ if q.TierName == "candidate" {
+ hasCandidateQuery = true
+ break
+ }
+ }
+ if !hasCandidateQuery {
+ t.Errorf("variant %+v does not query candidate tier - some platforms only run TechPreview tests in candidate-tier jobs", variant)
+ }
+ }
+}
diff --git a/tools/codegen/pkg/sippy/json_types.go b/tools/codegen/pkg/sippy/json_types.go
index 2785d496f4a..0795c2519a4 100644
--- a/tools/codegen/pkg/sippy/json_types.go
+++ b/tools/codegen/pkg/sippy/json_types.go
@@ -101,10 +101,10 @@ func QueriesFor(cloud, architecture, topology, networkStack, os, jobTiers, testP
})
}
- // Parse JobTiers - comma-separated string, default to standard/informing/blocking if empty
+ // Parse JobTiers - comma-separated string, default to standard/informing/blocking/candidate if empty
var jobTiersList []string
if jobTiers == "" {
- jobTiersList = []string{"standard", "informing", "blocking"}
+ jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
// Split by comma, trim whitespace, and deduplicate using sets
tierSet := sets.New[string]()
@@ -115,7 +115,7 @@ func QueriesFor(cloud, architecture, topology, networkStack, os, jobTiers, testP
}
// If all tiers were whitespace/empty after trimming, use defaults
if tierSet.Len() == 0 {
- jobTiersList = []string{"standard", "informing", "blocking"}
+ jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
jobTiersList = sets.List(tierSet)
}