Operator
The operator is the engine. It's a Kubernetes controller (built with controller-runtime, Apache-2.0) that keeps the logical mirror of the cluster up to date and runs operations on it. One pod runs two controllers:
- ClusterMirror — always on. Rebuilds the mirror on an interval and publishes the cluster's current risk.
- UpgradeAnalysis — only when the chart sets
upgrade.enabled=true. Computes the upgrade verdict for a target version.
What it does
- Collects a cluster snapshot: workloads, nodes, services, storage, PDBs, add-ons, deprecated APIs.
- Builds the dependency graph and propagates risk — on every mirror rebuild.
- Publishes the mirror's counters to the ClusterMirror
statusand its report (<name>.mirops) to the configured destination, overwriting the previous one. - Evaluates add-on compatibility against an analysis's target version using the embedded matrix.
- Scores readiness (Health · Capacity · Stability · Compatibility) and computes the deterministic verdict.
- Publishes the verdict to the UpgradeAnalysis
statusand its report (<name>.mirops) to the configured destination. - Serves reports back over an in-cluster HTTP endpoint (reading remote destinations on demand).
- Remediates — executes approved RemediationPlans.
Custom resources
The operator defines three cluster-scoped CRDs and installs them itself at startup (embedded via go:embed) — no separate CRD apply step. It creates none of the inputs for you:
| CRD | Purpose |
|---|---|
ClusterMirror | The always-on mirror: scope, refresh interval, report destination. Its status holds the counters and per-namespace risk. |
UpgradeAnalysis | What upgrade to analyze and how. Its status holds the verdict. |
RemediationPlan | AI-proposed fixes, gated on approval. |
See the full field reference in the CRD reference.
Minimal resources
apiVersion: mirops.mirops.io/v1
kind: ClusterMirror
metadata:
name: default
spec: {} # every field has a default
---
apiVersion: mirops.mirops.io/v1
kind: UpgradeAnalysis # needs upgrade.enabled=true in the chart
metadata:
name: to-1-35
spec:
targetVersion: "1.35" # the only required field
Configuration surface
| Resource | Area | Fields |
|---|---|---|
| ClusterMirror | Scope | scope.mode, scope.excludeNamespaces |
| ClusterMirror | Refresh | refresh.interval |
| ClusterMirror | Source | source.type (file / s3 / blob / pvc) + destination fields |
| UpgradeAnalysis | Target | targetVersion, scoringProfile |
| UpgradeAnalysis | Scope | scope.mode, scope.excludeNamespaces |
| UpgradeAnalysis | Source | source.type (file / s3 / blob / pvc) + destination fields |
| UpgradeAnalysis | AI | ai.enabled, ai.provider, ai.model, ai.maxTokens, ai.credentialsSecret, ai.remediation.* |
| UpgradeAnalysis | Resync | resync.interval |
Deterministic and reproducible
The operator recomputes the same verdict for the same mirror. An analysis reconciles on spec changes and on-demand refresh — not on its own status writes — so it doesn't loop, and (with AI on) it memoizes the result per unchanged snapshot to avoid redundant model calls. A mirror rebuilds on its interval, on a spec change, and on the mirops.io/refresh annotation. With leader election on (the chart's default), only the leader rebuilds and writes reports.
Built with
The operator is scaffolded with Kubebuilder and built on standard Kubernetes and cloud SDKs:
| Tool / library | Version |
|---|---|
| Go | 1.24 |
| Kubebuilder (project layout) | 4.10 · go.kubebuilder.io/v4 |
| controller-runtime | v0.22.4 |
controller-tools (controller-gen) | v0.19.0 |
Kubernetes libraries (client-go / api) | v0.34 |
| AWS SDK for Go v2 (S3 reports) | aws-sdk-go-v2 v1.41 · service/s3 v1.101 |
| Azure SDK for Go (Blob reports) | azidentity v1.13 · storage/azblob v1.6 |
Install
Install with the Helm chart, or jump to your first mirror and analysis.
- Source: github.com/miropshq/mirops