You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reject storage version migrator in namespace scope
The StorageVersionMigrator watches cluster-scoped CustomResourceDefinitions
and re-stores custom resources across all namespaces, so it requires
cluster-scoped RBAC and a cluster-scoped manager cache. A namespace-scoped
operator gets only per-namespace RoleBindings and a namespace-restricted
cache, so the controller cannot sync its CRD informer and wedges the
manager — which is what broke the multi-tenancy chainsaw E2E once the
feature was defaulted on.
Add a Helm validation that fails the render when storageVersionMigrator is
true under operator.rbac.scope=namespace, with an actionable message. Fail
loudly rather than silently dropping the feature: a silent drop would let a
namespace-scoped admin believe storedVersions are being kept clean when they
are not, leading to the skip-a-version upgrade trap later.
- Add validateStorageVersionMigrator helper; invoke it from deployment.yaml
- Opt the namespace-scoped multi-tenancy chainsaw setup out of the migrator
- Pin the rejection and the supported namespace opt-out in helm unittests;
disable the migrator in the namespace-scope suite's shared values
- Document the cluster-scope requirement and the standalone-migrator
alternative for namespace-scoped clusters
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: deploy/charts/operator/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,7 +61,7 @@ The command removes all the Kubernetes components associated with the chart and
61
61
| operator.defaultRedis.existingSecretKey | string |`""`| existingSecretKey is the key within existingSecret that holds the password. Empty means use global.redis.existingSecretKey or fall back to "redis-password". |
62
62
| operator.env | list |`[]`| Environment variables to set in the operator container. Supported toolhive-specific variables include: - TOOLHIVE_SKIP_UPDATE_CHECK: set to "true" to disable the operator's periodic update check against the ToolHive update API. Also disables the usage-metrics collection that is gated on the same check. |
63
63
| operator.features.experimental | bool |`false`| Enable experimental features |
64
-
| operator.features.storageVersionMigrator | bool |`true`| Enable the StorageVersionMigrator controller, which auto-cleans status.storedVersions on opted-in toolhive.stacklok.dev CRDs so a future release can drop deprecated versions (e.g. v1alpha1) without orphaning etcd objects in the cluster. Enabled by default; set to false to opt out and handle storage-version cleanup yourself. Sets TOOLHIVE_ENABLE_STORAGE_VERSION_MIGRATOR in the operator deployment. |
64
+
| operator.features.storageVersionMigrator | bool |`true`| Enable the StorageVersionMigrator controller, which auto-cleans status.storedVersions on opted-in toolhive.stacklok.dev CRDs so a future release can drop deprecated versions (e.g. v1alpha1) without orphaning etcd objects in the cluster. Enabled by default; set to false to opt out and handle storage-version cleanup yourself. Sets TOOLHIVE_ENABLE_STORAGE_VERSION_MIGRATOR in the operator deployment. Requires `operator.rbac.scope=cluster` — the controller watches cluster-scoped CRDs and re-stores resources across all namespaces, so the chart rejects this being true when scope is namespace. |
65
65
| operator.gc | object |`{"gogc":75,"gomemlimit":"110MiB"}`| Go memory limits and garbage collection percentage for the operator container |
66
66
| operator.gc.gogc | int |`75`| Go garbage collection percentage for the operator container |
67
67
| operator.gc.gomemlimit | string |`"110MiB"`| Go memory limits for the operator container |
{{- fail "operator.features.storageVersionMigrator requires operator.rbac.scope=cluster: the StorageVersionMigrator controller watches cluster-scoped CustomResourceDefinitions and re-stores resources across all namespaces, which a namespace-scoped operator cannot do. Set operator.features.storageVersionMigrator=false for namespace-scoped installs." -}}
Copy file name to clipboardExpand all lines: docs/operator/storage-version-migration.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -83,6 +83,19 @@ operator:
83
83
84
84
Once enabled, the controller is dormant on CRDs whose `storedVersions` already equals `[<currentStorageVersion>]` — most of the time, most CRDs. It only does meaningful work when a CRD's stored-versions list is dirty (typically right after a graduation release).
85
85
86
+
### Requires cluster-scoped RBAC
87
+
88
+
The migrator only works when the operator runs cluster-scoped (`operator.rbac.scope=cluster`, the default). It watches cluster-scoped `CustomResourceDefinition` objects and re-stores custom resources across **all** namespaces — neither is possible for a namespace-scoped operator, which gets only per-namespace `RoleBindings` and a namespace-restricted manager cache. To prevent a silently wedged operator, the chart **fails the render** if `storageVersionMigrator: true` is combined with `operator.rbac.scope=namespace`:
89
+
90
+
```
91
+
operator.features.storageVersionMigrator requires operator.rbac.scope=cluster: the
92
+
StorageVersionMigrator controller watches cluster-scoped CustomResourceDefinitions and
93
+
re-stores resources across all namespaces, which a namespace-scoped operator cannot do.
94
+
Set operator.features.storageVersionMigrator=false for namespace-scoped installs.
95
+
```
96
+
97
+
Namespace-scoped installs must set `storageVersionMigrator: false` and handle storage-version cleanup by other means — e.g. running the standalone [`kube-storage-version-migrator`](https://github.com/kubernetes-sigs/kube-storage-version-migrator), which is a cluster-scoped component with its own identity, before any version-removal release.
98
+
86
99
## Per-CRD emergency escape hatch
87
100
88
101
Removing the label on a live cluster excludes that single CRD from migration immediately:
0 commit comments