Skip to main content

Readiness score vs. upgrade decision

Mirops reports two things on two separate axes, and keeping them separate is central to the design:

  • The readiness score (0–100) is a health gauge. Higher is healthier.
  • The decision (decision.level) is the upgrade gate. It is deterministic.

A healthy score never overrides a blocker, and a low score never invents one. A cluster can score 88 and still be CRITICAL (an add-on is incompatible); it can score 40 and only be WARNING (unstable, but nothing hard-blocks the drain).

This static view highlights the separation: readiness describes health, while deterministic blockers control the upgrade gate.

Static diagram showing the ClusterMirror branching into a readiness score gauge and deterministic blockers that control the upgrade gate

The verdict​

decision.level is the authoritative field. decision.allow and decision.blockers come with it:

LevelallowMeaning
SAFEtrueNo blocker, and the score meets the profile's SAFE threshold.
WARNINGtrueAttention required (e.g. score below the bar, unstable pods) but no deterministic blocker.
CRITICALfalseAt least one deterministic blocker — do not upgrade until it's fixed.
ERROR—The analysis could not run (e.g. targetVersion isn't higher than the current version).

decision.blockers lists every reason it's CRITICAL, so you fix them all — not just the first.

The readiness score​

The base score is the weighted sum of four dimensions (out of 100):

DimensionWeightMeasures
Health25Proportion of pods ready.
Capacity30CPU/memory headroom (requests / allocatable), split 15/15.
Stability20Crash-loops, restart trend, and pod drop since the last run.
Compatibility25Add-on compatibility with the target version.

When AI scoring is enabled, the final score is blended base × 0.7 + AI × 0.3. The AI only touches the gauge — it can never block or unblock an upgrade.

The blockers​

A CRITICAL verdict comes from deterministic conditions the engine can prove — not from the score:

  • A PodDisruptionBudget that would stall a node drain.
  • CPU pressure > 90% or memory pressure > 90% of allocatable.
  • Too many pods not ready (beyond the profile's ratio).
  • An incompatible add-on for the target version.
  • A Lost PersistentVolumeClaim.

Each of these blocks regardless of the score, because each would concretely break an upgrade in progress.

An example​

A cluster you want to move from 1.29 → 1.31: a payments Deployment (3 replicas) across two nodes, behind ingress-nginx 1.11, with TLS from cert-manager 1.16, a PodDisruptionBudget that keeps 2 of the 3 pods available, and a payments-db StatefulSet on a Bound PVC. Upgrade analysis reads the mirror against 1.31:

Signal in the mirrorWhat it tells the decision
Compatibilityingress-nginx 1.11 supports Kubernetes only up to 1.30 → incompatible with 1.31. cert-manager 1.16 supports up to 1.32 → fine.
Relationshipspayments sits behind ingress-nginx, so the incompatible add-on's risk propagates to it.
Disruptionthe PDB (min 2/3) still lets one pod drain at a time → the node drain won't stall.
Storagethe PVC is Bound → no data-availability blocker.

The incompatible add-on makes it CRITICAL, whatever the score: upgrade ingress-nginx to ≥1.12 first, then the move to 1.31 is safe. Run it again afterwards and the same mirror yields the same answer — deterministic.

Why two axes​

If the score were the gate, a team couldn't trust it: "why did it block today but not yesterday, with the same cluster?" By keeping the gate deterministic and the score a gauge, the verdict is reproducible and CI-safe — the same mirror always yields the same decision.

Next​