Skip to content

Add option to prevent events from being emitted in default namespace#6733

Merged
negz merged 1 commit intocrossplane:mainfrom
orange-cloudfoundry:filter-event-option
Nov 19, 2025
Merged

Add option to prevent events from being emitted in default namespace#6733
negz merged 1 commit intocrossplane:mainfrom
orange-cloudfoundry:filter-event-option

Conversation

@frigaut-orange
Copy link
Copy Markdown
Contributor

@frigaut-orange frigaut-orange commented Aug 19, 2025

Description of your changes

Add a --restrict-namespaced-events option to prevent events from being emitted in the default namespace.
Can be useful when crossplane does not have full admin permissions on the cluster.

Fixes #6348

Requires crossplane-runtime#868

I have:

Need help with this checklist? See the cheat sheet.

@negz negz closed this Sep 19, 2025
@negz negz reopened this Sep 19, 2025
@negz
Copy link
Copy Markdown
Member

negz commented Sep 19, 2025

Closed and reopened to unstuck CI.

@jbw976
Copy link
Copy Markdown
Member

jbw976 commented Sep 19, 2025

Looks like most of the CI checks are failing with errors similar to:

             +go-build *failed* | internal/controller/apiextensions/composition/reconciler.go:71:70: o.EventFilterFunctions undefined (type "github.com/crossplane/crossplane/v2/internal/controller/apiextensions/controller".Options has no field or method EventFilterFunctions)

Does this PR perhaps now need a rebase? Let us know if any help with that or debugging is needed @frigaut-orange 🙏

@frigaut-orange
Copy link
Copy Markdown
Contributor Author

Hi @jbw976, the PR currently fails because crossplane/crossplane-runtime#868 is still open, and I did not update the dependency in crossplane's PR. Let me know if there is something I can do to temporarily point to my PR on crossplane-runtime (or do something less dirty 😬)

Maybe I should add a little more context to this PR: I originally published crossplane/crossplane-runtime#829 which was merged (thanks again btw), adding the possibility to use a filter function while emitting events.

However, I did not find a better way than what is suggested in this PR in order to pass this filter function as a parameter, so that controllers can use it (hence the addition of an attribute in the Options struct). This implementation should not affect existing code and should be completely retro-compatible, but please let me know if you see a better option.

@negz
Copy link
Copy Markdown
Member

negz commented Oct 7, 2025

Just approved and merged the runtime PR./\

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 29, 2025

📝 Walkthrough

Walkthrough

Adds an optional CLI flag to restrict recorded events to namespaced (non-empty, non-"default") resources and threads variadic EventFilterFunctions through event.NewAPIRecorder invocations across controller setup code so recorders can apply those filters.

Changes

Cohort / File(s) Summary
Core CLI and init
cmd/crossplane/core/core.go
Added RestrictNamespacedEvents bool to startCommand; build EventFilterFunctions when enabled (filters out objects with empty namespace or namespace == "default"); pass EventFilterFunctions into controller options.
Docs / example
contributing/guide-adding-external-secret-stores.md
Example updated to pass o.EventFilterFunctions... to event.NewAPIRecorder(...) and adjust connection publisher setup usage.
API extensions controllers
internal/controller/apiextensions/...
activationpolicy/setup.go, composition/reconciler.go, definition/reconciler.go, managed/setup.go, offered/reconciler.go, revision/reconciler.go
Updated recorder construction to call event.NewAPIRecorder(mgr.GetEventRecorderFor(name), o.EventFilterFunctions...) in each reconciler setup.
Operations controllers
internal/controller/ops/...
cronoperation/constructor.go, operation/constructor.go, watchoperation/constructor.go
Updated NewAPIRecorder(...) calls to include o.EventFilterFunctions....
Package/runtime controllers
internal/controller/pkg/...
manager/reconciler.go, revision/reconciler.go, runtime/reconciler.go
Passed o.EventFilterFunctions... to API recorder creation in multiple reconciler setups.
Protection & RBAC controllers
internal/controller/protection/usage/reconciler.go, internal/controller/rbac/...
definition/reconciler.go, provider/binding/reconciler.go, provider/roles/reconciler.go
Updated event recorder initialization to include variadic o.EventFilterFunctions... so recorders can apply the CLI-configured filters.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI Flag Parser
    participant Core as Core Init
    participant Filter as Event Filter Builder
    participant RecorderFactory as event.NewAPIRecorder
    participant Reconciler as Controller Setup/Reconciler

    CLI->>Core: parse RestrictNamespacedEvents flag
    Core->>Filter: build EventFilterFunctions (if enabled)
    alt enabled
        Filter-->>Core: [namespace non-empty && namespace != "default"]
    else disabled
        Filter-->>Core: []
    end
    Core->>RecorderFactory: NewAPIRecorder(mgr.GetEventRecorderFor(name), EventFilterFunctions...)
    RecorderFactory-->>Core: configured recorder
    Core->>Reconciler: pass recorder via WithRecorder(...)
    Reconciler->>RecorderFactory: RecordEvent(...)
    RecorderFactory->>Filter: apply filters -> accept/reject
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify correctness of namespace filter predicate (exclude empty and "default") vs. intended behavior from linked issue.
  • Ensure every modified controller passes o.EventFilterFunctions... consistently and no call sites were missed.
  • Confirm event.NewAPIRecorder signature/usage aligns with updated variadic parameters and that tests/examples updated accordingly.

Suggested reviewers

  • jbw976
  • turkenh

Thank you — let me know if you want the predicate adjusted (e.g., configurable default namespaces) or a short test plan to validate behavior.

Pre-merge checks

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ❓ Inconclusive Issue #6348 requests enabling a namespaced mode for the Crossplane controller by watching Kubernetes-native resources only within the controller's namespace and optionally adding a --namespaced flag. The PR changes implement event filtering to exclude events from the default namespace via a --restrict-namespaced-events flag, but this addresses only the event-filtering aspect rather than the primary requirement of adapting the controller to watch namespaced resources exclusively. The PR appears to be a partial or complementary solution that filters events but does not implement the core structural changes requested in the issue (watching only namespaced resources or adjusting ClusterRole permissions). Please clarify whether this PR is intended as a complete solution to #6348 or as a partial/complementary fix. If it's a stepping stone, consider documenting in the PR or issue tracker how this filtering feature relates to the larger objective of enabling true namespaced-mode operation. If this is meant to be the complete fix, the PR description should be updated to explain why event filtering alone satisfies the requirements rather than watching only namespaced resources.
✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Add option to prevent events from being emitted in default namespace" is 68 characters (within the 72-character limit) and clearly describes what the change does. The title directly corresponds to the main feature being added—a CLI flag to control event emission behavior related to the default namespace. The title is descriptive and specific, accurately reflecting the core change across the changeset.
Out of Scope Changes Check ✅ Passed The changes are focused and in-scope for the stated objective. The PR modifies the CLI to add the new RestrictNamespacedEvents flag, updates event recorder initialization across multiple controller files to use the new filtering capability, and updates the guide documentation for external secret stores to reflect the API changes. All modifications directly support the feature of filtering events from the default namespace, with no unrelated changes detected.
Description Check ✅ Passed The pull request description is clearly related to the changeset and provides meaningful context. It explains the feature being added (--restrict-namespaced-events flag), the use case (when Crossplane lacks full admin permissions), and explicitly references both the related issue (#6348) and the required runtime PR. The description is specific and informative rather than vague or generic, making it easy to understand the motivation and scope.
Breaking Changes ✅ Passed The PR adds a new field RestrictNamespacedEvents to the startCommand struct in cmd/crossplane/core/core.go. Since startCommand begins with a lowercase letter, it is an unexported struct internal to the package and is not part of the public API. The new field is an optional boolean flag with a default value, maintaining backward compatibility. The remaining changes are all in the internal/controller/ directories, which are internal implementation details. No exported public fields or flags in the apis/ or cmd/ directories have been removed, renamed, made required, or had their behavior removed. This PR introduces no breaking changes to the public API surface.
Feature Gate Requirement ✅ Passed This PR adds the --restrict-namespaced-events CLI flag to support namespaced Crossplane deployments. The check requires feature gates for "new experimental features that affect apis/** or significantly affect behavior." While this feature does significantly affect event emission behavior, it does NOT meet the criteria for requiring an experimental feature gate because: (1) it is not classified as "experimental," "alpha," or "beta" in the codebase—unlike other conditional features; (2) it does not affect the apis/** directory; and (3) it is already properly gated behind a CLI flag that defaults to false, making adoption opt-in and fully backward compatible.

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

❤️ Share

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

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: 0

🧹 Nitpick comments (3)
contributing/guide-adding-external-secret-stores.md (1)

116-117: Consider adding brief explanation of EventFilterFunctions.

The guide now shows o.EventFilterFunctions... being passed to NewAPIRecorder, but doesn't explain what event filter functions are or when developers might want to use them when adding secret store support.

Would it be helpful to add a brief note explaining that EventFilterFunctions can be used to filter which events are recorded, and mentioning the --restrict-namespaced-events flag as an example?

cmd/crossplane/core/core.go (2)

137-137: Clarify the flag help text to match implementation behavior.

The current help text says "Prevent events from being produced on resources that are not namespaced," which is a bit confusing. The implementation actually prevents events for:

  1. Cluster-scoped resources (which have empty namespace)
  2. Resources in the "default" namespace

Based on issue #6348 and the use case, would this be clearer?

-RestrictNamespacedEvents bool `default:"false" help:"Prevent events from being produced on resources that are not namespaced. Useful when crossplane does not have permissions in the default namespace."`
+RestrictNamespacedEvents bool `default:"false" help:"Prevent events from being emitted to the default namespace. Filters out cluster-scoped resources and resources in the default namespace. Useful when Crossplane lacks permissions in the default namespace."`

This makes it clearer that:

  • Events that would go to the default namespace are filtered
  • Both cluster-scoped resources (which emit to default) and explicit default namespace resources are affected

234-242: Consider adding explanatory comment for filter logic clarity.

The filter logic is correct, but a brief comment would help future maintainers understand why both empty namespace and "default" namespace are filtered:

 eventFilterFns := []event.FilterFn{}
-// If the option to restrict event emission to namespaced resources if active,
-// we create a filter function accordingly
+// Filter events that would be emitted to the default namespace.
+// Cluster-scoped resources (empty namespace) emit events to "default",
+// so we filter both empty namespace and explicit "default" namespace.
 if c.RestrictNamespacedEvents {
     eventFilterFns = append(eventFilterFns, func(obj runtime.Object, e event.Event) bool {
         m, err := kmeta.Accessor(obj)
         return (err == nil && m.GetNamespace() != "" && m.GetNamespace() != "default")
     })
 }

This clarifies the "why" behind filtering both conditions, especially the non-obvious fact that cluster-scoped resources emit events to the default namespace.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1da505f and 4ddaf8b.

📒 Files selected for processing (18)
  • cmd/crossplane/core/core.go (4 hunks)
  • contributing/guide-adding-external-secret-stores.md (1 hunks)
  • internal/controller/apiextensions/activationpolicy/setup.go (1 hunks)
  • internal/controller/apiextensions/composition/reconciler.go (1 hunks)
  • internal/controller/apiextensions/definition/reconciler.go (1 hunks)
  • internal/controller/apiextensions/managed/setup.go (1 hunks)
  • internal/controller/apiextensions/offered/reconciler.go (1 hunks)
  • internal/controller/apiextensions/revision/reconciler.go (1 hunks)
  • internal/controller/ops/cronoperation/constructor.go (1 hunks)
  • internal/controller/ops/operation/constructor.go (1 hunks)
  • internal/controller/ops/watchoperation/constructor.go (1 hunks)
  • internal/controller/pkg/manager/reconciler.go (3 hunks)
  • internal/controller/pkg/revision/reconciler.go (3 hunks)
  • internal/controller/pkg/runtime/reconciler.go (2 hunks)
  • internal/controller/protection/usage/reconciler.go (3 hunks)
  • internal/controller/rbac/definition/reconciler.go (1 hunks)
  • internal/controller/rbac/provider/binding/reconciler.go (1 hunks)
  • internal/controller/rbac/provider/roles/reconciler.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: Enforce Crossplane-specific patterns: Use crossplane-runtime/pkg/errors
for wrapping. Check variable naming (short for local scope, descriptive
for wider scope). Ensure 'return early' pattern. Verify error scoping
(declare in conditionals when possible). For nolint directives, require
specific linter names and explanations. CRITICAL: Ensure all error
messages are meaningful to end users, not just developers - avoid
technical jargon, include context about what the user was trying to do,
and suggest next steps when possible.

Files:

  • internal/controller/apiextensions/activationpolicy/setup.go
  • internal/controller/protection/usage/reconciler.go
  • internal/controller/apiextensions/offered/reconciler.go
  • internal/controller/rbac/provider/binding/reconciler.go
  • internal/controller/ops/operation/constructor.go
  • internal/controller/ops/cronoperation/constructor.go
  • internal/controller/pkg/manager/reconciler.go
  • internal/controller/ops/watchoperation/constructor.go
  • internal/controller/pkg/runtime/reconciler.go
  • internal/controller/rbac/definition/reconciler.go
  • internal/controller/apiextensions/composition/reconciler.go
  • internal/controller/rbac/provider/roles/reconciler.go
  • internal/controller/pkg/revision/reconciler.go
  • internal/controller/apiextensions/definition/reconciler.go
  • cmd/crossplane/core/core.go
  • internal/controller/apiextensions/managed/setup.go
  • internal/controller/apiextensions/revision/reconciler.go
**/internal/controller/**

⚙️ CodeRabbit configuration file

**/internal/controller/**: Review controller logic for proper reconciliation patterns, error
handling, and resource management. Pay special attention to conditions
and events: Conditions must be actionable for users (not developers),
stable/deterministic, with proper Type/Reason/Message format. Events
only when something actually happens, with specific details about what
changed. No transient errors in conditions/events. All error messages
must be meaningful to end users - include context about what
resource/operation failed and why.

Files:

  • internal/controller/apiextensions/activationpolicy/setup.go
  • internal/controller/protection/usage/reconciler.go
  • internal/controller/apiextensions/offered/reconciler.go
  • internal/controller/rbac/provider/binding/reconciler.go
  • internal/controller/ops/operation/constructor.go
  • internal/controller/ops/cronoperation/constructor.go
  • internal/controller/pkg/manager/reconciler.go
  • internal/controller/ops/watchoperation/constructor.go
  • internal/controller/pkg/runtime/reconciler.go
  • internal/controller/rbac/definition/reconciler.go
  • internal/controller/apiextensions/composition/reconciler.go
  • internal/controller/rbac/provider/roles/reconciler.go
  • internal/controller/pkg/revision/reconciler.go
  • internal/controller/apiextensions/definition/reconciler.go
  • internal/controller/apiextensions/managed/setup.go
  • internal/controller/apiextensions/revision/reconciler.go
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Ensure Markdown files are wrapped at 100 columns for consistency and
readability. Lines can be longer if it makes links more readable, but
otherwise should wrap at 100 characters. Check for proper heading
structure, clear language, and that documentation is helpful for users.

Files:

  • contributing/guide-adding-external-secret-stores.md
**/cmd/**

⚙️ CodeRabbit configuration file

**/cmd/**: Review CLI commands for proper flag handling, help text, and error
messages. Ensure commands follow Crossplane CLI conventions. Ask about
backward compatibility and user experience. CLI error messages must be
especially user-friendly - avoid internal error details, provide
actionable guidance.

Files:

  • cmd/crossplane/core/core.go
🧬 Code graph analysis (10)
internal/controller/apiextensions/offered/reconciler.go (1)
internal/controller/apiextensions/activationpolicy/setup.go (1)
  • WithRecorder (63-67)
internal/controller/ops/cronoperation/constructor.go (1)
internal/controller/ops/operation/constructor.go (1)
  • WithRecorder (67-71)
internal/controller/pkg/manager/reconciler.go (1)
internal/controller/apiextensions/activationpolicy/setup.go (1)
  • WithRecorder (63-67)
internal/controller/pkg/runtime/reconciler.go (1)
internal/controller/pkg/revision/reconciler.go (1)
  • WithRecorder (152-156)
internal/controller/rbac/definition/reconciler.go (1)
internal/controller/rbac/provider/binding/reconciler.go (1)
  • WithRecorder (94-98)
internal/controller/apiextensions/composition/reconciler.go (1)
internal/controller/apiextensions/composite/reconciler.go (1)
  • WithRecorder (287-291)
internal/controller/rbac/provider/roles/reconciler.go (1)
internal/controller/rbac/provider/binding/reconciler.go (1)
  • WithRecorder (94-98)
internal/controller/pkg/revision/reconciler.go (1)
internal/controller/pkg/manager/reconciler.go (1)
  • WithRecorder (146-150)
internal/controller/apiextensions/definition/reconciler.go (1)
internal/controller/apiextensions/activationpolicy/setup.go (1)
  • WithRecorder (63-67)
cmd/crossplane/core/core.go (4)
internal/controller/ops/controller/options.go (1)
  • Options (28-36)
internal/controller/pkg/controller/options.go (1)
  • Options (27-49)
internal/controller/rbac/controller/options.go (1)
  • Options (40-47)
internal/controller/apiextensions/controller/options.go (1)
  • Options (28-36)
🔍 Remote MCP

Based on the search results, I can provide you with relevant context about the event filtering mechanism and the linked issue. Let me gather a bit more specific information about the runtime PR dependency.## Summary of Additional Context Found

Event Filtering Mechanism Foundation

The foundational event filtering capability was introduced by frigaut-orange in crossplane-runtime PR #829 titled "Add filter functions to event recorder". This PR established the infrastructure allowing event filters to be applied at the recorder level across the Crossplane control plane.

Core Problem Addressed (Issue #6348)

This PR attempts to solve a legitimate security and operational challenge. Crossplane emits events for cluster scoped resources to the 'default' namespace. The underlying issue #6348 identifies that Kubernetes namespaces events, while most Crossplane resources (XRs, etc) are cluster scoped, creating a permission and security concern. The original issue describes problems where the controller requests cluster-wide list/watch permissions and logs forbidden errors when these are denied, leading to potential CrashLoopBackOff scenarios.

Implementation Approach: Event-Level Filtering

The PR takes an event filtering approach rather than a full namespaced controller mode. The implementation:

  1. CLI Option: Adds --restrict-namespaced-events flag to filter events at emission time
  2. Filter Logic: Creates event filters that exclude objects with empty namespaces or those in the "default" namespace
  3. Systematic Application: Applies these filters across 15+ controller setup files by passing o.EventFilterFunctions... to event.NewAPIRecorder

Dependency Status - Critical Issue

The PR currently cannot build because it depends on crossplane-runtime PR #868, which has not yet been merged. CI failures indicate that o.EventFilterFunctions is undefined in the controller options, as the runtime changes introducing this field have not been released. The PR author acknowledged this dependency issue and the implementation is flagged as backward compatible.

Architectural Context for Assessment

The approach uses a variadic event filter function pattern similar to other controller-runtime patterns in Crossplane, suggesting a design that supports multiple composable filters. However, as noted in the PR comments, Crossplane emits events for cluster scoped resources to the 'default' namespace, meaning this solution specifically addresses event emission concerns rather than the broader namespace-scoped controller capability mentioned in issue #6348.

[::web_search::]

🔇 Additional comments (10)
internal/controller/apiextensions/offered/reconciler.go (1)

149-149: LGTM - consistent event filter wiring.

The addition of o.EventFilterFunctions... to the recorder setup follows the established pattern across the codebase.

internal/controller/apiextensions/activationpolicy/setup.go (1)

40-40: LGTM - consistent event filter wiring.

The addition of o.EventFilterFunctions... follows the established pattern.

internal/controller/protection/usage/reconciler.go (1)

108-108: LGTM - consistent event filter wiring across all Usage controllers.

All three Setup functions (Usage, ClusterUsage, and LegacyUsage) correctly wire o.EventFilterFunctions... to the API recorder.

Also applies to: 123-123, 139-139

internal/controller/apiextensions/managed/setup.go (1)

43-43: LGTM - consistent event filter wiring.

The addition of o.EventFilterFunctions... follows the established pattern.

internal/controller/apiextensions/definition/reconciler.go (1)

180-180: LGTM - consistent event filter wiring.

The addition of o.EventFilterFunctions... follows the established pattern.

cmd/crossplane/core/core.go (1)

32-32: LGTM - clean integration of event filtering infrastructure.

The imports and wiring of EventFilterFunctions into the controller options is well-structured. The feature is properly gated behind the CLI flag with a safe default of false.

Also applies to: 48-48, 250-250

internal/controller/ops/operation/constructor.go (1)

46-46: LGTM - consistent event filter wiring.

The addition of o.EventFilterFunctions... follows the established pattern across all controller setups.

internal/controller/rbac/definition/reconciler.go (1)

79-79: Event filtering for cluster-scoped resources requires verification after crossplane-runtime dependency is available.

Thank you for raising this concern—your structural analysis is sound. I've confirmed that all the affected controllers reconcile cluster-scoped resources (CompositeResourceDefinition, Composition, CompositionRevision, ProviderRevision, Provider, Configuration, and Function), and as you noted, Kubernetes emits events for these to the default namespace by design.

However, I cannot fully verify the filtering behavior here because EventFilterFunctions is not defined in this codebase—it comes from the crossplane-runtime dependency (PR #868, which you've already flagged as having a build issue). Without access to that implementation, I cannot confirm:

  1. Whether the filter distinguishes between cluster-scoped and truly namespaced resources
  2. How it treats events in the default namespace
  3. Whether suppressing cluster-scoped resource events is the intended outcome

Recommendation: Once crossplane-runtime PR #868 is merged and integrated, verify that:

  • The filter logic is namespace-aware and can distinguish cluster-scoped resources from namespaced ones
  • Events for cluster-scoped resources are handled intentionally (either recorded or filtered as designed)
  • The behavior aligns with user expectations for observability of cluster-scoped resources like XRDs and Providers
internal/controller/rbac/provider/roles/reconciler.go (1)

87-87: Clarify event filtering behavior for cluster-scoped resources.

The addition of o.EventFilterFunctions... is mechanically consistent across both setup paths, which is good. However, I'd like to understand the intended behavior better:

This controller manages ClusterRoles (cluster-scoped resources) and emits events such as:

  • Line 251: Warning events when applying roles fails
  • Line 302: Warning events for individual role apply errors
  • Line 315: Normal events listing successfully applied ClusterRoles

According to the PR context, Crossplane emits events for cluster-scoped resources to the 'default' namespace. If --restrict-namespaced-events filters out events in the default namespace, would all RBAC ClusterRole events be suppressed?

This could impact users' ability to:

  • Troubleshoot RBAC permission issues
  • Audit which ClusterRoles were created or updated
  • Diagnose provider installation problems

Could you clarify whether filtering is intended for these cluster-scoped resource events, or if the filter should distinguish between namespaced and cluster-scoped resources?

Also applies to: 103-103

internal/controller/ops/cronoperation/constructor.go (1)

47-47: Let me search for information about crossplane-runtime PR #868 and verify the status of the v2.1.0-rc.0 release.

Let me search more specifically for PR #868 in crossplane-runtime.

Based on my verification, I found that the code implementation is mechanically correct and follows the established pattern used across 23+ controller files in the codebase. However, the reference to PR #868 appears to be incorrect—PR #838 ("Update Event recorder to not parse % format characters in error message") was the event-related improvement in the v2.0.0 release.

The legitimate concern remains about test coverage: the CronOperation reconciler tests contain no tests verifying that events are properly filtered when the --restrict-namespaced-events flag is enabled. This gap deserves attention since event filtering behavior could inadvertently suppress important diagnostic information.

Please verify the specific crossplane-runtime PR reference and add tests for CronOperation event filtering behavior.

Key findings:

  • o.EventFilterFunctions usage is correct and consistent across the codebase
  • ✅ go.mod shows v2.1.0-rc.0, which should include necessary event filtering support
  • ⚠️ PR #868 reference cannot be verified; the actual event recorder fix was in PR #838
  • ❌ No specific event filtering tests exist for CronOperation

Signed-off-by: François Rigaut <francois.rigaut@orange.com>
@frigaut-orange
Copy link
Copy Markdown
Contributor Author

Thanks @negz !

Rebased + fixed linting.

I thought about adding unit and/or e2e tests, but unit tests seem out of topic for this PR, and e2e test are more related to testing updates on k8s resources, so I don't know if it can apply to this PR. Let me know if there is something I can do.

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: 0

🧹 Nitpick comments (1)
cmd/crossplane/core/core.go (1)

234-242: Consider adding a comment to clarify filter behavior.

The filter logic correctly implements the intended behavior, but a more descriptive comment would help future maintainers understand what gets filtered and why.

Consider enhancing the comment:

 eventFilterFns := []event.FilterFn{}
-// If the option to restrict event emission to namespaced resources if active,
-// we create a filter function accordingly
+// If the option to restrict event emission is active, create a filter that:
+// - Excludes cluster-scoped resources (no namespace)
+// - Excludes resources in the "default" namespace
+// - Allows events for resources in other namespaces
+// This is useful when Crossplane lacks permissions in default namespace or for cluster-scoped resources.
 if c.RestrictNamespacedEvents {
     eventFilterFns = append(eventFilterFns, func(obj runtime.Object, _ event.Event) bool {
         m, err := kmeta.Accessor(obj)
         return (err == nil && m.GetNamespace() != "" && m.GetNamespace() != "default")
     })
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ddaf8b and e1cd80c.

📒 Files selected for processing (18)
  • cmd/crossplane/core/core.go (4 hunks)
  • contributing/guide-adding-external-secret-stores.md (1 hunks)
  • internal/controller/apiextensions/activationpolicy/setup.go (1 hunks)
  • internal/controller/apiextensions/composition/reconciler.go (1 hunks)
  • internal/controller/apiextensions/definition/reconciler.go (1 hunks)
  • internal/controller/apiextensions/managed/setup.go (1 hunks)
  • internal/controller/apiextensions/offered/reconciler.go (1 hunks)
  • internal/controller/apiextensions/revision/reconciler.go (1 hunks)
  • internal/controller/ops/cronoperation/constructor.go (1 hunks)
  • internal/controller/ops/operation/constructor.go (1 hunks)
  • internal/controller/ops/watchoperation/constructor.go (1 hunks)
  • internal/controller/pkg/manager/reconciler.go (3 hunks)
  • internal/controller/pkg/revision/reconciler.go (3 hunks)
  • internal/controller/pkg/runtime/reconciler.go (2 hunks)
  • internal/controller/protection/usage/reconciler.go (3 hunks)
  • internal/controller/rbac/definition/reconciler.go (1 hunks)
  • internal/controller/rbac/provider/binding/reconciler.go (1 hunks)
  • internal/controller/rbac/provider/roles/reconciler.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/controller/pkg/manager/reconciler.go
  • internal/controller/rbac/provider/binding/reconciler.go
  • internal/controller/pkg/revision/reconciler.go
  • internal/controller/apiextensions/definition/reconciler.go
  • internal/controller/rbac/definition/reconciler.go
  • internal/controller/ops/cronoperation/constructor.go
  • contributing/guide-adding-external-secret-stores.md
  • internal/controller/pkg/runtime/reconciler.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: Enforce Crossplane-specific patterns: Use crossplane-runtime/pkg/errors
for wrapping. Check variable naming (short for local scope, descriptive
for wider scope). Ensure 'return early' pattern. Verify error scoping
(declare in conditionals when possible). For nolint directives, require
specific linter names and explanations. CRITICAL: Ensure all error
messages are meaningful to end users, not just developers - avoid
technical jargon, include context about what the user was trying to do,
and suggest next steps when possible.

Files:

  • internal/controller/apiextensions/offered/reconciler.go
  • internal/controller/rbac/provider/roles/reconciler.go
  • internal/controller/ops/operation/constructor.go
  • internal/controller/apiextensions/managed/setup.go
  • internal/controller/apiextensions/composition/reconciler.go
  • internal/controller/ops/watchoperation/constructor.go
  • internal/controller/apiextensions/revision/reconciler.go
  • internal/controller/protection/usage/reconciler.go
  • internal/controller/apiextensions/activationpolicy/setup.go
  • cmd/crossplane/core/core.go
**/internal/controller/**

⚙️ CodeRabbit configuration file

**/internal/controller/**: Review controller logic for proper reconciliation patterns, error
handling, and resource management. Pay special attention to conditions
and events: Conditions must be actionable for users (not developers),
stable/deterministic, with proper Type/Reason/Message format. Events
only when something actually happens, with specific details about what
changed. No transient errors in conditions/events. All error messages
must be meaningful to end users - include context about what
resource/operation failed and why.

Files:

  • internal/controller/apiextensions/offered/reconciler.go
  • internal/controller/rbac/provider/roles/reconciler.go
  • internal/controller/ops/operation/constructor.go
  • internal/controller/apiextensions/managed/setup.go
  • internal/controller/apiextensions/composition/reconciler.go
  • internal/controller/ops/watchoperation/constructor.go
  • internal/controller/apiextensions/revision/reconciler.go
  • internal/controller/protection/usage/reconciler.go
  • internal/controller/apiextensions/activationpolicy/setup.go
**/cmd/**

⚙️ CodeRabbit configuration file

**/cmd/**: Review CLI commands for proper flag handling, help text, and error
messages. Ensure commands follow Crossplane CLI conventions. Ask about
backward compatibility and user experience. CLI error messages must be
especially user-friendly - avoid internal error details, provide
actionable guidance.

Files:

  • cmd/crossplane/core/core.go
🧬 Code graph analysis (1)
internal/controller/protection/usage/reconciler.go (1)
internal/controller/apiextensions/activationpolicy/setup.go (1)
  • WithRecorder (63-67)
🔇 Additional comments (11)
internal/controller/ops/operation/constructor.go (1)

46-46: LGTM - Event filter functions correctly propagated.

The variadic o.EventFilterFunctions... parameter is properly passed to NewAPIRecorder, enabling event filtering for the Operation controller when configured.

internal/controller/protection/usage/reconciler.go (1)

108-108: LGTM - Consistent event filter propagation across all Usage controller variants.

All three setup functions (SetupUsage, SetupClusterUsage, SetupLegacyUsage) correctly pass o.EventFilterFunctions... to the API recorder, ensuring consistent event filtering behavior across usage-related controllers.

Also applies to: 123-123, 139-139

internal/controller/ops/watchoperation/constructor.go (1)

102-102: LGTM - Event filtering enabled for WatchOperation controller.

The variadic o.EventFilterFunctions... parameter is correctly passed through to the recorder.

cmd/crossplane/core/core.go (2)

137-137: Verify the intended behavior for cluster-scoped resources.

The flag description states it prevents events from being emitted for resources "that are not namespaced." This might be ambiguous - does it mean:

  1. Resources without a namespace (cluster-scoped), OR
  2. Resources not in a specific namespace?

The implementation (lines 238-241) filters out events for cluster-scoped resources (empty namespace) AND resources in the "default" namespace. Could you clarify whether filtering cluster-scoped resources is intentional? Based on issue #6348, this might be desired to reduce permission requirements, but it would be helpful to make this explicit in the flag description.

Consider updating the help text to be more explicit:

-RestrictNamespacedEvents bool `default:"false" help:"Prevent events from being produced on resources that are not namespaced. Useful when crossplane does not have permissions in the default namespace."`
+RestrictNamespacedEvents bool `default:"false" help:"Prevent events from being emitted for cluster-scoped resources (no namespace) and resources in the 'default' namespace. Useful when Crossplane does not have full cluster admin permissions."`

238-241: Event filtering logic looks correct but could benefit from verification.

The filter returns true (allow event) when:

  • No error accessing metadata AND
  • Namespace is not empty (not cluster-scoped) AND
  • Namespace is not "default"

This means events are filtered out (not emitted) for:

  • Objects where metadata access fails
  • Cluster-scoped resources (empty namespace)
  • Resources in the "default" namespace

This aligns with the PR objectives to avoid emitting events when Crossplane lacks permissions. However, it would be valuable to verify that filtering cluster-scoped resource events doesn't break any expected functionality.

Would you like me to generate a script to search for any code that might rely on events being emitted for cluster-scoped resources?

internal/controller/rbac/provider/roles/reconciler.go (1)

87-87: LGTM - Event filtering consistently applied to both RBAC reconciler paths.

Both reconciler initialization paths (with and without AllowClusterRole) correctly pass o.EventFilterFunctions... to the API recorder.

Also applies to: 103-103

internal/controller/apiextensions/composition/reconciler.go (1)

71-71: LGTM - Event filtering enabled for Composition revision controller.

The change correctly propagates event filter functions to the API recorder.

internal/controller/apiextensions/revision/reconciler.go (1)

62-62: LGTM - Event filtering correctly wired for CompositionRevision controller.

The variadic o.EventFilterFunctions... parameter is properly passed through.

internal/controller/apiextensions/offered/reconciler.go (1)

149-149: LGTM - Event filtering enabled for composite resource claim controller.

The change correctly passes event filter functions to the API recorder, completing the consistent propagation across all controllers.

internal/controller/apiextensions/activationpolicy/setup.go (1)

40-40: Let me search for information about crossplane-runtime v2.1.0-rc.0 and whether it includes the event filter functions support:

Based on comprehensive verification, the original review comment is accurate and grounded in reality.

Code addition requires crossplane-runtime PR #868 to be merged first.

The current NewAPIRecorder in crossplane-runtime accepts only a single record.EventRecorder parameter, but the code at line 40 passes o.EventFilterFunctions... as an additional argument. This will not compile.

The go.mod specifies github.com/crossplane/crossplane-runtime/v2 v2.1.0-rc.0, which does not include the multi-parameter NewAPIRecorder signature needed to support event filter functions. The changes correctly follow the pattern used across 22+ controller files, but all depend on the runtime PR being merged and the dependency updated.

Clarify the dependency resolution plan before merging:

  • Will this PR wait for crossplane-runtime PR #868 to be merged and a new version released?
  • Or will go.mod temporarily reference a fork/branch containing the runtime changes?
internal/controller/apiextensions/managed/setup.go (1)

43-43: Confirm the pattern is consistent and working as intended; verify event filtering has adequate test coverage and documentation.

The code change in managed/setup.go line 43 is correct—it properly integrates the event filtering pattern used across other controllers (activationpolicy, revision, pkg runtime, etc.). The variadic parameter usage is sound.

However, your concerns about testing and user impact are valid and worth addressing at the feature level:

  1. Testing gap confirmed: The event filtering logic in core.go (which enables RestrictNamespacedEvents to filter out cluster-scoped and default namespace events) has no corresponding tests in the managed controller tests or elsewhere. The testRecorder in reconciler tests only records events but doesn't validate the filtering mechanism.

  2. User visibility concern confirmed: When events are filtered, they silently disappear from kubectl describe and kubectl get events outputs. There's no logging to help users understand why. While RestrictNamespacedEvents has CLI help text, end users won't see documentation about filtered events when troubleshooting.

The change itself is sound and consistent. Before merging, it would be valuable to verify that the EventFilterFunctions feature across all controllers has adequate test coverage for the filtering behavior and considers adding logging/documentation to help users understand when and why events are suppressed.

@negz negz merged commit 9da017c into crossplane:main Nov 19, 2025
40 of 45 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 30, 2026
7 tasks
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.

Enable a namespaced mode for the crossplane controller pod

3 participants