Skip to content

[wip]Fix empty lister for API server#1397

Open
hongkailiu wants to merge 18 commits into
openshift:mainfrom
hongkailiu:pr1338-fix-empty-lister-simple
Open

[wip]Fix empty lister for API server#1397
hongkailiu wants to merge 18 commits into
openshift:mainfrom
hongkailiu:pr1338-fix-empty-lister-simple

Conversation

@hongkailiu
Copy link
Copy Markdown
Member

@hongkailiu hongkailiu commented Jun 1, 2026

Will rebase after #1338 gets in

Summary by CodeRabbit

  • New Features

    • New CLI flags to override TLS minimum version and cipher suites; a central TLS profile manager applies cluster APIServer profiles with runtime updates and ensures overrides take final precedence. Startup now ensures APIServer readiness and metrics serving uses the managed TLS settings.
  • Tests

    • Added comprehensive tests for profile application, override precedence, event-driven updates, and failure/recovery.
  • Chores

    • Updated Go module dependencies.

DavidHurta and others added 17 commits May 27, 2026 16:10
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>
# Conflicts:
#	pkg/cvo/cvo.go
#	pkg/start/start.go
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>
Initializing ProfileManager before the APIServer informer syncs causes
the lister to be empty, forcing CVO to use library default TLS settings
instead of the cluster's configured TLS profile from the APIServer resource.
This creates inconsistency where CVO starts with different TLS settings
than what the cluster administrator configured, potentially causing
connection issues or security policy violations.

Move ProfileManager initialization to after informer sync in start.go
to ensure the cluster's actual TLS profile is applied from the start.

Co-authored-by: Claude Code <claude@anthropic.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
@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 Jun 1, 2026
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Jun 1, 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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: fa5234ac-bd06-4d74-bf41-97fc60de0e18

📥 Commits

Reviewing files that changed from the base of the PR and between 3a50eca and f89bd0c.

📒 Files selected for processing (2)
  • pkg/cvo/cvo.go
  • pkg/start/start.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/start/start.go
  • pkg/cvo/cvo.go

Walkthrough

This PR implements event-driven TLS profile management for the cluster-version-operator. It adds CLI flags for TLS overrides (--tls-min-version, --tls-cipher-suites), creates a ProfileManager that watches APIServer resources and caches TLS configuration, integrates this manager into the operator and metrics server, and wires initialization at startup with proper precedence: secure defaults → central profile → explicit overrides.

Changes

TLS Profile Management Integration

Layer / File(s) Summary
CLI flags and TLS options contract
cmd/cluster-version-operator/start.go, pkg/tls/tls.go
CLI flags --tls-min-version (string) and --tls-cipher-suites (string slice) are registered with dynamic help text; Options type with MinVersionOverride and CipherSuitesOverride fields provides CreateOverrides() to parse/validate into Settings structure and GetOverrides() to retrieve parsed settings.
TLS package and types
pkg/tls/tls.go
Adds ProfileManager, Settings, and Options types with imports and basic structure for event-driven profile management.
ProfileManager construction & event handlers
pkg/tls/tls.go
NewProfileManager resolves initial settings, registers APIServer informer add/update/delete handlers, and updates cached apply function when TLS-relevant fields change.
Profile resolution and ApplySettings logic
pkg/tls/tls.go
resolveTLSProfile creates an apply function from APIServer TLS profile when adherence is enabled; ApplySettings normalizes via crypto.SecureTLSConfig, applies central profile under lock, then applies explicit overrides last.
Options parsing & overrides validation
pkg/tls/tls.go
Options.CreateOverrides() validates MinVersionOverride and CipherSuitesOverride with cliflag helpers and produces a Settings object or errors.
ProfileManager tests
pkg/tls/tls_test.go
Comprehensive tests with informer helpers covering built-in and custom profiles, overrides validation, precedence semantics, informer-driven updates/deletes, initialization and runtime error recovery, change detection, and adherence variations.
CVO operator TLS integration
pkg/cvo/cvo.go
Operator gains profileMgr field; New() accepts tlsProfileMgr *cvotls.ProfileManager and assigns it; new Operator.ApplySettings() returns the manager's apply function.
Metrics server TLS callback
pkg/cvo/metrics.go
RunMetrics gained an applySettings func(config *tls.Config) parameter; per-handshake GetConfigForClient now calls applySettings(config) before returning TLS config.
Startup orchestration and initialization
pkg/start/start.go
Options.ValidateAndComplete creates and validates TLS overrides and logs them; Run forces APIServer lister sync before feature gate processing; NewControllerContext creates ProfileManager with APIServer informer and overrides, passes it to cvo.New(), and threads ApplySettings into metrics initialization.
Dependency updates
go.mod
Bumped test deps, added github.com/openshift/controller-runtime-common, moved k8s.io/component-base to direct require, and added/updated several indirect modules used by TLS/profile utilities and tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title '[wip]Fix empty lister for API server' is vague and does not clearly convey the scope of changes, which include comprehensive TLS profile management infrastructure, CLI flags, and event-driven configuration. Consider a more descriptive title that reflects the primary changes, such as 'Add TLS profile manager with CLI overrides' or similar, to better communicate the changeset's main purpose.
Test Structure And Quality ❓ Inconclusive The custom check targets Ginkgo test code (It blocks, BeforeEach/AfterEach), but the PR adds standard Go testing code (t.Run patterns) without Ginkgo. Clarify if check applies to standard Go tests or Ginkgo. If standard Go: Test_tlsProfileManager_EventHandlers tests 5 behaviors; uses time.Sleep instead of timeouts.
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 93.75% 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 The PR adds standard Go tests in pkg/tls/tls_test.go using testing.T, not Ginkgo framework. No Ginkgo tests were added or modified, so this check is not applicable.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests were added. The PR adds unit tests in pkg/tls/tls_test.go (standard Go testing package), not Ginkgo e2e tests, so the check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added in this PR. The new test file (pkg/tls/tls_test.go) contains only standard Go unit tests (using testing.T), not Ginkgo-style tests (It/Describe/Context/When).
Topology-Aware Scheduling Compatibility ✅ Passed No scheduling constraints, deployment manifests, or pod specifications are added or modified. Changes are limited to TLS profile management logic in operator code.
Ote Binary Stdout Contract ✅ Passed PR introduces no process-level stdout writes; all logging occurs in non-process-level code (constructors, handlers) via klog which respects existing stderr configuration.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests added. Only standard Go unit tests in pkg/tls/tls_test.go with no IPv4 assumptions or external connectivity requirements.
No-Weak-Crypto ✅ Passed No weak crypto (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB), custom crypto implementations, or insecure secret comparisons found. Uses only Go stdlib and approved OpenShift libraries.
Container-Privileges ✅ Passed PR modifies only Go source files and go.mod; no Kubernetes manifests were changed. No privileged container settings were added or modified.
No-Sensitive-Data-In-Logs ✅ Passed No sensitive data (passwords, tokens, keys, PII) is logged. Only standard TLS configuration details (version numbers, cipher suite names) are logged, which are public security information.

✏️ 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.

@hongkailiu hongkailiu marked this pull request as ready for review June 1, 2026 20:21
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Jun 1, 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 Jun 1, 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.

🧹 Nitpick comments (3)
pkg/tls/tls_test.go (2)

41-44: ⚡ Quick win

Assert that the cache actually synced.

WaitForCacheSync's result is discarded, so if sync fails (e.g. the ctx timeout fires) the helper returns an informer with an empty lister and downstream tests fail in confusing, indirect ways. Given this PR specifically targets the empty-lister problem, verifying sync here makes failures explicit.

♻️ Proposed change
 	informerFactory.Start(ctx.Done())
-	informerFactory.WaitForCacheSync(ctx.Done())
+	for typ, synced := range informerFactory.WaitForCacheSync(ctx.Done()) {
+		if !synced {
+			t.Fatalf("failed to sync informer cache for %v", typ)
+		}
+	}
 
 	return apiServerInformer, fakeClient, cancel
🤖 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 41 - 44, The test currently starts
informers but ignores informerFactory.WaitForCacheSync's return value; capture
the sync result (e.g. syncMap := informerFactory.WaitForCacheSync(ctx.Done()) or
syncOK := informerFactory.WaitForCacheSync(ctx.Done()) and assert that the
expected informer(s) synced (for example verify all values in the returned map
are true, or the single bool is true) and fail the test with t.Fatalf or
t.Fatalf-like helper if sync did not complete; also ensure the created ctx
cancel is invoked (defer cancel()) so the timeout is properly released. Use the
existing symbols ctx, cancel, informerFactory.Start and
informerFactory.WaitForCacheSync to locate where to add the checks.

434-435: ⚡ Quick win

Replace fixed time.Sleep waits with bounded polling to avoid flakiness.

The event-driven assertions rely on a fixed time.Sleep(100 * time.Millisecond) to let the informer process each update/delete. On loaded CI this is a classic source of flaky failures (and, conversely, wastes time when processing is fast). The same pattern repeats at Lines 469, 494, and 511. Prefer polling the manager state with a timeout (e.g. k8s.io/apimachinery/pkg/util/wait PollUntilContextTimeout) until the expected MinVersion is observed.

Note also that the informer's lifetime is bounded by the 5*time.Second context timeout in setupInformerWithClient; if the cumulative sub-test runtime ever exceeds it, later events silently stop propagating. Polling against the manager rather than sleeping makes the intent explicit and decouples correctness from wall-clock timing.

🤖 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 434 - 435, Replace the fixed sleeps
(time.Sleep(100 * time.Millisecond)) used to wait for informer events with
bounded polling against the manager state: instead of sleeping in the tests that
call setupInformerWithClient, use a context-aware poll (e.g.
k8s.io/apimachinery/pkg/util/wait.PollImmediateWithContext or
PollUntilContextTimeout) to repeatedly check the manager/handler's observed
MinVersion until it equals the expected value or the timeout elapses; update the
four occurrences (including the spots that assert MinVersion after
update/delete) to poll with a short interval and a conservative timeout that
fits within setupInformerWithClient's 5*time.Second context, and add the
required import for wait.
pkg/cvo/cvo.go (1)

138-138: 💤 Low value

Unused field apiServerLister added to Operator struct.

This field is added but never assigned in the constructor. If it's for future use, consider adding it when needed to avoid dead code.

🤖 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/cvo/cvo.go` at line 138, The Operator struct contains an unused field
apiServerLister of type configlistersv1.APIServerLister; remove this field from
the Operator struct to avoid dead code, or if it was intended to be used, wire
it up in the constructor (e.g., in NewOperator) by accepting/obtaining a
configlistersv1.APIServerLister and assigning it to o.apiServerLister so the
field is actually initialized and used.
🤖 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.

Nitpick comments:
In `@pkg/cvo/cvo.go`:
- Line 138: The Operator struct contains an unused field apiServerLister of type
configlistersv1.APIServerLister; remove this field from the Operator struct to
avoid dead code, or if it was intended to be used, wire it up in the constructor
(e.g., in NewOperator) by accepting/obtaining a configlistersv1.APIServerLister
and assigning it to o.apiServerLister so the field is actually initialized and
used.

In `@pkg/tls/tls_test.go`:
- Around line 41-44: The test currently starts informers but ignores
informerFactory.WaitForCacheSync's return value; capture the sync result (e.g.
syncMap := informerFactory.WaitForCacheSync(ctx.Done()) or syncOK :=
informerFactory.WaitForCacheSync(ctx.Done()) and assert that the expected
informer(s) synced (for example verify all values in the returned map are true,
or the single bool is true) and fail the test with t.Fatalf or t.Fatalf-like
helper if sync did not complete; also ensure the created ctx cancel is invoked
(defer cancel()) so the timeout is properly released. Use the existing symbols
ctx, cancel, informerFactory.Start and informerFactory.WaitForCacheSync to
locate where to add the checks.
- Around line 434-435: Replace the fixed sleeps (time.Sleep(100 *
time.Millisecond)) used to wait for informer events with bounded polling against
the manager state: instead of sleeping in the tests that call
setupInformerWithClient, use a context-aware poll (e.g.
k8s.io/apimachinery/pkg/util/wait.PollImmediateWithContext or
PollUntilContextTimeout) to repeatedly check the manager/handler's observed
MinVersion until it equals the expected value or the timeout elapses; update the
four occurrences (including the spots that assert MinVersion after
update/delete) to poll with a short interval and a conservative timeout that
fits within setupInformerWithClient's 5*time.Second context, and add the
required import for wait.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: c7d6ad8a-f3f0-4d83-8653-6f362d24253d

📥 Commits

Reviewing files that changed from the base of the PR and between 8a13554 and 82b6d6f.

⛔ 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 (7)
  • cmd/cluster-version-operator/start.go
  • go.mod
  • pkg/cvo/cvo.go
  • pkg/cvo/metrics.go
  • pkg/start/start.go
  • pkg/tls/tls.go
  • pkg/tls/tls_test.go

@hongkailiu
Copy link
Copy Markdown
Member Author

Repeating the steps in #1338 (comment):

$ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1397/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-operator/2061543740815708160/artifacts/e2e-agnostic-operator/gather-extra/artifacts/pods/openshift-cluster-version_cluster-version-operator-799dd575f7-rs6kk_cluster-version-operator.log | grep tls.go
I0601 20:49:38.088660       1 tls.go:109] Synced cached TLS profile
I0601 20:49:38.088844       1 tls.go:109] Synced cached TLS profile

@hongkailiu hongkailiu force-pushed the pr1338-fix-empty-lister-simple branch from 82b6d6f to 3a50eca Compare June 1, 2026 21:46
Follow up the issue from openshift#1338 (comment):

The right way to call things out for `InformerFactory`: e.g., [processInitialFeatureGate](https://github.com/openshift/cluster-version-operator/blob/c1dc3e8f0b7c366d45177af391c4f888059b446d/pkg/start/start.go#L269-L280),

* `Lister()` such as `configInformerFactory.Config().V1().FeatureGates().Lister()`
* `Start()` such as `configInformerFactory.Start(ctx.Done())`
* `WaitForCacheSync*()` such as `configInformerFactory.WaitForCacheSync(ctx.Done())`

The lister is ready to retrieve instance, e.g., via `Get()`.

```console
$ git --no-pager log --pretty=oneline -1
6a8c607 (HEAD -> pr1338-e2e-6a8c6072b2f4e45fe5ab9269b4efa96d4608b0d0) make update
```

The issue with the commit above:

```
start.go: Line 190
func (o *Options) Run(ctx context.Context) error

* Line 216
  o.NewControllerContext()
	* Line 650
      cvo, err := cvo.New() where Lister() and cvotls.NewProfileManager(apiServerInformer, tlsOverrides) are called where apiServerInformer.Lister().Get(tlsprofile.APIServerName) is called

* Line 221
  o.run()
	* start and sync
```

The issue is that `get()` is called in the wrong order: `Lister()`, `Get()`, `Start()` and `Sync()`. It explains `apiserver.config.openshift.io "cluster" not found` from openshift#1338 (comment). In short, we need the informer factory is ready before initializing the TLS profile manager.

The fix is moving `NewProfileManager()` from `cvo.go` to `start.go` and the instance passes to `cvo.go`.

Note that the same `configInformerFactory` instance is started and synced in `o.processInitialFeatureGate`. The only thing missing is invoking its `List()` ahead of it.
Once it is done, we do not need to do those again in `cvo.go`.

I prefer the current fix over the reverted one because the code is simpler this way. `cvo` does not need to have the fields such as the informer and the overrides that actually belong to the TLS profile manager.
@hongkailiu hongkailiu force-pushed the pr1338-fix-empty-lister-simple branch from 3a50eca to f89bd0c Compare June 1, 2026 21:57
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Jun 2, 2026

@hongkailiu: all tests passed!

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.

@hongkailiu
Copy link
Copy Markdown
Member Author

Take this job: Still good.

$ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1397/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-operator/2061567949415649280/artifacts/e2e-agnostic-operator/gather-extra/artifacts/pods/openshift-cluster-version_cluster-version-operator-5d77bb76d6-2gfpb_cluster-version-operator.log | grep tls.go
I0601 22:25:57.217822       1 tls.go:109] Synced cached TLS profile
I0601 22:25:57.217929       1 tls.go:109] Synced cached TLS profile

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. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants