Skip to content

refactor: separate Config process exclusions from validation data sync - #4784

Open
anneheartrecord wants to merge 2 commits into
open-policy-agent:masterfrom
anneheartrecord:fix/4775-config-exclusions-vs-data-sync
Open

anneheartrecord wants to merge 2 commits into
open-policy-agent:masterfrom
anneheartrecord:fix/4775-config-exclusions-vs-data-sync

Conversation

@anneheartrecord

Copy link
Copy Markdown

What this PR does / why we need it:

setupControllers always built the cache-manager registrar, the sync metrics cache, the cache manager and the expectations pruner, and controller.AddToManager always registered the cache manager runnable and the sync controller. #4423 made that stack tolerate a missing constraint client via a no-op CFDataClient, which unblocked the mutation-webhook startup fix but left mutation-only pods carrying validation data-sync infrastructure only so the Config controller could update process exclusions.

This splits the two responsibilities of the Config resource apart.

pkg/operations/capabilities.go names the four capabilities the wiring depends on:

predicate gates
NeedsValidationDataSync cache manager, its registrar, sync controller, expectations pruner
NeedsProcessExclusions the excluder the admission/audit/sync paths read
NeedsGenerateConfigNotifications Config-triggered CT reconciliation for VAP scope
NeedsConfigReconciliation whether the Config controller runs at all

With those in place:

  • setupControllers only builds the registrar, sync metrics cache, cache manager and pruner when an operation syncs validation data. Mutation-webhook-only no longer instantiates a no-op CFDataClient or registers data watches.
  • AddToManager only adds the cache manager runnable and the sync controller when a cache manager exists.
  • Dependencies.Validate fails setup with a message naming the assigned operations when the plan does not match them — a missing cache manager / sync channel / watch manager for data-sync operations, a cache manager built for operations that do not sync, or a missing process excluder. Previously a mismatch would have surfaced as a nil dereference somewhere downstream.
  • The Config reconciler takes the *process.Excluder directly. With a cache manager it still swaps through ExcludeProcesses so the re-list scheduling is unchanged; without one it replaces the excluder in place and skips UpsertSource entirely.
  • The Config controller is not registered when nothing in the pod reads the Config resource (subsets of mutation-controller / mutation-status). The readiness tracker stops expecting Config for exactly that case, so --operation=mutation-status does not wait forever on an observation that can no longer arrive.

Audit, validating webhook and generate keep their current behavior: audit and webhook still sync referential data, generate still gets Config-triggered VAP scope reconciliation, and ConfigPodStatus is still written for every operation set that reconciles Config. Default all-operations behavior is unchanged.

NeedsValidationDataSync is defined in terms of HasValidationOperations rather than re-deriving the operation list, so status-only pods drop the cache manager, sync controller and pruner as soon as #4770 / #4780 lands — no change needed here.

Which issue(s) this PR fixes:
Fixes #4775

Special notes for your reviewer:

Tests:

  • pkg/operations/capabilities_test.go walks each capability across the operation sets that matter (audit, validating webhook, mutation-webhook-only, mutation-status-only, mutation-controller-only, generate-only, all).
  • pkg/controller/controller_test.go covers Dependencies.Validate, including the case where a cache manager is handed to operations that do not sync data.
  • pkg/controller/config/config_capability_test.go pins the registration gate (mutation-status-only registers nothing even with no dependencies set; mutation-webhook still errors on a missing excluder) and reconciles a Config with a nil cache manager, asserting the mutation-webhook exclusions land and ConfigPodStatus is still written. Reverting either gate fails these.

I deliberately left main_test.go alone — #4779 and #4780 are both creating it, and #4776 covers the table-driven setupControllers matrix.

This touches main.go alongside #4777 / #4778 / #4779 / #4780, but in a different block; happy to rebase in whatever order you merge them.

setupControllers always built the cache-manager registrar, the sync metrics
cache, the cache manager and the expectations pruner, and AddToManager always
registered the cache manager runnable and the sync controller. Since open-policy-agent#4423 the
Config controller tolerated a missing constraint client through a no-op
CFDataClient, so mutation-webhook-only pods carried the whole validation
data-sync stack for the sole purpose of updating process exclusions.

Name the capabilities the wiring actually depends on in pkg/operations and let
each of them decide independently:

- the cache manager, its registrar, the sync controller and the expectations
  pruner are only built when an operation syncs validation data;
- the Config reconciler takes the process excluder directly, so exclusions are
  applied without a cache manager behind them and syncOnly is skipped;
- the Config controller is not registered at all when nothing in the pod reads
  the Config resource, and readiness stops waiting on it in that case;
- Dependencies.Validate rejects a plan that does not match the assigned
  operations instead of letting it surface as a nil dereference later.

NeedsValidationDataSync is derived from HasValidationOperations, so status-only
pods drop the stack as soon as open-policy-agent#4770 lands.

Signed-off-by: Charles Cheng <charlescheng@rezona.ai>
@anneheartrecord
anneheartrecord requested a review from a team as a code owner August 25, 2026 11:49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0c10b5500

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +50 to +54
func NeedsConfigReconciliation() bool {
return NeedsProcessExclusions() ||
NeedsValidationDataSync() ||
NeedsGenerateConfigNotifications()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep reconciling readiness stats on mutation-only pods

When a pod is assigned only mutation-controller or mutation-status, every predicate here is false, so the Config controller is skipped even though its reconciler is also the sole consumer of Config.spec.readiness.statsEnabled. Consequently, enabling readiness stats no longer calls Tracker.EnableStats() on these pods, and the mutation expectation diagnostics in statsPrinter remain disabled precisely when their readiness is stuck. Include this Config responsibility in the reconciliation capability or handle the setting separately.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed - mutation.Enabled() covers mutation-status and mutation-controller, so those pods do run a tracker, statsPrinter still reports their mutator expectations, and this reconciler is the only thing that reads spec.readiness.statsEnabled.

Fixed in 2a81529. Readiness stats is now its own capability and NeedsConfigReconciliation() includes it, so the Config controller registers again on mutation-only pods. Dependencies.Validate correspondingly requires the process excluder wherever that controller registers, since the reconciler needs somewhere to put the exclusions it parses.

Side effect worth calling out: NeedsConfigReconciliation() is now true for every operation set. The gating the issue asks for is unaffected - the cache manager, registrar, sync controller and pruner are still only built for validation data sync, and mutation-webhook-only still gets exclusions without a no-op data client - but no pod can skip the Config controller until the readiness stats switch moves out of it. I kept the predicate rather than deleting it so that stays visible; happy to drop it if you would rather not carry an always-true one.

The Config reconciler is the only consumer of
Config.spec.readiness.statsEnabled, and every operation set gets a readiness
tracker - mutation-only pods included, whose mutator expectations are exactly
what that verbose logging reports. Skipping Config reconciliation on pods that
neither exclude processes nor sync data therefore silenced the readiness
diagnostics precisely where they are needed.

Name the readiness stats switch as its own capability and fold it into
NeedsConfigReconciliation, which makes Config reconciliation unconditional
today. Dependencies.Validate now demands the process excluder wherever the
Config controller registers, since the reconciler needs somewhere to put the
exclusions it parses.

Signed-off-by: Charles Cheng <charlescheng@rezona.ai>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Several gates remain effectively unconditional or depend on an unmerged PR, leaving stated operation-specific behavior unmet.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Separates Config process exclusions from validation data-sync infrastructure.

Changes:

  • Adds operation capability predicates and dependency validation.
  • Conditionally initializes data-sync components.
  • Supports Config reconciliation without a cache manager and adds tests.
File summaries
File Description
main.go Gates data-sync component construction.
pkg/controller/controller.go Validates dependencies and conditionally registers sync components.
pkg/controller/controller_test.go Tests dependency validation.
pkg/controller/config/config_controller.go Separates exclusions from cache-manager synchronization.
pkg/controller/config/config_controller_test.go Updates reconciler setup.
pkg/controller/config/config_capability_test.go Tests cache-manager-free reconciliation.
pkg/operations/capabilities.go Defines operation capability predicates.
pkg/operations/capabilities_test.go Tests capability combinations.
pkg/operations/testing.go Adds operation assignment test support.
pkg/readiness/ready_tracker.go Gates Config readiness expectations.
Review details
  • Files reviewed: 10/10 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread main.go
events chan event.GenericEvent
cm *cachemanager.CacheManager
)
if operations.NeedsValidationDataSync() {
Comment on lines +25 to +26
func NeedsValidationDataSync() bool {
return HasValidationOperations()
Comment on lines +62 to +66
func NeedsConfigReconciliation() bool {
return NeedsProcessExclusions() ||
NeedsValidationDataSync() ||
NeedsGenerateConfigNotifications() ||
NeedsReadinessStats()

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Separate Config process exclusions from validation data-sync dependencies

2 participants