Help us shape the future of Go! Take the 2026 Go Developer Survey

cronguard

module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2026 License: Apache-2.0

README

CronGuard

CI Release Artifact Hub Go Report Card License Go Version

SLO-style observability for Kubernetes CronJobs.

CronGuard is a small Kubernetes operator that wraps any existing batch/v1.CronJob with a CronJobMonitor custom resource. It watches child Jobs, tracks execution history, and exposes Prometheus metrics so you can alert on missed runs, duration overruns, and consecutive failures without hand-rolling PromQL against kube-state-metrics.

Why

Kubernetes CronJobs fail silently in several common ways: a skipped run under concurrencyPolicy: Forbid, a Job that completes with no succeeded pods, control-plane drift pushing starts minutes late. Teams keep re-inventing the same fragile PromQL:

time() - kube_cronjob_status_last_successful_time{cronjob="..."} > 86400

CronGuard replaces that with a declarative SLO per CronJob.

Quickstart

Helm (OCI)
helm install cronguard oci://ghcr.io/dmazhukov/charts/cronguard \
  --version 0.3.3 \
  --namespace cronguard-system --create-namespace
Helm (GitHub Pages)
helm repo add cronguard https://dmazhukov.github.io/cronguard/
helm repo update
helm install cronguard cronguard/cronguard --version 0.3.3 \
  --namespace cronguard-system --create-namespace
Raw manifests
kubectl apply -f https://github.com/dmazhukov/cronguard/releases/download/v0.3.3/install.yaml

Apply a sample monitor:

kubectl apply -f config/samples/cronjob_example.yaml
kubectl apply -f config/samples/monitoring_v1alpha1_cronjobmonitor.yaml
kubectl get cronjobmonitors
# NAME                  SCHEDULE      LASTSUCCESS   CONSECFAILS   MISSED   READY   AGE
# nightly-settlement    0 2 * * *     5m            0             0        True    10m

See docs/distribution.md for the full installation reference and all configuration knobs.

Example

apiVersion: monitoring.cronguard.io/v1alpha1
kind: CronJobMonitor
metadata:
  name: nightly-settlement
spec:
  cronJobRef:
    name: nightly-settlement
  schedule: "0 2 * * *"
  timeZone: "Europe/Moscow"      # optional; falls back to CronJob.spec.timeZone, then UTC
  maxDurationSeconds: 1800       # SLO: finish within 30 minutes
  maxConsecutiveFailures: 2      # alert after 2 failures in a row
  alertAfterMissedRuns: 2        # alert after 2 missed starts
  gracePeriodSeconds: 60
  historyLimit: 10

Walkthrough

asciicast

Recorded against a real kind cluster. Replay locally with:

asciinema play docs/cast/install.cast

Or open the .cast file directly.

Metrics

The per-CronJob gauge family is labelled {namespace, name, cronjob} (name is the CronJobMonitor; cronjob is the referenced CronJob):

Metric Type Meaning
cronguard_last_success_timestamp_seconds gauge Unix time of last successful Job
cronguard_last_failure_timestamp_seconds gauge Unix time of last failed Job
cronguard_last_schedule_timestamp_seconds gauge Unix time of last Job start (success or failure)
cronguard_running_jobs gauge Currently-running Jobs owned by the watched CronJob
cronguard_next_expected_timestamp_seconds gauge Unix time of next expected run
cronguard_consecutive_failures gauge Consecutive failed runs
cronguard_missed_runs gauge Consecutive missed runs
cronguard_schedule_drift_seconds gauge Drift of most recent run
cronguard_last_duration_seconds gauge Duration of last completed run
cronguard_condition gauge 1/0/-1 per condition type/reason (extra labels type, reason)

Counters and operator self-metrics (different label sets):

Metric Type Labels Meaning
cronguard_missed_runs_total counter namespace, name Cumulative missed runs observed; drives the burn-rate alerts
cronguard_reconcile_total counter namespace, name, result Reconcile invocations per outcome (success/error/requeue)
cronguard_reconcile_duration_seconds histogram namespace, name Reconcile latency
cronguard_build_info gauge version, commit, build_date Always 1; carries build metadata (also the operator-up signal)

Example alerts (PromQL):

# No success in 25 hours for a daily CronJob
(time() - cronguard_last_success_timestamp_seconds) > 90000

# Execution SLO breached
cronguard_condition{type="ExecutionHealthy"} == 0

Dashboards

A pre-built Grafana dashboard ships under config/grafana/ — six panels covering the core CronGuard metrics. See the dashboards README for import instructions.

Architecture

  User applies CronJobMonitor YAML
           │
           ▼
    apiserver ──► Reconciler ──► computes SLO ──► patches .status
           ▲                            │
           └──── Job events (informer) ─┘

    /metrics scrape ──► Collector reads cache ──► emits cronguard_*

Roadmap

Shipped (v0.3.x, v0.4.0): CEL admission validation, burn-rate SLO alerts (cronguard_missed_runs_total), HA metrics deduplication (leader-only scrape), drift annotation re-stamping, bounded schedule math with proven DST semantics, the missed-run floor from CronJob.status (ADR 0001), attested release artifacts. See CHANGELOG.md for details.

Shipped (v0.2.x): operator core, CRD with timezone-aware schedules (spec.timeZone with fallback to CronJob.spec.timeZone), envtest suite, raw kubectl apply manifests, Prometheus metrics, Helm chart (OCI + GitHub Pages), Grafana dashboard, default PrometheusRule, ServiceMonitor, kind-based e2e, Artifact Hub listing.

v0.5 — Data-Freshness SLO (planned):

  • spec.freshness probe — a PromQL query that returns the timestamp of the last downstream data update, and a window. A job can exit 0 while writing nothing; this axis asks whether the data moved, not whether the Job ran. Surfaces as a DataFresh condition, a cronguard_data_freshness_seconds gauge and a CronGuardDataStale alert. It is the first outbound call the operator makes (to Prometheus), so it ships behind an ADR and is off unless a monitor asks for it.

Later, gated on adoption signal:

  • ClusterCronJobMonitor resource — cluster-scoped CRD that selects CronJobs by label/regex and applies a SLO template to all of them. Per-CronJob CronJobMonitor resources still override the cluster template when present.
  • API promotion v1alpha1 → v1beta1 — multi-version serving with v1beta1 as the storage version, after the spec fields above settle.

Considered, not yet planned:

  • Operator self-observability metrics (reconcile queue depth, leader-election state, status-patch conflicts)
  • Backfill of execution history from existing Jobs at first install
  • OLM bundle on OperatorHub (deferred until real OpenShift demand)
  • CNCF Sandbox application (gated on real-user adoption)

Development

# Run unit + envtest
make test

# Run the manager against your current kube-context
make run

# Build a local Docker image
make docker-build IMG=cronguard:dev

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md.

License

Apache 2.0 — see LICENSE.

Directories

Path Synopsis
api
v1alpha1
Package v1alpha1 contains API Schema definitions for the monitoring v1alpha1 API group.
Package v1alpha1 contains API Schema definitions for the monitoring v1alpha1 API group.
internal
history
Package history provides a ring-buffer merge for ExecutionRecord slices.
Package history provides a ring-buffer merge for ExecutionRecord slices.
leader
Package leader provides a label-updater that flips the operator pod's `cronguard.io/role` label between "leader" and "standby" based on controller-runtime's leader-election lifecycle.
Package leader provides a label-updater that flips the operator pod's `cronguard.io/role` label between "leader" and "standby" based on controller-runtime's leader-election lifecycle.
metrics
Package metrics implements a Prometheus custom collector for CronJobMonitor.
Package metrics implements a Prometheus custom collector for CronJobMonitor.
schedule
Package schedule parses cron expressions and provides helpers for computing expected run times, drift, and missed-run counts.
Package schedule parses cron expressions and provides helpers for computing expected run times, drift, and missed-run counts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL