Skip to content

ROX-33799: replace detector.New() with builder pattern#19741

Open
Stringy wants to merge 1 commit intomasterfrom
giles/ROX-33799-refactor-detector-constructor
Open

ROX-33799: replace detector.New() with builder pattern#19741
Stringy wants to merge 1 commit intomasterfrom
giles/ROX-33799-refactor-detector-constructor

Conversation

@Stringy
Copy link
Copy Markdown
Contributor

@Stringy Stringy commented Apr 1, 2026

Description

The 14-parameter positional constructor was hard to read and extend. Replaced with a Builder struct and chained WithX() methods as described in the ticket. Exported the ClusterIDProvider interface (formerly clusterIDPeekWaiter) to support the exported Builder type.

There may be some more refactoring possible here - I thought perhaps creating a StoreProvider interface in common/store so that the builder can be simplified to accept WithStoreProvider rather than many individual WithXStore methods.

User-facing documentation

Testing and quality

  • the change is production ready: the change is GA, or otherwise the functionality is gated by a feature flag
  • CI results are inspected

Automated testing

  • added unit tests
  • added e2e tests
  • added regression tests
  • added compatibility tests
  • modified existing tests

How I validated my change

This is a superficial refactor to ease code readability; build & unit tests are enough validation.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Consider having Build return (Detector, error) and validating that all required dependencies (e.g., clusterID, enforcer, stores, label providers) are set, rather than silently constructing a detectorImpl with nil fields.
  • Now that ClusterIDProvider is exported and used as part of the builder’s public API, it may be worth adding clarification (in comments or type naming) about whether Get vs GetNoWait have any ordering/initialization guarantees that callers must respect.
  • If the longer-term plan is to introduce a StoreProvider to simplify the builder, you might want to leave a TODO near the multiple With*Store methods in builder.go to capture that intended follow-up refactor in context.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider having `Build` return `(Detector, error)` and validating that all required dependencies (e.g., `clusterID`, `enforcer`, stores, label providers) are set, rather than silently constructing a `detectorImpl` with nil fields.
- Now that `ClusterIDProvider` is exported and used as part of the builder’s public API, it may be worth adding clarification (in comments or type naming) about whether `Get` vs `GetNoWait` have any ordering/initialization guarantees that callers must respect.
- If the longer-term plan is to introduce a `StoreProvider` to simplify the builder, you might want to leave a TODO near the multiple `With*Store` methods in `builder.go` to capture that intended follow-up refactor in context.

## Individual Comments

### Comment 1
<location path="sensor/common/detector/builder.go" line_range="118-119" />
<code_context>
+	return b
+}
+
+// Build creates a new Detector from the builder configuration.
+func (b *Builder) Build() Detector {
+	detectorStopper := concurrency.NewStopper()
+	netFlowQueueSize := queueScaler.ScaleSizeOnNonDefault(env.DetectorNetworkFlowBufferSize)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider validating required fields in Build (and/or returning an error) instead of silently constructing with possible nil dependencies.

With the builder pattern, a `Detector` can now be built without setting critical dependencies (`clusterID`, `enforcer`, stores, label providers, etc.), leaving them nil and causing hard-to-trace panics later. Please make `Build` validate required fields—either by returning `(Detector, error)` on missing dependencies or by panicking with a clear message—so misconfiguration fails fast at construction time.

Suggested implementation:

```golang
 // Build creates a new Detector from the builder configuration.
func (b *Builder) Build() Detector {
	// Fail fast on misconfiguration: ensure required dependencies are set.
	if b.clusterLabelProvider == nil {
		panic("detector.Builder.Build: clusterLabelProvider must be set; call WithClusterLabelProvider before Build")
	}
	if b.namespaceLabelProvider == nil {
		panic("detector.Builder.Build: namespaceLabelProvider must be set; call WithNamespaceLabelProvider before Build")
	}

	detectorStopper := concurrency.NewStopper()
	netFlowQueueSize := queueScaler.ScaleSizeOnNonDefault(env.DetectorNetworkFlowBufferSize)

```

You should apply the same pattern to all other required `Builder` fields that are not visible in this snippet (for example `clusterID`, `enforcer`, and the various stores). At the top of `Build`, add `nil` / empty checks for each such field and panic with a clear, actionable message (e.g. `"detector.Builder.Build: enforcer must be set; call WithEnforcer before Build"`). This keeps the `Build` signature unchanged while ensuring misconfiguration fails fast with an obvious error.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Stringy Stringy requested a review from AlexVulaj April 1, 2026 11:29
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Refactor
    • Refactored internal detector initialization to use a builder pattern for improved code organization and configuration management.

Walkthrough

Introduces a fluent Builder pattern for constructing Detector instances, replacing the monolithic New() constructor. The ClusterIDProvider interface is exported from enricher.go. The builder consolidates dependency injection setup with chainable With*() methods and a Build() finalizer that initializes internal queues and wiring.

Changes

Cohort / File(s) Summary
Builder Pattern Introduction
sensor/common/detector/builder.go, sensor/common/detector/detector.go, sensor/common/detector/enricher.go
Added new Builder type with initializer NewBuilder() and fourteen With*() chain methods to configure dependencies. Removed the original New() constructor from detector.go. Exported and renamed clusterIDPeekWaiter to ClusterIDProvider interface in enricher.go and updated newEnricher() to use it.
Sensor Integration
sensor/kubernetes/sensor/sensor.go
Updated CreateSensor() to construct policyDetector using detector.NewBuilder() with chained With*() calls instead of direct New() invocation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'ROX-33799: replace detector.New() with builder pattern' accurately and clearly summarizes the main change: replacing the old constructor with a builder pattern.
Description check ✅ Passed The PR description includes the main 'Description' section explaining the refactoring rationale and interface export. It acknowledges template sections for documentation and testing, with the author noting this is a superficial refactor validated by build and unit tests.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch giles/ROX-33799-refactor-detector-constructor

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sensor/common/detector/builder.go`:
- Around line 119-191: Builder.Build currently constructs a detectorImpl without
validating required wiring, which can lead to nil dereferences at runtime; add
explicit nil checks at the start of Builder.Build for the required dependencies
(e.g., b.clusterLabelProvider, b.namespaceLabelProvider, b.imageCache,
b.serviceAccountStore, b.deploymentStore, b.nodeStore, b.enforcer,
b.admCtrlSettingsMgr, b.auditLogUpdater, b.networkPolicyStore and any other
fields you rely on inside detectorImpl or methods like
newEnricher/newDeduper/baseline.NewBaselineEvaluator), and if any are nil fail
fast (e.g., panic or log.Fatalf with a clear message naming Builder.Build and
the missing field) so mis-wired detectors are detected immediately; add/update
unit tests to assert that Build panics/errs when required fields are not
provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 3eebce49-a6bf-4a00-a4d5-c0619932b164

📥 Commits

Reviewing files that changed from the base of the PR and between 2e362e4 and 8e5d6b2.

📒 Files selected for processing (4)
  • sensor/common/detector/builder.go
  • sensor/common/detector/detector.go
  • sensor/common/detector/enricher.go
  • sensor/kubernetes/sensor/sensor.go
💤 Files with no reviewable changes (1)
  • sensor/common/detector/detector.go

The 14-parameter positional constructor was hard to read and extend.
Replaced with a Builder struct and chained WithX() methods as described
in the ticket. Exported the ClusterIDProvider interface (formerly
clusterIDPeekWaiter) to support the exported Builder type.

Build() validates that the 13 required dependencies are set, returning
an error if any are missing. Only admCtrlSettingsMgr is optional
(nil-guarded in the detector implementation).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Stringy Stringy force-pushed the giles/ROX-33799-refactor-detector-constructor branch from 8e5d6b2 to 3d2c9d8 Compare April 1, 2026 11:39
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 0.69444% with 143 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.57%. Comparing base (a5de12f) to head (3d2c9d8).
⚠️ Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
sensor/common/detector/builder.go 0.00% 143 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #19741      +/-   ##
==========================================
- Coverage   49.59%   49.57%   -0.02%     
==========================================
  Files        2756     2757       +1     
  Lines      207961   208107     +146     
==========================================
+ Hits       103129   103177      +48     
- Misses      97172    97268      +96     
- Partials     7660     7662       +2     
Flag Coverage Δ
go-unit-tests 49.57% <0.69%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant