Freeze a node pool
Prevent selected users from creating, updating, or deleting Nodes in a worker
pool every Sunday from 02:00 to 04:00 UTC. The pool is identified by the Node
label nodepool.example.com/name: workers; change that label key and value to
match your cluster.
Complete example
apiVersion: addons.projectcapsule.dev/v1alpha1
kind: ChangeWindow
metadata:
name: worker-pool-maintenance
spec:
schedules:
- name: sunday
cron: "0 2 * * 0"
timeZone: UTC
duration: 2h
targets:
- name: worker-pool
apiVersion: v1
kind: Node
selector:
matchLabels:
nodepool.example.com/name: workers
admission:
- name: freeze-nodes
targets: [worker-pool]
operations: [CREATE, UPDATE, DELETE]
audience:
include:
- kind: User
name: node-operator
- kind: Group
name: platform-developers
- kind: ServiceAccount
name: deployer
namespace: platform
exclude:
- kind: User
name: break-glass
when: Active
message: "Worker pool maintenance is active until {{ .window.end }}."
config:
admission:
warnings:
- name: upcoming
targets: [worker-pool]
operations: [CREATE, UPDATE, DELETE]
audience:
include:
- kind: Group
name: platform-developers
exclude:
- kind: User
name: break-glass
when: BeforeStart
within: 1h
message: "Worker pool maintenance begins at {{ .window.start }}."Download node-pool-maintenance.yaml
Who is affected
The denial applies to user node-operator, members of group
platform-developers, and ServiceAccount platform/deployer. The
break-glass user is excluded even when they belong to that group. Everyone
still needs normal Kubernetes RBAC permissions; an exclusion grants no access.
The advance warning has its own audience. Members of platform-developers
receive it on matching writes in the hour before maintenance, except for
break-glass. Audiences are independent on every denial and warning entry.
What is protected
The target selector matches labels on Nodes themselves. Omit
namespaceSelectors for a Node-only window because Nodes have no namespace.
During the active window, affected users cannot cordon a selected Node, change
its taints or labels, or delete it. Both old and new labels are checked, so
removing the pool label does not bypass the rule. Nodes in other pools and users
outside the audience remain unaffected by this ChangeWindow.
This protects writes to Node objects. It does not govern a cloud provider’s node pool API, a separate NodePool custom resource, or Pod eviction requests. Node status and lease updates are outside its scope. Node targets support admission rules and warnings; scheduled apply actions on Nodes are rejected.
Try the policy
After installing the controller, apply the downloaded manifest and inspect readiness:
kubectl apply -f node-pool-maintenance.yaml
kubectl wait --for=condition=Ready changewindow/worker-pool-maintenance --timeout=60s
kubectl get nodes -l nodepool.example.com/name=workersDuring a scheduled window, a user included in the audience and authorized to patch Nodes can verify the denial without changing the Node:
kubectl patch node <worker-node> --type=merge \
-p '{"spec":{"unschedulable":true}}' --dry-run=serverThe response contains Worker pool maintenance. The same write to an unselected
Node, or by an excluded user, is allowed by this policy. Dry runs use the same
matching and schedule as real writes. For a local test, temporarily use cron
* * * * * and duration 2m to make the window continuously active, then restore
the intended schedule.