Read reports
The operator writes two kinds of report, both JSON with a top-level kind:
| Report | Written by | Holds |
|---|---|---|
<name>.mirops | a ClusterMirror, on every rebuild | The cluster's current state and risk. |
<name>.mirops | an UpgradeAnalysis, on every run | The upgrade verdict for one target version. |
The mirror report
| Field | Use |
|---|---|
generatedAt, clusterVersion | When the mirror was rebuilt, and the version the cluster runs. |
summary | Components, edges, components at risk, namespaces at risk. |
atRisk | Each component with risk 50 or higher, what depends on it, and where its risk comes from (inheritedFrom). |
risk.byNamespace | Per namespace: highest component risk, components, components at risk. |
issues, pods, workloads | Live problems, pod health, and the inventory. |
addons | Add-ons detected (compatibility is the upgrade report's job). |
graph | The full dependency graph. |
upgrade | enabled — whether this install runs upgrade analysis — and analyses[]: each UpgradeAnalysis's name, target version, decision, score, and where its report is. |
The upgrade report
| Field | Use |
|---|---|
decision.level | The verdict — SAFE / WARNING / CRITICAL. |
decision.allow | Boolean gate — false means blocked. |
decision.blockers | Every reason it's CRITICAL. Fix them all. |
scores.total | The readiness gauge (0–100). Not the gate. |
addons | Each add-on's compatibility with the target version, and the version to upgrade to. |
upgradeImpact | For each add-on the target version breaks, everything that depends on it. |
workloads.*, risk, graph | The inventory at analysis time, as in 0.1.0. |
From the CLI
# the impact of a change, judged against the mirror report
mirops scan --source s3://my-reports/prod/default.mirops -f k8s/ -n payments --enforce
# a namespace's current state, from the mirror report
mirops scan --source s3://my-reports/prod/default.mirops -n payments
# the upgrade verdict
mirops scan --source s3://my-reports/prod/default.mirops \
--upgrade --upgrade-source s3://my-reports/prod/to-1-35.mirops
# machine-readable
mirops scan --source ./default.mirops -n all --output json | jq '.checks.namespaces'
--source is the mirror report. For the upgrade check, --upgrade-source is the analysis report — you give its URL; the CLI doesn't look it up. Pointing --source straight at a .mirops, as in 0.1.0, still gates on that analysis.
A local path reads a file on the CLI's own filesystem — not the operator's pod. A file-destination report lives inside the pod and is served over HTTP; read it with http://… (see below) or use a remote destination for CI outside the cluster.
From a remote destination
The CLI reads S3, Azure Blob, and HTTP directly, each with the pipeline's own credentials for its scheme:
# S3 (IRSA / env vars / static keys)
mirops scan --source s3://my-bucket/clusters/default.mirops -n all
# Azure Blob (az login / Workload Identity)
mirops scan --source azure://mystorageacct/reports/default.mirops -n all
# the operator's in-cluster HTTP endpoint — it reads any destination back for you
mirops scan --source "http://mirops-reports.mirops:8084/reports/default.mirops?kind=ClusterMirror" -n all
Azure Blob source format: azure://<storageaccount>/<container>/<blob>. S3: s3://<bucket>/<key>.
Configure with environment variables
Every flag has an environment-variable equivalent, MIROPS_<FLAG>, so a pipeline sets its config once and runs a bare mirops scan. Flags take precedence over env vars.
| Env var | Flag | Default | Example |
|---|---|---|---|
MIROPS_SOURCE | --source | — | s3://my-bucket/prod/default.mirops |
MIROPS_FILE | -f, --file | — | k8s/, deploy.yaml, - (stdin) |
MIROPS_NAMESPACE | -n, --namespace | — | payments, payments,checkout, all |
MIROPS_MAX_MIRROR_AGE | --max-mirror-age | 1h | 30m |
MIROPS_UPGRADE | --upgrade | false | true |
MIROPS_UPGRADE_SOURCE | --upgrade-source | — | s3://my-bucket/prod/to-1-35.mirops |
MIROPS_OUTPUT | --output | table | table | json |
MIROPS_ENFORCE | --enforce | false | true |
MIROPS_ENFORCE_LEVEL | --enforce-level | critical | critical | warning |
For remote sources, the usual cloud-credential env vars apply too — AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY for S3, or AZURE_CLIENT_ID / AZURE_TENANT_ID / AZURE_CLIENT_SECRET for Azure Blob. Or use a role (OIDC in CI, IRSA / Workload Identity in the cluster) and set nothing — see Credentials.
From Kubernetes status
You don't always need the full report — the headline numbers live on the CRs:
kubectl get clustermirror default \
-o jsonpath='{.status.atRisk} at risk, rebuilt {.status.lastSync}{"\n"}'
kubectl get upgradeanalysis to-1-35 \
-o jsonpath='{.status.decision} {.status.totalScore}{"\n"}'
From the HTTP endpoint
The mirops-reports service serves both reports, wherever they're stored:
kubectl -n mirops port-forward svc/mirops-reports 8084:8084
curl "http://localhost:8084/reports/default.mirops?kind=ClusterMirror" | jq '.summary'
curl http://localhost:8084/reports/to-1-35.mirops | jq '.decision'
In the Web UI
The Web UI renders both — the Cluster Mirror page from the mirror report, each analysis from its upgrade report — reading them through the operator's endpoint.