Skip to content

k8s-cleaner - Event and log-based resource selection

Introduction

Besides the resource's own spec/status (obj) and Prometheus metrics (metrics), k8s-cleaner can fetch two more live signals for each candidate resource before it is evaluated: the Kubernetes Events involving that resource, and — for Pods — the tail of its container logs. Both are opt-in and both are scoped to the evaluate script only (not transform, not the ResourcePolicySet-level aggregated selection function).

Set includeEvents: true to fetch recent Events for each candidate resource, exposed as the global events table. Set logSource (only meaningful when the ResourceSelector targets Pods) to fetch container log tails, exposed as the logs/logsByContainer globals (and logsPrevious/ logsPreviousByContainer when logSource.previous is set).

resourceSelectors:
- kind: Pod
  group: ""
  version: v1
  includeEvents: true
  logSource:
    tailLines: 100
  evaluate: |
    function evaluate(obj)
      hs = {}
      hs.matching = false
      if logs ~= nil and string.find(logs, "OutOfMemoryError") ~= nil then
        hs.matching = true
      end
      return hs
    end

Both fetches happen once per candidate resource — after the kind/group/version/namespace/labelFilters selection has already narrowed down the list, not once per scan. A fetch failure for one resource (e.g. a container that has since been removed) is logged and treated as "no data available" for that resource; it does not abort evaluation of the other candidates in the same ResourceSelector.


The events Global

events is an array, most recent first, of:

Field Description
reason Machine-readable reason, e.g. FailedMount, BackOff
message Human-readable event message
type Normal or Warning
count Number of occurrences the apiserver has deduplicated into this Event
lastTimestamp RFC 3339 timestamp of the most recent occurrence

This includes Events raised through the newer events.k8s.io/v1 API — the apiserver converts between the two representations, so nothing raised by a modern controller (or by k8s-cleaner itself) is missed.

The logs / logsByContainer Globals

logs concatenates every selected container's tail into one string — headered with ==> <container> <== only when the Pod has more than one selected container, so string.find(logs, "pattern") works whether the Pod has one container or five, and the script never has to name a container. When you do need to know which container matched, logsByContainer["<name>"] gives the same content split per container.

logSource.containers restricts which containers are fetched; leaving it empty (the default) fetches every container in the Pod. logSource.tailLines defaults to 50. Setting logSource.previous: true additionally populates logsPrevious/logsPreviousByContainer for every selected container that has actually restarted (RestartCount > 0) — useful for inspecting why a container in CrashLoopBackOff last exited, since its current instance's log may be empty or unrelated to the crash.

All four globals are always defined — empty string / empty table when logSource is unset, or when the ResourceSelector's kind is not Pod — so a script never needs a nil check before using them.


Example — Delete Pods With Repeated FailedMount Events

The following Cleaner deletes Pods that have accumulated more than 5 FailedMount Events — a Pod stuck unable to mount a volume is unlikely to recover on its own.

---
apiVersion: apps.projectsveltos.io/v1alpha1
kind: Cleaner
metadata:
  name: delete-pods-with-repeated-failedmount
spec:
  schedule: "*/10 * * * *"
  action: Delete
  resourcePolicySet:
    resourceSelectors:
    - kind: Pod
      group: ""
      version: v1
      includeEvents: true
      evaluate: |
        function evaluate(obj)
          hs = {}
          hs.matching = false
          for _, e in ipairs(events) do
            if e.reason == "FailedMount" and e.count > 5 then
              hs.matching = true
              hs.message = "repeated FailedMount events: " .. e.count
              break
            end
          end
          return hs
        end

Example — Delete Crash-Looping Pods Whose Previous Log Shows an OOM

The following Cleaner deletes Pods whose app container has restarted and whose previous instance's log (the one that actually crashed) mentions an out-of-memory error — the current instance's log, right after a fresh restart, would not show it yet.

---
apiVersion: apps.projectsveltos.io/v1alpha1
kind: Cleaner
metadata:
  name: delete-oom-crashlooping-pods
spec:
  schedule: "*/5 * * * *"
  action: Delete
  resourcePolicySet:
    resourceSelectors:
    - kind: Pod
      group: ""
      version: v1
      logSource:
        containers: ["app"]
        tailLines: 100
        previous: true
      evaluate: |
        function evaluate(obj)
          hs = {}
          hs.matching = logsPrevious ~= nil and
            string.find(logsPrevious, "OutOfMemoryError") ~= nil
          return hs
        end

RBAC

Reading Events (get/list/watch on events) and container logs (get on pods/log) is already granted to k8s-cleaner's default ClusterRole — no extra permissions are needed to use includeEvents or logSource.