Lifecycle
Because Patterns and Smells are custom resources, the analysis has a lifecycle that a report produced by a single run does not have. A finding is created once, updated in place on every run, and deleted when the problem goes away.
Not in a tagged release yet
The minimum duration (for), the Pending and Active phases, spec.since, and the per-pattern pruning described on this page are on the main branch and arrive with the next release. The latest tagged release at the time of writing is v0.0.6, which keeps Smells updated in place but has none of these. To try them now, see Try unreleased features.
Identity
The name of a Smell is derived from its pattern and from the UID of its target. The same finding is therefore updated in place across runs instead of being created again, and kubectl get smells -A always lists the current problems.
First observation and the minimum duration
Each Smell records when it was first observed, in spec.since. That lets a pattern declare a minimum duration with for, with the same meaning that for has in Prometheus alerting rules.
spec:
for: 1h| Phase | The Smell is… |
|---|---|
Pending | created, but its condition has held for less than for |
Active | confirmed: its condition has held for at least for |
When the condition stops holding, the Smell is deleted, and the clock starts again if the condition comes back. A pattern without for has no waiting period, and its Smells are Active from the first observation.
Why it matters
A scan keeps no state between runs, so it cannot tell a problem from a moment. Take a resource that stays unreferenced for a few minutes while a GitOps tool deletes and re-creates its only referrer, or a widget that is created before the layout that will list it. A plain scan flags both. With for, they stay Pending and vanish.
A filter on the age of the object is not enough. It only helps with newly created objects, and does nothing when an old object briefly loses its referrer, as in the GitOps case.
Hourly CronJob · pattern with for: 90m
A transient state
A GitOps tool deletes and re-creates the only referrer of a resource. For a few minutes the resource looks unused.
| Row | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 |
|---|---|---|---|---|---|
| Condition holds | No | Yes | No | No | No |
| Smell | No smell | Pending | Deleted | No smell | No smell |
Never becomes Active. Nobody is paged for a blip.
A real smell
Nothing refers to the resource, and nothing is going to.
| Row | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 |
|---|---|---|---|---|---|
| Condition holds | No | Yes | Yes | Yes | Yes |
| Smell | No smell | Pending | Pending | Active | Active |
Becomes Active at run 4, the first run at least 90 minutes after the first observation.
What it costs
The price is detection latency. A real smell becomes Active at the first run after for has elapsed, so the delay is for rounded up to the next run. A transient that outlasts for is still reported, and a Pending Smell costs the same writes as an Active one, so for adds no extra request.
Choosing a duration
There is no universal value. Think about how long the state can legitimately last in your delivery pipeline, and give the pattern a little more than that. These starting points come from the patterns KubePattern was evaluated with.
| Duration | A good fit for | Why |
|---|---|---|
none or 0s | Misconfigurations | The defect is wrong as soon as it exists, such as a pooler named like a database cluster. |
10m | Dangling references and missing companions | An apply order settles within a sync: a Composition applied before its XRD, a backup schedule applied right after its Cluster. |
1h | Resources referred to by GitOps-managed objects | A delete-and-recreate of the only referrer leaves the resource unused for a few minutes. |
24h | Catalogue entries and template rotations | Definitions are published before their first consumer, and a rotation leaves a template unreferenced for one step. |
Keeping only what has persisted
The phase is mirrored in a label, so a selector can keep only the confirmed findings:
kubectl get smells -A -l kubepattern.dev/phase=ActiveThe message of a pattern can also show them, with the {{smell.since}} and {{smell.phase}} placeholders:
message: "Page {{target.metadata.name}} has not been referenced since {{smell.since}}."Self-healing
At the end of the evaluation of each pattern, the engine deletes only the Smells of that pattern whose target no longer satisfies the condition. This has three consequences.
- A fixed smell disappears at the next run, with no manual clean-up.
- A skipped pattern keeps its findings. If the analyzer cannot read one of the kinds a pattern needs, for example after the RBAC is narrowed, the Smells of that pattern stay as they were. An unreadable resource is never reported as "resolved".
- Overlapping or cut runs are safe. A run cut by its deadline, or two runs that overlap, do not delete each other's valid findings.
The Smells of a pattern that is no longer installed are removed at the end of the next run.
Suppression
You can mark a Smell as a false positive with spec.suppress. The suppression survives the following runs, because the engine keeps it when it updates a Smell. See Smells.