Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: 1.0.5
name: k8spoddisruptionbudget
displayName: Pod Disruption Budget
createdAt: "2026-06-23T11:44:53Z"
description: |-
Disallow the following scenarios when deploying PodDisruptionBudgets or resources that implement the replica subresource (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet): 1. Deployment of PodDisruptionBudgets with .spec.maxUnavailable == 0 2. Deployment of PodDisruptionBudgets with .spec.minAvailable == .spec.replicas of the resource with replica subresource This will prevent PodDisruptionBudgets from blocking voluntary disruptions such as node draining.
https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
digest: 3a184a16d293c7c23740cce1101f28b03c9edf0d4af6d91195f4ad96062e4061
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/poddisruptionbudget
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# Pod Disruption Budget
Disallow the following scenarios when deploying PodDisruptionBudgets or resources that implement the replica subresource (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet): 1. Deployment of PodDisruptionBudgets with .spec.maxUnavailable == 0 2. Deployment of PodDisruptionBudgets with .spec.minAvailable == .spec.replicas of the resource with replica subresource This will prevent PodDisruptionBudgets from blocking voluntary disruptions such as node draining.
https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/poddisruptionbudget/1.0.5/template.yaml
```
provider:
name: Gatekeeper Library
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPodDisruptionBudget
metadata:
name: pod-distruption-budget
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet"]
- apiGroups: ["policy"]
kinds: ["PodDisruptionBudget"]
- apiGroups: [""]
kinds: ["ReplicationController"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-allowed-1
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
example: allowed-deployment-1
template:
metadata:
labels:
app: nginx
example: allowed-deployment-1
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-allowed-2
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
example: allowed-deployment-2
template:
metadata:
labels:
app: nginx
example: allowed-deployment-2
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-allowed-3
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
example: allowed-deployment-3
template:
metadata:
labels:
app: nginx
example: allowed-deployment-3
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-allowed-4
namespace: default
labels:
app: non-matching-nginx
spec:
replicas: 1
selector:
matchLabels:
app: non-matching-nginx
example: allowed-deployment-4
template:
metadata:
labels:
app: non-matching-nginx
example: allowed-deployment-4
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb-allowed
namespace: default
spec:
maxUnavailable: 1
selector:
matchLabels:
foo: bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-disallowed
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
example: disallowed-deployment
template:
metadata:
labels:
app: nginx
example: disallowed-deployment
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb-disallowed
namespace: default
spec:
maxUnavailable: 0
selector:
matchLabels:
foo: bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: inventory-nginx-pdb-allowed-1
namespace: default
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx
example: allowed-deployment-1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: inventory-nginx-pdb-allowed-2
namespace: default
spec:
maxUnavailable: 1
selector:
matchLabels:
app: nginx
example: allowed-deployment-2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: inventory-nginx-pdb-allowed-3
namespace: default
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: inventory-mongo-pdb-allowed-3
namespace: default
spec:
minAvailable: 2
selector:
matchLabels:
app: mongo
example: non-matching-deployment-3
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: inventory-nginx-pdb-disallowed
namespace: default
spec:
minAvailable: 3
selector:
matchLabels:
app: nginx
example: disallowed-deployment
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
metadata:
name: poddisruptionbudget
tests:
- name: pod-disruption-budget
template: template.yaml
constraint: samples/poddisruptionbudget/constraint.yaml
cases:
- name: example-allowed-pdb
object: samples/poddisruptionbudget/example_allowed_pdb.yaml
assertions:
- violations: no
- name: example-disallowed-pdb
object: samples/poddisruptionbudget/example_disallowed_pdb.yaml
assertions:
- violations: yes
- name: example-allowed-min-available
object: samples/poddisruptionbudget/example_allowed_deployment1.yaml
inventory:
- samples/poddisruptionbudget/example_inventory_allowed1.yaml
assertions:
- violations: no
- name: example-allowed-max-unavailable
object: samples/poddisruptionbudget/example_allowed_deployment2.yaml
inventory:
- samples/poddisruptionbudget/example_inventory_allowed2.yaml
assertions:
- violations: no
- name: example-allowed-subset-selector
object: samples/poddisruptionbudget/example_allowed_deployment3.yaml
inventory:
- samples/poddisruptionbudget/example_inventory_allowed3.yaml
assertions:
- violations: no
- name: example-allowed-nomatch-selector
object: samples/poddisruptionbudget/example_allowed_deployment4.yaml
inventory:
- samples/poddisruptionbudget/example_inventory_allowed4.yaml
assertions:
- violations: no
- name: example-disallowed-min-available
object: samples/poddisruptionbudget/example_disallowed_deployment.yaml
inventory:
- samples/poddisruptionbudget/example_inventory_disallowed.yaml
assertions:
- violations: yes
11 changes: 11 additions & 0 deletions artifacthub/library/general/poddisruptionbudget/1.0.5/sync.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: "gatekeeper-system"
spec:
sync:
syncOnly:
- group: "policy"
version: "v1"
kind: "PodDisruptionBudget"
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spoddisruptionbudget
annotations:
metadata.gatekeeper.sh/title: "Pod Disruption Budget"
metadata.gatekeeper.sh/version: 1.0.5
metadata.gatekeeper.sh/requires-sync-data: |
"[
[
{
"groups":["policy"],
"versions": ["v1"],
"kinds": ["PodDisruptionBudget"]
}
]
]"
description: >-
Disallow the following scenarios when deploying PodDisruptionBudgets or resources that implement the replica subresource (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet):
1. Deployment of PodDisruptionBudgets with .spec.maxUnavailable == 0
2. Deployment of PodDisruptionBudgets with .spec.minAvailable == .spec.replicas of the resource with replica subresource
This will prevent PodDisruptionBudgets from blocking voluntary disruptions such as node draining.

https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
spec:
crd:
spec:
names:
kind: K8sPodDisruptionBudget
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spoddisruptionbudget

violation[{"msg": msg}] {
input.review.kind.kind == "PodDisruptionBudget"
pdb := input.review.object

not valid_pdb_max_unavailable(pdb)
msg := sprintf(
"PodDisruptionBudget <%v> has maxUnavailable of 0, only positive integers are allowed for maxUnavailable",
[pdb.metadata.name],
)
}

violation[{"msg": msg}] {
obj := input.review.object
pdb := data.inventory.namespace[obj.metadata.namespace]["policy/v1"].PodDisruptionBudget[_]

matchLabels := { [label, value] | some label; value := pdb.spec.selector.matchLabels[label] }
labels := { [label, value] | some label; value := obj.spec.template.metadata.labels[label] }
count(matchLabels - labels) == 0

not valid_pdb_max_unavailable(pdb)
msg := sprintf(
"%v <%v> has been selected by PodDisruptionBudget <%v> but has maxUnavailable of 0, only positive integers are allowed for maxUnavailable",
[obj.kind, obj.metadata.name, pdb.metadata.name],
)
}

violation[{"msg": msg}] {
obj := input.review.object
pdb := data.inventory.namespace[obj.metadata.namespace]["policy/v1"].PodDisruptionBudget[_]

matchLabels := { [label, value] | some label; value := pdb.spec.selector.matchLabels[label] }
labels := { [label, value] | some label; value := obj.spec.template.metadata.labels[label] }
count(matchLabels - labels) == 0

not valid_pdb_min_available(obj, pdb)
msg := sprintf(
"%v <%v> has %v replica(s) but PodDisruptionBudget <%v> has minAvailable of %v, PodDisruptionBudget count should always be lower than replica(s), and not used when replica(s) is set to 1",
[obj.kind, obj.metadata.name, obj.spec.replicas, pdb.metadata.name, pdb.spec.minAvailable],
)
}

valid_pdb_min_available(obj, pdb) {
# default to -1 if minAvailable is not set so valid_pdb_min_available is always true
# for objects with >= 0 replicas. If minAvailable defaults to >= 0, objects with
# replicas field might violate this constraint if they are equal to the default set here
min_available := object.get(pdb.spec, "minAvailable", -1)
obj.spec.replicas > min_available
}

valid_pdb_max_unavailable(pdb) {
# default to 1 if maxUnavailable is not set so valid_pdb_max_unavailable always returns true.
# If maxUnavailable defaults to 0, it violates this constraint because all pods needs to be
# available and no pods can be evicted voluntarily
max_unavailable := object.get(pdb.spec, "maxUnavailable", 1)
max_unavailable > 0
}
Loading