Skip to content

profile-compaction: CollapseConfig CRD + projection overlay + user-maged lifecycle#325

Merged
matthyx merged 3 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/sbob-crd-compaction
Jun 26, 2026
Merged

profile-compaction: CollapseConfig CRD + projection overlay + user-maged lifecycle#325
matthyx merged 3 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/sbob-crd-compaction

Conversation

@entlein

@entlein entlein commented May 15, 2026

Copy link
Copy Markdown
Contributor
  • CollapseConfig CRD (v1beta1 with conversion, deepcopy, protobuf, openapi, generated clientset/informers/listers, etcd registration)
    lets cluster operators declare path-prefix policies that drive profile-compaction without code changes
  • applicationprofile_processor + containerprofile_processor consume the CollapseConfig provider to choose per-profile collapse strategies at admission time
  • pkg/registry/file/cleanup.go: cleanup loop skips user-managed resources (annotation-based marker, not labels —
    labels would silently miss every user-managed profile)
  • artifacts/collapseconfiguration-default-sample.yaml ships a sample CR

Summary by CodeRabbit

  • New Features

    • Added a cluster-scoped CollapseConfiguration CRD (including a default sample) to control dynamic path-collapse thresholds, with per-prefix longest-prefix-wins overrides.
    • Dynamic collapse settings are now applied during profile deflation.
    • Network neighbor profiles now support IPAddresses (list form) for wildcard-aware IP matching, alongside expanded NetworkNeighborhood admission validation.
  • Bug Fixes

    • Resource cleanup now preserves user-managed resources.
    • Watch behavior now prefers “idle” watches to avoid tight re-watch loops.
  • Documentation

    • Added IP/DNS wildcard matching docs and updated README/watchlist/requirements guidance.
  • Tests

    • Expanded coverage for CRD translation, protobuf compatibility, admission validation, and network matching correctness.

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds CollapseConfiguration API support, runtime collapse-settings translation and wiring, wildcard-aware IP/DNS matching with admission validation, server startup logging and WatchList handling updates, idle watch behavior changes, and annotation-based cleanup skipping.

Changes

Dynamic Path Collapse Configuration and Network Matching

Layer / File(s) Summary
CollapseConfiguration and NetworkNeighbor API types
pkg/apis/softwarecomposition/collapse_types.go, pkg/apis/softwarecomposition/v1beta1/collapse_types.go, pkg/apis/softwarecomposition/register.go, pkg/apis/softwarecomposition/v1beta1/generated.proto, pkg/apis/softwarecomposition/network_types.go, pkg/apis/softwarecomposition/v1beta1/network_types.go, artifacts/collapseconfiguration-default-sample.yaml
CollapseConfiguration is added as a cluster-scoped API type with threshold fields and per-prefix overrides, and NetworkNeighbor gains IPAddresses []string alongside deprecated IPAddress.
Generated support code and serialization tests
pkg/apis/softwarecomposition/v1beta1/zz_generated.*.go, pkg/apis/softwarecomposition/zz_generated.deepcopy.go, pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
Generated deepcopy, conversion, and OpenAPI methods are added for CollapseConfiguration, and protobuf tests cover NetworkNeighbor.IPAddresses round-trip and omission behavior.
IP and DNS wildcard matching
pkg/registry/file/networkmatch/doc.go, pkg/registry/file/networkmatch/match_ip.go, pkg/registry/file/networkmatch/match_dns.go, pkg/registry/file/networkmatch/validate.go, pkg/registry/file/networkmatch/match_ip_test.go, pkg/registry/file/networkmatch/match_dns_test.go, pkg/registry/file/networkmatch/validate_test.go, pkg/registry/file/networkmatch/bench_test.go, pkg/registry/file/networkmatch/README.md, pkg/registry/file/networkmatch/match_differential_test.go
MatchIP and MatchDNS implement literal, CIDR, wildcard, and dynamic-label matching; validators enforce entry syntax; benchmarks and tests pin matching behavior and performance.
Runtime collapse settings translation
pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go, pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
CollapseSettings conversion helpers translate between CRDs and runtime settings, preserve slice copies, and round-trip configuration values.
CollapseConfiguration storage and admission strategy
pkg/registry/softwarecomposition/collapseconfiguration/etcd.go, pkg/registry/softwarecomposition/collapseconfiguration/strategy.go, pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
REST storage and registry strategy are added for CollapseConfiguration, including validation of thresholds, prefixes, and duplicate entries.
CRD-backed collapse settings provider
pkg/registry/file/collapse_config_provider.go, pkg/registry/file/collapse_config_provider_test.go
A storage-backed provider reads CollapseConfiguration/default, falls back to defaults when absent or unavailable, and reflects stored updates.
Profile processors and API server integration
pkg/registry/file/applicationprofile_processor.go, pkg/registry/file/containerprofile_processor.go, pkg/apiserver/apiserver.go, pkg/registry/file/applicationprofile_processor_test.go, pkg/registry/file/applicationprofile_processor_collapse_provider_test.go, pkg/registry/file/containerprofile_processor_collapse_provider_test.go
Processors now accept collapse-settings providers and use them during deflation; the apiserver wires the shared provider and registers the CollapseConfiguration REST endpoint; tests cover the resulting collapse behavior.
NetworkNeighborhood network profile validation
pkg/registry/softwarecomposition/networkneighborhood/strategy.go, pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
NetworkNeighborhood validation now checks ipAddresses and dnsNames entries, including deprecated singular fields, and reports indexed field-path errors.
Sample configuration and documentation
README.md, docs/features/watchlist-disabled.md, pkg/registry/file/networkmatch/README.md
A sample CollapseConfiguration manifest and related documentation describe the new configuration, matching surface, and WatchList behavior.
Build and dependency support
Makefile, go.mod
A test target is added to the Makefile and goleak is added as a direct dependency.

Server Startup and Watch Behavior

Layer / File(s) Summary
Startup logging and feature gates
main.go, pkg/cmd/server/start.go, pkg/cmd/server/start_test.go
Startup logging now respects KS_LOGGER_LEVEL=debug, feature-gate setup applies WatchList=false, and the tests cover the resulting gate state.
Idle watch behavior
pkg/registry/file/watch.go, pkg/registry/file/storage.go, pkg/registry/file/idlewatch_test.go
Namespace and immutable watches now return idle watches, with lifecycle and leak tests covering cancellation and Stop handling.
Server docs and support
README.md, docs/features/watchlist-disabled.md, Makefile, go.mod
The README, WatchList feature note, Makefile test target, and goleak dependency update are added together.

User-Managed Cleanup Handling

Layer / File(s) Summary
Cleanup and user-managed resource handling
pkg/registry/file/cleanup.go, pkg/registry/file/cleanup_test.go
Namespace cleanup skips resources marked user-managed by annotation, and tests cover the annotation-based check.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Suggested reviewers

  • matthyx

Poem

🐰 I hopped through configs, wildcards, and names,
Thresholds now dance in CRD-flamed games.
IPs and DNS hop clean and true,
With collapse settings flowing through.
WatchList hums, and idle watches rest,
Crunch! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title captures the main changes: a CollapseConfig/CollapseConfiguration CRD, projection-based profile compaction, and user-managed lifecycle behavior.
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.

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

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

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.

@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
🧩 Analysis chain

@entlein: Triggering a fresh full review of #325 now. I'll also pull lightweight metadata from the related PRs in your tier map to cross-check coupling.


🏁 Script executed:

#!/bin/bash
# Gather PR details for this PR and related storage PRs to check cross-PR coupling
echo "=== PR `#325` ==="
gh pr view 325 --repo kubescape/storage --json title,body,files,baseRefName,headRefName 2>/dev/null | jq '{title,baseRefName,headRefName,files: [.files[].path]}'

echo ""
echo "=== PR `#323` (Tier 0 - path-wildcards + CollapseConfig API) ==="
gh pr view 323 --repo kubescape/storage --json title,body,files,baseRefName,headRefName 2>/dev/null | jq '{title,baseRefName,headRefName,files: [.files[].path]}'

echo ""
echo "=== PR `#322` (Tier 2 - CompareExecArgs) ==="
gh pr view 322 --repo kubescape/storage --json title,body,files,baseRefName,headRefName 2>/dev/null | jq '{title,baseRefName,headRefName,files: [.files[].path]}'

echo ""
echo "=== PR `#324` (Tier 3 - networkmatch) ==="
gh pr view 324 --repo kubescape/storage --json title,body,files,baseRefName,headRefName 2>/dev/null | jq '{title,baseRefName,headRefName,files: [.files[].path]}'

echo ""
echo "=== PR `#326` (Umbrella) ==="
gh pr view 326 --repo kubescape/storage --json title,body,files,baseRefName,headRefName 2>/dev/null | jq '{title,baseRefName,headRefName,files: [.files[].path]}'

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Try alternate approach to fetch PR data
gh auth status 2>&1

echo "=== PR `#325` files ==="
gh api repos/kubescape/storage/pulls/325/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

echo "=== PR `#323` files ==="
gh api repos/kubescape/storage/pulls/323/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

echo "=== PR `#322` files ==="
gh api repos/kubescape/storage/pulls/322/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

echo "=== PR `#324` files ==="
gh api repos/kubescape/storage/pulls/324/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

echo "=== PR `#326` files ==="
gh api repos/kubescape/storage/pulls/326/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

echo "=== PR `#325` base/head ==="
gh api repos/kubescape/storage/pulls/325 2>/dev/null | jq '{title: .title, base: .base.ref, head: .head.ref, state: .state}' 2>/dev/null || echo "failed"

Length of output: 208


@entlein: A full re-review of #325 has been kicked off above. Here is my atomicity confirmation and cross-PR coupling analysis based on the PR objectives and your tier map:


Atomicity Assessment — #325 (CollapseConfig CRD + Processors)

✅ Internally Atomic

The contents of #325 are well-scoped for a CRD introduction:

Component Justification
CRD types + v1beta1 conversion Core — inseparable
Generated deepcopy / protobuf / openapi Codegen artifacts — must travel with the type definition
Generated clientset / informers / listers Codegen artifacts — must travel with the type definition
etcd registration Required to make the resource storable — inseparable from CRD introduction
applicationprofile_processor + containerprofile_processor First consumer in this repo — reasonable to bundle here
pkg/registry/file/cleanup.go Logically cohesive (user-managed lifecycle gate) but is a separable concern
artifacts/collapseconfiguration-default-sample.yaml Low-risk, conventional to ship with the CRD

The only split worth flagging is that cleanup.go is a behavioral policy change independent of the CRD API surface. If you ever need to bisect a regression, that separation could help — but it is not a blocking concern.


Cross-PR Coupling Flags

🔴 Hard compile-time dependency: #325#323

applicationprofile_processor and containerprofile_processor in #325 import and call the CollapseConfig provider, whose Go types are introduced in #323. #325 cannot compile without #323's types in the target branch. This is your expected and correctly modeled Tier 0 → Tier 2 dependency.

Merge gate: #323 must be merged (or squashed per your advisory) before #325 can target main.

🟡 Same-tier ordering: #325 and #322

Both are Tier 2. If the processors in #325 reference exec-related types added by #322 (e.g., CompareExecArgs), you have an intra-tier ordering constraint. If the processors only depend on CollapseConfig (#323) and existing types, #322 and #325 are independently mergeable in any order after #323.

Recommend verifying: Do applicationprofile_processor or containerprofile_processor in #325 import anything from #322's CompareExecArgs surface? If yes, #322 must precede #325.

🟡 Cross-repo dependency: #325 → node-agent#808

The projection overlay in node-agent#808 presumably consumes the CollapseConfig CRD registered by #325. This is your expected Tier 2 → Tier 4 dependency. No action needed in #325 itself, but node-agent#808 cannot target a release that doesn't include #325.

✅ No coupling detected: #325#324 (networkmatch)

The networkmatch package (Tier 3) operates on a distinct resource surface and shares no apparent file-level overlap with the CollapseConfig CRD or the exec processors.


Summary Table

`#323` (Tier 0) ──► `#325` (Tier 2) ──► node-agent#808 (Tier 4)
                      │
              possible order dep
                      │
                  `#322` (Tier 2)

The tier structure is correct. The one item to double-check before merging #325 is whether its processors import any symbol from #322 — if so, serialize #322 before #325 within Tier 2.

✅ Actions performed

Full review triggered.

@matthyx matthyx moved this to WIP in KS PRs tracking May 26, 2026
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.IPAddresses != nil {

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.

Blocking: this generated deepcopy now expects NetworkNeighbor.IPAddresses, but both internal and v1beta1 NetworkNeighbor still expose IPAddress (singular). go test ./pkg/apis/softwarecomposition fails immediately here. Unless the field rename is included in this PR/rebased prerequisite, this codegen drift leaves the branch unbuildable.

func deflateApplicationProfileContainer(container softwarecomposition.ApplicationProfileContainer, sbomSet mapset.Set[string]) softwarecomposition.ApplicationProfileContainer {
opens, err := dynamicpathdetector.AnalyzeOpens(container.Opens, dynamicpathdetector.NewPathAnalyzer(OpenDynamicThreshold), sbomSet)
func deflateApplicationProfileContainer(container softwarecomposition.ApplicationProfileContainer, sbomSet mapset.Set[string], settings dynamicpathdetector.CollapseSettings) softwarecomposition.ApplicationProfileContainer {
opens, err := dynamicpathdetector.AnalyzeOpens(container.Opens, dynamicpathdetector.NewPathAnalyzerWithConfigs(settings.OpenDynamicThreshold, settings.CollapseConfigs), sbomSet)

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.

Blocking: this PR now calls dynamicpathdetector.NewPathAnalyzerWithConfigs(...), but that symbol (along with CollapseConfig, DefaultCollapseConfigs, and the threshold constants used by collapse_config_from_crd.go) does not exist on current main. As-is, the branch still depends on an unmerged prerequisite and cannot compile until those path-wildcards changes are rebased/cherry-picked in.

)
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = map[string]rest.Storage{
"applicationprofiles": ep(applicationprofile.NewREST, applicationProfileStorageImpl),
"collapseconfigurations": ep(collapseconfiguration.NewREST),

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.

Blocking: registering the CRD endpoint here does not wire it into the live deflate path. applicationProfileStorageImpl and containerProfileStorageImpl are still constructed with the default collapse-settings providers, and I cannot find any non-test call to SetCollapseSettings(...) / assignment to CollapseSettings. So CollapseConfiguration/default is stored, but never consulted when profiles are compacted; applying the sample manifest is a no-op.

@matthyx matthyx 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.

I found three merge blockers:

  • the generated deepcopy drift now references NetworkNeighbor.IPAddresses while the handwritten API types still expose IPAddress, so the branch does not build;
  • this branch calls dynamicpathdetector APIs that are not on current main (NewPathAnalyzerWithConfigs, CollapseConfig, DefaultCollapseConfigs, etc.), so it still depends on an unmerged prerequisite;
  • the new CollapseConfiguration resource is registered, but the application/container profile processors still use compiled-in defaults, so the CRD is never read in production and the sample manifest is a no-op.

Please fix those before merge.

@entlein entlein force-pushed the upstream-pr/sbob-crd-compaction branch 2 times, most recently from 7cba45a to 69c895e Compare May 27, 2026 17:41

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/registry/file/applicationprofile_processor.go (1)

85-106: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Snapshot collapse settings once per PreSave execution.

Line 105 resolves settings inside the per-container loop. With a CRD-backed provider, this can trigger repeated storage lookups and can apply mixed thresholds within the same profile if config changes mid-iteration. Resolve once and reuse for all containers in this save pass.

Proposed fix
 	// size is the sum of all fields in all containers
 	var size int
+	settings := a.effectiveCollapseSettings()
 
 	// Define a function to process a slice of containers
 	processContainers := func(containers []softwarecomposition.ApplicationProfileContainer) []softwarecomposition.ApplicationProfileContainer {
 		for i, container := range containers {
@@
-			containers[i] = deflateApplicationProfileContainer(container, sbomSet, a.effectiveCollapseSettings())
+			containers[i] = deflateApplicationProfileContainer(container, sbomSet, settings)
 			size += len(containers[i].Execs)
🤖 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/registry/file/applicationprofile_processor.go` around lines 85 - 106, The
code calls a.effectiveCollapseSettings() inside the per-container loop (in
processContainers) causing repeated CRD/storage lookups and potential mixed
thresholds; fix by resolving the collapse settings once at the start of the
PreSave/save pass (e.g., capture into a local variable like collapseSettings
before creating/entering processContainers) and then pass that single
collapseSettings value into deflateApplicationProfileContainer instead of
calling a.effectiveCollapseSettings() for each container so all containers use
the same resolved settings for the entire save operation.
🧹 Nitpick comments (1)
pkg/registry/file/applicationprofile_processor_test.go (1)

21-25: ⚡ Quick win

Derive /usr/lib threshold from active collapse configs instead of the global constant.

These tests currently assume /usr/lib uses OpenDynamicThreshold. If a prefix-specific override is added, assertions will fail for the wrong reason. Match the /etc pattern and read threshold via FindConfigForPath("/usr/lib/...").

Proposed refactor
-func openThreshold() int {
-	return dynamicpathdetector.OpenDynamicThreshold
+func openThresholdForPath(path string) int {
+	analyzer := dynamicpathdetector.NewPathAnalyzerWithConfigs(
+		dynamicpathdetector.OpenDynamicThreshold,
+		dynamicpathdetector.DefaultCollapseConfigs(),
+	)
+	return analyzer.FindConfigForPath(path).Threshold
 }
@@
-	numOpens := openThreshold() + 1
+	numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1
@@
-	numOpens := openThreshold() + 1
+	numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1
@@
-	usrLibThreshold := openThreshold()
+	usrLibThreshold := openThresholdForPath("/usr/lib/libX.so")
@@
-	numOpens := openThreshold() + 1
+	numOpens := openThresholdForPath("/usr/lib/x86_64-linux-gnu/libX.so.1") + 1

Also applies to: 273-275, 303-304, 338-340, 420-421

🤖 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/registry/file/applicationprofile_processor_test.go` around lines 21 - 25,
Tests currently hardcode dynamicpathdetector.OpenDynamicThreshold for /usr/lib
paths; update them to derive the threshold from active collapse configs like the
/etc pattern by calling dynamicpathdetector.FindConfigForPath("/usr/lib/...")
(or the package's exported FindConfigForPath) and reading the config's collapse
threshold instead of using OpenDynamicThreshold; change the openThreshold helper
(and other occurrences referencing dynamicpathdetector.OpenDynamicThreshold) to
fetch the config for "/usr/lib/..." and return its threshold, and apply the same
replacement for the other test spots that currently assume the global constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 71-73: Replace the unbounded context used in s.Get with a bounded
context so storage reads can't hang indefinitely: create a short timeout context
(e.g., via context.WithTimeout) before calling s.Get, defer the cancel, and pass
that ctx into s.Get; keep the existing error/empty-name check and return
dynamicpathdetector.DefaultCollapseSettings() on error or deadline. Ensure you
update the call site where s.Get is invoked (the s.Get(...) call that populates
crd) and leave the rest of the function (including use of crd and
dynamicpathdetector.DefaultCollapseSettings) unchanged.

---

Outside diff comments:
In `@pkg/registry/file/applicationprofile_processor.go`:
- Around line 85-106: The code calls a.effectiveCollapseSettings() inside the
per-container loop (in processContainers) causing repeated CRD/storage lookups
and potential mixed thresholds; fix by resolving the collapse settings once at
the start of the PreSave/save pass (e.g., capture into a local variable like
collapseSettings before creating/entering processContainers) and then pass that
single collapseSettings value into deflateApplicationProfileContainer instead of
calling a.effectiveCollapseSettings() for each container so all containers use
the same resolved settings for the entire save operation.

---

Nitpick comments:
In `@pkg/registry/file/applicationprofile_processor_test.go`:
- Around line 21-25: Tests currently hardcode
dynamicpathdetector.OpenDynamicThreshold for /usr/lib paths; update them to
derive the threshold from active collapse configs like the /etc pattern by
calling dynamicpathdetector.FindConfigForPath("/usr/lib/...") (or the package's
exported FindConfigForPath) and reading the config's collapse threshold instead
of using OpenDynamicThreshold; change the openThreshold helper (and other
occurrences referencing dynamicpathdetector.OpenDynamicThreshold) to fetch the
config for "/usr/lib/..." and return its threshold, and apply the same
replacement for the other test spots that currently assume the global constant.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b453484d-6b0b-4912-91f5-2638c966dfd3

📥 Commits

Reviewing files that changed from the base of the PR and between 98649ae and 7cba45a.

⛔ Files ignored due to path filters (17)
  • pkg/apis/softwarecomposition/v1beta1/generated.pb.go is excluded by !**/*.pb.go
  • pkg/apis/softwarecomposition/v1beta1/generated.protomessage.pb.go is excluded by !**/*.pb.go
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigentry.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigurationspec.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/utils.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/generated_expansion.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/generic.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/interface.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/expansion_generated.go is excluded by !**/generated/**
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (39)
  • artifacts/collapseconfiguration-default-sample.yaml
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/apis/softwarecomposition/network_types.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/v1beta1/generated.proto
  • pkg/apis/softwarecomposition/v1beta1/network_types.go
  • pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/applicationprofile_processor_test.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/networkmatch/README.md
  • pkg/registry/file/networkmatch/bench_test.go
  • pkg/registry/file/networkmatch/doc.go
  • pkg/registry/file/networkmatch/match_dns.go
  • pkg/registry/file/networkmatch/match_dns_test.go
  • pkg/registry/file/networkmatch/match_ip.go
  • pkg/registry/file/networkmatch/match_ip_test.go
  • pkg/registry/file/networkmatch/validate.go
  • pkg/registry/file/networkmatch/validate_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go

Comment thread pkg/registry/file/collapse_config_provider.go

@matthyx matthyx 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.

Rechecked after the latest push: the three blockers from my previous review are fixed, but I found two new blockers.

  • pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy.go:340 and :419 still generate peers/policy refs only from the deprecated singular neighbor.IPAddress / neighbor.DNS fields. The new ipAddresses payload added in this PR is never consumed here, so a profile that uses only ipAddresses produces no corresponding IPBlock entries in the generated NetworkPolicy.
  • pkg/registry/file/networkneighborhood_processor.go:91 merges duplicate neighbors by Identifier, but on merge it only appends DNSNames and Ports. The new IPAddresses slice is not appended/deduped, so when multiple observations for the same identifier contribute different IPs, every IP except the first neighbor's slice is dropped before the profile is stored.

@matthyx

matthyx commented May 28, 2026

Copy link
Copy Markdown
Contributor

One more blocker after checking the generated artifacts: the API type now exposes ipAddresses, but the generated surfaces are still stale.

  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/networkneighbor.go still only has IPAddress / WithIPAddress, so server-side-apply builders cannot express the new field.
  • pkg/generated/openapi/zz_generated.openapi.go still serves only ipAddress and even keeps it in the required list, so the published schema is out of sync with the updated Go type.

Please regenerate the applyconfiguration/OpenAPI artifacts for NetworkNeighbor as part of this change.

@entlein entlein force-pushed the upstream-pr/sbob-crd-compaction branch from 69c895e to a3dd980 Compare June 23, 2026 20:24

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/apis/softwarecomposition/network_types.go`:
- Around line 69-71: The IPAddresses field comment uses the IPv4 CIDR format
example `a.b.c.d/n`, which incorrectly suggests only IPv4 is supported, while
the code actually accepts both IPv4 and IPv6 CIDR entries. Update the comment
for the IPAddresses field to be protocol-agnostic by clarifying that each entry
can be a literal IP (supporting both IPv4 and IPv6), a CIDR in either IPv4 or
IPv6 format, or the "*" sentinel. This will accurately reflect what downstream
code and tests already support.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 806fddb0-5a24-4267-af4a-6f624b6890be

📥 Commits

Reviewing files that changed from the base of the PR and between 7cba45a and a3dd980.

⛔ Files ignored due to path filters (17)
  • pkg/apis/softwarecomposition/v1beta1/generated.pb.go is excluded by !**/*.pb.go
  • pkg/apis/softwarecomposition/v1beta1/generated.protomessage.pb.go is excluded by !**/*.pb.go
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigentry.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigurationspec.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/utils.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/generated_expansion.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/generic.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/interface.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/expansion_generated.go is excluded by !**/generated/**
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (39)
  • artifacts/collapseconfiguration-default-sample.yaml
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/apis/softwarecomposition/network_types.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/v1beta1/generated.proto
  • pkg/apis/softwarecomposition/v1beta1/network_types.go
  • pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/applicationprofile_processor_test.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/networkmatch/README.md
  • pkg/registry/file/networkmatch/bench_test.go
  • pkg/registry/file/networkmatch/doc.go
  • pkg/registry/file/networkmatch/match_dns.go
  • pkg/registry/file/networkmatch/match_dns_test.go
  • pkg/registry/file/networkmatch/match_ip.go
  • pkg/registry/file/networkmatch/match_ip_test.go
  • pkg/registry/file/networkmatch/validate.go
  • pkg/registry/file/networkmatch/validate_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
✅ Files skipped from review due to trivial changes (9)
  • artifacts/collapseconfiguration-default-sample.yaml
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/registry/file/networkmatch/doc.go
  • pkg/registry/file/networkmatch/validate_test.go
  • pkg/registry/file/networkmatch/README.md
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
🚧 Files skipped from review as they are similar to previous changes (28)
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
  • pkg/apis/softwarecomposition/v1beta1/network_types.go
  • pkg/registry/file/networkmatch/match_ip_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/networkmatch/match_ip.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/file/networkmatch/match_dns.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/networkmatch/bench_test.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/file/networkmatch/match_dns_test.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/networkmatch/validate.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/applicationprofile_processor_test.go

Comment thread pkg/apis/softwarecomposition/network_types.go
@entlein entlein force-pushed the upstream-pr/sbob-crd-compaction branch from a3dd980 to d0b21b2 Compare June 23, 2026 20:48
@entlein

entlein commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

@CodeRabbit, please review all PRs 328 324 and 325 together now

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh api repos/kubescape/storage/pulls/328 2>/dev/null | jq '{number: .number, title: .title, state: .state, base: .base.ref, head: .head.ref, body: .body[0:500]}' 2>/dev/null || echo "failed"

echo "=== PR `#328` files ==="
gh api repos/kubescape/storage/pulls/328/files 2>/dev/null | jq '.[].filename' 2>/dev/null || echo "failed"

Length of output: 2414



@entlein: I've pulled the details for all three PRs. Here's the combined cross-PR analysis, followed by the review trigger for the current PR (#325). Note that I can only trigger a review for this PR (#325) via the action tag — please drop a @coderabbitai review comment directly on #324 and #328 to trigger reviews there.


Combined Analysis — #324 · #325 · #328

PR #328 — "Addon to #323: Moving Exec Args from NA to Storage"

#328 moves compare_exec_args.go (and adversarial/property/memoize tests) into pkg/registry/file/dynamicpathdetector/, alongside the analyzers already there. This is the storage-side home for what was previously called "CompareExecArgs" in the tier map.

🔴 Newly identified compile-time dependency: #325#324

The AI summary for #325 reveals that pkg/registry/softwarecomposition/networkneighborhood/strategy.go now calls networkmatch validators (ValidateIPEntry, ValidateDNSEntry) for admission-time checks on ipAddresses[] and dnsNames[]. The networkmatch package is introduced by #324 (Tier 3). This means #325 cannot compile without #324 in the target branch, even though #325 is Tier 2 and #324 is Tier 3 in your map.

Action required: Either promote #324 to Tier 2 (merge it before #325), or extract the networkneighborhood strategy additions into a separate PR that follows #324.

🟡 Same-package overlap: #325#328 in dynamicpathdetector

Both #325 and #328 touch pkg/registry/file/dynamicpathdetector/. #325 adds collapse_config_from_crd.go and uses NewPathAnalyzerWithConfigs/CollapseSettings (from #323). #328 adds compare_exec_args.go. Based on the file lists, there is no symbol-level dependency between the two — they add orthogonal things to the same package directory. They should be independently mergeable after #323, in any intra-Tier-2 order.

🔴 Stale generated artifacts (previously flagged by @matthyx)

pkg/generated/applyconfiguration/softwarecomposition/v1beta1/networkneighbor.go and pkg/generated/openapi/zz_generated.openapi.go are still missing the IPAddresses field and keep ipAddress in the required list. This must be regenerated before merge.

Updated dependency graph

graph TD
    A["`#323` Tier 0\npath-wildcards + CollapseConfig API"] --> B["`#325` Tier 2\nCollapseConfig CRD + processors"]
    A --> C["`#328` Tier 2\ncompare_exec_args → dynamicpathdetector"]
    D["`#324` Tier 3\nnetworkmatch"] --> B
    B --> E["node-agent#808 Tier 4"]
    style D fill:rgba(255,60,60,0.5)
    style B fill:rgba(255,180,0,0.5)
Loading

#324 is a hard prerequisite for #325 — the tier labels need updating to reflect this.


Triggering the full review for #325 now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@entlein

entlein commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a fix for the request-path stall flagged in review (commit a6fce873).

Root cause: the CollapseConfig provider performed an unbounded s.Get(context.Background(), …) per deflate call against the apiserver's own storage. Under component-test load (every system pod produces a ContainerProfile) this self-referential read stalled the request path, so application profiles were never produced and profile-dependent tests timed out (post-timeout activity >1m in the storage logs).

Fix: bound each read with a short timeout and move it off the request path into an async refresher that publishes an atomic snapshot, falling back to DefaultCollapseSettings on timeout/error. Operator edits still go live within one refresh interval — no per-call storage hit.

Validated end-to-end against the component-test harness: with this fix the storage apiserver no longer stalls and the previously-timing-out profile tests pass.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the fix commit
gh api repos/kubescape/storage/commits/a6fce873 2>/dev/null | jq '{sha: .sha, message: .commit.message, files: [.files[].filename]}' 2>/dev/null || echo "commit lookup failed"

echo ""
echo "=== Changed file diffs ==="
gh api repos/kubescape/storage/commits/a6fce873 2>/dev/null | jq -r '.files[] | "### \(.filename)\n```diff\n\(.patch // "(binary or no patch)")\n```"' 2>/dev/null || echo "diff fetch failed"

Length of output: 7299



Good find and fix on the root cause, @entlein. The async-refresher pattern is correct in structure — moving the Get off the hot path with an atomic snapshot is exactly the right approach. I spotted one behavioral regression in the fallback logic that's worth addressing before merge, plus a few secondary items:


🔴 Issue: transient error overwrites the last known-good snapshot

In refresh():

if err != nil || crd.Name == "" {
    settings = dynamicpathdetector.DefaultCollapseSettings()
} else {
    settings = dynamicpathdetector.CollapseSettingsFromCRD(crd)
}
current.Store(&settings) // ← always writes, even on error

If a tick fires during a transient storage blip (e.g., etcd leader election, brief network partition), the provider reverts to compiled defaults, dropping the operator-applied CollapseConfiguration until the next successful tick 10 s later. The original pre-fix version at least preserved the last-read value by returning early.

Suggested fix — only update current on success:

refresh := func() {
    ctx, cancel := context.WithTimeout(context.Background(), collapseConfigGetTimeout)
    defer cancel()
    crd := &softwarecomposition.CollapseConfiguration{}
    err := s.Get(ctx, key, storage.GetOptions{IgnoreNotFound: true}, crd)
    if err != nil {
        return // keep last known-good snapshot; next tick will retry
    }
    settings := dynamicpathdetector.CollapseSettingsFromCRD(crd) // handles crd.Name=="" → defaults internally
    current.Store(&settings)
}

This keeps the last good snapshot on transient errors and only reverts to defaults when the CR is genuinely absent (which CollapseSettingsFromCRD(nil/empty) already handles).


🟡 Goroutine has no stop mechanism

The background ticker goroutine runs for the lifetime of the process with no context.Context or stop channel:

go func() {
    t := time.NewTicker(collapseConfigRefreshInterval)
    defer t.Stop()
    for range t.C {
        refresh()
    }
}()

For a server-lifetime provider this is probably acceptable, but it makes the goroutine impossible to cleanly shut down in tests or graceful-shutdown sequences. Consider accepting a ctx context.Context in NewCRDCollapseSettingsProvider and using case <-ctx.Done(): return alongside case <-t.C: refresh(). Not a blocker for this fix, but worth a follow-up.


🟡 Initial refresh() blocks startup for up to collapseConfigGetTimeout (2 s)

The synchronous call before returning the provider means NewCRDCollapseSettingsProvider itself can take up to 2 s. For the server init path this is likely fine, but it's worth a comment so future readers don't mistake it for a hot-path call.


🟡 No new tests for the async refresh path

The existing collapse_config_provider_test.go tests use a synchronous fake storage. The new goroutine + ticker + atomic.Pointer path has no dedicated coverage. A test that injects a fake storage, calls NewCRDCollapseSettingsProvider, updates the stored CR, waits slightly longer than collapseConfigRefreshInterval, and asserts the snapshot updated would guard against regressions here.


Triggering the full review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/registry/file/collapse_config_provider.go (1)

40-42: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use the API group constant when building the storage key.

pkg/apiserver/apiserver.go registers this API group through softwarecomposition.GroupName; duplicating the literal here creates a silent fallback-to-default failure mode if the group string ever changes.

Proposed refactor
 func collapseConfigurationKey(name string) string {
-	return K8sKeysToPath("", "spdx.softwarecomposition.kubescape.io", "collapseconfigurations", "", "", name)
+	return K8sKeysToPath("", softwarecomposition.GroupName, "collapseconfigurations", "", "", name)
 }
🤖 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/registry/file/collapse_config_provider.go` around lines 40 - 42, The
storage key builder in collapseConfigurationKey is hardcoding the API group
string, which can drift from the registered group. Update
collapseConfigurationKey to use the shared API group constant from
softwarecomposition.GroupName instead of the literal, keeping the K8sKeysToPath
call otherwise unchanged so the storage key stays aligned with the apiserver
registration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 27-31: Update the comment near collapseConfigGetTimeout and the
collapse config provider logic/comments to match the real fallback behavior: on
Get error or missing CR, the provider uses DefaultCollapseSettings() rather than
keeping the last known settings. Remove the CodeRabbit/review-tool reference
from the source comment, and ensure the description around the functions in
collapse_config_provider.go accurately reflects the behavior implemented in the
provider’s read path.

---

Nitpick comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 40-42: The storage key builder in collapseConfigurationKey is
hardcoding the API group string, which can drift from the registered group.
Update collapseConfigurationKey to use the shared API group constant from
softwarecomposition.GroupName instead of the literal, keeping the K8sKeysToPath
call otherwise unchanged so the storage key stays aligned with the apiserver
registration.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b74958e3-aef2-42fe-8602-13a68945e3f7

📥 Commits

Reviewing files that changed from the base of the PR and between a3dd980 and a6fce87.

⛔ Files ignored due to path filters (17)
  • pkg/apis/softwarecomposition/v1beta1/generated.pb.go is excluded by !**/*.pb.go
  • pkg/apis/softwarecomposition/v1beta1/generated.protomessage.pb.go is excluded by !**/*.pb.go
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigentry.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigurationspec.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/utils.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/generated_expansion.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/generic.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/interface.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/expansion_generated.go is excluded by !**/generated/**
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (26)
  • artifacts/collapseconfiguration-default-sample.yaml
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/v1beta1/generated.proto
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/applicationprofile_processor_test.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/networkmatch/match_differential_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
✅ Files skipped from review due to trivial changes (5)
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • artifacts/collapseconfiguration-default-sample.yaml
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
🚧 Files skipped from review as they are similar to previous changes (18)
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apiserver/apiserver.go
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/applicationprofile_processor_test.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go

Comment thread pkg/registry/file/collapse_config_provider.go Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 86-95: The cached snapshot in collapse_config_provider.go is
exposing shared slice state through a shallow copy of CollapseSettings, so
callers can mutate CollapseConfigs and affect later reads. Update the
snapshot-return path around the current.Store / retrieval logic in the
CollapseSettings provider to return a defensive copy of CollapseConfigs (for
both DefaultCollapseSettings() and CollapseSettingsFromCRD(crd) cases) before
handing settings back to callers, preserving isolation of the atomic cache.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a820b325-9bb9-4dbd-8f3d-13a38bf32f46

📥 Commits

Reviewing files that changed from the base of the PR and between a6fce87 and e034f99.

📒 Files selected for processing (2)
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go

Comment thread pkg/registry/file/collapse_config_provider.go Outdated
@entlein

entlein commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai — addressed in e034f99.

Good catch on the fallback regression — fixed. The refresher no longer overwrites the snapshot on a transient read error: on err != nil it now returns early and keeps the last known-good CollapseConfiguration, so an etcd blip / brief partition can't revert an operator-applied config to compiled defaults. Defaults apply only when the CR is genuinely absent.

One adjustment to the suggested diff: I kept the explicit crd.Name == "" guard rather than relying on CollapseSettingsFromCRD(crd) to default. With IgnoreNotFound: true an absent CR comes back as a non-nil zero struct, and CollapseSettingsFromCRD only defaults for a nil pointer — so dropping the guard would yield all-zero thresholds instead of defaults on the absent-CR path.

Also from the review:

  • Goroutine shutdown — the refresher now takes an injectable stop channel (unexported core); the exported constructor passes nil (process-lifetime, as before). Wiring a real stop signal from the apiserver (PostStartHook/stop-channel) is left as the follow-up you flagged.
  • Async-refresh coverage — replaced the old no-cache assertion (which contradicted this fix) with a test that drives the goroutine + ticker + atomic.Pointer path on a short interval, plus a transient-error-keeps-last-good test. The fake storage is now mutex-guarded so the suite is clean under -race.
  • Startup note — documented that the one synchronous bounded refresh seeds the snapshot at construction and is never on the request path.

Provider tests pass under -race; component tests are green end-to-end against an image built from this commit.

@entlein

entlein commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

@entlein: All the concerns from the last round look well-resolved:

  • Fallback regression — the early-return-on-error pattern is exactly right, and the explanation for retaining the explicit crd.Name == "" guard is technically correct. With IgnoreNotFound: true the storage layer populates the destination pointer with a zero-value struct (non-nil), so CollapseSettingsFromCRD would produce all-zero thresholds rather than defaults if the guard were dropped. Keeping it explicit is the safer and clearer choice.
  • Stop channel — the injectable-for-tests / nil-for-process-lifetime split is a clean approach.
  • Test coverage — mutex-guarded fake storage under -race plus the async-refresh and transient-error-preservation tests are exactly what was requested.
  • Startup documentation — good to have the comment there so future readers don't mistake the initial bounded Get for a hot-path call.

Triggering the full review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@entlein entlein force-pushed the upstream-pr/sbob-crd-compaction branch from e034f99 to b359654 Compare June 25, 2026 07:44
@entlein entlein requested a review from matthyx June 25, 2026 07:44

@coderabbitai coderabbitai 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.

♻️ Duplicate comments (1)
pkg/registry/file/collapse_config_provider.go (1)

54-57: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Move storage reads off the request path and bound the read context.

Line 71 performs s.Get with context.Background() on each call, so storage slowness can stall admission/deflate paths. This also regresses the stated async-snapshot design in the PR objective. Please switch to a background refresher with an atomic last-known-good snapshot (or at minimum use context.WithTimeout for the read path).

Suggested direction
- err := s.Get(context.Background(), key, storage.GetOptions{IgnoreNotFound: true}, crd)
+ ctx, cancel := context.WithTimeout(context.Background(), collapseConfigGetTimeout)
+ defer cancel()
+ err := s.Get(ctx, key, storage.GetOptions{IgnoreNotFound: true}, crd)

Also applies to: 71-73

🤖 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/registry/file/collapse_config_provider.go` around lines 54 - 57, Move the
storage fetch in collapse_config_provider’s deflate path off the request path
and avoid blocking with context.Background(). Update the closure around the
current s.Get call to read from an atomic last-known-good snapshot, refreshed
asynchronously in the background, or at minimum wrap the storage read in a
bounded context.WithTimeout. Keep the logic centered on the existing collapse
config provider behavior so edits still pick up without restart while preventing
admission/deflate stalls.
🧹 Nitpick comments (3)
pkg/cmd/server/start_test.go (1)

69-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Source-order test dependency is fragile. TestPersistentPreRunERejectsUnknownGate relies on being declared last because it pollutes the process-global kube feature gate. This holds today (Go executes top-level tests in source order), but the contract silently breaks if a test is added below it or if these are ever parallelized. Consider isolating the stale-gate scenario in its own package/binary or resetting the registry in a cleanup hook so ordering is no longer load-bearing.

🤖 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/cmd/server/start_test.go` around lines 69 - 82, The test
`TestPersistentPreRunERejectsUnknownGate` depends on source order because it
leaves stale state in the process-global kube feature gate, making later tests
fragile. Update this test setup so the
`NewCommandStartWardleServer`/`PersistentPreRunE` stale-gate scenario is
isolated from the shared registry, either by moving it into a separate
package/binary-style test or by adding cleanup that resets the feature-gate
registry after the assertion. Remove the comment/assumption that it must remain
last once the test no longer relies on declaration order.
main.go (1)

44-54: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Logic is correct; minor naming nuance. zap.IncreaseLevel can only raise severity, so the default IncreaseLevel(zap.FatalLevel) mutes klog, while KS_LOGGER_LEVEL=debug resolves to zap.InfoLevel (already the NewProduction floor) and surfaces Info-and-above. Note that higher klog V-level "debug" lines (which zapr maps to sub-Info levels) will still be filtered, so the debug flag effectively exposes Info diagnostics rather than verbose debug output. Functionally fine for the stated goal of unmuting startup/readyz diagnostics.

🤖 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 `@main.go` around lines 44 - 54, The klog level toggle in main.go is
functionally correct, but the KS_LOGGER_LEVEL=debug setting is misleading
because zap.IncreaseLevel(zap.InfoLevel) only exposes Info-and-above logs, not
full debug/V-level output. Update the naming or inline comment around the
zap.IncreaseLevel and klog.SetLogger setup so it accurately states that “debug”
un-mutes startup/readyz diagnostics to Info level rather than enabling verbose
debug logging.
pkg/cmd/server/start.go (1)

150-158: 📐 Maintainability & Code Quality | 🔵 Trivial

Correct inaccurate version reference in the feature gate comment.

The comment suggests ServerSideApply reached GA in "Kubernetes 1.35", but it actually reached General Availability in v1.22 (and the feature gate was removed entirely in v1.32). The behavioral note regarding the token validation remains valid.

🤖 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/cmd/server/start.go` around lines 150 - 158, The feature-gate comment
near the startup flag setup has an inaccurate Kubernetes version reference for
ServerSideApply. Update the comment text in the block that sets the feature
gates and calls defaults.ComponentGlobalsRegistry.Set() so it refers to
ServerSideApply reaching General Availability in v1.22 and note that the gate
was removed in v1.32, while keeping the token validation note unchanged.
🤖 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.

Duplicate comments:
In `@pkg/registry/file/collapse_config_provider.go`:
- Around line 54-57: Move the storage fetch in collapse_config_provider’s
deflate path off the request path and avoid blocking with context.Background().
Update the closure around the current s.Get call to read from an atomic
last-known-good snapshot, refreshed asynchronously in the background, or at
minimum wrap the storage read in a bounded context.WithTimeout. Keep the logic
centered on the existing collapse config provider behavior so edits still pick
up without restart while preventing admission/deflate stalls.

---

Nitpick comments:
In `@main.go`:
- Around line 44-54: The klog level toggle in main.go is functionally correct,
but the KS_LOGGER_LEVEL=debug setting is misleading because
zap.IncreaseLevel(zap.InfoLevel) only exposes Info-and-above logs, not full
debug/V-level output. Update the naming or inline comment around the
zap.IncreaseLevel and klog.SetLogger setup so it accurately states that “debug”
un-mutes startup/readyz diagnostics to Info level rather than enabling verbose
debug logging.

In `@pkg/cmd/server/start_test.go`:
- Around line 69-82: The test `TestPersistentPreRunERejectsUnknownGate` depends
on source order because it leaves stale state in the process-global kube feature
gate, making later tests fragile. Update this test setup so the
`NewCommandStartWardleServer`/`PersistentPreRunE` stale-gate scenario is
isolated from the shared registry, either by moving it into a separate
package/binary-style test or by adding cleanup that resets the feature-gate
registry after the assertion. Remove the comment/assumption that it must remain
last once the test no longer relies on declaration order.

In `@pkg/cmd/server/start.go`:
- Around line 150-158: The feature-gate comment near the startup flag setup has
an inaccurate Kubernetes version reference for ServerSideApply. Update the
comment text in the block that sets the feature gates and calls
defaults.ComponentGlobalsRegistry.Set() so it refers to ServerSideApply reaching
General Availability in v1.22 and note that the gate was removed in v1.32, while
keeping the token validation note unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 49af8c06-cf17-4b41-9cb8-9940e89214be

📥 Commits

Reviewing files that changed from the base of the PR and between e034f99 and b359654.

⛔ Files ignored due to path filters (17)
  • pkg/apis/softwarecomposition/v1beta1/generated.pb.go is excluded by !**/*.pb.go
  • pkg/apis/softwarecomposition/v1beta1/generated.protomessage.pb.go is excluded by !**/*.pb.go
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigentry.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/softwarecomposition/v1beta1/collapseconfigurationspec.go is excluded by !**/generated/**
  • pkg/generated/applyconfiguration/utils.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/fake/fake_softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/generated_expansion.go is excluded by !**/generated/**
  • pkg/generated/clientset/versioned/typed/softwarecomposition/v1beta1/softwarecomposition_client.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/generic.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/informers/externalversions/softwarecomposition/v1beta1/interface.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/collapseconfiguration.go is excluded by !**/generated/**
  • pkg/generated/listers/softwarecomposition/v1beta1/expansion_generated.go is excluded by !**/generated/**
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**
📒 Files selected for processing (50)
  • Makefile
  • README.md
  • artifacts/collapseconfiguration-default-sample.yaml
  • docs/features/watchlist-disabled.md
  • go.mod
  • main.go
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/apis/softwarecomposition/network_types.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/v1beta1/generated.proto
  • pkg/apis/softwarecomposition/v1beta1/network_types.go
  • pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apiserver/apiserver.go
  • pkg/cmd/server/start.go
  • pkg/cmd/server/start_test.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/applicationprofile_processor_test.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/collapse_config_provider.go
  • pkg/registry/file/collapse_config_provider_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/idlewatch_test.go
  • pkg/registry/file/networkmatch/README.md
  • pkg/registry/file/networkmatch/bench_test.go
  • pkg/registry/file/networkmatch/doc.go
  • pkg/registry/file/networkmatch/match_differential_test.go
  • pkg/registry/file/networkmatch/match_dns.go
  • pkg/registry/file/networkmatch/match_dns_test.go
  • pkg/registry/file/networkmatch/match_ip.go
  • pkg/registry/file/networkmatch/match_ip_test.go
  • pkg/registry/file/networkmatch/validate.go
  • pkg/registry/file/networkmatch/validate_test.go
  • pkg/registry/file/storage.go
  • pkg/registry/file/watch.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
✅ Files skipped from review due to trivial changes (9)
  • artifacts/collapseconfiguration-default-sample.yaml
  • go.mod
  • pkg/registry/file/networkmatch/doc.go
  • docs/features/watchlist-disabled.md
  • README.md
  • pkg/registry/file/networkmatch/README.md
  • pkg/registry/file/networkmatch/bench_test.go
  • pkg/apis/softwarecomposition/zz_generated.deepcopy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go
🚧 Files skipped from review as they are similar to previous changes (31)
  • pkg/registry/file/networkmatch/validate_test.go
  • pkg/apis/softwarecomposition/v1beta1/register.go
  • pkg/registry/file/dynamicpathdetector/collapse_config_from_crd.go
  • pkg/registry/softwarecomposition/collapseconfiguration/etcd.go
  • pkg/apis/softwarecomposition/network_types.go
  • pkg/apis/softwarecomposition/register.go
  • pkg/apis/softwarecomposition/v1beta1/collapse_types.go
  • pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.model_name.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
  • pkg/registry/file/networkmatch/validate.go
  • pkg/apiserver/apiserver.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy_test.go
  • pkg/apis/softwarecomposition/v1beta1/network_types.go
  • pkg/apis/softwarecomposition/collapse_types.go
  • pkg/registry/file/dynamicpathdetector/tests/collapse_config_crd_test.go
  • pkg/registry/file/containerprofile_processor.go
  • pkg/registry/file/containerprofile_processor_collapse_provider_test.go
  • pkg/registry/softwarecomposition/collapseconfiguration/strategy.go
  • pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go
  • pkg/registry/file/networkmatch/match_dns.go
  • pkg/registry/file/cleanup.go
  • pkg/registry/file/networkmatch/match_differential_test.go
  • pkg/registry/file/networkmatch/match_ip.go
  • pkg/registry/file/networkmatch/match_dns_test.go
  • pkg/registry/file/applicationprofile_processor.go
  • pkg/registry/softwarecomposition/networkneighborhood/strategy.go
  • pkg/registry/file/applicationprofile_processor_collapse_provider_test.go
  • pkg/registry/file/networkmatch/match_ip_test.go
  • pkg/registry/file/cleanup_test.go
  • pkg/registry/file/applicationprofile_processor_test.go

matthyx added a commit to k8sstormcenter/storage that referenced this pull request Jun 26, 2026
…naged lifecycle

Introduces a CollapseConfiguration CRD with a projection overlay and a
user-managed lifecycle, and wires it into the profile deflate path so
operator-supplied compaction settings actually take effect.

Adds NewCRDCollapseSettingsProvider(storage.Interface) — a closure that
reads CollapseConfiguration/default on every deflate call, projects
through dynamicpathdetector.CollapseSettingsFromCRD, and falls back to
DefaultCollapseSettings when the CR is absent or storage is nil.

apiserver.go refactors processor construction so both the application
and container profile processors are reachable after the storage
backend is built, then wires the same provider into both via
SetCollapseSettings (ApplicationProfileProcessor) and direct field
assignment (ContainerProfileProcessor). One shared closure keeps the
two compaction paths consistent on every CR edit.

The no-cache design lets bobctl autotune-style write->read cycles take
effect immediately without restart or invalidation. Deflate frequency
is low compared to disk Get latency; if benchmarks ever surface this
as hot, wrap with a watched cache.

Tests pin five contract points: fall-back on absent CR, faithful
projection of an applied CR, nil-storage defence, transient-error
fall-back, and live-update without invalidation.

Resolves matthyx review on apiserver.go:164 (2026-05-27).

Signed-off-by: entlein <einentlein@gmail.com>

Docs-exempt: rebase/squash of contributor PR kubescape#325; CRD feature docs to be handled in PR review
@matthyx matthyx force-pushed the upstream-pr/sbob-crd-compaction branch from b359654 to e234650 Compare June 26, 2026 15:30

@matthyx matthyx 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.

Automated review of the current head (e2346504).

Scope of this head is the CollapseConfiguration CRD + projection overlay, the cleanup user-managed skip, and the networkmatch differential test. Good news first:

  • The three blockers from the 2026-05-27 review are resolved on this head: the dynamicpathdetector symbols (NewPathAnalyzerWithConfigs, CollapseConfig, DefaultCollapseConfigs, FindConfigForPath) exist on the branch, and the CRD is now wired into the live deflate path via NewCRDCollapseSettingsProvider → both processors.
  • The two 2026-05-28 blockers (NetworkNeighbor IPAddresses not consumed in networkpolicy.go / networkneighborhood_processor.go) reference code that is not in this PR's diff anymore — main...HEAD touches only networkmatch/match_differential_test.go under network*. They appear resolved-by-rescope; worth confirming the ipAddresses feature landed (or is tracked) elsewhere.

One blocker remains, plus a few non-blocking notes.


🔴 Blocker — a partial / zero-valued CollapseConfiguration silently over-collapses every profile

See the inline comments on strategy.go and collapse_config_from_crd.go. In short: updateNodeStats collapses once node.Count > threshold, so an effective threshold of 0 turns every path into . Validation allows the two global thresholds to be 0 (only < 0 is rejected) while each per-prefix entry must be >= 1, and the v1beta1 JSON fields are not omitempty — so a CR that omits openDynamicThreshold (or only sets collapseConfigs) decodes to 0 and is passed through verbatim, silently nuking all opens/endpoints in production. Fix: require >= 1 for the global thresholds too, or treat 0 as "use compiled default" in CollapseSettingsFromCRD.

🟡 Notes (non-blocking)

  1. Storage-instance asymmetry (apiserver.go). The collapseconfigurations REST endpoint is registered as ep(collapseconfiguration.NewREST) → backed by the plain storageImpl, but the provider reads the CR from applicationProfileStorageBackend (the with-collector backend). It works today only because both point at the same DefaultStorageRoot on the same OsFs. Reading the CR through the same storageImpl the endpoint writes to would remove a latent footgun.
  2. Singleton name not enforced. Only CollapseConfiguration/default is ever consulted; creating it under any other name is a silent no-op. Consider a WarningsOnCreate/WarningsOnUpdate when metadata.name != "default".
  3. Breaking signature on an exported func. DeflateContainerProfileSpec gained a settings parameter. If anything outside this repo imports it (e.g. node-agent), that's a compile break — please confirm there are no external callers or provide a wrapper.
  4. Truncated license header in pkg/registry/file/collapse_config_provider.go (only the first 3 lines of the Apache header; the warranty paragraph other files carry is missing) — license-header linters may flag it.
  5. DCO check is currently failing — independent merge gate; needs a Signed-off-by on all commits.

Nice work on the test coverage — the defensive-copy / nil-provider / live-update pins and the networkmatch differential oracle are thorough.

// longest-prefix-wins outcome at runtime).
func validateCollapseConfigurationSpec(spec *softwarecomposition.CollapseConfigurationSpec, fp *field.Path) field.ErrorList {
var errs field.ErrorList
if spec.OpenDynamicThreshold < 0 {

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.

Blocker: the global thresholds accept 0, which collapses everything.

updateNodeStats collapses a node once node.Count > threshold (analyzer.go:334), so an effective threshold of 0 collapses every node that has any children — i.e. every open/endpoint path becomes .

This validator rejects only < 0 for OpenDynamicThreshold / EndpointDynamicThreshold, while it requires each per-prefix Threshold >= 1 (line 146). The inconsistency is dangerous: the v1beta1 JSON fields are not omitempty, so a CR that simply omits openDynamicThreshold decodes to 0, and CollapseSettingsFromCRD passes it through verbatim (collapse_config_from_crd.go:81). The result is that applying a CR which sets only collapseConfigs silently zeroes the global thresholds and over-collapses all profiles in production.

Suggest requiring >= 1 here too (consistent with the per-entry rule), or treating 0 as “use the compiled default” in CollapseSettingsFromCRD.

}
}
return CollapseSettings{
OpenDynamicThreshold: int(crd.Spec.OpenDynamicThreshold),

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.

Related to the threshold-validation blocker. This projection copies Spec values verbatim, with two consequences worth deciding explicitly:

  1. A 0 global threshold is taken literally (see the strategy.go validation thread) → collapses everything.
  2. CollapseConfigs is replaced wholesale — a CR with a single collapseConfigs entry drops all compiled-in per-prefix defaults (/etc=100, /opt=50, /var/run=50, …) rather than overlaying on top of them.

If wholesale-replace is intended, please document it on the CRD type and in artifacts/collapseconfiguration-default-sample.yaml so operators know a partial CR is not a partial override. If overlay was intended, merge with DefaultCollapseSettings() here and guard zero-valued thresholds.

@matthyx

matthyx commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Re-checking the current origin/main...HEAD diff, I found one remaining code blocker:

  • pkg/registry/file/applicationprofile_processor.go:146
  • pkg/registry/file/containerprofile_processor.go:826

Both endpoint deflate paths call AnalyzeEndpoints(..., NewPathAnalyzerWithConfigs(settings.EndpointDynamicThreshold, nil)), which drops settings.CollapseConfigs entirely. The new CollapseConfiguration API documents collapseConfigs as per-prefix overrides for the dynamic-path-detectors open/endpoint collapse step, so applying the CR currently affects opens but not endpoint compaction.

Please pass settings.CollapseConfigs into the endpoint analyzer in both places and add a regression test that proves a CRD-supplied prefix override changes endpoint collapse output.

@matthyx

matthyx commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

I found another blocker in the current diff:

  • pkg/registry/file/collapse_config_provider.go:36

collapseConfigurationKey() builds the lookup key with K8sKeysToPath("", "spdx.softwarecomposition.kubescape.io", "collapseconfigurations", "", "", name). Because both cluster and namespace are empty here, that helper expands to /spdx.softwarecomposition.kubescape.io/collapseconfigurations//default (note the extra /).

But CollapseConfiguration is registered as cluster-scoped (pkg/registry/softwarecomposition/collapseconfiguration/strategy.go), so the generic REST store persists it under the non-namespaced key /<group>/<resource>/<name> — i.e. /spdx.softwarecomposition.kubescape.io/collapseconfigurations/default.

Result: the provider reads the wrong path, misses the applied CR, and falls back to defaults every time. That makes applying CollapseConfiguration/default a no-op in practice.

Please build the cluster-scoped key without the empty namespace segment (or use the same key-generation path the REST store uses) and add an integration-style regression test that writes the CR through real storage and verifies the provider can read it back.

matthyx added a commit to k8sstormcenter/storage that referenced this pull request Jun 26, 2026
…r-scoped CR key

Two blockers in the CollapseConfiguration wiring found in PR kubescape#325 review:

1. Endpoint compaction ignored CollapseConfiguration.collapseConfigs.
   deflateApplicationProfileContainer and DeflateContainerProfileSpec built
   the endpoint analyzer with NewPathAnalyzerWithConfigs(EndpointDynamicThreshold,
   nil), so per-prefix overrides reached opens but never endpoints. Pass
   settings.CollapseConfigs into both endpoint analyzers so the CRD drives
   endpoint compaction too.

2. collapseConfigurationKey() built /spdx.../collapseconfigurations//default
   (a stray empty namespace segment) via K8sKeysToPath, while the cluster-scoped
   REST storage keys the CR as /spdx.../collapseconfigurations/default
   (genericregistry NoNamespaceKeyFunc). The provider therefore missed the
   applied CR and silently fell back to defaults. Add a K8sClusterScopedKeysToPath
   helper next to K8sKeysToPath (whose namespace segment is unconditional and
   cannot express a cluster-scoped key) and build the key through it, keeping
   the storage-key format centralized.

Adds regression tests pinning the exact storage key string and endpoint
collapse behavior. The existing provider tests stored and read through the
same helper, so they were self-consistent and could not catch the key bug.

Docs-exempt: bug fixes to unreleased CollapseConfiguration feature; no behavioral doc exists yet

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
matthyx and others added 3 commits June 26, 2026 18:58
…naged lifecycle

Introduces a CollapseConfiguration CRD with a projection overlay and a
user-managed lifecycle, and wires it into the profile deflate path so
operator-supplied compaction settings actually take effect.

Adds NewCRDCollapseSettingsProvider(storage.Interface) — a closure that
reads CollapseConfiguration/default on every deflate call, projects
through dynamicpathdetector.CollapseSettingsFromCRD, and falls back to
DefaultCollapseSettings when the CR is absent or storage is nil.

apiserver.go refactors processor construction so both the application
and container profile processors are reachable after the storage
backend is built, then wires the same provider into both via
SetCollapseSettings (ApplicationProfileProcessor) and direct field
assignment (ContainerProfileProcessor). One shared closure keeps the
two compaction paths consistent on every CR edit.

The no-cache design lets bobctl autotune-style write->read cycles take
effect immediately without restart or invalidation. Deflate frequency
is low compared to disk Get latency; if benchmarks ever surface this
as hot, wrap with a watched cache.

Tests pin five contract points: fall-back on absent CR, faithful
projection of an applied CR, nil-storage defence, transient-error
fall-back, and live-update without invalidation.

Resolves matthyx review on apiserver.go:164 (2026-05-27).

Signed-off-by: entlein <einentlein@gmail.com>

Docs-exempt: rebase/squash of contributor PR kubescape#325; CRD feature docs to be handled in PR review
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
…r-scoped CR key

Two blockers in the CollapseConfiguration wiring found in PR kubescape#325 review:

1. Endpoint compaction ignored CollapseConfiguration.collapseConfigs.
   deflateApplicationProfileContainer and DeflateContainerProfileSpec built
   the endpoint analyzer with NewPathAnalyzerWithConfigs(EndpointDynamicThreshold,
   nil), so per-prefix overrides reached opens but never endpoints. Pass
   settings.CollapseConfigs into both endpoint analyzers so the CRD drives
   endpoint compaction too.

2. collapseConfigurationKey() built /spdx.../collapseconfigurations//default
   (a stray empty namespace segment) via K8sKeysToPath, while the cluster-scoped
   REST storage keys the CR as /spdx.../collapseconfigurations/default
   (genericregistry NoNamespaceKeyFunc). The provider therefore missed the
   applied CR and silently fell back to defaults. Add a K8sClusterScopedKeysToPath
   helper next to K8sKeysToPath (whose namespace segment is unconditional and
   cannot express a cluster-scoped key) and build the key through it, keeping
   the storage-key format centralized.

Adds regression tests pinning the exact storage key string and endpoint
collapse behavior. The existing provider tests stored and read through the
same helper, so they were self-consistent and could not catch the key bug.

Docs-exempt: bug fixes to unreleased CollapseConfiguration feature; no behavioral doc exists yet

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
…nt replace semantics

Addresses two PR kubescape#325 review blockers on the CollapseConfiguration CRD.

1. Zero-valued global thresholds silently over-collapsed every profile.
   A partial CR that omits openDynamicThreshold / endpointDynamicThreshold
   (or sets only collapseConfigs) decodes the field to 0, and updateNodeStats
   collapses any node with Count > threshold — so 0 flattens every open AND
   endpoint to a single dynamic identifier. The earlier endpoints fix widened
   the blast radius to endpoints too.

   CollapseSettingsFromCRD now treats a non-positive global threshold as
   'use the compiled-in default' rather than copying 0 verbatim. This is the
   load-bearing guard: it holds even for direct storage writes that bypass
   admission. The two global thresholds are also made optional in the v1beta1
   type (omitempty + +optional + protobuf opt) so partial CRs are first-class;
   per-prefix entries keep their stricter >= 1 admission check (an explicit
   override has no default to fall back to). Regenerated openapi/proto/
   applyconfiguration accordingly.

2. Wholesale-replace of CollapseConfigs was undocumented. A CR with one entry
   drops the compiled-in /etc, /opt, /var/run defaults rather than overlaying
   them. Documented the replace-not-merge contract on both the internal and
   v1beta1 CRD types and in the sample manifest (which already lists the full
   default set), plus the zero-means-default semantics.

Adds regression tests for the zero-guard (omitted globals fall back to
defaults; only-collapseConfigs keeps global defaults).

Docs-exempt: documents CRD behavior inline on the API type + sample; no docs/ feature page exists for this unreleased CRD

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
@matthyx matthyx force-pushed the upstream-pr/sbob-crd-compaction branch from a1aef11 to b90b528 Compare June 26, 2026 16:58
@matthyx matthyx merged commit edd2fb8 into kubescape:main Jun 26, 2026
7 checks passed
@matthyx matthyx moved this from WIP to To Archive in KS PRs tracking Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants