Skip to content

e2e: Fix broken assertions - #1571

Open
oblau wants to merge 3 commits into
openshift:mainfrom
oblau:fix/e2e-broken-assertions
Open

e2e: Fix broken assertions#1571
oblau wants to merge 3 commits into
openshift:mainfrom
oblau:fix/e2e-broken-assertions

Conversation

@oblau

@oblau oblau commented Jul 23, 2026

Copy link
Copy Markdown
Member

Split into 3 commits by bug pattern — each fixes a distinct class of broken assertion across different test files.

  1. Offlined CPU: add missing matcher (5 sites)
    Expect(cpuSet.Equals(other)) without a matcher is a Gomega no-op — always passes.
    Added .To(BeTrue()) with expected/got CPU sets in the failure message.
  2. Mixedcpus: fix Get() no-ops + related test breakage
    • Expect(client.Get(...)) without a matcher silently swallows errors → Expect(...Get...).To(Succeed()) (3 sites).
    • Fix misleading log that printed Isolated twice; now logs Isolated and Shared.
    • Guaranteed pods: cpu:1cpu:2, and remove the setup path that set full-pcpus-only=false on small nodes. With default SMT alignment, cpu:1 cannot schedule; CI has enough CPUs for cpu:2, so the cancel-SMT workaround is unnecessary.
    • When expanding Shared from 1 CPU to a new 2-CPU set, Union the previous Shared CPU back into Isolated so it is not left unassigned (broke later assertions).
  3. PPC: replace no-op log-on-match with real assertions (4 sites)
    if ok { testlog.Info(...) } after regexp.MatchString never failed when the expected error was missing.
    Replaced with gexec.Exit after Wait, then ContainSubstring on leaf error text (not outer wraps / not regex).
    For reserved-count-too-high, assert dynamic [1,%d] via maxReservedCPUCountFromMustGather (TotalThreads-1).
    Also fix success-path "marshal" → "unmarshal" messages.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end validation for performance profile, mixed-CPU, and offlined CPU scenarios.
    • Enhanced failure messages to show expected and actual CPU sets and command output.
    • Updated mixed-CPU scenarios to use accurate CPU allocations and prevent unassigned CPUs during affinity tests.
    • Added more reliable process completion checks and dynamically calculated reserved CPU expectations.
    • Corrected YAML error expectations and related CPU-set logging.

@qodo-for-rh-openshift

Copy link
Copy Markdown

PR Summary by Qodo

E2E: Fix no-op Gomega assertions in performance profile tests

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Fix Gomega no-op assertions by adding missing matchers to boolean expectations.
• Ensure Kubernetes client Get() calls fail tests on API errors instead of silently passing.
• Make PPC output validation assert on missing expected error messages (not just log).
Diagram

graph TD
  A["E2E test cases"] --> B["Gomega Expect()"] --> C["Assert: BeTrue / NoError"]
  A --> D["K8s client Get()"] --> E[("PerformanceProfile CR")]
  A --> F["CLI output"] --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Gomega string/regex matchers directly
  • ➕ More idiomatic: Expect(output).To(MatchRegexp(...)) or ContainSubstring(...) avoids manual ok boolean plumbing
  • ➕ Failure messages can be more consistent and expressive with matcher output
  • ➖ May require small refactors where output is a []byte or needs specific formatting/context
  • ➖ Some existing tests may prefer explicit MatchString patterns for readability
2. Introduce a small helper to wrap client.Get + error assertion
  • ➕ Reduces repetition and prevents reintroducing Expect(client.Get(...)) no-ops
  • ➕ Centralizes consistent error messages/log context
  • ➖ Adds indirection for a small number of call sites
  • ➖ May be overkill unless this pattern appears broadly across the suite

Recommendation: The PR’s approach is correct and minimal-risk for fixing broken tests: add missing matchers and explicit error assertions at the call sites. If this pattern continues to appear elsewhere, consider a follow-up to use Gomega’s MatchRegexp/ContainSubstring matchers for output checks and/or a small helper for Get()+assertion to prevent future no-op expectations.

Files changed (3) +15 / -21

Bug fix (1) +6 / -3
mixedcpus.goCheck errors from ControlPlaneClient.Get() calls +6/-3

Check errors from ControlPlaneClient.Get() calls

• Captures the return value from 'ControlPlaneClient.Get(...)' into 'err' and asserts 'Expect(err).ToNot(HaveOccurred())'. Prevents silent passes where 'Expect(Get(...))' previously had no matcher and thus never failed on API errors.

test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go

Tests (2) +9 / -18
ppc.goAssert on expected error text presence in PPC tests +4/-13

Assert on expected error text presence in PPC tests

• Replaces log-only handling after 'regexp.MatchString' with 'Expect(ok).To(BeTrue(...))', ensuring the test fails when the expected error message is absent. Also drops an unused test logging import after removing the log-on-match pattern.

test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go

updating_profile.goMake offlined CPU set equality checks actually assert +5/-5

Make offlined CPU set equality checks actually assert

• Adds '.To(BeTrue())' to 'Expect(offlinedCPUSet.Equals(...))' at multiple sites, converting previously no-op boolean expectations into real assertions. Includes clearer mismatch messages with expected vs actual CPU sets.

test/e2e/performanceprofile/functests/2_performance_update/updating_profile.go

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Walkthrough

Performance profile end-to-end tests now use explicit Gomega assertions, direct PPC output checks, dynamic reserved-CPU expectations, corrected mixed-CPU state handling, and detailed offline CPU-set diagnostics.

Changes

Performance test updates

Layer / File(s) Summary
PPC assertions and dynamic expectations
test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go
PPC tests now check process exit before output parsing, use substring assertions with expected and actual output, derive the reserved CPU limit from must-gather data, and correct YAML unmarshalling diagnostics.
Mixed CPU workload and state handling
test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
Mixed-CPU tests request two shared CPUs, require successful profile reads, report the shared CPU set, restore CPU assignments, and remove the small-node SMT override.
Offline CPU-set diagnostics
test/e2e/performanceprofile/functests/2_performance_update/updating_profile.go
Offline CPU scenarios explicitly assert equality and report expected and actual CPU sets on failure.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: jmencak, mrniranjan

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning The PR adds five gexec.Exit and three ControlPlaneClient.Get assertions without diagnostic messages; the check requires meaningful failure messages for assertions. Add context-specific messages to each new exit and Get assertion, such as the test ID, expected exit status, object key, and operation.
✅ Passed checks (13 passed)
Check name Status Explanation
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 complete PR diff adds or changes no It, Describe, Context, or When declarations; all inspected titles remain static strings.
Microshift Test Compatibility ✅ Passed The cumulative diff only changes existing test bodies and helper logic; it adds no It/Describe/Context/When nodes or unavailable MicroShift API/resource references.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The PR diff adds no It, Describe, Context, or When declarations. It only changes existing assertions and CPU-set logic, with no listed multi-node or HA assumption introduced.
Topology-Aware Scheduling Compatibility ✅ Passed The diff changes only three e2e test files. It adds no manifests, operator/controller scheduling fields, affinity, topology spread, replicas, PDBs, or node-role constraints.
Ote Binary Stdout Contract ✅ Passed The PR diff adds no process-level stdout writes; its only added log call is in BeforeEach via testlog, which writes to GinkgoWriter. The existing fmt.Printf is unchanged and called by It tests.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The cumulative diff adds no Ginkgo test declarations; it only changes existing assertions and helpers, with no added IPv4 literals or external connectivity.
No-Weak-Crypto ✅ Passed The PR diff only changes PPC test assertions and must-gather CPU handling; scans found no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret comparisons.
Container-Privileges ✅ Passed The PR changes only three Go test files. Diff and exact searches show no added privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation settings.
No-Sensitive-Data-In-Logs ✅ Passed Diff review found only CPU-set diagnostics and PPC error assertion context; PPC sources contain fixed messages or TotalThreads, with no passwords, tokens, PII, hostnames, or customer data.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing broken assertions in end-to-end tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@openshift-ci
openshift-ci Bot requested review from Tal-or and jmencak July 23, 2026 10:34
@openshift-ci

openshift-ci Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: oblau
Once this PR has been reviewed and has the lgtm label, please assign marsik 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

@qodo-for-rh-openshift

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

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Misleading CPU-set log ✓ Resolved 🐞 Bug ◔ Observability
Description
In mixedcpus.go, the log line meant to print both the new isolated and new shared CPU sets prints
the isolated value twice, so the test output can misreport the applied shared CPU set. This reduces
debuggability when investigating profile-update failures in this scenario.
Code

test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go[325]

				testlog.Infof("new isolated CPU set=%q\nnew shared CPU set=%q", string(*profile.Spec.CPU.Isolated), string(*profile.Spec.CPU.Isolated))
Relevance

⭐⭐⭐ High

Trivial correctness fix: log currently misreports values; team has accepted similar
debuggability/typo log fixes in tests.

PR-#1324
PR-#1361

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The log format string contains both “new isolated” and “new shared”, but the arguments pass
profile.Spec.CPU.Isolated twice; elsewhere in the same block the code updates/uses
profile.Spec.CPU.Shared, showing the shared value is available and intended to be reported.

test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go[323-326]
test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go[309-313]

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

### Issue description
A log statement prints the isolated CPU set for both the “isolated” and “shared” fields, making the output misleading.

### Issue Context
This happens right after re-fetching the updated PerformanceProfile, so the log is expected to reflect both updated fields.

### Fix Focus Areas
- test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go[323-326]

### Suggested change
Update the second argument to use `profile.Spec.CPU.Shared`:
```go
testlog.Infof("new isolated CPU set=%q\nnew shared CPU set=%q",
 string(*profile.Spec.CPU.Isolated),
 string(*profile.Spec.CPU.Shared),
)
```

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


Grey Divider

Qodo Logo

Comment thread test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go Outdated

@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

🤖 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 `@test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go`:
- Around line 164-167: Update the error matching in the test around errString
and regexp.MatchString to escape the literal CPU range before regex evaluation,
using regexp.QuoteMeta or an equivalent approach. Preserve the existing expected
message and assertions while ensuring the output comparison matches the literal
“[1,3]” text.
🪄 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: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ff43d9d3-90fb-489e-be11-f312db4d9f15

📥 Commits

Reviewing files that changed from the base of the PR and between a9d25d5 and b138108.

📒 Files selected for processing (3)
  • test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
  • test/e2e/performanceprofile/functests/2_performance_update/updating_profile.go

Comment thread test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go Outdated
@oblau

oblau commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

/retest

Comment thread test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go Outdated
errString := "Error: failed to obtain data from flags not appropriate to split reserved CPUs in case of topology-manager-policy: single-numa-node"
ok, err := regexp.MatchString(errString, string(output))
Expect(err).ToNot(HaveOccurred())
if ok {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's a change in the behavior. it makes the test more restrict. is that what we want here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is changing behavior - but isn't this the intention here?
If we are not asserting on ok then i fail to see why we need the errString
and regexp.MatchString(errString, string(output)) to begin with.
Maybe im missing something here.

Is the goal to make sure the correct error message appears, or that PPC script fails in general?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After reevaluation I think this change is ok.
the only change I would add is in the message to make it more clear:

Expect(ok).To(BeTrue(), "expected error %q to be found in output: %s", errString, output)

@oblau
oblau force-pushed the fix/e2e-broken-assertions branch from b138108 to 9c24e1d Compare July 27, 2026 13:49
@oblau

oblau commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

/retest

@openshift-ci

openshift-ci Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@oblau: 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/e2e-aws-ovn 9c24e1d link true /test e2e-aws-ovn

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.

oblau added 3 commits August 12, 2026 13:53
Expect(bool) without .To() is a Gomega no-op; these tests never fail on these assertions.
…pand fix

- Add .To(Succeed()) / err checks on three ControlPlaneClient.Get() calls.
  Bare Expect(Get(...)) is a Gomega no-op, so API errors were ignored.
- Change scheduling Guaranteed pods from cpu:1 to cpu:2, and remove the
  setup() path that set full-pcpus-only=false on small nodes. With default
  SMT alignment, cpu:1 cannot schedule; CI has enough CPUs for cpu:2, so
  the cancel-SMT workaround is unnecessary.
- When exec-cpu-affinity expands Shared from 1 CPU to a new 2-CPU set,
  Union the previous shared CPU into Isolated. Replacing Shared without
  that left the old shared CPU in neither set and broke later assertions.
The `if ok { testlog.Info(...) }` pattern after regexp.MatchString
never failed the test when the expected error was absent from output.
Replace with Wait + gexec.Exit and ContainSubstring (not regexp) so a
missing error message actually fails the test case.

Match leaf error text only; outer wraps have changed and are not the
intent of these cases:
- 41405: https://github.com/openshift/cluster-node-tuning-operator/blob/a9d25d502ca894272f88753f1bd7ecef82fb188e/pkg/performanceprofile/profilecreator/cmd/root.go#L221
- 41419: https://github.com/openshift/cluster-node-tuning-operator/blob/a9d25d502ca894272f88753f1bd7ecef82fb188e/pkg/performanceprofile/profilecreator/profilecreator.go#L363
- 41420: https://github.com/openshift/cluster-node-tuning-operator/blob/a9d25d502ca894272f88753f1bd7ecef82fb188e/pkg/performanceprofile/profilecreator/profilecreator.go#L128
  ([1,%d] is TotalThreads-1; format at runtime via
  maxReservedCPUCountFromMustGather from the must-gather MCP node)

Also move gexec.Exit checks to right after Wait (before content
asserts), and fix success-path "marshal" -> "unmarshal" messages.
@oblau
oblau force-pushed the fix/e2e-broken-assertions branch from 9c24e1d to 3cafd46 Compare August 12, 2026 11:01

@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

🤖 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 `@test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go`:
- Line 149: Update setup in
test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go to verify at
least two isolated CPUs are available when mixed CPUs are enabled, and skip
these workloads when they are not; alternatively provision that capacity before
creation. Apply the protection to the two-CPU workload definitions at lines 149,
181, 201, 218, 249, 325, and 437, ensuring shared-CPU resources are not treated
as isolated CPU capacity.
🪄 Autofix

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: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d8b0ebb-5852-4f4a-b8ca-3ef3c534df2e

📥 Commits

Reviewing files that changed from the base of the PR and between 9c24e1d and 3cafd46.

📒 Files selected for processing (2)
  • test/e2e/performanceprofile/functests/10_performance_ppc/ppc.go
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go

It("verify cpu load balancing still works with mixed cpus", func() {
rl := &corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceCPU: resource.MustParse("2"),

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go --items all

rg -n -C 5 \
  'ResourceCPU:\s+resource\.MustParse\("2"\)|func setup\(|CPU\.Isolated|isolated.*Size\(\)|createPod\(' \
  test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go

fd -e go . test/e2e/performanceprofile | while IFS= read -r file; do
  rg -n -C 3 'MixedCPUs|CPU\.Isolated|isolated.*cpus|isolated.*CPU' "$file" || true
done

Repository: openshift/cluster-node-tuning-operator

Length of output: 50394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test profile and suite setup ---'
sed -n '55,155p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
sed -n '810,875p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go

printf '%s\n' '--- all direct uses of the seven workloads ---'
sed -n '140,270p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
sed -n '286,355p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
sed -n '428,470p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go

printf '%s\n' '--- resource and scheduling helpers ---'
sed -n '900,1035p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
rg -n -C 4 'enable-shared-cpus|sharedCpusResource|MixedCpus|mixed cpus|mixed CPUs|ResourceCPU' \
  pkg test/e2e/performanceprofile/functests/utils | head -n 300

Repository: openshift/cluster-node-tuning-operator

Length of output: 50394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- profile construction and test entry points ---'
sed -n '95,155p' test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
rg -n -C 5 'testProfile\(\)|MixedCPUs|NodeSelectorLabels|ProfileNotFound' \
  test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go \
  test/e2e/performanceprofile/functests/utils

printf '%s\n' '--- mixed-CPU resource implementation ---'
rg -n -C 8 --glob '*.go' \
  'enable-shared-cpus|shared_cpus|shared cpus|SharedCPUs|MixedCpus|MixedCPUs' \
  . | head -n 500

printf '%s\n' '--- current change context for mixedcpus.go ---'
git diff --stat -- test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
git diff --unified=12 -- test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go | sed -n '1,260p'

Repository: openshift/cluster-node-tuning-operator

Length of output: 50394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- files defining the shared CPU resource ---'
git ls-files | rg -i 'mixed|shared|cpu.*manager|kubelet|crio'
printf '%s\n' '--- exact resource-name references ---'
rg -n --glob '*.go' --glob '*.yaml' --glob '*.yml' --glob '*.json' \
  'workload\.openshift\.io/enable-shared-cpus' . || true

printf '%s\n' '--- test profile defaults and discovery behavior ---'
rg -n -C 8 --glob '*.go' \
  'IsolatedCPUs|SharedCPUs|NewPerformanceProfile|GetDiscoveryPerformanceProfile|PerformanceProfileName' \
  pkg test e2e | head -n 400

printf '%s\n' '--- concise static invariant check ---'
python3 - <<'PY'
from pathlib import Path
import re

p = Path("test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go")
s = p.read_text()

resource_blocks = re.findall(
    r'rl := &corev1\.ResourceList\{(.*?)\n\s*\}',
    s,
    re.S,
)
target = []
for i, block in enumerate(resource_blocks, 1):
    if 'resource.MustParse("2")' in block and 'sharedCpusResource' in block:
        target.append(i)

setup = s[s.index('func setup('):s.index('func cpuSetToPerformanceCPUSet(')]
print(f"target resource-list blocks: {len(target)}")
print(f"target blocks contain CPU=2 and shared resource: {target}")
print("setup changes isolated CPUs only under !profileutil.IsMixedCPUsEnabled(profile):",
      '!profileutil.IsMixedCPUsEnabled(profile)' in setup)
print("setup has no isolated-size guard:",
      'Size()' not in setup and 'List()' not in setup)
PY

Repository: openshift/cluster-node-tuning-operator

Length of output: 50394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- generated mixed-CPU configuration ---'
cat assets/performanceprofile/configs/openshift-workload-mixed-cpus
sed -n '1,220p' docs/performanceprofile/kubelet-config-snippets.md | rg -n -C 8 \
  'mixed|shared|reservedSystemCPUs|cpu-shared'

printf '%s\n' '--- E2E profile defaults and discovery ---'
rg -n -C 6 --glob '*.go' \
  'IsolatedCPUs|SharedCPUs|NewPerformanceProfile|GetDiscoveryPerformanceProfile|PerformanceProfileName' \
  pkg test | head -n 300

printf '%s\n' '--- static invariant check ---'
python3 - <<'PY'
from pathlib import Path
import re

p = Path("test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go")
s = p.read_text()

blocks = re.findall(r'rl := &corev1\.ResourceList\{(.*?)\n\s*\}', s, re.S)
targets = [
    (i, 'CPU=2' in b, 'shared=1' in b)
    for i, b in enumerate(blocks, 1)
    if 'resource.MustParse("2")' in b and 'sharedCpusResource' in b
]
setup = s[s.index('func setup('):s.index('func cpuSetToPerformanceCPUSet(')]

print("target resource-list blocks:", len(targets))
print("all target blocks request CPU=2 and shared resource=1:",
      all(cpu and shared for _, cpu, shared in targets))
print("setup changes the profile only when mixed CPUs are disabled:",
      '!profileutil.IsMixedCPUsEnabled(profile)' in setup)
print("setup checks isolated CPU count:", 'Size()' in setup)
print("setup checks isolated CPU list length:", 'List()' in setup)
PY

Repository: openshift/cluster-node-tuning-operator

Length of output: 371


🏁 Script executed:

#!/bin/bash
set -u

python3 - <<'PY'
from pathlib import Path
import re

p = Path("test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go")
s = p.read_text()

blocks = re.findall(r'rl := &corev1\.ResourceList\{(.*?)\n\s*\}', s, re.S)
targets = [
    b for b in blocks
    if 'resource.MustParse("2")' in b and 'sharedCpusResource' in b
]
setup = s[s.index('func setup('):s.index('func cpuSetToPerformanceCPUSet(')]

print("target resource-list blocks:", len(targets))
print("all target blocks request CPU=2:", all('corev1.ResourceCPU' in b for b in targets))
print("all target blocks request shared resource=1:",
      all('sharedCpusResource' in b and 'resource.MustParse("1")' in b for b in targets))
print("setup changes the profile only when mixed CPUs are disabled:",
      '!profileutil.IsMixedCPUsEnabled(profile)' in setup)
print("setup checks isolated CPU count:", 'Size()' in setup or 'len(isolated.List())' in setup)
PY

Repository: openshift/cluster-node-tuning-operator

Length of output: 400


🌐 Web query:

OpenShift mixed CPUs allocation workload.openshift.io/enable-shared-cpus ordinary CPU request isolated CPUs

💡 Result:

The annotation workload.openshift.io/enable-shared-cpus is part of the Mixed-CPUs feature in OpenShift, which allows Guaranteed Quality of Service (QoS) pods to utilize both isolated and shared CPU pools [1][2][3]. Typically, Guaranteed QoS pods with integer CPU requests are assigned exclusive (isolated) CPUs, meaning they cannot access the shared CPU pool [4]. The Mixed-CPUs feature changes this behavior by permitting such pods to request access to the shared CPU pool via this specific resource annotation [1][2]. Key aspects of this functionality include: 1. Admission Validation: To use this feature, a pod must be in the Guaranteed QoS class [1][2][3]. The admission controller also verifies that the pod is deployed in a namespace explicitly allowed to use mixed-CPU workloads, typically indicated by the annotation workload.mixedcpus.openshift.io/allowed [2][3]. 2. Runtime Hinting: Because container runtimes (like CRI-O) are not natively aware of extended OpenShift resources, the admission controller injects an annotation (e.g., cpu-shared.crio.io/) into the pod definition [2][3]. This annotation informs the runtime that the specific container has been granted access to shared CPUs [2]. 3. Constraint: A pod can request no more than a single instance of the workload.openshift.io/enable-shared-cpus resource [1][3]. This feature is distinct from standard workload partitioning (enabled via cpuPartitioningMode: AllNodes), which separates cluster management pods into a reserved CPU pool while isolating customer workloads on separate cores [5][6][7]. The Mixed-CPUs feature specifically addresses the flexibility for customer workloads that require both isolation and access to shared resources within a Guaranteed QoS context [1][3].

Citations:


Guard the two-isolated-CPU requirement.

These workloads request two whole CPUs. The shared-CPU resource only grants shared-CPU access. setup does not check isolated CPU capacity when mixed CPUs are already enabled. Skip the workloads or provision at least two isolated CPUs before creating them.

📍 Affects 1 file
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L149-L149 (this comment)
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L181-L181
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L201-L201
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L218-L218
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L249-L249
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L325-L325
  • test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go#L437-L437
🤖 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 `@test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go` at line 149,
Update setup in test/e2e/performanceprofile/functests/11_mixedcpus/mixedcpus.go
to verify at least two isolated CPUs are available when mixed CPUs are enabled,
and skip these workloads when they are not; alternatively provision that
capacity before creation. Apply the protection to the two-CPU workload
definitions at lines 149, 181, 201, 218, 249, 325, and 437, ensuring shared-CPU
resources are not treated as isolated CPU capacity.


coreSiblings, err := nodes.GetCoreSiblings(ctx, workerRTNode)
Expect(err).ToNot(HaveOccurred())
// When Shared already has 1 CPU and we need 2, we replace Shared with a new pair from

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But with the new logic we always have 2 shared CPUs by default, so why the comment says
"When Shared already has 1 CPU and we need 2"?

Besides the wrong comment, we don't even need to change anything, since now 2 CPUs is the default.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants