fix(runner/triggers): dedupe CronJob-run findings, skip agent-managed scan jobs - #533
fix(runner/triggers): dedupe CronJob-run findings, skip agent-managed scan jobs#533mayankpande88 wants to merge 1 commit into
Conversation
… scan jobs Every run of a CronJob (and of an orchestrator-scheduled scan Job) fired a brand-new Finding chain: the pod's one-level owner walk returned the generated Job name (mycron-29123456, trivy-image-scan-b0690686), so each run produced a distinct fingerprint that never deduped, and downstream each minted its own triage signal class. - canonicalOwner now strips the CronJob controller's scheduled-time suffix (a controller-defined format, same trust level as the existing ReplicaSet pod-template-hash strip), so all runs of a CronJob resolve to the same owner. Other generated-name shapes are deliberately not guessed at. - The agent's own scheduled Jobs (trivy/popeye/nova scans) no longer produce Findings at all: BuildJob now propagates the managed-by and orchestrator labels to the pod template, and Engine.Match skips any object labeled app.kubernetes.io/managed-by=nudgebee-agent. Their failures are the scan orchestrator's run accounting, not a customer-facing finding (e.g. ImagePullBackOff on a trivy scan pod).
There was a problem hiding this comment.
Code Review
This pull request propagates agent management labels to scheduled scan pods and filters them out in the trigger engine to prevent internal scan failures from generating customer-facing findings. It also normalizes CronJob-created Job names by stripping the scheduled-time suffix to ensure consistent owner resolution and deduplication of findings across runs. Feedback is provided to optimize the CronJob suffix stripping function by replacing the regular expression with a manual string scan to avoid heap allocations on a hot path.
| var cronJobScheduleSuffix = regexp.MustCompile(`-\d{8,10}$`) | ||
|
|
||
| // stripJobGeneratedSuffix normalizes a CronJob-created Job name to the | ||
| // CronJob's own name, so every scheduled run resolves to the same owner. | ||
| // Names without the scheduled-time suffix pass through unchanged. | ||
| func stripJobGeneratedSuffix(name string) string { | ||
| if stripped := cronJobScheduleSuffix.ReplaceAllString(name, ""); stripped != name && stripped != "" { | ||
| return stripped | ||
| } | ||
| return name | ||
| } |
There was a problem hiding this comment.
The stripJobGeneratedSuffix function is called on a hot path (for every kubewatch event matching a Job). Using a regular expression here introduces unnecessary overhead and allocations. We can optimize this by performing a simple string scan to check if the name ends with a hyphen followed by 8 to 10 digits. This approach is significantly faster and avoids any heap allocations.
// stripJobGeneratedSuffix normalizes a CronJob-created Job name to the
// CronJob's own name, so every scheduled run resolves to the same owner.
// Names without the scheduled-time suffix pass through unchanged.
func stripJobGeneratedSuffix(name string) string {
idx := strings.LastIndexByte(name, '-')
if idx <= 0 {
return name
}
suffix := name[idx+1:]
if len(suffix) < 8 || len(suffix) > 10 {
return name
}
for i := 0; i < len(suffix); i++ {
if suffix[i] < '0' || suffix[i] > '9' {
return name
}
}
return name[:idx]
}
Summary
Every run of a CronJob — and of an orchestrator-scheduled scan Job (trivy/popeye/nova) — fired a brand-new Finding chain. The pod's one-level owner walk returned the generated Job name (
mycron-29123456,trivy-image-scan-<hex>), so each run produced a distinct fingerprint that never deduped against the previous run's, and downstream each event minted its own triage signal class (one LLM classification call per scan-job run).Two changes:
CronJob-run owner normalization —
canonicalOwnerstrips the CronJob controller's scheduled-time suffix (fmt.Sprintf("%s-%d", cronJob.Name, unixMinutes)— a controller-defined format, same trust level as the existing ReplicaSet pod-template-hash strip). All runs of a CronJob now resolve to the same owner → same fingerprint → dedup and recurrence work. Other generated-name shapes (random hex tails etc.) are deliberately NOT guessed at — those are conventions, not contracts, and stripping them risks merging distinct hand-named Jobs.Agent-managed Jobs no longer produce Findings —
BuildJobnow propagates theapp.kubernetes.io/managed-by=nudgebee-agent+nudgebee.com/orchestratorlabels to the pod template (previously Job-object-only, invisible to pod-scoped matchers), andEngine.Matchskips any object carrying the agent's managed-by value. An ImagePullBackOff on a trivy scan pod is the scan orchestrator's run accounting, not a customer-facing finding. Foreignmanaged-byvalues (Helm etc.) still match — pinned by test.Type of change
Chart version
Test plan
make validate(fmt + lint + test) passes inrunner/TestResolveOwner_JobStripsCronJobTimestamp,TestResolveOwner_JobKeepsNonCronName,TestStripJobGeneratedSuffix(incl. hand-named / short-numeric / hex-tail / empty-strip edge cases),TestEngine_SkipsAgentManagedObjects(pod + Job + foreign managed-by), pod-template label assertions inTestBuildJob_HygieneInvariants