Conversation
SubmitScanJob returns nil in four places: the three sentinel errors ErrReplicaSetNotFound, ErrNoRunningPods and ErrUnSupportedKind, and a generated scan job with no containers. Each is a deliberate decision not to scan, and each leaves nothing behind -- no scan job, no report, no event -- so a skipped workload and a workload scanned clean look identical from outside. Every existing metric is derived from report objects that exist, which cannot cover a report that was never meant to be created. This adds trivy_workload_scan_skipped_total, labelled by namespace, workload kind and reason, so the question "why does this workload have no report" can be answered without turning on debug logging. The log lines stay at V(1) on purpose. Promoting them would be noisy: no_running_pods fires on every reconcile of a scaled-to-zero Deployment, so the useful shape is a counter to aggregate, not a line per event. Reason is a closed set of four values to bound cardinality, and ScanSkippedReason returns "" for anything else, so an unrelated error still propagates as a failure rather than being counted as a skip. Found while working out why 79 containers in one cluster have a ConfigAuditReport and no VulnerabilityReport. Config audits need no scan job; vulnerability and exposed-secret reports do. Which of these four applies is still unknown, and that is the point -- there is no way to tell without rebuilding the operator or enabling dev-mode logging. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #1668. Independent of #3072 — that one counts reports rejected on write; this counts scans that were never attempted. Together they cover both ways a workload ends up with no report.
The gap
SubmitScanJobreturnsnilin four places:Each is a deliberate decision not to scan, and each leaves nothing behind — no scan job, no report, no event. Every existing metric is derived from report objects that exist, which by construction cannot cover a report that was never going to be created. So a skipped workload and a workload scanned clean are indistinguishable.
What this adds
reasonis a closed set of four values, so cardinality stays bounded:replicaset_not_foundno_running_podsunsupported_kindno_containersScanSkippedReasonreturns""for anything else, so an unrelated error still propagates as a failure instead of being silently counted as a skip.Deliberately not changing the log level
Promoting those
V(1)lines would be noisy —no_running_podsfires on every reconcile of a scaled-to-zero Deployment. A counter you can aggregate is the right shape; a line per event is not:Why I went looking
In one cluster, 79 containers have a
ConfigAuditReportand noVulnerabilityReport. Config audits need no scan job; vulnerability and exposed-secret reports do — and neither exists for them. Forcing a reconcile produced no scan job at all, while an unrelated new workload was scanned within ten seconds, so the operator is responsive. Every Deployment'sdeployment.kubernetes.io/revisionmatches its ReplicaSet, which rules outErrReplicaSetNotFoundvia a revision mismatch.Which of the four applies, I still don't know — and that is exactly the point. There is currently no way to find out short of enabling dev-mode logging or rebuilding the operator. I'd rather ship the instrument than guess at the cause.
Testing
%w, and that unrelated errors are not classified as skips) and for the counter viatestutil.CollectAndCompare.go build ./...,go vet, and the existingpkg/metrics,pkg/vulnerabilityreport,pkg/vulnerabilityreport/controllerandpkg/operatorsuites all pass on go1.26.7 withGOEXPERIMENT=jsonv2.docs/tutorials/integrations/metrics.md.No chart values changed, so no
mage generate:docsrun was needed.🤖 Generated with Claude Code