Known Limitations¶
This page lists what CAPTF does not do, or does with a catch, as the code stands today. Each item says what the limit is, what follows from it, and where the detail is. A limit that has a workaround says so. For versions and what has and has not been tested, see Compatibility.
Maturity¶
- Pre-alpha. No release is published. Every API kind is
v1alpha1, and the module contract isv1alpha1and provisional: it may change before a real module has provisioned a cluster with it. See Project status. - No end-to-end run against a live management cluster has happened. The tests are unit tests that mock the Kubernetes API and Job execution; CI runs unit tests, lint and the offline verifications. Anything that needs a real cluster, a real cloud or a real provider is untested: see Compatibility.
Approvals and gates¶
- Only the
TerraformClusteris fully gated.applyPolicy: Manualand the destructive-plan guard exist on the cluster. A machine’s apply never waits for approval. A pool’s apply is guarded in one case only: when it renders changed cluster exports. See What is guarded. - A pool’s exports guard has limits. A destructive pool apply of changed exports is held: the pool keeps applying with the exports of its last successful apply until the change is approved. The guard needs the record of the last applied exports, which shares the durable Secret’s budget with the rendered inputs, and
clusterctl movere-runs a blocked plan once. Keep shared and destructive infrastructure in the cluster module, keep exports stable, and useprevent_destroyon what must not go. See Machine pools and Cluster outputs reach pools and machines. - An approval binds a plan, not the apply. It names the plan’s hash and the apply must plan the same again; it does not promise what the provider does. See An approval binds a plan.
Security¶
A module image can read every Secret in its namespace and forge a plan hash
- The module image can read the plan key. The per-object key that makes plan hashes unforgeable by accident is mounted read-only in the module’s container for plan and approved-apply Jobs. A hostile module can read it and forge a plan hash. It protects against drift, not against the module. See The plan key is readable by the module.
- The runner can read and write every Secret in its namespace. RBAC cannot scope the state backend’s access. Namespaces are the only tenant boundary. See Multi-Tenancy.
- No encryption at rest of its own. State, inputs and credential mirrors are Kubernetes Secrets. See No encryption at rest.
State¶
- OpenTofu state encryption is unsupported. An encrypted state reads as
StateReadable=False/StateEncrypted, and no backup of it is ever taken. See Unreadable State. - The state file version must be 4. Another version reads as
StateCorrupt. - Backups are not disaster recovery. They are in the same namespace, owned by the object, and rotated out after
--state-backups. See Disaster Recovery. - Owner references are repaired on the next reconcile, not at once. The controller owns a state chunk, backup, durable inputs Secret and plan key again on the next reconcile that finds no Job running. The gaps are narrow: nothing is re-owned while the object is paused (so
clusterctl moveis not raced), state chunks wait while a Job holds the run lease, and a restored Secret that still names an old UID can be garbage-collected before the first reconcile. See Disaster Recovery.
Jobs and leases¶
- The lease grace gap. If a
TerraformCluster’s inputs change while it waits for machine operations, the old leases are held by a Job that will never exist, and the new Job waits out the one-minute grace. It resolves itself. See The known gap. - No flag caps running Jobs. The concurrency flags cap reconciles per kind, not Jobs. The bounds are one Job per object and your quotas.
- No quota-specific condition. A ResourceQuota refusal shows as a reconcile error or a Job that never starts. See Production Readiness.
- A single manager watches one namespace or all of them. The leader-election lease name is fixed, so per-namespace manager instances do not work; use
--watch-filter. See Namespace scoping.
Deletion and move¶
- A paused object’s deletion waits. A paused object, or one under a paused Cluster, never runs a destroy.
Deletingsays so. This is by design, soclusterctl movecan delete source objects. See Pause stops a deletion. - A moved object in a deleted namespace looks never-applied. Status does not move, and the
captf.io/appliedmarker is on a Secret the namespace deletion removes. Its finalizer then comes off with nothing destroyed. See The known limit. - The credential source Secret does not move. Copy it to the target yourself. See clusterctl move.
Machine pools¶
- The Kubernetes Cluster Autoscaler is unsupported on pools. The controller supports cloud-native autoscaling in the module, driven by the pool’s min and max annotations, and writes the observed replicas back to
MachinePool.spec.replicas. The Cluster Autoscaler’sclusterapiprovider needs MachinePool Machines and drains nodes before it scales down, and neither exists. Its changes tospec.replicasare overwritten by the write-back. See Machine Pools. - MachinePool Machines are unsupported. Pool instances have no
Machineobjects, so aMachineHealthChecknever selects them. - The bootstrap Secret is not watched. A rotated token reaches the pool at its next reconcile, at the latest one membership-refresh interval later.