Lock
Scope: namespaced · Guardrail
Lock protects another resource from being deleted by mistake — the same idea as an Azure CanNotDelete resource lock. While a Lock referencing a resource exists, any kubectl delete (or API delete) of that resource is rejected at admission time; you must delete the Lock first.
yaml
apiVersion: core.navique.com/v1alpha1
kind: Lock
metadata:
name: protect-forge-stack
namespace: forge
spec:
targetRef:
apiVersion: core.navique.com/v1alpha1
kind: Stack
name: forge
reason: "Production environment — remove this Lock deliberately before deleting."console
$ kubectl delete stack forge -n forge
Error from server (Forbidden): admission webhook ... denied the request:
resource is protected by Lock(s) [protect-forge-stack]; delete the Lock(s) before deleting this resourceHow it works
Kubernetes has no built-in resource lock, and a controller alone cannot stop a delete — by the time a controller sees it, the deletion is already irreversible. So the Lock is enforced where deletes can actually be rejected: the API server's admission stage. Two cooperating pieces:
- The Lock controller stamps the target with a
core.navique.com/lockedannotation listing the Locks that protect it. When the lastLockis removed, the annotation is cleared. - A cluster
ValidatingAdmissionPolicyrejects anyDELETEof acore.navique.comresource that carries a non-emptycore.navique.com/lockedannotation.
This requires Kubernetes 1.30+ (ValidatingAdmissionPolicy GA). The policy is installed with the operator (Helm value lockGuard.enabled, default true).
Fields
| Field | Required | Description |
|---|---|---|
targetRef.kind | yes | Kind of the protected resource, e.g. Stack, PostgresCluster, Gateway. |
targetRef.name | yes | Name of the target, in the same namespace as the Lock. |
targetRef.apiVersion | no | Defaults to core.navique.com/v1alpha1. Only the operator's API group is enforceable. |
reason | no | Free-text note, surfaced in status and in the denied-delete message. |
Behaviour & limits
- Same namespace, our group only. A
Lockcan only protect acore.navique.comresource in its own namespace. Other groups (and locking aLockitself) are rejected withActive: falseand anInvalidTargetreason. - Multiple locks stack. Several Locks may protect one target; it stays protected until the last one is deleted.
- Cascade deletes are blocked too. Deleting a
Stackwhose child is locked will be blocked for that child (garbage-collection deletes also pass through admission), leaving the Stack partially torn down. Remove the child'sLockfirst, or don't lock individual Stack-owned children if you want the Stack to delete cleanly — lock theStackinstead. - It's a guardrail, not a security control. Anyone with RBAC to delete the
Lock(or edit the target's annotation) can lift the protection. Use RBAC if you need a hard boundary. - Deletes only. A
Lockblocks deletion, not updates (Azure'sReadOnlymode is not implemented — it would also block the operator's own reconciles).
Status
| Field | Meaning |
|---|---|
status.active | true once the target carries the lock annotation (deletion is being rejected). |
status.conditions[Ready] | True when protecting; False with reason MissingReference (target not found yet) or InvalidTarget. |