A standalone Go library for rendering OLM registry+v1 bundles to plain Kubernetes manifests.
Extracted from operator-framework/operator-controller/internal/rukpak/render and compatible with operator-framework/operator-lifecycle-manager rendering behavior.
go get github.com/perdasilva/rv1package main
import (
"fmt"
"os"
"github.com/perdasilva/rv1"
)
func main() {
// Load a bundle from a directory on disk
source := rv1.FromFS(os.DirFS("path/to/bundle"))
bundle, err := source.GetBundle()
if err != nil {
panic(err)
}
// Build a renderer and render the bundle
renderer := rv1.NewRendererBuilder().Build()
objects, err := renderer.Render(bundle, "my-namespace",
rv1.WithTargetNamespaces("watch-ns"),
)
if err != nil {
panic(err)
}
fmt.Printf("Rendered %d objects\n", len(objects))
}The builder supports certificate providers for webhook TLS and deployment customization:
renderer := rv1.NewRendererBuilder().
WithCertificateProvider(rv1.CertManagerProvider{}).
WithDeploymentConfig(&rv1.DeploymentConfig{
NodeSelector: map[string]string{"kubernetes.io/os": "linux"},
}).
Build()The rv1 CLI renders bundles from the command line. It reads a bundle tar stream from stdin:
go install github.com/perdasilva/rv1/cmd/rv1@latest
# Render from a container image using docker
docker export $(docker create quay.io/my/bundle:v1 /bin/true) | rv1 render --install-namespace my-ns
# Or using crane
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace my-ns
# Render with watch namespaces
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace my-ns --watch-namespace ns1
# Render with a config file
crane export quay.io/my/bundle:v1 - | rv1 render --config render.yamlConfig file format (render.yaml):
certificateProvider:
type: cert-manager # or: openshift-service-ca, secret, none
deploymentConfig:
nodeSelector:
kubernetes.io/os: linuxThe secret provider generates a kubernetes.io/tls Secret with optional cert/key data:
certificateProvider:
type: secret
secret:
cert: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----If cert and key are omitted, an empty Secret is rendered so you can populate it externally (Vault, cert-manager ExternalSecret, etc.).
The library includes opt-in support for rendering behaviors from the original operator-lifecycle-manager (OLMv0).
Use WithProvidedAPIsClusterRoles() to generate aggregated admin/edit/view ClusterRoles for each owned CRD, matching OLMv0 behavior:
renderer := rv1.NewRendererBuilder().Build()
objs, err := renderer.Render(bundle, "my-namespace",
rv1.WithProvidedAPIsClusterRoles(),
)This generates 4 ClusterRoles per owned CRD:
<name>-<version>-admin— full access (*)<name>-<version>-edit— write access (create,update,patch,delete)<name>-<version>-view— read access (get,list,watch)<name>-<version>-crd-view— read access to the CRD definition itself
These roles use Kubernetes aggregated ClusterRoles so they're automatically included in the built-in admin/edit/view roles.
The --watch-namespace flag controls which namespaces the operator watches. The behavior depends on the bundle's supported install modes:
When the bundle supports AllNamespaces, omitting --watch-namespace watches all namespaces:
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace opsWatch only the install namespace — set --watch-namespace to the same value as --install-namespace:
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace ops --watch-namespace opsWatch a single namespace different from the install namespace:
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace ops --watch-namespace target-nsWatch multiple namespaces by repeating the flag:
crane export quay.io/my/bundle:v1 - | rv1 render --install-namespace ops --watch-namespace ns1 --watch-namespace ns2This library extracts and consolidates the registry+v1 bundle rendering logic from two operator-framework projects:
- operator-controller — the OLMv1 rendering pipeline (
internal/rukpak/render), which provides the coreBundleRenderer, validators, and resource generators - operator-lifecycle-manager — the OLMv0 rendering behaviors, available as opt-in options (e.g., provided API ClusterRoles)
The goal is upstream fidelity — rendering output matches the upstream implementations for the same inputs. The library includes regression tests with golden-file fixtures to verify this.
Requires Go 1.25+. Dev tools are managed via bingo and built automatically on first use.
make build # build the rv1 CLI binary
make test # run all unit tests
make lint # run golangci-lint
make fmt # run gofmt and goimports
make vet # run go vet
make verify # full quality gate (fmt + vet + lint + test)
make clean # remove build artifactsSee CONTRIBUTING.md for the full contributor guide.
Apache-2.0
