Skip to content

OCPBUGS-99459: Include health check of SNO and compact jobs on metal platform in verify-feature-promotion checks#2943

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

OCPBUGS-99459: Include health check of SNO and compact jobs on metal platform in verify-feature-promotion checks#2943
sadasu wants to merge 3 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.

sadasu added 2 commits July 22, 2026 11:14
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.
@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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 19bb7769-cca0-44c8-8d92-cfbe0041a5ae

📥 Commits

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

📒 Files selected for processing (1)
  • tools/codegen/cmd/featuregate-test-analyzer.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • tools/codegen/cmd/featuregate-test-analyzer.go

📝 Walkthrough

Walkthrough

The feature-gate analyzer adds Install-specific aggregation, validation, and per-variant reporting for install should succeed: overall, including a 100% pass-rate requirement. It expands optional metal variants, categorizes validation results, filters infrastructure failures from promotion pass-rate totals, and standardizes Sippy errors. A capability-filtered Sippy query builder and tests for Install validation scenarios were added.

Possibly related PRs

  • openshift/api#2937: Adds closely related Install-specific validation and aggregation for the same feature-gate test.
  • openshift/api#2944: Modifies promotion pass-rate triage to handle infrastructure-failure job runs similarly.

Suggested reviewers: joelspeed, everettraven

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No PR description was provided, so the check cannot assess whether it summarizes the change. Add a short description of the functional change and affected checks so reviewers can quickly understand the PR.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the PR objective and main change by describing verify-feature-promotion health checks for SNO and compact metal jobs.
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 PASS — no Ginkgo-style titles or dynamic test names were added; the new unit-test names are static strings.
Test Structure And Quality ✅ Passed Touched tests are plain table-driven unit tests, not Ginkgo/cluster specs; the new install-specific test is narrow, self-contained, and uses clear failure messages.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; touched files only add unit tests and helper code, with no MicroShift-sensitive APIs or markers.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e specs were added; changes are unit tests and helper code only, with no SNO-sensitive assumptions to audit.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only changes codegen/test-analyzer and Sippy query helpers; no deployment manifests, controllers, or pod scheduling primitives were added.
Ote Binary Stdout Contract ✅ Passed The PR only adds CLI reporting in a codegen tool; no new main/init/TestMain/top-level stdout writes for an OTE test binary were introduced.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the changed tests are unit tests and contain no IPv4-only or external connectivity assumptions.
No-Weak-Crypto ✅ Passed No weak ciphers/hashes, custom crypto, or secret/token comparisons were added; remaining strings.Compare calls sort public job-variant fields and tls is only HTTP transport setup.
Container-Privileges ✅ Passed PASS: The PR only changes Go code; no container/K8s manifests were modified, and searches found no privileged/hostPID/hostNetwork/etc. settings.
No-Sensitive-Data-In-Logs ✅ Passed New logging only prints feature gates, variants, counts, and public test URLs; no secrets, PII, or internal hostnames were introduced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented
The command is terminated due to an error: build linters: unable to load custom analyzer "kubeapilinter": tools/_output/bin/kube-api-linter.so, plugin: not implemented


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 (4) 📘 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 🐞 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 🐞 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 🐞 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 on lines +686 to +695
{
Cloud: "metal",
Architecture: "amd64",
Topology: "single",
},
{
Cloud: "metal",
Architecture: "amd64",
Topology: "compact",
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether filterVariants sets Optional=true for matches drawn from the
# optional list, to fully validate the bypass-loop bug.
rg -n -B2 -A 25 'func filterVariants' tools/codegen/cmd/featuregate-test-analyzer.go

Repository: openshift/api

Length of output: 1408


🏁 Script executed:

#!/bin/bash
set -euo pipefail

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

echo '--- variants around 686-695 ---'
sed -n '670,710p' "$file" | cat -n

echo
echo '--- append loop around 860-880 ---'
sed -n '850,885p' "$file" | cat -n

echo
echo '--- any Optional assignments in file ---'
rg -n 'Optional\s*:' "$file"

echo
echo '--- any mention of optionalSelfManagedPlatformVariants ---'
rg -n -C 2 'optionalSelfManagedPlatformVariants|filterVariants\(' "$file"

Repository: openshift/api

Length of output: 4752


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file=tools/codegen/cmd/featuregate-test-analyzer.go

sed -n '405,435p' "$file" | cat -n

Repository: openshift/api

Length of output: 1619


Mark the metal single/compact variants as optional
tools/codegen/cmd/featuregate-test-analyzer.go:686-695, 869-874 — these variants are appended directly to jobVariantsToCheck, so jobVariant.Optional stays false and checkIfTestingIsSufficient will turn their failures into blocking errors. Set Optional: true before appending.

🤖 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 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.

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
Ignore runs with internal and external infrastrcuture failures while
calculating pass percentages.
@sadasu
sadasu force-pushed the fix-feature-promotion-checks-metal branch from d7fce23 to b31e16a Compare July 22, 2026 18:45
@openshift-ci

openshift-ci Bot commented Jul 22, 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 b31e16a 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