Skip to content

OCPBUGS-99459: Improve verify-feature-promotion checks for Install featuregates#2943

Open
sadasu wants to merge 5 commits into
openshift:masterfrom
sadasu:fix-feature-promotion-checks-metal
Open

OCPBUGS-99459: Improve verify-feature-promotion checks for Install featuregates#2943
sadasu wants to merge 5 commits into
openshift:masterfrom
sadasu:fix-feature-promotion-checks-metal

Conversation

@sadasu

@sadasu sadasu commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@openshift-ci-robot openshift-ci-robot added jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jul 22, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@sadasu: This pull request references Jira Issue OCPBUGS-99459, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci

openshift-ci Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Hello @sadasu! Some important instructions when contributing to openshift/api:
API design plays an important part in the user experience of OpenShift and as such API PRs are subject to a high level of scrutiny to ensure they follow our best practices. If you haven't already done so, please review the OpenShift API Conventions and ensure that your proposed changes are compliant. Following these conventions will help expedite the api review process for your PR.

@sadasu

sadasu commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

/jira refresh

@openshift-ci-robot openshift-ci-robot added the jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. label Jul 22, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@sadasu: This pull request references Jira Issue OCPBUGS-99459, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)
Details

In response to this:

/jira refresh

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot removed the jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. label Jul 22, 2026
@qodo-for-rh-openshift

Copy link
Copy Markdown

PR Summary by Qodo

Enhance verify-feature-promotion checks for Install gates and metal (SNO/compact)

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add Install-gate reporting and stricter validation for the "install should succeed: overall" test.
• Always include metal single (SNO) and compact variants as optional verify-feature-promotion
 checks.
• Extend Sippy query generation to filter by Capability and cover new validation logic with tests.
Diagram

graph TD
  A["verify-feature-promotion"] --> B["featuregate-test-analyzer"] --> C["Sippy query builders"] --> D[("Sippy API")]
  B --> E["Install test validation"] --> F["Markdown/text report"]
  B --> G["Variant selection"]
  subgraph Legend
    direction LR
    _cli["CLI/Tool"] ~~~ _mod["Module"] ~~~ _api[("External API")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Unify Sippy HTTP fetch logic into a shared client/helper
  • ➕ Avoids duplicating Transport/Client setup between variant-level and test-level calls
  • ➕ Makes timeouts/TLS policy consistent and easier to change
  • ➕ Simplifies adding future test-level queries
  • ➖ Slight refactor cost and bigger diff
  • ➖ May require more careful unit testing/mocking boundaries
2. Extend existing QueriesFor() to optionally include Capability filter
  • ➕ Avoids adding a second query builder API surface
  • ➕ Keeps query construction in a single function
  • ➖ Can complicate the existing signature/option matrix
  • ➖ Less explicit intent than a dedicated Capability-aware function

Recommendation: The PR’s approach is sound: it keeps the Install-gate special-case explicit (100% install success requirement) and scopes Capability filtering to a dedicated query builder. If follow-up work is planned, consider consolidating Sippy HTTP request code into a shared helper/client to reduce duplication and keep transport settings consistent.

Files changed (3) +522 / -21

Enhancement (2) +338 / -20
featuregate-test-analyzer.goAdd Install-gate install-test stats/validation and include metal SNO/compact variants +240/-20

Add Install-gate install-test stats/validation and include metal SNO/compact variants

• Adds Install-specific handling to report pass percentage for "install should succeed: overall" and enforces a 100% pass-rate requirement (with run-count checks) without double-validating it under the generic rules. Expands variant selection to always include metal single and compact topologies as optional checks, and introduces a JobVariant.String() to improve readable output. Also adds a Capability-aware test-level Sippy query path for Install gates and improves HTTP status error messages.

tools/codegen/cmd/featuregate-test-analyzer.go

json_types.goAdd Capability-aware Sippy query generator +98/-0

Add Capability-aware Sippy query generator

• Introduces QueriesForWithCapability() to generate per-JobTier Sippy queries that include a Capability filter alongside platform/arch/topology and test-name matching. Handles optional networkStack/OS filters and defaults/deduplicates JobTier selection similarly to existing query generation patterns.

tools/codegen/pkg/sippy/json_types.go

Tests (1) +184 / -1
featuregate-test-analyzer_test.goAdd unit coverage for Install feature gate sufficiency rules +184/-1

Add unit coverage for Install feature gate sufficiency rules

• Updates listTestResultFor() call sites for the new return value and adds a dedicated test suite validating Install-gate behavior (missing install test => warning, insufficient runs => error, <100% pass => error, multi-variant mixed outcomes). Ensures non-Install gates do not trigger the Install-specific validation path.

tools/codegen/cmd/featuregate-test-analyzer_test.go

@openshift-ci openshift-ci Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The feature-gate analyzer adds Install-specific aggregation, validation, and reporting for install should succeed: overall, including a 100% pass-rate requirement. It categorizes validation outcomes, excludes infrastructure-failure runs from pass-rate totals, standardizes Sippy client handling, adds capability-filtered queries, and expands Install validation tests.

Suggested reviewers: mtulio, everettraven, joelspeed

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No meaningful pull request description was provided, so its relationship to the changeset cannot be assessed. Add a brief description of the install featuregate verification and test updates made in this pull request.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: improved verify-feature-promotion checks for Install featuregates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The added/modified tests use static table-driven names; no Ginkgo titles or runtime-generated identifiers were introduced.
Test Structure And Quality ✅ Passed No Ginkgo/cluster-interaction specs were added; the changed tests are pure table-driven unit tests with subtests and clear t.Errorf/t.Logf messages.
Microshift Test Compatibility ✅ Passed The PR only adds/changes Go unit tests in tools/codegen; no Ginkgo e2e tests or MicroShift-unsupported APIs/features were introduced.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Only codegen code and Go unit tests changed; no Ginkgo e2e specs or SNO-sensitive node assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only changes codegen/test helpers and Sippy query builders; no manifests, controllers, pod specs, or scheduling constraints were modified.
Ote Binary Stdout Contract ✅ Passed Touched main/init code only uses klog (stderr by default) and command wiring; new fmt.Fprintf calls are inside RunE/Run, not process-level entrypoints.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo/e2e tests were added; the changed test file is standard Go unit tests with no IPv4-only assumptions or external connectivity.
No-Weak-Crypto ✅ Passed No weak-crypto primitives, custom crypto, or non-constant-time secret comparisons appear in the changed code.
Container-Privileges ✅ Passed PASS: The only changed files are Go source/test files; no container/K8s manifests were modified, and no privilege/securityContext flags appear in the diff.
No-Sensitive-Data-In-Logs ✅ Passed The new diff only prints feature-gate names, job-variant metadata, and run counts; no passwords, tokens, PII, or customer data were added.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@openshift-ci
openshift-ci Bot requested review from JoelSpeed and everettraven July 22, 2026 15:34
@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Jul 22, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 29 rules
✅ Skills: api-review

Grey Divider


Action required

1. Install test not validated ✓ Resolved 🐞 Bug ≡ Correctness
Description
checkIfTestingIsSufficient() looks for "install should succeed: overall" inside
testedVariant.TestResults, but Install feature gates use verifyJobBasedFeatureGatePromotion() which
populates TestResults with Sippy job names, so the install test is usually missing and only a
warning is emitted. The newly fetched installTestLevelData is printed but never fed into validation,
making the 100% install requirement ineffective.
Code

tools/codegen/cmd/featuregate-test-analyzer.go[R471-499]

+		// For Install feature gates, validate "install should succeed: overall" test
+		if strings.Contains(featureGate, "Install") {
+			installTest := testResultByName(testedVariant.TestResults, "install should succeed: overall")
+			if installTest == nil {
+				results = append(results, ValidationResult{
+					Error: fmt.Errorf("warning: \"install should succeed: overall\" test not found for Install feature gate %q on %v",
+						featureGate, jobVariant),
+					IsWarning: true,
+				})
+			} else {
+				if installTest.TotalRuns < requiredNumberOfTestRunsPerVariant {
+					results = append(results, ValidationResult{
+						Error: fmt.Errorf("error: \"install should succeed: overall\" only has %d runs, need at least %d runs for %q on %v",
+							installTest.TotalRuns, requiredNumberOfTestRunsPerVariant, featureGate, jobVariant),
+						IsWarning: isOptional,
+					})
+				}
+				if installTest.TotalRuns > 0 {
+					passPercent := float32(installTest.SuccessfulRuns) / float32(installTest.TotalRuns)
+					if passPercent < requiredPassRateForInstallTest {
+						displayExpected := int(requiredPassRateForInstallTest * 100)
+						displayActual := int(passPercent * 100)
+						results = append(results, ValidationResult{
+							Error: fmt.Errorf("error: \"install should succeed: overall\" only passed %d%%, need at least %d%% for %q on %v",
+								displayActual, displayExpected, featureGate, jobVariant),
+							IsWarning: isOptional,
+						})
+					}
+				}
Relevance

⭐⭐ Medium

No prior evidence on install-test validation mismatch; installer gating recently reworked (PR#2903).

PR-#2903

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Validation searches for the install test in testedVariant.TestResults, but Install feature gates
route to job-based promotion where TestName is job.Name; separately fetched install test stats
are only printed, not validated.

tools/codegen/cmd/featuregate-test-analyzer.go[415-500]
tools/codegen/cmd/featuregate-test-analyzer.go[1083-1087]
tools/codegen/cmd/featuregate-test-analyzer.go[1241-1255]
tools/codegen/cmd/featuregate-test-analyzer.go[1258-1294]
tools/codegen/cmd/featuregate-test-analyzer.go[181-243]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Install-only requirement ("install should succeed: overall" must be 100%) is validated by searching `testedVariant.TestResults`, but Install feature gates use job-based results (`verifyJobBasedFeatureGatePromotion`) where `TestName` is the Sippy **job** name, not the test name. The PR also fetches `installTestLevelData` but only prints it, so the enforcement never applies to the fetched test-level stats.

## Issue Context
`checkIfTestingIsSufficient()` should validate the install-test pass rate using the same data returned by `getInstallTestLevelData()` (or merge that data into `testingResults` in a way that `checkIfTestingIsSufficient()` consumes).

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[415-501]
- tools/codegen/cmd/featuregate-test-analyzer.go[848-901]
- tools/codegen/cmd/featuregate-test-analyzer.go[991-1087]

### Suggested approach
- Option A (clean): change `checkIfTestingIsSufficient(featureGate, testingResults, installTestLevelData)` and validate the install test against `installTestLevelData[jobVariant]`.
- Option B: when Install feature gate, inject the returned install test result into `results[jobVariant].TestResults` with `TestName == "install should succeed: overall"` so existing validation finds it.
- Also consider making "test not found" an error for non-optional variants (or at least consistent with `isOptional`), since the requirement is stated as mandatory.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. HTTP response body leak ✓ Resolved 🐞 Bug ☼ Reliability
Description
getInstallTestLevelData() returns on non-2xx responses and on ReadAll errors without closing
response.Body, preventing connection reuse and leaking resources. Over multiple variants/tiers, this
can exhaust the transport pool and cause timeouts/flaky verify-feature-promotion runs when Sippy
misbehaves.
Code

tools/codegen/cmd/featuregate-test-analyzer.go[R1039-1052]

+
+		response, err := sippyClient.Do(req)
+		if err != nil {
+			return nil, err
+		}
+		if response.StatusCode < 200 || response.StatusCode > 299 {
+			return nil, fmt.Errorf("error getting sippy results (status=%d) for: %v", response.StatusCode, currURL.String())
+		}
+		queryResultBytes, err := io.ReadAll(response.Body)
+		if err != nil {
+			return nil, err
+		}
+		response.Body.Close()
+
Relevance

⭐⭐ Medium

No historical evidence found about closing HTTP response bodies on early returns in this
repo/tooling.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code checks StatusCode and can return before closing; Close() is only executed after a
successful ReadAll.

tools/codegen/cmd/featuregate-test-analyzer.go[1039-1052]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In `getInstallTestLevelData()`, `response.Body.Close()` is only called after a successful `io.ReadAll`. On non-2xx responses and `ReadAll` failures, the function returns without closing the body.

## Issue Context
Leaked bodies prevent HTTP keep-alive reuse and can consume resources in the transport, especially when this function is called for many variants and tiers.

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[1039-1052]

### Suggested approach
- Immediately after a successful `Do(req)`, add `defer response.Body.Close()`.
- Then remove the manual `response.Body.Close()` call after `ReadAll` (or keep it but ensure it doesn’t double-close).
- Optionally, when status is non-2xx, read and include a small portion of the body in the error for debugging (still with the deferred close).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Duplicate metal variant queries 🐞 Bug ➹ Performance
Description
listTestResultFor() appends platform-filtered variants and then unconditionally appends metal
single/compact variants again, which duplicates entries when the feature gate string matches "metal"
and filterVariants already selected them. This causes redundant Sippy API calls and extra
runtime/load with no behavioral benefit.
Code

tools/codegen/cmd/featuregate-test-analyzer.go[R869-874]

+		// Always include metal single and compact variants as optional checks
+		for _, v := range optionalSelfManagedPlatformVariants {
+			if strings.ToLower(v.Cloud) == "metal" && (strings.ToLower(v.Topology) == "single" || strings.ToLower(v.Topology) == "compact") {
+				jobVariantsToCheck = append(jobVariantsToCheck, v)
+			}
+		}
Relevance

⭐⭐ Medium

No historical evidence on deduping variant lists; prior work focused on correctness, not perf
(PR#2763).

PR-#2763

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The unconditional append of metal single/compact happens after already appending filtered variants,
and filterVariants selects by cloud substring match (so metal variants can be selected twice).

tools/codegen/cmd/featuregate-test-analyzer.go[858-875]
tools/codegen/cmd/featuregate-test-analyzer.go[904-918]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`listTestResultFor()` can add the same `JobVariant` more than once (append filtered variants, then append metal single/compact again), leading to duplicated Sippy calls.

## Issue Context
`filterVariants()` includes variants when the feature gate string contains the cloud name (e.g. "metal"), so metal single/compact can already be present before the unconditional append.

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[858-875]
- tools/codegen/cmd/featuregate-test-analyzer.go[904-918]

### Suggested approach
- Deduplicate `jobVariantsToCheck` before validation/querying (e.g., `map[JobVariant]struct{}` preserving a stable order).
- Or, only append metal single/compact if not already present.
- If these are meant to be non-blocking, also consider setting `Optional: true` on those appended variants (or clarifying the comment).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. TLS verification disabled ✓ Resolved 🐞 Bug ⛨ Security
Description
getInstallTestLevelData() sets tls.Config.InsecureSkipVerify=true, disabling certificate/hostname
verification for HTTPS calls to Sippy. This allows spoofed responses to influence feature-promotion
analysis output and (once fixed) enforcement.
Code

tools/codegen/cmd/featuregate-test-analyzer.go[R996-1006]

+	defaultTransport := &http.Transport{
+		Proxy:                 http.ProxyFromEnvironment,
+		ForceAttemptHTTP2:     true,
+		MaxIdleConns:          100,
+		IdleConnTimeout:       90 * time.Second,
+		TLSHandshakeTimeout:   10 * time.Second,
+		ExpectContinueTimeout: 1 * time.Second,
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true,
+		},
+	}
Relevance

⭐ Low

InsecureSkipVerify pattern is already merged and retained in analyzer/Sippy calls (PR#2782,
PR#2903).

PR-#2782
PR-#2903

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new Sippy client explicitly disables TLS verification in its transport.

tools/codegen/cmd/featuregate-test-analyzer.go[991-1011]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getInstallTestLevelData()` configures the HTTP transport with `TLSClientConfig.InsecureSkipVerify=true`, which disables TLS peer verification.

## Issue Context
This code fetches CI signal from `https://sippy.dptools.openshift.org`. Disabling verification makes the connection vulnerable to MITM/spoofing.

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[996-1006]

### Suggested approach
- Remove the custom `TLSClientConfig` entirely and rely on the default TLS verification.
- If a custom CA is required in CI, load it explicitly (e.g., via `x509.SystemCertPool()` + appending, or a configurable CA bundle), rather than disabling verification.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +471 to +499
// For Install feature gates, validate "install should succeed: overall" test
if strings.Contains(featureGate, "Install") {
installTest := testResultByName(testedVariant.TestResults, "install should succeed: overall")
if installTest == nil {
results = append(results, ValidationResult{
Error: fmt.Errorf("warning: \"install should succeed: overall\" test not found for Install feature gate %q on %v",
featureGate, jobVariant),
IsWarning: true,
})
} else {
if installTest.TotalRuns < requiredNumberOfTestRunsPerVariant {
results = append(results, ValidationResult{
Error: fmt.Errorf("error: \"install should succeed: overall\" only has %d runs, need at least %d runs for %q on %v",
installTest.TotalRuns, requiredNumberOfTestRunsPerVariant, featureGate, jobVariant),
IsWarning: isOptional,
})
}
if installTest.TotalRuns > 0 {
passPercent := float32(installTest.SuccessfulRuns) / float32(installTest.TotalRuns)
if passPercent < requiredPassRateForInstallTest {
displayExpected := int(requiredPassRateForInstallTest * 100)
displayActual := int(passPercent * 100)
results = append(results, ValidationResult{
Error: fmt.Errorf("error: \"install should succeed: overall\" only passed %d%%, need at least %d%% for %q on %v",
displayActual, displayExpected, featureGate, jobVariant),
IsWarning: isOptional,
})
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Install test not validated 🐞 Bug ≡ Correctness

checkIfTestingIsSufficient() looks for "install should succeed: overall" inside
testedVariant.TestResults, but Install feature gates use verifyJobBasedFeatureGatePromotion() which
populates TestResults with Sippy job names, so the install test is usually missing and only a
warning is emitted. The newly fetched installTestLevelData is printed but never fed into validation,
making the 100% install requirement ineffective.
Agent Prompt
## Issue description
The new Install-only requirement ("install should succeed: overall" must be 100%) is validated by searching `testedVariant.TestResults`, but Install feature gates use job-based results (`verifyJobBasedFeatureGatePromotion`) where `TestName` is the Sippy **job** name, not the test name. The PR also fetches `installTestLevelData` but only prints it, so the enforcement never applies to the fetched test-level stats.

## Issue Context
`checkIfTestingIsSufficient()` should validate the install-test pass rate using the same data returned by `getInstallTestLevelData()` (or merge that data into `testingResults` in a way that `checkIfTestingIsSufficient()` consumes).

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[415-501]
- tools/codegen/cmd/featuregate-test-analyzer.go[848-901]
- tools/codegen/cmd/featuregate-test-analyzer.go[991-1087]

### Suggested approach
- Option A (clean): change `checkIfTestingIsSufficient(featureGate, testingResults, installTestLevelData)` and validate the install test against `installTestLevelData[jobVariant]`.
- Option B: when Install feature gate, inject the returned install test result into `results[jobVariant].TestResults` with `TestName == "install should succeed: overall"` so existing validation finds it.
- Also consider making "test not found" an error for non-optional variants (or at least consistent with `isOptional`), since the requirement is stated as mandatory.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1039 to +1052

response, err := sippyClient.Do(req)
if err != nil {
return nil, err
}
if response.StatusCode < 200 || response.StatusCode > 299 {
return nil, fmt.Errorf("error getting sippy results (status=%d) for: %v", response.StatusCode, currURL.String())
}
queryResultBytes, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
response.Body.Close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Http response body leak 🐞 Bug ☼ Reliability

getInstallTestLevelData() returns on non-2xx responses and on ReadAll errors without closing
response.Body, preventing connection reuse and leaking resources. Over multiple variants/tiers, this
can exhaust the transport pool and cause timeouts/flaky verify-feature-promotion runs when Sippy
misbehaves.
Agent Prompt
## Issue description
In `getInstallTestLevelData()`, `response.Body.Close()` is only called after a successful `io.ReadAll`. On non-2xx responses and `ReadAll` failures, the function returns without closing the body.

## Issue Context
Leaked bodies prevent HTTP keep-alive reuse and can consume resources in the transport, especially when this function is called for many variants and tiers.

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[1039-1052]

### Suggested approach
- Immediately after a successful `Do(req)`, add `defer response.Body.Close()`.
- Then remove the manual `response.Body.Close()` call after `ReadAll` (or keep it but ensure it doesn’t double-close).
- Optionally, when status is non-2xx, read and include a small portion of the body in the error for debugging (still with the deferred close).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +869 to +874
// Always include metal single and compact variants as optional checks
for _, v := range optionalSelfManagedPlatformVariants {
if strings.ToLower(v.Cloud) == "metal" && (strings.ToLower(v.Topology) == "single" || strings.ToLower(v.Topology) == "compact") {
jobVariantsToCheck = append(jobVariantsToCheck, v)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Duplicate metal variant queries 🐞 Bug ➹ Performance

listTestResultFor() appends platform-filtered variants and then unconditionally appends metal
single/compact variants again, which duplicates entries when the feature gate string matches "metal"
and filterVariants already selected them. This causes redundant Sippy API calls and extra
runtime/load with no behavioral benefit.
Agent Prompt
## Issue description
`listTestResultFor()` can add the same `JobVariant` more than once (append filtered variants, then append metal single/compact again), leading to duplicated Sippy calls.

## Issue Context
`filterVariants()` includes variants when the feature gate string contains the cloud name (e.g. "metal"), so metal single/compact can already be present before the unconditional append.

## Fix Focus Areas
- tools/codegen/cmd/featuregate-test-analyzer.go[858-875]
- tools/codegen/cmd/featuregate-test-analyzer.go[904-918]

### Suggested approach
- Deduplicate `jobVariantsToCheck` before validation/querying (e.g., `map[JobVariant]struct{}` preserving a stable order).
- Or, only append metal single/compact if not already present.
- If these are meant to be non-blocking, also consider setting `Optional: true` on those appended variants (or clarifying the comment).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@openshift-ci

openshift-ci Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign everettraven for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/codegen/cmd/featuregate-test-analyzer.go (1)

187-268: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use one source for the Install stats and validation numbers. checkIfTestingIsSufficient validates testingResults, but the Install block prints installTestLevelData from a separate Sippy query path. That can make the summary counts differ from the blocking/error text and doubles the Sippy calls for the same feature gate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 187 - 268,
Update the Install statistics block and validation flow around
checkIfTestingIsSufficient to use the same test-result data source and counts.
Remove the separate installTestLevelData lookup/query path, derive the displayed
Install “install should succeed: overall” statistics from testingResults, and
preserve the existing validation and output behavior while avoiding duplicate
Sippy calls.

Source: Linters/SAST tools

🧹 Nitpick comments (1)
tools/codegen/cmd/featuregate-test-analyzer_test.go (1)

484-666: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Solid coverage of the new Install validation path; consider adding an Optional: true case.

All six scenarios correctly exercise run-count/pass-rate thresholds and the missing-test warning. None of them set Optional: true on the JobVariant, so the isOptional branch in the dedicated Install validation (which turns these same failures into warnings for optional variants) isn't covered here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer_test.go` around lines 484 - 666,
The table-driven test Test_checkIfTestingIsSufficient_InstallFeatureGates lacks
coverage for optional JobVariant handling. Add a scenario with Optional: true
that triggers the Install validation failure, such as an install test below the
required pass rate or run count, and assert it produces a warning rather than a
blocking error while preserving the existing required-variant cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 686-695: Update the metal amd64 single and compact variants in the
variant definitions used by jobVariantsToCheck to set Optional: true before
appending them, ensuring checkIfTestingIsSufficient treats failures as
non-blocking.
- Around line 991-1081: Update getInstallTestLevelData to maintain separate
TestResults accumulators keyed by currTest.Name, matching the grouping behavior
of listTestResultForVariant. Accumulate each test’s run counters only into its
name-specific result, and return the grouped results without merging distinct
test names into the first-seen entry.

In `@tools/codegen/pkg/sippy/json_types.go`:
- Around line 182-279: Refactor QueriesForWithCapability to delegate shared
query construction to QueriesFor rather than duplicating base filters, optional
network/OS handling, and JobTier parsing. Preserve the existing capability
filter by incorporating it through the delegated query-building path, and retain
the same default, deduplicated tier behavior and returned TierName values.

---

Outside diff comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 187-268: Update the Install statistics block and validation flow
around checkIfTestingIsSufficient to use the same test-result data source and
counts. Remove the separate installTestLevelData lookup/query path, derive the
displayed Install “install should succeed: overall” statistics from
testingResults, and preserve the existing validation and output behavior while
avoiding duplicate Sippy calls.

---

Nitpick comments:
In `@tools/codegen/cmd/featuregate-test-analyzer_test.go`:
- Around line 484-666: The table-driven test
Test_checkIfTestingIsSufficient_InstallFeatureGates lacks coverage for optional
JobVariant handling. Add a scenario with Optional: true that triggers the
Install validation failure, such as an install test below the required pass rate
or run count, and assert it produces a warning rather than a blocking error
while preserving the existing required-variant cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 6720eb14-cba1-4786-b953-c54d5cf31cf4

📥 Commits

Reviewing files that changed from the base of the PR and between ef67063 and e5fa120.

📒 Files selected for processing (3)
  • tools/codegen/cmd/featuregate-test-analyzer.go
  • tools/codegen/cmd/featuregate-test-analyzer_test.go
  • tools/codegen/pkg/sippy/json_types.go

Comment thread tools/codegen/cmd/featuregate-test-analyzer.go
Comment on lines +991 to +1081
func getInstallTestLevelData(featureGate string, jobVariant JobVariant) (*TestResults, error) {
testPattern := "install should succeed: overall"
queries := sippy.QueriesForWithCapability(jobVariant.Cloud, jobVariant.Architecture, jobVariant.Topology,
jobVariant.NetworkStack, jobVariant.OS, jobVariant.JobTiers, testPattern, featureGate)

defaultTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}

sippyClient := &http.Client{
Timeout: 2 * time.Minute,
Transport: defaultTransport,
}

release, err := getRelease()
if err != nil {
return nil, fmt.Errorf("couldn't fetch latest release version: %w", err)
}

var installTestResult *TestResults
for _, currQuery := range queries {
currURL := &url.URL{
Scheme: "https",
Host: "sippy.dptools.openshift.org",
Path: "api/tests",
}
queryParams := currURL.Query()
queryParams.Add("release", release)
queryParams.Add("period", "default")
filterJSON, err := json.Marshal(currQuery)
if err != nil {
return nil, err
}
queryParams.Add("filter", string(filterJSON))
currURL.RawQuery = queryParams.Encode()

req, err := http.NewRequest(http.MethodGet, currURL.String(), nil)
if err != nil {
return nil, err
}

response, err := sippyClient.Do(req)
if err != nil {
return nil, err
}
if response.StatusCode < 200 || response.StatusCode > 299 {
return nil, fmt.Errorf("error getting sippy results (status=%d) for: %v", response.StatusCode, currURL.String())
}
queryResultBytes, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
response.Body.Close()

testInfos := []sippy.SippyTestInfo{}
if err := json.Unmarshal(queryResultBytes, &testInfos); err != nil {
return nil, err
}

for _, currTest := range testInfos {
if installTestResult == nil {
installTestResult = &TestResults{
TestName: currTest.Name,
}
}

// Accumulate results across multiple JobTier queries
if currTest.CurrentRuns >= requiredNumberOfTestRunsPerVariant {
installTestResult.TotalRuns += currTest.CurrentRuns
installTestResult.SuccessfulRuns += currTest.CurrentSuccesses
installTestResult.FailedRuns += currTest.CurrentFailures
installTestResult.FlakedRuns += currTest.CurrentFlakes
} else {
installTestResult.TotalRuns += currTest.CurrentRuns + currTest.PreviousRuns
installTestResult.SuccessfulRuns += currTest.CurrentSuccesses + currTest.PreviousSuccesses
installTestResult.FailedRuns += currTest.CurrentFailures + currTest.PreviousFailures
installTestResult.FlakedRuns += currTest.CurrentFlakes + currTest.PreviousFlakes
}
}
}

return installTestResult, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='tools/codegen/cmd/featuregate-test-analyzer.go'

echo '--- outline ---'
ast-grep outline "$file" --view expanded || true

echo
echo '--- symbols around listTestResultForVariant / getInstallTestLevelData ---'
rg -n "func listTestResultForVariant|func getInstallTestLevelData|QueriesForWithCapability|CurrentRuns|TestName|context.Context|InsecureSkipVerify" "$file"

echo
echo '--- relevant slices ---'
sed -n '860,1125p' "$file"

Repository: openshift/api

Length of output: 15074


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='tools/codegen/cmd/featuregate-test-analyzer.go'

echo '--- listTestResultForVariant body ---'
sed -n '1083,1245p' "$file"

echo
echo '--- name/pattern handling ---'
rg -n "install should succeed: overall|QueriesForWithCapability|QueriesFor\\(|testResultByName\\(|map\\[string\\]\\*TestResults|strings.Contains\\(featureGate, \"Install\"\\)" "$file"

Repository: openshift/api

Length of output: 7771


🌐 Web query:

Sippy API /api/tests filter "name contains" query returns multiple test names QueryForWithCapability install should succeed overall

💡 Result:

In the context of the OpenShift Sippy testing dashboard, the /api/tests endpoint supports advanced filtering using JSON-formatted parameters [1]. When using the "contains" operator with a "name" column, the API is designed to return all test names that match the specified substring criteria [1]. If a query like "QueryForWithCapability" is passed as a filter, the system will return all test records where the name field contains that string. If multiple tests contain that sequence, all matching entries will be returned in the API response, which is the expected behavior for this endpoint [1]. There is no native "QueryForWithCapability" function or specific hardcoded behavior that restricts the result to a single test name; rather, the filter logic acts as a standard substring search [1]. If "QueryForWithCapability" is intended to be a specific, singular test name, ensure the filter is precise. If your intent is to confirm why multiple names are returned, it is because the "contains" operator is performing a partial match across the available test dataset [1]. To refine your query: - Ensure you are using the correct JSON filter format as defined in the Sippy API documentation [1]. - If you require a specific test, consider if a more restrictive operator (like an "equals" check if supported) or a different column filter (e.g., matching a unique ID) is more appropriate than "contains" [1]. Please note that Sippy's API filtering follows a structured format [1]: { "columnName": "name", "operatorValue": "contains", "value": "QueryForWithCapability" } If the system returns multiple results, it confirms that multiple tests contain that string in their name [1].

Citations:


Bucket install results by test name getInstallTestLevelData should group testInfos by currTest.Name like listTestResultForVariant; Sippy can return multiple matching names, and this single accumulator will silently merge them into the first-seen result.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 995-1005: This http.Transport is configured with a tls.Config that sets InsecureSkipVerify: true, which disables TLS certificate verification for every request made through the resulting http.Client. The server's certificate chain and host name are not validated, exposing the connection to man-in-the-middle attacks. Remove InsecureSkipVerify (or set it to false) and supply a proper RootCAs pool if you need to trust custom certificates.
Context: http.Transport{
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
Note: [CWE-295] Improper Certificate Validation.

(http-transport-tls-skip-verify-go)


[warning] 1002-1004: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
InsecureSkipVerify: true,
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures

(missing-ssl-minversion-go)

🪛 OpenGrep (1.25.0)

[ERROR] 1003-1005: TLS certificate verification is disabled via InsecureSkipVerify. This allows man-in-the-middle attacks. Remove InsecureSkipVerify or set it to false.

(coderabbit.tls.go-insecure-skip-verify)


[ERROR] 1003-1005: TLS certificate verification is disabled via InsecureSkipVerify. This allows man-in-the-middle attacks. Remove InsecureSkipVerify or set it to false.

(coderabbit.tls.go-insecure-skip-verify)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 991 - 1081,
Update getInstallTestLevelData to maintain separate TestResults accumulators
keyed by currTest.Name, matching the grouping behavior of
listTestResultForVariant. Accumulate each test’s run counters only into its
name-specific result, and return the grouped results without merging distinct
test names into the first-seen entry.

Comment on lines +182 to +279
func QueriesForWithCapability(cloud, architecture, topology, networkStack, os, jobTiers, testPattern, capability string) []*SippyQueryStruct {
// Build base query items that are common to all JobTier queries
baseItems := []SippyQueryItem{
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Platform:%s", cloud),
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Architecture:%s", architecture),
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Topology:%s", topology),
},
{
ColumnField: "name",
Not: false,
OperatorValue: "contains",
Value: testPattern,
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Capability:%s", capability),
},
}

if networkStack != "" {
baseItems = append(baseItems, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("NetworkStack:%s", networkStack),
})
}

if os != "" {
baseItems = append(baseItems, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("OS:%s", os),
})
}

// Parse JobTiers - comma-separated string, default to standard/informing/blocking/candidate if empty
var jobTiersList []string
if jobTiers == "" {
jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
// Split by comma, trim whitespace, and deduplicate using sets
tierSet := sets.New[string]()
for _, tier := range strings.Split(jobTiers, ",") {
if trimmed := strings.TrimSpace(tier); trimmed != "" {
tierSet.Insert(trimmed)
}
}
// If all tiers were whitespace/empty after trimming, use defaults
if tierSet.Len() == 0 {
jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
jobTiersList = sets.List(tierSet)
}
}

// Generate one query per JobTier (to work around API's single LinkOperator limitation)
var queries []*SippyQueryStruct
for _, jobTier := range jobTiersList {
// Copy base items for this query
items := make([]SippyQueryItem, len(baseItems))
copy(items, baseItems)

// Add JobTier filter
items = append(items, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("JobTier:%s", jobTier),
})

queries = append(queries, &SippyQueryStruct{
Items: items,
LinkOperator: "and",
TierName: jobTier,
})
}

return queries
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Delegate to QueriesFor instead of duplicating ~90 lines.

QueriesForWithCapability is an almost byte-for-byte copy of QueriesFor (tier parsing, defaults, network/OS filters) with only one extra item appended. Any future fix/change to tier parsing or defaults must now be made in two places, risking drift.

♻️ Proposed refactor
 func QueriesForWithCapability(cloud, architecture, topology, networkStack, os, jobTiers, testPattern, capability string) []*SippyQueryStruct {
-	// Build base query items that are common to all JobTier queries
-	baseItems := []SippyQueryItem{
-		...
-	}
-
-	if networkStack != "" { ... }
-	if os != "" { ... }
-
-	// Parse JobTiers ...
-	var jobTiersList []string
-	...
-
-	var queries []*SippyQueryStruct
-	for _, jobTier := range jobTiersList {
-		...
-	}
-
-	return queries
+	queries := QueriesFor(cloud, architecture, topology, networkStack, os, jobTiers, testPattern)
+	capabilityItem := SippyQueryItem{
+		ColumnField:   "variants",
+		Not:           false,
+		OperatorValue: "contains",
+		Value:         fmt.Sprintf("Capability:%s", capability),
+	}
+	for _, q := range queries {
+		q.Items = append(q.Items, capabilityItem)
+	}
+	return queries
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func QueriesForWithCapability(cloud, architecture, topology, networkStack, os, jobTiers, testPattern, capability string) []*SippyQueryStruct {
// Build base query items that are common to all JobTier queries
baseItems := []SippyQueryItem{
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Platform:%s", cloud),
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Architecture:%s", architecture),
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Topology:%s", topology),
},
{
ColumnField: "name",
Not: false,
OperatorValue: "contains",
Value: testPattern,
},
{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Capability:%s", capability),
},
}
if networkStack != "" {
baseItems = append(baseItems, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("NetworkStack:%s", networkStack),
})
}
if os != "" {
baseItems = append(baseItems, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("OS:%s", os),
})
}
// Parse JobTiers - comma-separated string, default to standard/informing/blocking/candidate if empty
var jobTiersList []string
if jobTiers == "" {
jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
// Split by comma, trim whitespace, and deduplicate using sets
tierSet := sets.New[string]()
for _, tier := range strings.Split(jobTiers, ",") {
if trimmed := strings.TrimSpace(tier); trimmed != "" {
tierSet.Insert(trimmed)
}
}
// If all tiers were whitespace/empty after trimming, use defaults
if tierSet.Len() == 0 {
jobTiersList = []string{"standard", "informing", "blocking", "candidate"}
} else {
jobTiersList = sets.List(tierSet)
}
}
// Generate one query per JobTier (to work around API's single LinkOperator limitation)
var queries []*SippyQueryStruct
for _, jobTier := range jobTiersList {
// Copy base items for this query
items := make([]SippyQueryItem, len(baseItems))
copy(items, baseItems)
// Add JobTier filter
items = append(items, SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("JobTier:%s", jobTier),
})
queries = append(queries, &SippyQueryStruct{
Items: items,
LinkOperator: "and",
TierName: jobTier,
})
}
return queries
}
func QueriesForWithCapability(cloud, architecture, topology, networkStack, os, jobTiers, testPattern, capability string) []*SippyQueryStruct {
queries := QueriesFor(cloud, architecture, topology, networkStack, os, jobTiers, testPattern)
capabilityItem := SippyQueryItem{
ColumnField: "variants",
Not: false,
OperatorValue: "contains",
Value: fmt.Sprintf("Capability:%s", capability),
}
for _, q := range queries {
q.Items = append(q.Items, capabilityItem)
}
return queries
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/pkg/sippy/json_types.go` around lines 182 - 279, Refactor
QueriesForWithCapability to delegate shared query construction to QueriesFor
rather than duplicating base filters, optional network/OS handling, and JobTier
parsing. Preserve the existing capability filter by incorporating it through the
delegated query-building path, and retain the same default, deduplicated tier
behavior and returned TierName values.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/codegen/cmd/featuregate-test-analyzer.go (1)

1101-1112: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Close the response body on every exit path
response.Body is only closed after io.ReadAll, so the non-2xx return and a ReadAll error leak the underlying connection. Move defer response.Body.Close() immediately after Do succeeds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 1101 - 1112,
Update the response handling after sippyClient.Do succeeds to defer
response.Body.Close() immediately, before the status-code validation and
io.ReadAll calls, ensuring every subsequent return path closes the body; remove
the later explicit close if it becomes redundant.
🧹 Nitpick comments (1)
tools/codegen/cmd/featuregate-test-analyzer.go (1)

1057-1072: 🚀 Performance & Scalability | 🔵 Trivial

Consider reusing the shared *http.Client pattern used elsewhere instead of constructing a new client per call.

verifyJobPassRate, getJobRunsFromSippy, and getTriagedTestFailuresFromSippy all accept a shared *http.Client parameter, but getInstallTestLevelData builds its own http.Transport/http.Client on every invocation (per job variant, per feature gate). This duplicates the TLS configuration (including the insecure setting flagged above) and forgoes connection-pool reuse across calls.
[recommended_refactor]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 1057 - 1072, The
getInstallTestLevelData flow should reuse the shared *http.Client pattern
already passed to verifyJobPassRate, getJobRunsFromSippy, and
getTriagedTestFailuresFromSippy. Remove its per-invocation defaultTransport and
sippyClient construction, update the function to accept and use the shared
client, and propagate that client through its callers so connection pooling and
centralized TLS configuration are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 517-536: Remove the blank fmt.Errorf separator from
groupErrorsByCategory so its returned slice contains only actual
ValidationResult errors. Keep category grouping order intact, and rely
exclusively on writeGroupedValidationResults for display spacing via
md.Text("").

---

Outside diff comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 1101-1112: Update the response handling after sippyClient.Do
succeeds to defer response.Body.Close() immediately, before the status-code
validation and io.ReadAll calls, ensuring every subsequent return path closes
the body; remove the later explicit close if it becomes redundant.

---

Nitpick comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 1057-1072: The getInstallTestLevelData flow should reuse the
shared *http.Client pattern already passed to verifyJobPassRate,
getJobRunsFromSippy, and getTriagedTestFailuresFromSippy. Remove its
per-invocation defaultTransport and sippyClient construction, update the
function to accept and use the shared client, and propagate that client through
its callers so connection pooling and centralized TLS configuration are
preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 1b715e85-82e6-4833-9508-1fc141a57057

📥 Commits

Reviewing files that changed from the base of the PR and between e5fa120 and d7fce23.

📒 Files selected for processing (1)
  • tools/codegen/cmd/featuregate-test-analyzer.go

Comment thread tools/codegen/cmd/featuregate-test-analyzer.go
@sadasu
sadasu force-pushed the fix-feature-promotion-checks-metal branch from d7fce23 to b31e16a Compare July 22, 2026 18:45
sadasu added 4 commits July 23, 2026 13:13
Augment `verify-feature-promotion` output to indicate pass percentage
for `install should succeed` tests for featuregates that include
"Install" in their name.

This update gives a better indication of whether Install features
are failing at installation or later during execution of e2e
conformance tests.

This update does not change the criteria for reporting success
but adds more information in the output for easier analysis of
feature state.
Ignore runs with internal and external infrastrcuture failures while
calculating pass percentages.

The fix does the following:
  1. Fetches all jobs for the feature gate variant
  2. Fetches individual runs for each job
  3. Skips any run with OverallResult == "N" or "n" (infra failures) entirely
  4. For remaining runs: checks if "install should succeed: overall" is in FailedTestNames — if not, it's a success
Install feature gates require entirely new jobs to be created that
execise the new feature. New tests may be added and existing tests
are usually updated to account for the new feature. Adjust the
feature gate promotion verification code to account for that.
@sadasu
sadasu force-pushed the fix-feature-promotion-checks-metal branch from b31e16a to 3133041 Compare July 23, 2026 17:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
tools/codegen/cmd/featuregate-test-analyzer.go (1)

747-756: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Metal single/compact variants still aren't marked Optional: true — same finding as a previous review round, still unresolved.

The struct literals added at Lines 747-756 don't set Optional: true, and the loop at Lines 941-946 appends them straight into jobVariantsToCheck unconditionally for every self-managed feature gate (not just Install ones). Since checkIfTestingIsSufficient derives blocking/warning status purely from jobVariant.Optional (Line 424), any feature gate that happens to have partial (1-4, i.e. non-zero but below requiredNumberOfTests) test coverage on metal single/compact will now produce a blocking error instead of the intended non-blocking check, for variants that previously weren't checked at all.

🐛 Proposed fix
 		{
 			Cloud:        "metal",
 			Architecture: "amd64",
 			Topology:     "single",
+			Optional:     true,
 		},
 		{
 			Cloud:        "metal",
 			Architecture: "amd64",
 			Topology:     "compact",
+			Optional:     true,
 		},

Also applies to: 940-947

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 747 - 756, Mark
the metal amd64 single and compact variant literals as Optional so partial
coverage remains non-blocking. Update the corresponding entries appended by the
self-managed feature-gate loop near jobVariantsToCheck, ensuring both variants
retain Optional: true before checkIfTestingIsSufficient evaluates them.
🧹 Nitpick comments (1)
tools/codegen/cmd/featuregate-test-analyzer.go (1)

1064-1217: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

New Sippy-calling functions don't accept context.Context or use NewRequestWithContext.

getInstallTestResultsFromJobRuns and getInstallTestLevelData are new functions that issue outbound HTTP calls but take no context.Context and build requests with plain http.NewRequest/client.Get, so the caller's context (available in Run(ctx context.Context)) can't cancel or bound these calls beyond the client's blanket 2-minute timeout.

As per path instructions, "**/*.go: ... context.Context for cancellation and timeouts."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 1064 - 1217,
Update getInstallTestResultsFromJobRuns and getInstallTestLevelData to accept a
context.Context parameter, propagate it from their callers, and construct every
outbound request with http.NewRequestWithContext using that context. Ensure all
Sippy HTTP calls honor caller cancellation and deadlines instead of relying only
on the client timeout.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 1132-1142: Update the Sippy HTTP client setup shared by
listTestResultForVariant, getInstallTestResultsFromJobRuns, and
getInstallTestLevelData by extracting the duplicated http.Transport/tls.Config
construction into a newSippyClient helper and removing InsecureSkipVerify so
standard TLS certificate validation remains enabled. In each response-handling
path, close response.Body immediately after receiving the response, before
status checks or io.ReadAll, while preserving existing error returns.
- Around line 487-525: Update the missing install-test-data branch in the
Install handling around installTestLevelData[jobVariant] to set IsWarning from
isOptional instead of hardcoding false. Preserve the existing error message and
CategoryInstallTest behavior.
- Line 418: Update all three test call sites of checkIfTestingIsSufficient in
featuregate-test-analyzer_test.go to pass the appropriate installTestLevelData
argument as the third parameter, matching the function’s new signature and
preserving each test’s existing data setup.

---

Duplicate comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 747-756: Mark the metal amd64 single and compact variant literals
as Optional so partial coverage remains non-blocking. Update the corresponding
entries appended by the self-managed feature-gate loop near jobVariantsToCheck,
ensuring both variants retain Optional: true before checkIfTestingIsSufficient
evaluates them.

---

Nitpick comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 1064-1217: Update getInstallTestResultsFromJobRuns and
getInstallTestLevelData to accept a context.Context parameter, propagate it from
their callers, and construct every outbound request with
http.NewRequestWithContext using that context. Ensure all Sippy HTTP calls honor
caller cancellation and deadlines instead of relying only on the client timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: a5051f31-3812-471b-8aa4-d7c79d6ce778

📥 Commits

Reviewing files that changed from the base of the PR and between b31e16a and 3133041.

📒 Files selected for processing (4)
  • tools/codegen/cmd/featuregate-test-analyzer.go
  • tools/codegen/cmd/featuregate-test-analyzer_test.go
  • tools/codegen/cmd/root.go
  • tools/codegen/pkg/sippy/json_types.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/codegen/cmd/featuregate-test-analyzer_test.go
  • tools/codegen/pkg/sippy/json_types.go

Comment thread tools/codegen/cmd/featuregate-test-analyzer.go
Comment thread tools/codegen/cmd/featuregate-test-analyzer.go
Comment thread tools/codegen/cmd/featuregate-test-analyzer.go Outdated
@sadasu sadasu changed the title OCPBUGS-99459: Include health check of SNO and compact jobs on metal platform in verify-feature-promotion checks OCPBUGS-99459: Improve verify-feature-promotion checks for Install featuregates Jul 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tools/codegen/cmd/featuregate-test-analyzer.go (2)

272-275: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not label every warning as an optional-variant issue.

Candidate-tier warnings and Install informational findings are emitted for required variants too, so this heading misstates their cause. Use a neutral label such as “Non-blocking warnings and informational results.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 272 - 275,
Update the warning section heading in the len(warnings) block of the
feature-gate analysis flow to use neutral wording such as “Non-blocking warnings
and informational results,” rather than attributing all entries to optional
variants. Keep the existing writeGroupedValidationResults(warningResults, md)
behavior unchanged.

186-188: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Propagate ctx into the Sippy fetch path. Run already accepts cancellation, but listTestResultFor and the downstream Sippy helpers still create plain requests and rely only on the per-client timeout. A cancelled run can keep making Sippy calls for up to 2 minutes per request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/codegen/cmd/featuregate-test-analyzer.go` around lines 186 - 188,
Update Run’s Sippy fetch path so the existing ctx is passed through
listTestResultFor and every downstream Sippy helper, using context-aware request
creation instead of plain requests. Preserve the current results and error
propagation while ensuring cancellation stops in-flight and subsequent Sippy
calls promptly.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tools/codegen/cmd/featuregate-test-analyzer.go`:
- Around line 272-275: Update the warning section heading in the len(warnings)
block of the feature-gate analysis flow to use neutral wording such as
“Non-blocking warnings and informational results,” rather than attributing all
entries to optional variants. Keep the existing
writeGroupedValidationResults(warningResults, md) behavior unchanged.
- Around line 186-188: Update Run’s Sippy fetch path so the existing ctx is
passed through listTestResultFor and every downstream Sippy helper, using
context-aware request creation instead of plain requests. Preserve the current
results and error propagation while ensuring cancellation stops in-flight and
subsequent Sippy calls promptly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 444853aa-8a40-44d5-941d-26f6ea4d57ef

📥 Commits

Reviewing files that changed from the base of the PR and between 3133041 and 574bc88.

📒 Files selected for processing (2)
  • tools/codegen/cmd/featuregate-test-analyzer.go
  • tools/codegen/cmd/featuregate-test-analyzer_test.go

@sadasu
sadasu force-pushed the fix-feature-promotion-checks-metal branch from 574bc88 to b218be2 Compare July 23, 2026 19:32
@openshift-ci

openshift-ci Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@sadasu: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/verify-hypershift-integration b218be2 link true /test verify-hypershift-integration

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants