docs: fix flag defaults, drop phantom annotations, add chart README

Correct two wrong CLI flag defaults (leader-elect, verify-source-freshness) and add missing flags/Helm values. Remove documented-but-nonexistent annotations (excluded-namespaces, namespace-pattern) that silently did nothing, document the real exclude/paused and glob targeting, fix the mirror ownership-check example, and add the missing charts/kubemirror/README.md that README linked to.
This commit is contained in:
2026-06-21 13:27:00 +01:00
parent 28896ab1d6
commit 6a41222b34
3 changed files with 165 additions and 29 deletions
+40 -3
View File
@@ -271,6 +271,34 @@ metadata:
kubemirror.raczylo.com/allow-mirrors: "true"
```
### Pause or Exclude a Resource
Two annotations control opting a source out of mirroring:
- `kubemirror.raczylo.com/exclude: "true"` — opt the resource out entirely. It is
not mirrored, and any mirrors it previously created are **deleted**.
- `kubemirror.raczylo.com/paused: "true"`**freeze** the resource. Existing
mirrors are left exactly as they are (no updates, no cleanup) until the
annotation is removed. Useful during maintenance when you want mirrors to
persist but stop tracking source changes.
```yaml
apiVersion: v1
kind: Secret
metadata:
name: shared-credentials
namespace: default
labels:
kubemirror.raczylo.com/enabled: "true"
annotations:
kubemirror.raczylo.com/sync: "true"
kubemirror.raczylo.com/target-namespaces: "all"
# Freeze mirrors in place (or use /exclude to remove them):
kubemirror.raczylo.com/paused: "true"
data:
password: c2VjcmV0
```
### Mirror Custom Resources (CRDs)
KubeMirror works with any custom resource:
@@ -555,12 +583,17 @@ Complete configuration reference:
| **Resource Discovery** | | | |
| `controller.resourceTypes` | Explicit resource type list (empty = auto-discover all) | `[]` | `["Secret.v1", "ConfigMap.v1", "Ingress.v1.networking.k8s.io"]` |
| `controller.discoveryInterval` | Rediscovery interval for auto-discovery mode | `5m` | `10m`, `1h` |
| `controller.lazyWatcherInit` | Only watch resource types in use; lowers memory | `false` | `true`, `false` |
| `controller.watcherScanInterval` | Scan interval for new types in lazy mode | `5m` | `10m` |
| **Performance & Limits** | | | |
| `controller.leaderElect` | Enable leader election for HA | `true` | `true`, `false` |
| `controller.leaderElectionID` | Leader election lease name | `kubemirror-controller-leader` | |
| `controller.maxTargets` | Maximum mirrors per source resource | `100` | `50`, `200`, `500` |
| `controller.workerThreads` | Concurrent reconciliation workers | `5` | `10`, `20` |
| `controller.rateLimitQPS` | API rate limit (queries per second) | `50.0` | `100.0`, `200.0` |
| `controller.rateLimitBurst` | API burst allowance | `100` | `200`, `500` |
| `controller.resyncPeriod` | Full cache resync period | `10m` | `30m` |
| `controller.verifySourceFreshness` | Verify cache against a direct API read before mirroring | `false` | `true`, `false` |
| **Namespace Filtering** | | | |
| `controller.excludedNamespaces` | Comma-separated namespace exclusion list | `""` | `kube-system,kube-public,kube-node-lease` |
| `controller.includedNamespaces` | Comma-separated namespace inclusion list | `""` | `app-*,prod-*` |
@@ -579,15 +612,19 @@ When running the binary directly:
**Resource Discovery:**
- `--resource-types string` - Comma-separated list (e.g., `Secret.v1,ConfigMap.v1,Ingress.v1.networking.k8s.io`)
- `--discovery-interval duration` - Rediscovery interval (default: 5m)
- `--discovery-interval duration` - Rediscovery interval for auto-discovery mode (default: 5m)
- `--lazy-watcher-init` - Only create watchers for resource types actually in use; lowers memory (default: false)
- `--watcher-scan-interval duration` - Scan interval for new types in lazy mode (default: 5m)
**Performance & Limits:**
- `--leader-elect` - Enable leader election (default: true)
- `--leader-elect` - Enable leader election (default: false)
- `--leader-election-id string` - Leader election lease name (default: kubemirror-controller-leader)
- `--max-targets int` - Max mirrors per source (default: 100)
- `--worker-threads int` - Concurrent workers (default: 5)
- `--rate-limit-qps float32` - API rate limit (default: 50.0)
- `--rate-limit-burst int` - API burst limit (default: 100)
- `--verify-source-freshness` - Verify cache freshness before mirroring (default: false)
- `--resync-period duration` - Full cache resync period (default: 10m)
- `--verify-source-freshness` - Verify cache against a direct API read before mirroring (default: true)
**Namespace Filtering:**
- `--excluded-namespaces string` - Comma-separated exclusion list
+83
View File
@@ -0,0 +1,83 @@
# kubemirror Helm chart
Deploys the [kubemirror](https://github.com/lukaszraczylo/kubemirror) controller,
which mirrors Kubernetes resources (Secrets, ConfigMaps, and other types) across
namespaces.
- Chart version: `0.1.0`
- App version: `0.1.0`
- Image: `ghcr.io/lukaszraczylo/kubemirror`
## Install
```bash
helm install kubemirror ./charts/kubemirror -n kubemirror --create-namespace
```
With a values override:
```bash
helm install kubemirror ./charts/kubemirror -n kubemirror --create-namespace \
-f my-values.yaml
```
## Uninstall
```bash
helm uninstall kubemirror -n kubemirror
```
## What it creates
A single-replica `Deployment`, a `ServiceAccount`, a cluster-scoped `ClusterRole`
+ `ClusterRoleBinding` (the controller watches resources across all namespaces),
and a `Service` exposing the metrics (`8080`) and health (`8081`) ports. The
container runs as non-root (uid `65532`) with a read-only root filesystem and all
Linux capabilities dropped.
## Values
Defaults are taken from [`values.yaml`](values.yaml).
| Key | Default | Description |
| --- | --- | --- |
| `replicaCount` | `1` | Number of controller replicas. |
| `image.repository` | `ghcr.io/lukaszraczylo/kubemirror` | Controller image. |
| `image.tag` | `"0.1.0"` | Image tag (falls back to chart `appVersion` if empty). |
| `image.pullPolicy` | `IfNotPresent` | Image pull policy. |
| `serviceAccount.create` | `true` | Create a ServiceAccount. |
| `serviceAccount.name` | `""` | Override the generated ServiceAccount name. |
| `controller.metricsBindAddress` | `":8080"` | `--metrics-bind-address`. |
| `controller.healthProbeBindAddress` | `":8081"` | `--health-probe-bind-address`. |
| `controller.leaderElect` | `true` | Enable leader election (`--leader-elect`). |
| `controller.leaderElectionID` | `"kubemirror-controller-leader"` | Lease name (`--leader-election-id`). |
| `controller.resourceTypes` | `[]` | Resource types to mirror (`--resource-types`). Empty enables auto-discovery. |
| `controller.discoveryInterval` | `"5m"` | Auto-discovery interval (`--discovery-interval`). |
| `controller.resyncPeriod` | `"10m"` | Cache resync period (`--resync-period`). |
| `controller.maxTargets` | `100` | Max target namespaces per resource (`--max-targets`). |
| `controller.workerThreads` | `5` | Concurrent reconcile workers (`--worker-threads`). |
| `controller.rateLimitQPS` | `50.0` | API QPS limit (`--rate-limit-qps`). |
| `controller.rateLimitBurst` | `100` | API burst limit (`--rate-limit-burst`). |
| `controller.verifySourceFreshness` | `false` | Verify cache against a direct API read (`--verify-source-freshness`). |
| `controller.lazyWatcherInit` | `false` | Only watch resource types actually in use (`--lazy-watcher-init`). Lowers memory; recommended for production. |
| `controller.watcherScanInterval` | `"5m"` | Scan interval for new types in lazy mode (`--watcher-scan-interval`). |
| `controller.excludedNamespaces` | `""` | Comma-separated namespaces never mirrored to (`--excluded-namespaces`). |
| `controller.includedNamespaces` | `""` | Comma-separated namespace patterns to include (`--included-namespaces`). |
| `service.type` | `ClusterIP` | Service type. |
| `service.metricsPort` | `8080` | Metrics port. |
| `service.healthPort` | `8081` | Health-probe port. |
| `resources.requests` | `100m` CPU / `128Mi` | Container requests. |
| `resources.limits` | `500m` CPU / `512Mi` | Container limits. |
| `nodeSelector` / `tolerations` / `affinity` | `{}` / `[]` / `{}` | Standard scheduling controls. |
| `priorityClassName` | `""` | Pod priority class. |
> **Memory tip:** setting `controller.resourceTypes` to the exact types you mirror
> (e.g. `["Secret.v1", "ConfigMap.v1"]`), or enabling `controller.lazyWatcherInit`,
> avoids creating watchers for unused resource types and cuts memory use
> substantially versus full auto-discovery.
## Usage
After install, mark a source resource for mirroring and opt a target namespace
in. See the [project README](../../README.md) and [examples](../../examples/) for
the full annotation/label reference and worked scenarios.
+42 -26
View File
@@ -113,13 +113,15 @@ kubectl get middleware headers -n namespace-3
Verify that mirrored resources have the correct ownership labels:
```bash
# Check labels on a mirrored secret
kubectl get secret shared-credentials -n namespace-3 -o yaml | grep -A 5 labels
# Inspect a mirrored secret's metadata
kubectl get secret shared-credentials -n namespace-3 -o yaml
# Should include:
# kubemirror.raczylo.com/mirrored: "true"
# kubemirror.raczylo.com/source-namespace: namespace-1
# kubemirror.raczylo.com/source-name: shared-credentials
# Labels should include:
# kubemirror.raczylo.com/managed-by: kubemirror
# kubemirror.raczylo.com/mirror: "true"
# Annotations should include:
# kubemirror.raczylo.com/source-namespace: namespace-1
# kubemirror.raczylo.com/source-name: shared-credentials
```
## Testing Update Propagation
@@ -213,26 +215,12 @@ kubectl get pods -n kubemirror-system
## Advanced Examples
### Mirror to All Except Specific Namespaces
```yaml
apiVersion: v1
kind: Secret
metadata:
name: almost-all
namespace: namespace-1
annotations:
kubemirror.raczylo.com/sync: "true"
kubemirror.raczylo.com/target-namespaces: "all"
kubemirror.raczylo.com/excluded-namespaces: "namespace-3"
labels:
kubemirror.raczylo.com/enabled: "true"
data:
key: dmFsdWU= # "value" in base64
```
### Pattern-Based Mirroring
`target-namespaces` accepts glob patterns (`*` and `?`, matched with Go's
`filepath.Match`), so you can target a family of namespaces without listing each
one. Comma-separate multiple patterns, e.g. `"app-*,prod-*"`.
```yaml
apiVersion: v1
kind: ConfigMap
@@ -241,14 +229,42 @@ metadata:
namespace: namespace-1
annotations:
kubemirror.raczylo.com/sync: "true"
kubemirror.raczylo.com/target-namespaces: "all"
kubemirror.raczylo.com/namespace-pattern: "app-.*"
# Mirror to every namespace whose name starts with "app-"
kubemirror.raczylo.com/target-namespaces: "app-*"
labels:
kubemirror.raczylo.com/enabled: "true"
data:
config: "value"
```
> **Note:** Target patterns are include-only; there is no "all except namespace
> X" target syntax. For the two real exclusion mechanisms:
>
> - To stop a single source resource from being mirrored (and tear down any
> mirrors it already created), set `kubemirror.raczylo.com/exclude: "true"` on
> that resource.
> - To stop specific namespaces from ever receiving mirrors cluster-wide, use the
> controller's `--excluded-namespaces` flag.
### Excluding a Specific Resource
```yaml
apiVersion: v1
kind: Secret
metadata:
name: do-not-mirror
namespace: namespace-1
annotations:
kubemirror.raczylo.com/sync: "true"
kubemirror.raczylo.com/target-namespaces: "all"
# Opt this resource out: it will not be mirrored even though sync is set.
kubemirror.raczylo.com/exclude: "true"
labels:
kubemirror.raczylo.com/enabled: "true"
data:
key: dmFsdWU=
```
## ExternalSecrets Integration
KubeMirror integrates seamlessly with the [ExternalSecrets Operator](https://external-secrets.io/) to distribute secrets from external stores (1Password, Vault, AWS Secrets Manager, etc.) across multiple namespaces.