monedula-gitops docs menu
Safety gates: dry runs, approvals and pruning
How monedula-gitops gates risky changes: dry runs, --allow-destructive, --allow-delete and --prune approvals, and operator deletion policies.
On this page
Every change can be previewed: validate checks manifests without a running Kafka, and diff and apply --dry-run list the planned operations without changing anything. KafkaUser passwords are resolved only when an operation executes, never at plan time, so they never appear in a rendered plan.
Operations that need approval
Three kinds of operation run only with explicit approval. The CLI approves with flags on apply, the operator with annotations and fields on the resource:
| Gate | Covers | Without approval | CLI | Operator |
|---|---|---|---|---|
| Destructive | a partition increase, a replication factor change, a schema compatibility lowering (including a first-time level below the registry’s global default), a quota limit removal | reported as Blocked and not run; the rest of the plan runs |
--allow-destructive |
annotation gitops.monedula.dev/allow-destructive: "true" |
| Delete | Kafka cleanup when a KafkaTopic or KafkaAccessPolicy under deletionPolicy: Delete is deleted |
finalizer kept, DeleteNotApproved Warning event |
--allow-delete exists but has nothing to approve: the CLI never plans a deletion |
annotation gitops.monedula.dev/allow-delete: "true" |
| Prune | in-scope ACLs and role bindings that the manifests no longer declare | reported as PruneDisabled and kept |
--prune |
spec.prune: true |
Each gate is separate: --allow-destructive does not enable pruning, and the prune opt-in approves nothing else.
In the CLI, when Blocked operations are the only problem, apply exits 3: the plan is sound and waits for approval. In the operator, a blocked operation leaves the resource at Ready=False, and the operator emits an event.
Two changes never run, approved or not. A partition decrease is always rejected, because Kafka cannot shrink a topic. A replication factor change passes the gate but is then reported as Unsupported, because monedula-gitops cannot perform the partition reassignment it needs.
Pruning is opt-in
Pruning deletes in-scope ACLs and Confluent role bindings that are no longer desired, for example after you remove an operation from an access entry that still names the principal. Removing the whole entry takes that principal and topic pair out of the managed scope, so its ACLs are neither pruned nor reported. Pruning is off by default so that an accidentally truncated manifest cannot cut production access:
- Without the opt-in, a prune candidate is reported as
PruneDisabledbydiff,verifyandapply.verifycounts it as drift, but it is never deleted and it does not failapply. - In the operator, a candidate is deleted only when every resource whose scope covers it has
spec.prune: true; otherwise it stays asPruneDisableddrift.
Pruning never touches ACLs outside the managed scope.
Delete resources safely with --prune walks through a prune and lists what it leaves in place.
Deletion policies in the operator
When a custom resource is deleted, its finalizer, gitops.monedula.dev/finalizer, follows spec.deletionPolicy: Orphan leaves the Kafka objects in place, and Delete removes them.
| Kind | Default | Delete removes |
Needs allow-delete |
|---|---|---|---|
KafkaTopic |
Orphan |
the topic’s ACLs, then the topic, then the schema subjects whose content the topic manages | yes |
KafkaAccessPolicy |
Delete |
the policy’s managed ACLs | yes |
KafkaRoleBinding |
Delete |
its MDS role bindings | no |
KafkaUser |
Delete |
its SCRAM credential | no |
Delete keeps every ACL and role binding that another live resource still declares. The operator then emits a SharedACLsRetained or SharedRoleBindingsRetained Normal event that names up to three of those resources as Kind/namespace/name and counts the rest as “and N more”.
Role bindings and users need no annotation because their external state is their whole reason to exist: deleting the resource is itself the explicit action. A KafkaQuota has no deletion policy, and deleting it removes its quota.
If the operator cannot build a client for the cluster, because the KafkaCluster is gone or its credentials cannot be read, it keeps the finalizer and emits a DeletionBlocked Warning event. The annotation gitops.monedula.dev/force-finalizer-removal: "true" then removes the finalizer without cluster-side cleanup. When the KafkaCluster exists but Kafka is down or rejects the cleanup, that annotation is not consulted: a KafkaTopic or KafkaAccessPolicy retries with a DeleteFailed Warning event, and a KafkaRoleBinding with DeletionFailed, until the cleanup succeeds or you switch its deletionPolicy to Orphan. A KafkaQuota or KafkaUser emits a QuotaDeletionFailed or CredentialDeletionFailed Warning event and finishes the deletion anyway.
Execution never rolls back
apply is best-effort: independent operations continue past a failure, ACLs for a topic that failed to be created are skipped, and the next apply converges the rest. The CLI overview lists every operation outcome and exit code.
Related
- Delete topics from Kubernetes applies the deletion policies to a topic.
- Drift and reconciliation modes explains what counts as drift.