fix(kubernetes): validate absolute eviction format and guard against memory over-reservation

Add regex format validation for absolute eviction threshold values before
they reach toFloat conversion, preventing silent zero returns on invalid
input like "banana". Add a check that systemReservedMemory + kubeReservedMemory
does not exceed effective node memory. Include helm unit tests for both
validations.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
This commit is contained in:
Arsolitt committed 2026-04-23 17:02:53 +03:00
1 parent d21307a18b
commit 1f8ada572c
2 files changed
+206

No files matched your search

@@ -224,6 +224,17 @@ metadata:
{{- if and $kubeletOverride.kubeReservedCpu (not (regexMatch `^[0-9]+(\.[0-9]+)?m?$` ($kubeletOverride.kubeReservedCpu | toString))) }}
{{- fail (printf "nodeGroup %s: invalid kubeReservedCpu value %q — must be a valid Kubernetes CPU quantity (e.g. 100m, 0.5, 1)" $groupName ($kubeletOverride.kubeReservedCpu | toString)) }}
{{- end }}
{{/* Validate absolute eviction threshold formats before converting */}}
{{- if not (hasSuffix "%" $evictionHardMemory) }}
{{- if not (regexMatch `^[0-9]+(\.[0-9]+)?(Ki|Mi|Gi|Ti|Pi|Ei)?$` $evictionHardMemory) }}
{{- fail (printf "nodeGroup %s: invalid evictionHardMemory value %q — must be a percentage (e.g. 7%%) or a valid Kubernetes memory quantity (e.g. 500Mi, 1Gi)" $groupName $evictionHardMemory) }}
{{- end }}
{{- end }}
{{- if not (hasSuffix "%" $evictionSoftMemory) }}
{{- if not (regexMatch `^[0-9]+(\.[0-9]+)?(Ki|Mi|Gi|Ti|Pi|Ei)?$` $evictionSoftMemory) }}
{{- fail (printf "nodeGroup %s: invalid evictionSoftMemory value %q — must be a percentage (e.g. 10%%) or a valid Kubernetes memory quantity (e.g. 500Mi, 1Gi)" $groupName $evictionSoftMemory) }}
{{- end }}
{{- end }}
{{- if and (hasSuffix "%" $evictionHardMemory) (hasSuffix "%" $evictionSoftMemory) }}
{{- $hardPct := trimSuffix "%" $evictionHardMemory | float64 }}
{{- $softPct := trimSuffix "%" $evictionSoftMemory | float64 }}
@@ -239,6 +250,16 @@ metadata:
{{- else }}
{{- fail (printf "nodeGroup %s: evictionHardMemory and evictionSoftMemory must use the same unit type (both percentage or both absolute) — got evictionHardMemory=%s, evictionSoftMemory=%s" $groupName $evictionHardMemory $evictionSoftMemory) }}
{{- end }}
{{/* Validate that reserved memory does not exceed effective node memory */}}
{{- if $effectiveMemory }}
{{- $sysBytes := include "cozy-lib.resources.toFloat" $systemReservedMemory | float64 }}
{{- $kubeBytes := include "cozy-lib.resources.toFloat" $kubeReservedMemory | float64 }}
{{- $totalReservedBytes := mulf $sysBytes 1.0 | addf $kubeBytes }}
{{- $effectiveMemBytes := include "cozy-lib.resources.toFloat" $effectiveMemory | float64 }}
{{- if ge $totalReservedBytes $effectiveMemBytes }}
{{- fail (printf "nodeGroup %s: total reserved memory (systemReservedMemory=%s + kubeReservedMemory=%s) must be less than effective node memory (%s)" $groupName $systemReservedMemory $kubeReservedMemory $effectiveMemory) }}
{{- end }}
{{- end }}
---
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
@@ -925,3 +925,188 @@ tests:
path: spec.template.spec.joinConfiguration.nodeRegistration.kubeletExtraArgs.eviction-soft
value: "memory.available<15%"
documentIndex: 9
################################################
# Absolute eviction format validation #
################################################
- it: rejects invalid format for absolute evictionHardMemory
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources: {}
kubelet:
evictionHardMemory: "banana"
evictionSoftMemory: "500Mi"
asserts:
- failedTemplate:
errorMessage: "nodeGroup md0: invalid evictionHardMemory value \"banana\" — must be a percentage (e.g. 7%) or a valid Kubernetes memory quantity (e.g. 500Mi, 1Gi)"
- it: rejects invalid format for absolute evictionSoftMemory
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources: {}
kubelet:
evictionHardMemory: "200Mi"
evictionSoftMemory: "not-a-quantity"
asserts:
- failedTemplate:
errorMessage: "nodeGroup md0: invalid evictionSoftMemory value \"not-a-quantity\" — must be a percentage (e.g. 10%) or a valid Kubernetes memory quantity (e.g. 500Mi, 1Gi)"
- it: accepts valid absolute eviction thresholds with correct format
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources: {}
kubelet:
evictionHardMemory: "500Mi"
evictionSoftMemory: "1Gi"
asserts:
- equal:
path: spec.template.spec.joinConfiguration.nodeRegistration.kubeletExtraArgs.eviction-hard
value: "memory.available<500Mi"
documentIndex: 4
- equal:
path: spec.template.spec.joinConfiguration.nodeRegistration.kubeletExtraArgs.eviction-soft
value: "memory.available<1Gi"
documentIndex: 4
####################################################
# Reserved memory exceeds node memory validation #
####################################################
- it: rejects reserved memory exceeding effective node memory
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources:
memory: "1Gi"
kubelet:
systemReservedMemory: "512Mi"
kubeReservedMemory: "600Mi"
asserts:
- failedTemplate:
errorMessage: "nodeGroup md0: total reserved memory (systemReservedMemory=512Mi + kubeReservedMemory=600Mi) must be less than effective node memory (1Gi)"
- it: rejects reserved memory equal to effective node memory
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources:
memory: "1Gi"
kubelet:
systemReservedMemory: "512Mi"
kubeReservedMemory: "512Mi"
asserts:
- failedTemplate:
errorMessage: "nodeGroup md0: total reserved memory (systemReservedMemory=512Mi + kubeReservedMemory=512Mi) must be less than effective node memory (1Gi)"
- it: allows reserved memory within effective node memory bounds
release:
name: test-k8s
namespace: tenant-test
set:
_namespace:
etcd: etcd
ingress: nginx
host: example.com
_cluster:
cluster-domain: cozy.local
nodeGroups:
md0:
minReplicas: 0
maxReplicas: 10
instanceType: ""
ephemeralStorage: 20Gi
roles:
- worker
resources:
memory: "4Gi"
kubelet:
systemReservedMemory: "512Mi"
kubeReservedMemory: "512Mi"
asserts:
- equal:
path: spec.template.spec.joinConfiguration.nodeRegistration.kubeletExtraArgs.system-reserved
value: "memory=512Mi,cpu=100m"
documentIndex: 4
- equal:
path: spec.template.spec.joinConfiguration.nodeRegistration.kubeletExtraArgs.kube-reserved
value: "memory=512Mi,cpu=100m"
documentIndex: 4