This is a small utility that extracts the public API types from upstream Kubernetes operators (e.g., OpenTelemetry Operator, ECK) and republishes them as version-pinned, standalone Go modules.
This allows downstream consumers to depend on operator APIs in a reproducible, conflict-free way, without inheriting the upstream operator's dependency tree or Go module drift.
Many Kubernetes operators ship their CRD Go types inside complex monolithic repositories. Importing those API types directly often pulls in:
- Huge dependency graphs
- Unwanted Kubernetes version constraints
- Internal packages that are not meant to be consumed
- Frequent breaking changes as upstream moves fast
- Non-reproducible builds when upstream bumps its own dependencies
operator-api-mirrorer fixes this.
It produces stable, minimal, isolated Go modules containing only:
- the selected API directories (e.g.
apis/*,pkg/apis/elasticsearch/*) - the internal packages required to build them
- rewritten imports so the API code is self-contained
- a minimal
go.modwith just the required dependencies - optional overrides for Kubernetes versions or other modules
This guarantees reproducible builds while allowing consumers to use exactly the API types they need.
For each operator defined in operators.yaml, the tool:
- Clones the upstream repository at the specified version.
- Copies the requested API directories and filters out all non-Go files and test files.
- Parses API imports to discover and copy required internal packages.
- Rewrites import paths to point to the mirror module.
- Generates a fresh
go.modbased on upstream requirements. - Applies dependency overrides from
operators.yamlusingreplace. - Runs
go mod tidyto prune unused dependencies. - Writes the final result to:
mirrors/<slug>/
Each mirrored API version is a fully standalone Go module. Upgrading causes an overwrite of the existing mirror, therefore you should create a git tag (mirrors//) for each mirror you create.
operators:
- slug: otel-operator
repo: github.com/open-telemetry/opentelemetry-operator
currentVersion: v0.138.0
goModPath: go.mod
apiPaths:
- "apis/*"
overwriteDependencies:
- name: k8s.io/api
version: v0.32.10
- name: k8s.io/apimachinery
version: v0.32.10
- name: sigs.k8s.io/controller-runtime
version: v0.20.4
- slug: eck-operator
repo: github.com/elastic/cloud-on-k8s
currentVersion: v3.2.0
goModPath: go.mod
apiPaths:
- "pkg/apis/elasticsearch/*"
overwriteDependencies:
- name: k8s.io/api
version: v0.32.10
- name: k8s.io/apimachinery
version: v0.32.10The operator-api-mirrors repository contains ready-made mirrors that do not overwrite or pin dependencies differently from the upstream operators.
If you need custom dependency overrides (for example, to keep your Kubernetes libraries at specific versions different from what the upstream operator uses), you can create your own mirror repository using this Go tool as a library and CLI.
From another repo (your own mirror repo) you can install the CLI globally:
go install github.com/sourcehawk/operator-api-mirrorer/cmd/mirrorer@latestThis will put a mirrorer binary in your $GOBIN (usually ~/go/bin).
Or, if you’re working directly inside a clone of this repo:
make buildMirroring operator APIs is done via the mirror subcommand.
At minimum, you must specify your Go module prefix via --gitRepo.
This is the base of the module path used when writing the go.mod for each mirrored operator version:
module <gitRepo>/mirrors/otel-operator
./mirrorer mirror --gitRepo="github.com/sourcehawk/operator-api-mirrorer"This generates mirrored modules under:
mirrors/<operator>/
Example layout:
mirrors/
└── otel-operator/
├── apis/
├── internal/
├── pkg/
└── go.mod
The tool uses the following subcommands:
mirrorer mirror # generate/update mirrors
mirrorer tag # create tags for mirrored operator versions
| Flag | Default | Description |
|---|---|---|
--config |
operators.yaml |
Path to your operator definitions |
--mirrorsPath |
./mirrors |
Output directory for all generated mirrors |
--gitRepo |
required | Root Go module path (used inside generated module go.mod files) |
--target |
empty | Optional operator slug to mirror only that operator |
./mirrorer mirror \
--config ./operators.yaml \
--mirrorsPath ./mirrors \
--gitRepo github.com/my-org/operator-api-mirrorAfter mirrors have been generated and merged into main, you can create lightweight git tags for each defined operator version using:
./mirrorer tag \
--config ./operators.yaml \
--mirrorsPath ./mirrorsThis command:
- reads
operators.yaml - finds each operator’s current version
- creates tags of form:
mirrors/<operator>/<version>
Pushing tags is handled separately (e.g., in CI):
git push --tagsoverwriteDependencies applies replace-only overrides in go.mod:
replace k8s.io/apimachinery => k8s.io/apimachinery v0.32.10
replace k8s.io/api => k8s.io/api v0.32.10These do not modify upstream requirements; they simply redirect them.
This ensures:
- consistent Kubernetes versions across all mirrored modules
- maximum compatibility for downstream controllers
- no accidental dependency bumps caused by upstream operators
Because:
- Operators have huge transitive dependency trees
- Many require pinned Kubernetes versions that break your project
- They often pull in unrelated internal code
- They are not intended to be library-friendly
Using a mirror gives you:
- minimal deps
- reproducible API versions
- clean import paths
- stable builds
Some operator API code references internal utilities (e.g. ptr, hash, version).
We copy only what is needed and rewrite the import paths so everything is self-contained.
All upstream APIs are open-source (Apache 2.0); the mirror preserves licenses verbatim.
Contributions welcome!
Apache 2.0