Skip to content

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.

yaml
spec:
  for: 1h
PhaseThe Smell is…
Pendingcreated, but its condition has held for less than for
Activeconfirmed: 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.

RowRun 1Run 2Run 3Run 4Run 5
Condition holdsNoYesNoNoNo
SmellNo smellPendingDeletedNo smellNo smell

Never becomes Active. Nobody is paged for a blip.

A real smell

Nothing refers to the resource, and nothing is going to.

RowRun 1Run 2Run 3Run 4Run 5
Condition holdsNoYesYesYesYes
SmellNo smellPendingPendingActiveActive

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.

DurationA good fit forWhy
none or 0sMisconfigurationsThe defect is wrong as soon as it exists, such as a pooler named like a database cluster.
10mDangling references and missing companionsAn apply order settles within a sync: a Composition applied before its XRD, a backup schedule applied right after its Cluster.
1hResources referred to by GitOps-managed objectsA delete-and-recreate of the only referrer leaves the resource unused for a few minutes.
24hCatalogue entries and template rotationsDefinitions 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:

bash
kubectl get smells -A -l kubepattern.dev/phase=Active

The message of a pattern can also show them, with the {{smell.since}} and {{smell.phase}} placeholders:

yaml
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.