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
6 changes: 5 additions & 1 deletion helm/kagent-tools/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
#
# 2. Then visit http://127.0.0.1:{{.Values.service.ports.tools.port}}/mcp to use MCP
#
###############################################################################################
###############################################################################################{{- if and (.Values.podDisruptionBudget).enabled (not (include "kagent-tools.pdb.shouldRender" .)) }}

WARNING: podDisruptionBudget.enabled is true but replicaCount is {{ .Values.replicaCount }}.
The PodDisruptionBudget was not created; set replicaCount > 1 to enable it.
{{- end }}
39 changes: 39 additions & 0 deletions helm/kagent-tools/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,45 @@ over it would brick an install the value was never about.
{{- end -}}
{{- end -}}

{{/*
Prints "1" when a PDB budget value is set. null and "" mean unset; 0 and
percentage strings are valid budgets and must be kept.
*/}}
{{- define "kagent-tools.pdb.isSet" -}}
{{- if not (kindIs "invalid" .) -}}
{{- if ne (toString .) "" -}}1{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Kubernetes rejects a PodDisruptionBudget that sets both minAvailable and
maxUnavailable, and one that sets neither is meaningless, so both fail at
template time. Runs whenever the PDB is enabled, even if the replica guard
skips rendering, so misconfiguration surfaces before replicas are scaled up.
*/}}
{{- define "kagent-tools.pdb.validate" -}}
{{- $pdb := .Values.podDisruptionBudget | default dict -}}
{{- if $pdb.enabled -}}
{{- $hasMinAvailable := include "kagent-tools.pdb.isSet" $pdb.minAvailable -}}
{{- $hasMaxUnavailable := include "kagent-tools.pdb.isSet" $pdb.maxUnavailable -}}
{{- if and $hasMinAvailable $hasMaxUnavailable -}}
{{- fail "podDisruptionBudget: minAvailable and maxUnavailable are mutually exclusive. Set exactly one (to use minAvailable, set podDisruptionBudget.maxUnavailable=null)." -}}
{{- end -}}
{{- if not (or $hasMinAvailable $hasMaxUnavailable) -}}
{{- fail "podDisruptionBudget is enabled but neither minAvailable nor maxUnavailable is set. Set exactly one." -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
A PodDisruptionBudget is only rendered for more than one replica: with a single
replica it either protects nothing or blocks node drains entirely.
*/}}
{{- define "kagent-tools.pdb.shouldRender" -}}
{{- $pdb := .Values.podDisruptionBudget | default dict -}}
{{- if and $pdb.enabled (gt (int .Values.replicaCount) 1) -}}1{{- end -}}
{{- end -}}

{{/*
Pull secrets for the pod: the chart's own list merged (union) with
global.imagePullSecrets. Renders nothing when both are empty.
Expand Down
4 changes: 4 additions & 0 deletions helm/kagent-tools/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ metadata:
namespace: {{ include "kagent-tools.namespace" . }}
labels:
{{- include "kagent-tools.labels" . | nindent 4 }}
{{- with .Values.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
Expand Down
31 changes: 31 additions & 0 deletions helm/kagent-tools/templates/pdb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{{- include "kagent-tools.pdb.validate" . }}
{{- if include "kagent-tools.pdb.shouldRender" . }}
{{- $pdb := .Values.podDisruptionBudget }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "kagent-tools.fullname" . }}
namespace: {{ include "kagent-tools.namespace" . }}
labels:
{{- include "kagent-tools.labels" . | nindent 4 }}
{{- with $pdb.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $pdb.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if include "kagent-tools.pdb.isSet" $pdb.minAvailable }}
minAvailable: {{ $pdb.minAvailable }}
{{- else }}
maxUnavailable: {{ $pdb.maxUnavailable }}
{{- end }}
{{- with $pdb.unhealthyPodEvictionPolicy }}
unhealthyPodEvictionPolicy: {{ . }}
{{- end }}
selector:
matchLabels:
{{- /* Same helper as the Deployment's spec.selector, so the budget cannot drift from its pods. */}}
{{- include "kagent-tools.selectorLabels" . | nindent 6 }}
{{- end }}
20 changes: 20 additions & 0 deletions helm/kagent-tools/tests/deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,23 @@ tests:
value:
app.kubernetes.io/name: RELEASE-NAME
app.kubernetes.io/instance: RELEASE-NAME

- it: should not have deployment annotations by default
template: deployment.yaml
asserts:
- isNull:
path: metadata.annotations

- it: should render custom deployment annotations
template: deployment.yaml
set:
deploymentAnnotations:
keel.sh/policy: "force"
example.com/annotation: "test-value"
asserts:
- equal:
path: metadata.annotations["keel.sh/policy"]
value: "force"
- equal:
path: metadata.annotations["example.com/annotation"]
value: "test-value"
151 changes: 151 additions & 0 deletions helm/kagent-tools/tests/pdb_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
suite: test pod disruption budget
templates:
- pdb.yaml
tests:
- it: should not render pdb by default
asserts:
- hasDocuments:
count: 0

- it: should render default maxUnavailable of 1 when enabled with multiple replicas
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
asserts:
- isKind:
of: PodDisruptionBudget
- equal:
path: metadata.name
value: RELEASE-NAME
- equal:
path: spec.maxUnavailable
value: 1
- notExists:
path: spec.minAvailable
- notExists:
path: spec.unhealthyPodEvictionPolicy
- equal:
path: spec.selector.matchLabels
value:
app.kubernetes.io/name: RELEASE-NAME
app.kubernetes.io/instance: RELEASE-NAME

- it: should not render pdb with a single replica
set:
replicaCount: 1
podDisruptionBudget:
enabled: true
asserts:
- hasDocuments:
count: 0

- it: should still validate pdb settings with a single replica
set:
replicaCount: 1
podDisruptionBudget:
enabled: true
minAvailable: 1
asserts:
- failedTemplate:
errorMessage: "podDisruptionBudget: minAvailable and maxUnavailable are mutually exclusive. Set exactly one (to use minAvailable, set podDisruptionBudget.maxUnavailable=null)."

- it: should render minAvailable when maxUnavailable is nulled
set:
replicaCount: 3
podDisruptionBudget:
enabled: true
minAvailable: "50%"
maxUnavailable: null
asserts:
- equal:
path: spec.minAvailable
value: "50%"
- notExists:
path: spec.maxUnavailable

- it: should render minAvailable of zero
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
minAvailable: 0
maxUnavailable: null
asserts:
- equal:
path: spec.minAvailable
value: 0
- notExists:
path: spec.maxUnavailable

- it: should render maxUnavailable of zero
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
maxUnavailable: 0
asserts:
- equal:
path: spec.maxUnavailable
value: 0
- notExists:
path: spec.minAvailable

- it: should treat empty string budgets as unset
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
minAvailable: ""
maxUnavailable: "25%"
asserts:
- equal:
path: spec.maxUnavailable
value: "25%"
- notExists:
path: spec.minAvailable

- it: should render eviction policy, labels and annotations
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
unhealthyPodEvictionPolicy: AlwaysAllow
labels:
custom-label: pdb-label
annotations:
custom-annotation: pdb-annotation
asserts:
- equal:
path: spec.unhealthyPodEvictionPolicy
value: AlwaysAllow
- equal:
path: metadata.labels["custom-label"]
value: pdb-label
- equal:
path: metadata.labels["app.kubernetes.io/name"]
value: RELEASE-NAME
- equal:
path: metadata.annotations["custom-annotation"]
value: pdb-annotation

- it: should fail when both minAvailable and maxUnavailable are set
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
minAvailable: 1
maxUnavailable: 1
asserts:
- failedTemplate:
errorMessage: "podDisruptionBudget: minAvailable and maxUnavailable are mutually exclusive. Set exactly one (to use minAvailable, set podDisruptionBudget.maxUnavailable=null)."

- it: should fail when neither minAvailable nor maxUnavailable is set
set:
replicaCount: 2
podDisruptionBudget:
enabled: true
maxUnavailable: null
asserts:
- failedTemplate:
errorMessage: "podDisruptionBudget is enabled but neither minAvailable nor maxUnavailable is set. Set exactly one."
26 changes: 25 additions & 1 deletion helm/kagent-tools/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

# Annotations to add to the deployment
deploymentAnnotations: {}

podAnnotations: {}

podSecurityContext: {}
Expand All @@ -90,7 +93,28 @@ securityContext: {}
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000


# PodDisruptionBudget for the tools Deployment.
# Only rendered when replicaCount > 1: with a single replica a PDB either
# protects nothing or blocks node drains entirely.
podDisruptionBudget:
# -- Set to true to create the PodDisruptionBudget.
enabled: false
# -- Minimum number of pods that must remain available. Int or percentage
# string (e.g. `1` or `"50%"`). Mutually exclusive with `maxUnavailable`;
# to use it, also set `maxUnavailable: null`.
minAvailable: null
# -- Maximum number of pods that may be unavailable. Int or percentage
# string (e.g. `1` or `"50%"`). Mutually exclusive with `minAvailable`.
maxUnavailable: 1
# -- `spec.unhealthyPodEvictionPolicy`: `IfHealthyBudget` (Kubernetes
# default) or `AlwaysAllow`. Unset leaves the Kubernetes default.
unhealthyPodEvictionPolicy: ""
# -- Extra labels for the PodDisruptionBudget.
labels: {}
# -- Annotations for the PodDisruptionBudget.
annotations: {}

# Node taints which will be tolerated for Pod.
tolerations: []

Expand Down
Loading