Skip to content

Add an e2e test for TLS profile update#1393

Open
hongkailiu wants to merge 13 commits into
openshift:mainfrom
hongkailiu:pr1338-e2e
Open

Add an e2e test for TLS profile update#1393
hongkailiu wants to merge 13 commits into
openshift:mainfrom
hongkailiu:pr1338-e2e

Conversation

@hongkailiu
Copy link
Copy Markdown
Member

@hongkailiu hongkailiu commented May 24, 2026

This work from this pull:

Summary by CodeRabbit

  • New Features

    • New CLI flags --tls-min-version and --tls-cipher-suites enable TLS configuration overrides
    • Cluster-wide TLS profile settings are now integrated with operator initialization
    • TLS settings are applied with proper precedence: defaults, cluster profile, then overrides
  • Tests

    • Added comprehensive test coverage for TLS profile management and override behavior

DavidHurta and others added 2 commits May 24, 2026 08:19
Honor the central TLS profile [1] with event-driven dynamic updates.

Implementation uses an APIServer informer with event handlers to
proactively cache TLS settings, eliminating per-handshake lister calls
while maintaining dynamic reconfiguration capability. The cached
settings are applied during TLS handshakes via GetConfigForClient.

The commit aims to focus on availability over strict consistency on
errors, such as an error fetching the API server object. The CVO
provides critical metrics and as such, I am inclined towards
availability instead of strict TLS configuration consistency.

The TLS adherence feature is currently in Tech Preview. Components do
not need to check the feature gate explicitly though [2]:

> Component Interaction with the Feature Gate: The feature gate controls
> whether the tlsAdherence field is accepted by the API server —
> components themselves do not need to check the feature gate.
> Because the field is optional (+optional, omitempty), components only
> need to handle the field's value when unmarshaling the APIServer config
> ...
> This means components do not need to set up feature gate watching or
> add feature-gate-specific code paths. The ShouldHonorClusterTLSProfile
> helper in library-go encapsulates all of this logic.

The ShouldHonorClusterTLSProfile helper from library-go encapsulates
this logic.

Configuration precedence: crypto defaults → central profile → overrides
(override support added in next commit for HyperShift compatibility).

[1]: https://github.com/openshift/enhancements/blob/master/enhancements/security/centralized-tls-config.md
[2]: https://github.com/openshift/enhancements/blob/master/enhancements/security/centralized-tls-config.md#feature-gate

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
…perShift

Add --tls-min-version and --tls-cipher-suites flags based on
recommendations from the centralized TLS config enhancement [1] to
support HyperShift deployments:

> When these flags are set by the CPO, they take precedence over any
> value the component would read from
> apiservers.config.openshift.io/cluster. When they are not set, the
> component falls back to its normal behavior of watching the cluster config.

This allows hosted control planes components, which are deployed in the
management cluster, to have different TLS setting or for the components
to not need to read the management cluster Kubernetes API server.

[1]: https://github.com/openshift/enhancements/blob/master/enhancements/security/centralized-tls-config.md

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 24, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 24, 2026
@hongkailiu hongkailiu marked this pull request as ready for review May 24, 2026 12:38
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 24, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces central TLS profile management to the Cluster Version Operator. A new ProfileManager applies Kubernetes/OpenShift cluster-wide TLS policies to the operator's metrics server, accepts optional CLI overrides for minimum TLS version and cipher suites, and dynamically updates when the central APIServer configuration changes. The operator initialization and metrics server TLS handshake now integrate this manager.

Changes

TLS Profile Management Feature

Layer / File(s) Summary
TLS Profile Manager core logic
pkg/tls/tls.go
ProfileManager resolves and caches the central APIServer TLS profile; ApplySettings applies settings in precedence order: secure defaults → cached central profile → CLI overrides. Options parses and validates --tls-min-version and --tls-cipher-suites strings.
TLS Profile Manager test suite
pkg/tls/tls_test.go
Nine test functions cover ProfileManager initialization, central profile application across profile types, override validation and precedence, informer event handling, error recovery, and TLSAdherence policy variations. Test helpers create fake APIServer informers with cache sync.
CLI flag support for TLS overrides
cmd/cluster-version-operator/start.go
Registers --tls-min-version and --tls-cipher-suites flags on cmd.PersistentFlags() and wires them into opts.TLSOptions with dynamic help text listing valid TLS version values.
Operator TLS profile manager integration
pkg/cvo/cvo.go
Extends Operator struct with apiServerLister and profileMgr fields. Updates New(...) signature to accept apiServerInformer and tlsOverrides, initializes ProfileManager during construction, refactors event recording, and exposes ApplySettings() method.
Metrics server TLS config application
pkg/cvo/metrics.go
RunMetrics now accepts an applySettings func(config *tls.Config) callback and invokes it on each per-client TLS config in the GetConfigForClient handshake path.
Startup validation and TLS initialization wiring
pkg/start/start.go
Options gains TLSOptions field; ValidateAndComplete() validates overrides and logs configured settings; waits for config informer cache sync; passes APIServer informer and computed overrides to cvo.New(); wires cvo.ApplySettings() into metrics server call.
Dependency updates
go.mod
Advances ginkgo/v2 and gomega, promotes k8s.io/component-base to main require, and refreshes indirect test and sync dependencies.
Live-cluster TLS profile verification test
test/cvo/cvo.go, .openshift-tests-extension/openshift_payload_cluster-version-operator.json
New Ginkgo test fetches the APIServer, lists CVO pods, streams logs, and asserts the "Updated cached TLS profile" message is present. Test is registered as a blocking lifecycle scenario.

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 10 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning The e2e test has multiple quality issues: wrong namespace on line 128; missing assertion messages; no timeout on context; no polling/waiting patterns for cluster operations. Use external.DefaultCVONamespace on line 128; add messages to assertions; create context with timeout and use wait.PollUntilContextTimeout for async operations per existing patterns.
Microshift Test Compatibility ⚠️ Warning New e2e test "should update TLS profile" uses APIServer (config.openshift.io/v1) which is unavailable on MicroShift, but lacks required protection mechanisms. Add [Skipped:MicroShift], [apigroup:config.openshift.io] tag, or util.SkipIfMicroshift() check like other tests in the same file.
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective of the changeset: adding an end-to-end test for TLS profile updates, which is demonstrated across the test harness updates and test payload configuration.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.
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 All test names are static and deterministic. No dynamic values (pod names, timestamps, UUIDs) in Ginkgo or standard Go tests.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The new "should update TLS profile" test does not make multi-node assumptions. It lists CVO pods by label, reads logs from a single pod, and checks for a log message—all of which work on SNO.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds TLS profile management and e2e testing with no scheduling constraints, deployment manifests, or topology-specific pod configurations that would break SNO, Two-Node, or HyperShift.
Ote Binary Stdout Contract ✅ Passed All process-level logging uses klog configured with alsologtostderr=true, sending output to stderr. No fmt.Print, direct stdout writes, or problematic Ginkgo configuration found in process-level code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new e2e test in test/cvo/cvo.go uses only cluster-internal Kubernetes API operations with no IPv4 addresses, external connectivity, or IPv4-specific assumptions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 24, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hongkailiu

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

The pull request process is described 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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 24, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@pkg/tls/tls_test.go`:
- Around line 473-555: Replace the fixed time.Sleep(100 * time.Millisecond)
waits with bounded polling loops that repeatedly call mgr.ApplySettings(config)
and check config.MinVersion until it matches the expected value (or until a
timeout like 1–2s elapses), failing the test on timeout; do this for each case
that currently sleeps (tests referencing
fakeClient.ConfigV1().APIServers().Update/Delete and the Test blocks that assert
config.MinVersion), polling at a short interval (e.g., 10–50ms) and breaking
early when the condition is met so the informer propagation is robust under slow
CI.
- Around line 42-46: Wait for the informer cache to actually sync and fail the
test immediately if any informer did not sync: call
informerFactory.WaitForCacheSync(ctx.Done()) and capture its returned
map/boolean, iterate over the results and if any entry is false call t.Fatalf
(or t.Fatalf with context and the failing keys), and ensure the created
context's cancel() is deferred (defer cancel()) so the timeout is cleaned up;
this replaces the current blind call to
informerFactory.WaitForCacheSync(ctx.Done()) in the test.

In `@pkg/tls/tls.go`:
- Around line 173-197: CreateOverrides currently leaves o.settings set from a
previous run, causing stale TLS overrides to persist; to fix, ensure you clear
stale parsed overrides by setting o.settings = nil at the start of
Options.CreateOverrides (or before any early returns) so that when
MinVersionOverride or CipherSuitesOverride are empty or validation fails the
Options does not retain prior validated Settings; keep the rest of the
validation logic (validated, MinVersionOverride, CipherSuitesOverride) the same
and only assign validated to o.settings on successful validation.

In `@test/cvo/cvo.go`:
- Around line 126-136: Replace the single synchronous events list/assert with a
bounded retry/poll that repeatedly calls
kubeClient.CoreV1().Events(external.DefaultCVONamespace).List(ctx,
metav1.ListOptions{}) until an event with Reason ==
tls.EventReasonUpdateTLSProfile is observed or a timeout elapses; implement this
using a polling helper (e.g., wait.PollImmediate or gomega.Eventually) with a
clear interval and timeout, returning true when found and then assert that the
poll succeeded (instead of the current immediate loop/expect on events and
found).
- Around line 115-124: This test calls configv1Client.APIServers().Get(...)
without first skipping MicroShift environments; add an explicit MicroShift guard
(e.g., call exutil.IsMicroShiftCluster() and skip if true) at the start of the
g.It block for "should update TLS profile if ClusterTLSProfile should be
honored" so the APIServer API is not invoked on MicroShift clusters; place the
check before the API call to apiServer and keep the existing NotFound handling
intact.
🪄 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: 5c1014b2-5e0c-4baa-a6ac-678fd8a2607d

📥 Commits

Reviewing files that changed from the base of the PR and between bb338d4 and b779a05.

⛔ Files ignored due to path filters (141)
  • vendor/github.com/google/btree/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/btree.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/btree_generic.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/merge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/profile.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/proto.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/prune.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/CHANGELOG.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/format/format.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/gomega_dsl.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/have_key_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/match_error_strictly_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/pkg/tls/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/pkg/tls/tls.go is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/PATENTS is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/errgroup/errgroup.go is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/tools/go/ast/inspector/cursor.go is excluded by !vendor/**, !**/vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/jsonpatch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/ciphersuites_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/colon_separated_multimap_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/configuration_map.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/flags.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/langle_separated_map_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/map_string_bool.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/map_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/namedcertkey_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/noop.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/omitempty.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/sectioned.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/string_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/string_slice_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/tracker_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/tristate.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.gitignore is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.golangci.yml is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/FAQ.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/OWNERS is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/RELEASE.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/VERSIONING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/alias.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/name.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/httpserver/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/syncs/syncs.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/leaderelection.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_hubspoke.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_registry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (9)
  • cmd/cluster-version-operator/start.go
  • pkg/cvo/cvo.go
  • pkg/cvo/metrics.go
  • pkg/cvo/sync_worker.go
  • pkg/internal/constants.go
  • pkg/start/start.go
  • pkg/tls/tls.go
  • pkg/tls/tls_test.go
  • test/cvo/cvo.go

Comment thread pkg/tls/tls_test.go
Comment thread pkg/tls/tls_test.go Outdated
Comment thread pkg/tls/tls.go
Comment thread test/cvo/cvo.go Outdated
Comment thread test/cvo/cvo.go Outdated
DavidHurta and others added 7 commits May 24, 2026 08:49
Implements comprehensive test coverage for ProfileManager's event handlers
including Add, Update, and Delete events. Tests verify:
- Initial profile application from Add event
- Profile updates via Update events
- Error recovery when invalid profiles are received
- TLSAdherence policy changes
- Fallback to safe defaults on Delete events

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extracts the repeated pattern of creating and starting an APIServer
informer into helper functions to reduce code duplication.

Changes:
- Add setupInformer helper for simple test cases
- Add setupInformerWithClient helper for tests that need the fake
  client (e.g., for updating APIServer resources in event tests)
- Both helpers accept variadic apiServers and custom timeout
- Update all tests to use the new helpers, reducing ~80 lines of
  boilerplate code

The helpers use t.Helper() for better test failure reporting.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
pkg/tls/tls_test.go (2)

44-46: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fail fast when informer cache sync does not complete.

Line 45 ignores the WaitForCacheSync result, so cache startup failures are hidden and test failures become noisy downstream.

Suggested patch
 	informerFactory.Start(ctx.Done())
-	informerFactory.WaitForCacheSync(ctx.Done())
+	for informer, synced := range informerFactory.WaitForCacheSync(ctx.Done()) {
+		if !synced {
+			t.Fatalf("failed to sync informer cache %v: %v", informer, ctx.Err())
+		}
+	}
🤖 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 `@pkg/tls/tls_test.go` around lines 44 - 46, The test currently starts
informers with informerFactory.Start(ctx.Done()) but ignores the boolean results
returned by informerFactory.WaitForCacheSync(ctx.Done()), which hides cache
startup failures; update the test to capture the map[string]bool (or the
returned sync result) from WaitForCacheSync and assert that all informers are
true, failing fast (e.g., t.Fatalf or t.Fatalf with context) if any informer did
not sync; refer to informerFactory.Start, informerFactory.WaitForCacheSync, and
the test's *testing.T instance to implement the immediate failure on sync
failure.

478-480: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace fixed sleeps with bounded polling in informer-event assertions.

Lines 479, 513, 538, and 555 use fixed 100ms sleeps; informer propagation is asynchronous, so these assertions can be flaky under slower CI.

Suggested patch
+	waitForConfig := func(expect func(*tls.Config) bool, msg string) {
+		deadline := time.Now().Add(2 * time.Second)
+		for time.Now().Before(deadline) {
+			cfg := &tls.Config{}
+			mgr.ApplySettings(cfg)
+			if expect(cfg) {
+				return
+			}
+			time.Sleep(20 * time.Millisecond)
+		}
+		t.Fatalf("timeout waiting for expected TLS settings: %s", msg)
+	}
+
 	// Give the informer a moment to process the update
-	time.Sleep(100 * time.Millisecond)
+	waitForConfig(func(cfg *tls.Config) bool { return cfg.MinVersion == tls.VersionTLS13 }, "modern profile should set TLS 1.3")
@@
-	time.Sleep(100 * time.Millisecond)
+	waitForConfig(func(cfg *tls.Config) bool { return cfg.MinVersion == versionBefore }, "invalid update should retain previous profile")
@@
-	time.Sleep(100 * time.Millisecond)
+	waitForConfig(func(cfg *tls.Config) bool { return cfg.MinVersion >= tls.VersionTLS12 }, "NoOpinion should keep safe defaults")
@@
-	time.Sleep(100 * time.Millisecond)
+	waitForConfig(func(cfg *tls.Config) bool { return cfg.MinVersion >= tls.VersionTLS12 }, "delete should fall back to safe defaults")

Also applies to: 513-513, 538-538, 555-555

🤖 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 `@pkg/tls/tls_test.go` around lines 478 - 480, Tests in pkg/tls/tls_test.go use
fixed 100ms time.Sleep calls to wait for informer propagation (occurrences
around the time.Sleep calls at lines shown), which makes assertions flaky;
replace each fixed sleep with a bounded polling loop (e.g., wait.PollImmediate
or a for loop with time.After timeout) that repeatedly checks the informer state
or the expected condition (using the same assertion helpers like require/assert)
until success or a reasonable timeout (e.g., 2s), and fail the test if the
condition never becomes true; update the checks surrounding the existing
time.Sleep calls so they actively probe the informer cache/returned value
(instead of sleeping) and break early when the expected state is observed.
test/cvo/cvo.go (2)

117-124: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a MicroShift skip guard before the APIServer call.

Line 118 invokes the OpenShift Config API before any MicroShift check. In MicroShift, this API is unsupported and can fail before the intended skip logic.

Suggested patch
 g.It("should update TLS profile if ClusterTLSProfile should be honored", func() {
+	err := util.SkipIfMicroshift(ctx, restCfg)
+	o.Expect(err).NotTo(o.HaveOccurred(), "Failed to determine if cluster is MicroShift")
+
 	apiServer, err := configv1Client.APIServers().Get(ctx, tlsprofile.APIServerName, metav1.GetOptions{})
 	if kerrors.IsNotFound(err) {
 		g.Skip(fmt.Sprintf("This test is skipped because APIServer/%s doesn't exist", tlsprofile.APIServerName))

As per coding guidelines, "When adding new Ginkgo e2e tests, check whether they use APIs or features unavailable on MicroShift. MicroShift only supports Route and SecurityContextConstraints APIs; all other OpenShift-specific APIs ... are unavailable."

🤖 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/cvo/cvo.go` around lines 117 - 124, This test calls
configv1Client.APIServers().Get (using tlsprofile.APIServerName) before checking
for MicroShift; add a MicroShift skip guard at the start of the Ginkgo It block
so the test bails out on MicroShift platforms before invoking the OpenShift
Config API. Specifically, detect MicroShift (the same check pattern used
elsewhere in tests) and call g.Skip(...) if running on MicroShift, then proceed
to call configv1Client.APIServers().Get and the subsequent
crypto.ShouldHonorClusterTLSProfile logic only when not MicroShift.

128-137: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use bounded polling for event detection instead of a one-shot list.

Line 128 performs a single event list/read; event emission is asynchronous, so this can intermittently fail. Poll with timeout/interval, then assert. (Also fix the assertion text typo: TCPTLS.)

Suggested patch
-		events, err := kubeClient.CoreV1().Events(external.DefaultCVONamespace).List(ctx, metav1.ListOptions{})
-		o.Expect(err).NotTo(o.HaveOccurred())
-		var found bool
-		for _, event := range events.Items {
-			if event.Reason == tls.EventReasonUpdateTLSProfile {
-				found = true
-				break
-			}
-		}
-		o.Expect(found).To(o.BeTrue(), "Failed to any event about updating TCP profile")
+		o.Eventually(func() bool {
+			events, err := kubeClient.CoreV1().Events(external.DefaultCVONamespace).List(ctx, metav1.ListOptions{})
+			if err != nil {
+				return false
+			}
+			for _, event := range events.Items {
+				if event.Reason == tls.EventReasonUpdateTLSProfile {
+					return true
+				}
+			}
+			return false
+		}, "2m", "5s").Should(o.BeTrue(), "Failed to find any event about updating TLS profile")

As per coding guidelines, "Ginkgo e2e tests should follow quality requirements: ... (3) timeouts on cluster interactions ... Eventually/Consistently calls."

🤖 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/cvo/cvo.go` around lines 128 - 137, Replace the one-shot call to
kubeClient.CoreV1().Events(...).List(...) with a bounded poll (e.g., Gomega's
Eventually or wait.PollImmediate) that repeatedly lists events until
tls.EventReasonUpdateTLSProfile is observed or a timeout is reached; inside the
poll check for the event (current loop that sets found) and assert success after
the poll completes, and fix the expectation message to "Failed to find any event
about updating TLS profile" (referencing kubeClient.CoreV1().Events,
tls.EventReasonUpdateTLSProfile, the found boolean, and the final o.Expect
call).
🤖 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 `@pkg/tls/tls.go`:
- Around line 63-69: The APIServer informer UpdateFunc calls mgr.updateSettings
on every resync which triggers ProfileManager.updateSettings to emit
UpdateTLSProfile events even when nothing TLS-relevant changed; modify the
UpdateFunc to cast oldObj and newObj to *configv1.APIServer and short-circuit
unless the TLS-relevant fields actually changed (e.g., compare the relevant
fields or whole TLS-related sub-structure rather than blind invocation), so only
call mgr.updateSettings(apiServer) when those TLS-profile-affecting fields
differ between old and new; ensure you reference UpdateFunc and
mgr.updateSettings/ProfileManager.updateSettings/UpdateTLSProfile when locating
and changing the logic.

---

Duplicate comments:
In `@pkg/tls/tls_test.go`:
- Around line 44-46: The test currently starts informers with
informerFactory.Start(ctx.Done()) but ignores the boolean results returned by
informerFactory.WaitForCacheSync(ctx.Done()), which hides cache startup
failures; update the test to capture the map[string]bool (or the returned sync
result) from WaitForCacheSync and assert that all informers are true, failing
fast (e.g., t.Fatalf or t.Fatalf with context) if any informer did not sync;
refer to informerFactory.Start, informerFactory.WaitForCacheSync, and the test's
*testing.T instance to implement the immediate failure on sync failure.
- Around line 478-480: Tests in pkg/tls/tls_test.go use fixed 100ms time.Sleep
calls to wait for informer propagation (occurrences around the time.Sleep calls
at lines shown), which makes assertions flaky; replace each fixed sleep with a
bounded polling loop (e.g., wait.PollImmediate or a for loop with time.After
timeout) that repeatedly checks the informer state or the expected condition
(using the same assertion helpers like require/assert) until success or a
reasonable timeout (e.g., 2s), and fail the test if the condition never becomes
true; update the checks surrounding the existing time.Sleep calls so they
actively probe the informer cache/returned value (instead of sleeping) and break
early when the expected state is observed.

In `@test/cvo/cvo.go`:
- Around line 117-124: This test calls configv1Client.APIServers().Get (using
tlsprofile.APIServerName) before checking for MicroShift; add a MicroShift skip
guard at the start of the Ginkgo It block so the test bails out on MicroShift
platforms before invoking the OpenShift Config API. Specifically, detect
MicroShift (the same check pattern used elsewhere in tests) and call g.Skip(...)
if running on MicroShift, then proceed to call configv1Client.APIServers().Get
and the subsequent crypto.ShouldHonorClusterTLSProfile logic only when not
MicroShift.
- Around line 128-137: Replace the one-shot call to
kubeClient.CoreV1().Events(...).List(...) with a bounded poll (e.g., Gomega's
Eventually or wait.PollImmediate) that repeatedly lists events until
tls.EventReasonUpdateTLSProfile is observed or a timeout is reached; inside the
poll check for the event (current loop that sets found) and assert success after
the poll completes, and fix the expectation message to "Failed to find any event
about updating TLS profile" (referencing kubeClient.CoreV1().Events,
tls.EventReasonUpdateTLSProfile, the found boolean, and the final o.Expect
call).
🪄 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: 93685e51-fe33-4e03-bac3-215fb5b79dd6

📥 Commits

Reviewing files that changed from the base of the PR and between b779a05 and e3462bc.

⛔ Files ignored due to path filters (143)
  • go.sum is excluded by !**/*.sum, !go.sum
  • vendor/github.com/google/btree/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/btree.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/btree/btree_generic.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/merge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/profile.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/proto.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/google/pprof/profile/prune.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/CHANGELOG.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/format/format.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/gomega_dsl.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/have_key_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/match_error_strictly_matcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/pkg/tls/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/controller-runtime-common/pkg/tls/tls.go is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/PATENTS is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/sync/errgroup/errgroup.go is excluded by !vendor/**, !**/vendor/**
  • vendor/golang.org/x/tools/go/ast/inspector/cursor.go is excluded by !vendor/**, !**/vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/jsonpatch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/ciphersuites_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/colon_separated_multimap_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/configuration_map.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/flags.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/langle_separated_map_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/map_string_bool.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/map_string_string.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/namedcertkey_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/noop.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/omitempty.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/sectioned.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/string_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/string_slice_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/tracker_flag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/component-base/cli/flag/tristate.go is excluded by !vendor/**, !**/vendor/**
  • vendor/modules.txt is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.gitignore is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.golangci.yml is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/FAQ.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/OWNERS is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/RELEASE.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/TMP-LOGGING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/VERSIONING.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/alias.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/selector.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cache/multi_namespace_cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/certwatcher.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/certwatcher/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/name.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/healthz/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/httpserver/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/syncs/syncs.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/runnable_group.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_windows.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/client_go_adapter.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/leaderelection.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/registry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/server/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/metrics/workqueue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/predicate/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/predicate/predicate.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/source/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/source/source.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_hubspoke.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_registry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/decoder.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (11)
  • .openshift-tests-extension/openshift_payload_cluster-version-operator.json
  • cmd/cluster-version-operator/start.go
  • go.mod
  • pkg/cvo/cvo.go
  • pkg/cvo/metrics.go
  • pkg/cvo/sync_worker.go
  • pkg/internal/constants.go
  • pkg/start/start.go
  • pkg/tls/tls.go
  • pkg/tls/tls_test.go
  • test/cvo/cvo.go

Comment thread pkg/tls/tls.go
Comment on lines +63 to +69
UpdateFunc: func(oldObj, newObj interface{}) {
if apiServer, ok := newObj.(*configv1.APIServer); ok {
if err := mgr.updateSettings(apiServer); err != nil {
klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
}
}
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does client-go SharedInformer call UpdateFunc during periodic resyncs even when an object has not materially changed?

💡 Result:

Yes—during a resync period, client-go SharedInformer will call a handler’s UpdateFunc for every object in its local cache even if the object has not materially changed (i.e., it has not received an actual watch event or list delta). The shared informer’s resync mechanism is explicitly described as “delivering to the handler an update notification for every object in the informer's local cache” and it “does not add any interactions with the authoritative storage” [1]. In the shared informer implementation, resync notifications are routed through the informer’s event distribution as update notifications (Sync/Replaced/Updated/Added all funnel to the Update pathway) [2]. However, the handler will receive oldObj and newObj representing the cached state; many controllers treat resync as an “insurance” rescan and may internally ignore it if resourceVersion (or other fields) indicate no real change. But the UpdateFunc invocation itself is driven by the resync, not by whether the object materially changed [1]. Related nuance: Reflector can also periodically invoke Store.Resync based on ShouldResync; when that happens it triggers in-memory resync processing that results in update notifications to handlers [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read targeted portion of pkg/tls/tls.go around the UpdateFunc
sed -n '1,220p' pkg/tls/tls.go | nl -ba | sed -n '40,140p' || true

# 2) Find updateSettings and UpdateTLSProfile usage in pkg/tls/tls.go
rg -n "updateSettings|UpdateTLSProfile|TLSSecurityProfile|TLSAdherence" pkg/tls/tls.go || true

# 3) Inspect where informer resync period is set/created (pkg/start/start.go)
rg -n "NewSharedInformerFactory|resync|Resync|configInformerFactory" pkg/start/start.go || true
sed -n '1,260p' pkg/start/start.go | nl -ba | sed -n '1,220p' || true

Repository: openshift/cluster-version-operator

Length of output: 3569


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the exact handler code around lines ~50-90 in pkg/tls/tls.go
sed -n '40,105p' pkg/tls/tls.go

# Show the full updateSettings function body (approximate range based on discovered line numbers)
sed -n '80,160p' pkg/tls/tls.go

# Show some context around where updateSettings(nil) is called (around ~60-90)
sed -n '55,95p' pkg/tls/tls.go

# Search within pkg/tls/tls.go for any caching fields used by updateSettings
rg -n "cache|cached|last|previous|appl|profile|TLSAdherence|TLSSecurityProfile|ShouldHonorClusterTLSProfile" pkg/tls/tls.go || true

Repository: openshift/cluster-version-operator

Length of output: 9214


Gate the APIServer informer UpdateFunc to avoid resync-triggered TLS event spam

ProfileManager.updateSettings() emits the UpdateTLSProfile event on every invocation (including when it resolves to a nil apply function when TLS adherence disables the profile). Because the APIServer informer is configured with a non-zero resync period in pkg/start/start.go, this UpdateFunc will run during periodic resyncs and for unrelated APIServer changes, producing repeated false-positive TLS update events.

Suggested change
+import "reflect"
+
 		UpdateFunc: func(oldObj, newObj interface{}) {
-			if apiServer, ok := newObj.(*configv1.APIServer); ok {
-				if err := mgr.updateSettings(apiServer); err != nil {
+			oldAPIServer, oldOK := oldObj.(*configv1.APIServer)
+			newAPIServer, newOK := newObj.(*configv1.APIServer)
+			if !oldOK || !newOK {
+				return
+			}
+			if reflect.DeepEqual(oldAPIServer.Spec.TLSSecurityProfile, newAPIServer.Spec.TLSSecurityProfile) &&
+				reflect.DeepEqual(oldAPIServer.Spec.TLSAdherence, newAPIServer.Spec.TLSAdherence) {
+				return
+			}
+			if err := mgr.updateSettings(newAPIServer); err != nil {
 					klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
-				}
 			}
 		},
📝 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
UpdateFunc: func(oldObj, newObj interface{}) {
if apiServer, ok := newObj.(*configv1.APIServer); ok {
if err := mgr.updateSettings(apiServer); err != nil {
klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
}
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
oldAPIServer, oldOK := oldObj.(*configv1.APIServer)
newAPIServer, newOK := newObj.(*configv1.APIServer)
if !oldOK || !newOK {
return
}
if reflect.DeepEqual(oldAPIServer.Spec.TLSSecurityProfile, newAPIServer.Spec.TLSSecurityProfile) &&
reflect.DeepEqual(oldAPIServer.Spec.TLSAdherence, newAPIServer.Spec.TLSAdherence) {
return
}
if err := mgr.updateSettings(newAPIServer); err != nil {
klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
}
},
🤖 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 `@pkg/tls/tls.go` around lines 63 - 69, The APIServer informer UpdateFunc calls
mgr.updateSettings on every resync which triggers ProfileManager.updateSettings
to emit UpdateTLSProfile events even when nothing TLS-relevant changed; modify
the UpdateFunc to cast oldObj and newObj to *configv1.APIServer and
short-circuit unless the TLS-relevant fields actually changed (e.g., compare the
relevant fields or whole TLS-related sub-structure rather than blind
invocation), so only call mgr.updateSettings(apiServer) when those
TLS-profile-affecting fields differ between old and new; ensure you reference
UpdateFunc and mgr.updateSettings/ProfileManager.updateSettings/UpdateTLSProfile
when locating and changing the logic.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
pkg/tls/tls.go (2)

66-72: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gate APIServer update handling to TLS-relevant field changes.

UpdateFunc still calls updateSettings on every APIServer update/resync, so TLS update processing can run even when TLS profile/adherence did not change. Please short-circuit unless TLS-relevant fields differ between oldObj and newObj.

Proposed patch
 	UpdateFunc: func(oldObj, newObj interface{}) {
-		if apiServer, ok := newObj.(*configv1.APIServer); ok {
-			if err := mgr.updateSettings(apiServer); err != nil {
-				klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
-			}
-		}
+		oldAPIServer, oldOK := oldObj.(*configv1.APIServer)
+		newAPIServer, newOK := newObj.(*configv1.APIServer)
+		if !oldOK || !newOK {
+			return
+		}
+		if oldAPIServer.Spec.TLSAdherence == newAPIServer.Spec.TLSAdherence &&
+			oldAPIServer.Spec.TLSSecurityProfile == newAPIServer.Spec.TLSSecurityProfile {
+			return
+		}
+		if err := mgr.updateSettings(newAPIServer); err != nil {
+			klog.Errorf("Failed to apply TLS settings on APIServer update: %v", err)
+		}
 	},
🤖 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 `@pkg/tls/tls.go` around lines 66 - 72, The UpdateFunc currently calls
mgr.updateSettings on every APIServer update; change it to first type-assert
oldObj and newObj to *configv1.APIServer and compare only TLS-relevant fields
(e.g. Spec.TLSSecurityProfile, Spec.ServingInfo.TLS/cipher suite settings, any
TLS profile/adherence flags) using a deep-equality check (or field-by-field
comparison) and return early if they are equal; only call
mgr.updateSettings(apiServer) when those TLS-related fields differ, keeping the
existing error logging behavior.

189-193: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear cached overrides before parsing new CLI override state.

CreateOverrides() can retain stale o.settings when called again with empty/invalid inputs after a prior valid parse. Reset o.settings at function start.

Proposed patch
 func (o *Options) CreateOverrides() error {
+	o.settings = nil
+
 	// If no overrides, return nil (central profile or defaults will be used)
 	if o.MinVersionOverride == "" && len(o.CipherSuitesOverride) == 0 {
 		return nil
 	}

Also applies to: 213-214

🤖 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 `@pkg/tls/tls.go` around lines 189 - 193, Reset cached override state before
parsing: at the start of Options.CreateOverrides() clear o.settings (e.g., set
to nil or a zero-value) so stale settings from a prior successful parse can't be
reused when new inputs are empty/invalid; apply the same reset in the other
override-parsing routine in this file where o.settings is populated (the later
function that sets o.settings around the other override parsing code) so both
code paths always start from a clean state before parsing new CLI override
values.
test/cvo/cvo.go (2)

117-123: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an explicit MicroShift guard before APIServer calls.

This test should skip MicroShift before invoking configv1Client.APIServers().Get(...) to avoid environment-specific failures.

Proposed patch
 g.It("should update TLS profile if ClusterTLSProfile should be honored", func() {
+	err := util.SkipIfMicroshift(ctx, restCfg)
+	o.Expect(err).NotTo(o.HaveOccurred(), "Failed to determine if cluster is MicroShift")
+
 	apiServer, err := configv1Client.APIServers().Get(ctx, tlsprofile.APIServerName, metav1.GetOptions{})
 	if kerrors.IsNotFound(err) {
 		g.Skip(fmt.Sprintf("This test is skipped because APIServer/%s doesn't exist", tlsprofile.APIServerName))
🤖 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/cvo/cvo.go` around lines 117 - 123, Add an explicit MicroShift
environment guard at the start of the test "should update TLS profile if
ClusterTLSProfile should be honored" before calling
configv1Client.APIServers().Get(...): detect MicroShift (use the existing
platform check helper or add a small isMicroShift() check) and call g.Skip(...)
with a brief message when on MicroShift, then proceed with the APIServer get
call and existing error handling; update the test in test/cvo/cvo.go around the
g.It block to place this guard immediately before invoking
configv1Client.APIServers().Get so the call is never made in MicroShift
environments.

128-137: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use bounded polling for event detection to avoid flakes.

Event recording is asynchronous; a one-time list/assert can race. Poll until timeout for Reason == tls.EventReasonUpdateTLSProfile.

Proposed patch
-		events, err := kubeClient.CoreV1().Events(external.DefaultCVONamespace).List(ctx, metav1.ListOptions{})
-		o.Expect(err).NotTo(o.HaveOccurred())
-		var found bool
-		for _, event := range events.Items {
-			if event.Reason == tls.EventReasonUpdateTLSProfile {
-				found = true
-				break
-			}
-		}
-		o.Expect(found).To(o.BeTrue(), "Failed to any event about updating TCP profile")
+		o.Eventually(func() bool {
+			events, err := kubeClient.CoreV1().Events(external.DefaultCVONamespace).List(ctx, metav1.ListOptions{})
+			if err != nil {
+				return false
+			}
+			for _, event := range events.Items {
+				if event.Reason == tls.EventReasonUpdateTLSProfile {
+					return true
+				}
+			}
+			return false
+		}, "2m", "5s").Should(o.BeTrue(), "Failed to find any event about updating TLS profile")
🤖 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/cvo/cvo.go` around lines 128 - 137, The current one-time list of events
can race because event recording is asynchronous; replace the direct List+assert
with bounded polling until a timeout to wait for an event with Reason ==
tls.EventReasonUpdateTLSProfile. Use the existing
kubeClient.CoreV1().Events(...).List(...) call inside a retry loop (e.g.,
wait.PollImmediate or Gomega's Eventually) that re-lists events and checks
event.Reason against tls.EventReasonUpdateTLSProfile, returning success when
found or failing the test after the timeout; keep the same namespace
(external.DefaultCVONamespace) and context (ctx) and preserve the final
assertion message.
🤖 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/cvo/cvo.go`:
- Line 137: Update the assertion message in the Expect call that checks the
boolean variable found (the line with o.Expect(found).To(o.BeTrue(), ...)) to
replace the incorrect "TCP profile" text with "TLS profile" so the failure
message correctly reflects TLS-profile validation.

---

Duplicate comments:
In `@pkg/tls/tls.go`:
- Around line 66-72: The UpdateFunc currently calls mgr.updateSettings on every
APIServer update; change it to first type-assert oldObj and newObj to
*configv1.APIServer and compare only TLS-relevant fields (e.g.
Spec.TLSSecurityProfile, Spec.ServingInfo.TLS/cipher suite settings, any TLS
profile/adherence flags) using a deep-equality check (or field-by-field
comparison) and return early if they are equal; only call
mgr.updateSettings(apiServer) when those TLS-related fields differ, keeping the
existing error logging behavior.
- Around line 189-193: Reset cached override state before parsing: at the start
of Options.CreateOverrides() clear o.settings (e.g., set to nil or a zero-value)
so stale settings from a prior successful parse can't be reused when new inputs
are empty/invalid; apply the same reset in the other override-parsing routine in
this file where o.settings is populated (the later function that sets o.settings
around the other override parsing code) so both code paths always start from a
clean state before parsing new CLI override values.

In `@test/cvo/cvo.go`:
- Around line 117-123: Add an explicit MicroShift environment guard at the start
of the test "should update TLS profile if ClusterTLSProfile should be honored"
before calling configv1Client.APIServers().Get(...): detect MicroShift (use the
existing platform check helper or add a small isMicroShift() check) and call
g.Skip(...) with a brief message when on MicroShift, then proceed with the
APIServer get call and existing error handling; update the test in
test/cvo/cvo.go around the g.It block to place this guard immediately before
invoking configv1Client.APIServers().Get so the call is never made in MicroShift
environments.
- Around line 128-137: The current one-time list of events can race because
event recording is asynchronous; replace the direct List+assert with bounded
polling until a timeout to wait for an event with Reason ==
tls.EventReasonUpdateTLSProfile. Use the existing
kubeClient.CoreV1().Events(...).List(...) call inside a retry loop (e.g.,
wait.PollImmediate or Gomega's Eventually) that re-lists events and checks
event.Reason against tls.EventReasonUpdateTLSProfile, returning success when
found or failing the test after the timeout; keep the same namespace
(external.DefaultCVONamespace) and context (ctx) and preserve the final
assertion message.
🪄 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: 51587912-9e80-4737-ae46-dfc2aa770cf4

📥 Commits

Reviewing files that changed from the base of the PR and between e3462bc and 36162fc.

📒 Files selected for processing (3)
  • .openshift-tests-extension/openshift_payload_cluster-version-operator.json
  • pkg/tls/tls.go
  • test/cvo/cvo.go

Comment thread test/cvo/cvo.go Outdated
@hongkailiu
Copy link
Copy Markdown
Member Author

/retest-required

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
test/cvo/cvo.go (2)

120-126: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an explicit environment skip guard before the APIServer API call.

This test calls OpenShift config APIs immediately; in unsupported environments that can fail instead of skip cleanly.

Suggested patch
 g.It("should update TLS profile", func() {
+	err := util.SkipIfMicroshift(ctx, restCfg)
+	o.Expect(err).NotTo(o.HaveOccurred(), "Failed to determine if cluster is MicroShift")
+
 	apiServer, err := configV1Client.APIServers().Get(ctx, tlsprofile.APIServerName, metav1.GetOptions{})
 	if kerrors.IsNotFound(err) {
 		g.Skip(fmt.Sprintf("This test is skipped because APIServer/%s doesn't exist", tlsprofile.APIServerName))
 	} else {
 		o.Expect(err).NotTo(o.HaveOccurred())
 	}

As per coding guidelines, "When skipping tests for certain environments, document the reason".

🤖 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/cvo/cvo.go` around lines 120 - 126, Before calling
configV1Client.APIServers().Get in the It("should update TLS profile") test, add
an explicit environment skip guard that verifies the test is running in a
supported OpenShift/config API environment (e.g. a helper like
ensureConfigAPISupported or isOpenShiftEnvironment) and call g.Skip with a clear
reason string if not supported; move or wrap the existing kerrors.IsNotFound
handling after this guard so the Get is only attempted in supported environments
and document the skip reason in the g.Skip message referencing APIServer/config
API availability and tlsprofile.APIServerName.

154-154: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the assertion message to match what is actually asserted.

This assertion checks TLS log content, but the failure text currently references an event and “TCP profile”.

Suggested patch
-		o.Expect(strings.Contains(buf.String(), tls.LogMessageUpdatedTLSProfile)).To(o.BeTrue(), "Failed to any event about updating TCP profile when ShouldHonorClusterTLSProfile=%t", crypto.ShouldHonorClusterTLSProfile(apiServer.Spec.TLSAdherence))
+		o.Expect(strings.Contains(buf.String(), tls.LogMessageUpdatedTLSProfile)).To(o.BeTrue(), "Failed to find TLS profile update log when ShouldHonorClusterTLSProfile=%t", crypto.ShouldHonorClusterTLSProfile(apiServer.Spec.TLSAdherence))
🤖 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/cvo/cvo.go` at line 154, The failure message in the Expect assertion
mismatches the check: update the assertion's failure text to describe the TLS
log check rather than an event or "TCP profile". Modify the Expect call that
checks strings.Contains(buf.String(), tls.LogMessageUpdatedTLSProfile) to use a
message that references verifying the TLS log message (e.g., mention
tls.LogMessageUpdatedTLSProfile and
ShouldHonorClusterTLSProfile(apiServer.Spec.TLSAdherence)) so the failure text
accurately reflects the asserted condition.
🤖 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/cvo/cvo.go`:
- Around line 128-131: The pod list call uses the wrong namespace: replace the
use of external.DefaultClusterVersionName in kubeClient.CoreV1().Pods(...).List
with the CVO namespace variable (i.e. the package-level CVO namespace constant
used elsewhere) so the call targets the Cluster Version Operator namespace;
update the Pods(...).List invocation to pass that CVO namespace (rather than
external.DefaultClusterVersionName) to fix the test path.

---

Duplicate comments:
In `@test/cvo/cvo.go`:
- Around line 120-126: Before calling configV1Client.APIServers().Get in the
It("should update TLS profile") test, add an explicit environment skip guard
that verifies the test is running in a supported OpenShift/config API
environment (e.g. a helper like ensureConfigAPISupported or
isOpenShiftEnvironment) and call g.Skip with a clear reason string if not
supported; move or wrap the existing kerrors.IsNotFound handling after this
guard so the Get is only attempted in supported environments and document the
skip reason in the g.Skip message referencing APIServer/config API availability
and tlsprofile.APIServerName.
- Line 154: The failure message in the Expect assertion mismatches the check:
update the assertion's failure text to describe the TLS log check rather than an
event or "TCP profile". Modify the Expect call that checks
strings.Contains(buf.String(), tls.LogMessageUpdatedTLSProfile) to use a message
that references verifying the TLS log message (e.g., mention
tls.LogMessageUpdatedTLSProfile and
ShouldHonorClusterTLSProfile(apiServer.Spec.TLSAdherence)) so the failure text
accurately reflects the asserted condition.
🪄 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: a9721c2c-efd9-4a72-9824-857cca100719

📥 Commits

Reviewing files that changed from the base of the PR and between 96b110a and 6a8c607.

📒 Files selected for processing (3)
  • .openshift-tests-extension/openshift_payload_cluster-version-operator.json
  • pkg/tls/tls.go
  • test/cvo/cvo.go

Comment thread test/cvo/cvo.go Outdated
hongkailiu and others added 4 commits May 25, 2026 12:33
Replace fixed time.Sleep calls with wait.PollUntilContextTimeout in
Test_tlsProfileManager_EventHandlers to make tests more robust and faster.

Tests now poll for expected conditions with 50ms intervals and 5 second
timeout, completing as soon as the condition is met rather than always
waiting 100ms. This reduces flakiness from timing variations and improves
test execution time.

Changes:
- Add import for k8s.io/apimachinery/pkg/util/wait
- Replace sleep in "update event changes profile" test with polling
- Replace sleep in "update event disables profile" test with polling
- Replace sleep in "delete event falls back" test with polling
- Keep sleep for invalid profile test (negative assertion)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 25, 2026

@hongkailiu: 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-hypershift-conformance 3c8b0a1 link true /test e2e-hypershift-conformance

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

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants