Use cases¶
Six worked examples, all shipped as-is in the repo under examples/. Each section below quotes the interesting bits; follow the links for the full manifests (the examples include namespace scaffolding, illustrative ConfigMaps/Secrets, etc.).
1. ConfigMap fan-out across namespaces¶
File: examples/configmap-fan-out-selector.yaml
The canonical "same config, many namespaces" case. One Projection mirrors the source into every namespace carrying a matching label.
apiVersion: projection.be0x74a.io/v1
kind: Projection
metadata:
name: app-config-fanout
namespace: default
spec:
source:
apiVersion: v1
kind: ConfigMap
name: app-config
namespace: default
destination:
namespaceSelector:
matchLabels:
projection.be0x74a.io/mirror: "true"
# name omitted → defaults to source.name ("app-config") in every matching namespace
Expected outcome: configmap/app-config appears in every labeled namespace with the same .data plus projection.be0x74a.io/owned-by: default/app-config-fanout. Edits to the source propagate to all destinations in ~100 ms. Labeling a new namespace triggers a reconcile and the destination appears there too; removing the label cleans up the destination.
Gotchas: if any matching namespace already has an unowned ConfigMap/app-config, the Projection reports DestinationWritten=False reason=DestinationConflict for that namespace (other namespaces still get their destinations). The conflict message identifies which namespace is problematic.
When to pick single namespace instead: if you need per-destination overlays (e.g. a tenant: label distinct per namespace), one Projection per destination is still the right shape — see examples/multiple-destinations-from-one-source.yaml.
2. Secret across namespaces¶
File: examples/secret-cross-namespace.yaml
The distribute-a-TLS-cert scenario — typically the source Secret is authored by cert-manager, external-secrets, or sealed-secrets. When the destination is a single namespace you can set namespace directly; when the cert should reach every labeled namespace, use namespaceSelector as in use case 1.
apiVersion: projection.be0x74a.io/v1
kind: Projection
metadata:
name: tls-into-app-prod
namespace: cert-manager
spec:
source:
apiVersion: v1
kind: Secret
name: shared-tls
namespace: cert-manager
destination:
namespace: app-prod
name: tls
Expected outcome: Secret/tls exists in app-prod with the source's type: kubernetes.io/tls and data. Rotating the source Secret (e.g. cert-manager renewing) propagates.
Gotchas: projection only mirrors. It does not encrypt, rotate, or audit access. Cluster-level Secret protections (encryption at rest, RBAC) still apply to the destination exactly as they would to any other Secret.
3. Service mirror¶
File: examples/service-mirror.yaml
Demonstrates Kind-aware stripping. A Service's spec.clusterIP is apiserver-allocated; naively copying it to another namespace would fail with spec.clusterIP: field is immutable.
apiVersion: projection.be0x74a.io/v1
kind: Projection
metadata:
name: api-into-team-b
namespace: default
spec:
source:
apiVersion: v1
kind: Service
name: api
namespace: default
destination:
namespace: team-b
Expected outcome: Service/api appears in team-b with a fresh clusterIP, its own clusterIPs, ipFamilies, ipFamilyPolicy — all allocated by the apiserver on create. Ports, selector, and type are copied verbatim.
kubectl get svc -n default api -o jsonpath='{.spec.clusterIP}' # e.g. 10.96.X.X
kubectl get svc -n team-b api -o jsonpath='{.spec.clusterIP}' # 10.96.Y.Y (different)
Gotchas: the destination Service has its own endpoints — if the selector doesn't match any Pods in team-b, the destination Service will have no endpoints. This is usually what you want for type: ExternalName-style workflows; it's rarely what you want for ClusterIP. Think about whether you really need Service mirroring or whether an ExternalName pointing at the source FQDN is a better fit.
4. Multiple destinations from one source¶
File: examples/multiple-destinations-from-one-source.yaml
Until label-selector fan-out lands (see Roadmap), declare one Projection per destination.
# Snippet — three tenants, three Projections, one source
- name: org-policy-tenant-a
spec:
destination: { namespace: tenant-a }
overlay: { labels: { tenant: tenant-a } }
- name: org-policy-tenant-b
spec:
destination: { namespace: tenant-b }
overlay: { labels: { tenant: tenant-b } }
- name: org-policy-tenant-c
spec:
destination: { namespace: tenant-c }
overlay: { labels: { tenant: tenant-c } }
Expected outcome: each tenant sees its own copy of org-policy tagged with tenant=<tenant-id>. Status is per-Projection — a DestinationConflict in tenant-c doesn't block tenant-a/tenant-b from reconciling.
Gotchas: duplicates your YAML. Kustomize / Helm templating / GitOps generators are your friend here until multi-destination lands.
5. Overlay labels¶
File: examples/with-overlay-labels.yaml
Tag the destination with tenant/team/environment labels your observability stack can select on.
spec:
# ... source pointing at a ConfigMap with labels {env: prod, tenant: shared}
overlay:
labels:
projected-by: projection
tenant: tenant-a # overrides source's {tenant: shared}
Expected outcome: the destination carries {env: prod, projected-by: projection, tenant: tenant-a}. Source value of tenant: shared is overridden because overlay wins on conflict.
Gotchas: label removals are not expressible. You can only set or override. If you want the destination to lose a source label, you can't do that with overlay alone today.
6. Overlay annotations¶
File: examples/with-overlay-annotations.yaml
Same merge rules as labels — use it to stamp provenance:
spec:
overlay:
annotations:
mirror.example.com/source: platform/feature-flags
mirror.example.com/team: platform-eng
Expected outcome: destination annotations include both overlay entries plus the always-stamped projection.be0x74a.io/owned-by: <proj-ns>/<proj-name>.
Gotchas:
- Don't try to set
projection.be0x74a.io/owned-byvia overlay — the controller overwrites it on every reconcile. kubectl.kubernetes.io/last-applied-configurationis always stripped on the destination (carrying it would break three-way merge on laterkubectl applycalls against the destination).