Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save danielfm/2e63a07dff855bf6e5e22c79c6044885 to your computer and use it in GitHub Desktop.

Select an option

Save danielfm/2e63a07dff855bf6e5e22c79c6044885 to your computer and use it in GitHub Desktop.

Revisions

  1. @iyalang iyalang created this gist Jun 28, 2024.
    273 changes: 273 additions & 0 deletions pdb-validation-policy.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,273 @@
    ---
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: pdb-validation
    annotations:
    policies.kyverno.io/title: Check PodDisruptionBudget minAvailable and maxUnavailable
    policies.kyverno.io/category: Other
    policies.kyverno.io/subject: PodDisruptionBudget, Deployment, StatefulSet
    policies.kyverno.io/description: >-
    This policy ensures that all PodDisruptionBudgets (PDBs) are configured to allow for some disruptions.
    spec:
    validationFailureAction: Enforce
    background: false
    rules:
    # Check if maxUnavailable is > 0
    - name: pdb-maxunavailable
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    validate:
    message: "The value of maxUnavailable must be greater than zero."
    deny:
    conditions:
    any:
    - key: '{{ request.object.spec.maxUnavailable || "null" }}'
    operator: Equals
    value: 0
    - key: '{{ request.object.spec.maxUnavailable || "null" }}'
    operator: Equals
    value: "0%"
    # Check if minAvailable != 100%
    - name: pdb-minavailable-100-percent
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    validate:
    message: "minAvailable should not be 100% since that prevents all disruptions."
    deny:
    conditions:
    any:
    - key: '{{ request.object.spec.minAvailable || "none" }}'
    operator: Equals
    value: "100%"
    # Check if minAvailable != the number of Deployment replicas
    - name: pdb-minavailable-deployments
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: replicasDeployment
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/deployments'
    # This "none":"none" map is added to labels of deployments that have zero labels,
    # else the rule would fail with "failed to load context: Invalid type"
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.annotations."downscaler/original-replicas" || spec.replicas || "null"'
    - name: deploymentName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/deployments'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaCount
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{deploymentName}}'] | length(@)
    validate:
    message: >-
    The minAvailable value of the PDB should be lower than the number of replicas of the Deployment or StatefulSet.
    If your Deployment/StatefulSet has only one replica, please don't create any PDBs for it.
    deny:
    conditions:
    all:
    - key: '{{ request.object.spec.minAvailable || "0" }}'
    operator: GreaterThanOrEquals
    value: '{{ replicasDeployment }}'
    # Not checking min replicas if there is an HPA because replicas are set there
    - key: '{{ hpaCount }}'
    operator: Equals
    value: 0
    # Check if minAvailable != the number of StatefulSet replicas
    - name: pdb-minavailable-statefulsets
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: replicasStatefulSet
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/statefulsets'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.annotations."downscaler/original-replicas" || spec.replicas || "null"'
    - name: statefulSetName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/statefulsets'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaCount
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{statefulSetName}}'] | length(@)
    validate:
    message: >-
    The minAvailable value of the PDB should be lower than the number of replicas of the Deployment or StatefulSet.
    If your Deployment/StatefulSet has only one replica, please don't create any PDBs for it.
    deny:
    conditions:
    all:
    - key: '{{ request.object.spec.minAvailable || "0" }}'
    operator: GreaterThanOrEquals
    value: '{{ replicasStatefulSet }}'
    - key: '{{ hpaCount }}'
    operator: Equals
    value: 0
    # Check if Deployment has more than 1 replica
    - name: pdb-minavailable-deployments-one-replica
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: replicasDeployment
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/deployments'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.annotations."downscaler/original-replicas" || spec.replicas || "null"'
    - name: deploymentName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/deployments'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaCount
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{deploymentName}}'] | length(@)
    validate:
    message: >-
    Deployments or StatefulSets that have only 1 replica are not allowed to have PDBs since this would prevent any disruptions.
    deny:
    conditions:
    all:
    - key: '{{ replicasDeployment }}'
    operator: Equals
    value: 1
    - key: '{{ hpaCount }}'
    operator: Equals
    value: 0
    # Check if StatefulSet has more than 1 replica
    - name: pdb-minavailable-statefulsets-one-replica
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: replicasStatefulSet
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/statefulsets'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.annotations."downscaler/original-replicas" || spec.replicas || "null"'
    - name: statefulSetName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/statefulsets'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaCount
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{statefulSetName}}'] | length(@)
    validate:
    message: >-
    Deployments or StatefulSets that have only 1 replica are not allowed to have PDBs since this would prevent any disruptions.
    deny:
    conditions:
    all:
    - key: '{{ replicasStatefulSet }}'
    operator: Equals
    value: 1
    - key: '{{ hpaCount }}'
    operator: Equals
    value: 0
    # Check if minAvailable != minReplicas in the HPA (if it exists)
    - name: pdb-minavailable-hpa-deployments
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: deploymentName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/deployments'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaMinReplicasDeployment
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{deploymentName}}'] | [0] | metadata.annotations."downscaler/original-replicas" || spec.minReplicas
    validate:
    message: >-
    The HPA for the corresponding Deployment or StatefulSet has its minReplicas equal to the minAvailable value of the PDB
    which is not permitted. If minReplicas of the HPA equals 1, please don't create any PDBs.
    deny:
    conditions:
    any:
    - key: '{{ request.object.spec.minAvailable || "0" }}'
    operator: GreaterThanOrEquals
    value: '{{ hpaMinReplicasDeployment }}'
    # Check if minAvailable != minReplicas in the HPA (if it exists)
    - name: pdb-minavailable-hpa-statefulsets
    match:
    any:
    - resources:
    kinds:
    - PodDisruptionBudget
    preconditions:
    any:
    - key: '{{request.operation}}'
    operator: NotEquals
    value: DELETE
    context:
    - name: statefulSetName
    apiCall:
    urlPath: '/apis/apps/v1/namespaces/{{request.namespace}}/statefulsets'
    jmesPath: 'items[?label_match(`{{request.object.spec.selector.matchLabels}}`, spec.template.metadata.labels || {"none":"none"})] | [0] | metadata.name || "null"'
    - name: hpaMinReplicasStatefulSet
    apiCall:
    urlPath: '/apis/autoscaling/v2/namespaces/{{request.namespace}}/horizontalpodautoscalers'
    jmesPath: items[?spec.scaleTargetRef.name=='{{statefulSetName}}'] | [0] | metadata.annotations."downscaler/original-replicas" || spec.minReplicas || "null"
    validate:
    message: >-
    The HPA for the corresponding Deployment or StatefulSet has its minReplicas equal to the minAvailable value of the PDB
    which is not permitted. If minReplicas of the HPA equals 1, please don't create any PDBs.
    deny:
    conditions:
    any:
    - key: '{{ request.object.spec.minAvailable || "0" }}'
    operator: GreaterThanOrEquals
    value: '{{ hpaMinReplicasStatefulSet }}'