From cb2faa5d9cdbc42e95c311346133fddb8618b2cc Mon Sep 17 00:00:00 2001 From: eastagiletracker <310448263+eastagiletracker@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:00:49 +0000 Subject: [PATCH 1/2] fix: accept a fractional CPU limit on the task container A task_resource_requirements.limits.cpu of "1.5" or "0.5" is a valid Kubernetes quantity and passes the CRD, but cpu_string_to_decimal ran it through int() and raised ValueError, so the settings ConfigMap never rendered and every reconcile of the deployment failed. Parse the quantity as a Decimal, keep rounding down to whole CPUs as before, and raise the intended AnsibleFilterError for anything that is not a CPU quantity. That error path never worked either: __ERROR_MSG inside the class is name-mangled and raised NameError instead. --- .../filter_plugins/cpu_string_to_decimal.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/roles/installer/filter_plugins/cpu_string_to_decimal.py b/roles/installer/filter_plugins/cpu_string_to_decimal.py index ef9aae1b..9a0745a9 100644 --- a/roles/installer/filter_plugins/cpu_string_to_decimal.py +++ b/roles/installer/filter_plugins/cpu_string_to_decimal.py @@ -4,9 +4,13 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +from decimal import Decimal, InvalidOperation + from ansible.errors import AnsibleFilterError -__ERROR_MSG = "Not a valid cpu value. Cannot process value" +# Module-level on purpose: a double-underscore name read inside the class is +# mangled to _FilterModule__ERROR_MSG, which does not exist. +ERROR_MSG = "Not a valid cpu value. Cannot process value" class FilterModule(object): def filters(self): @@ -15,13 +19,21 @@ def filters(self): } def cpu_string_to_decimal(self, cpu_string): - # verify if task_output is a dict + # verify if cpu_string is a string if not isinstance(cpu_string, str): - raise AnsibleFilterError(__ERROR_MSG) + raise AnsibleFilterError(ERROR_MSG) + + # A Kubernetes CPU quantity is either millicores ("1500m") or cores, + # which may be fractional ("1.5"). Both round down to whole CPUs. + try: + if cpu_string.endswith('m'): + cpu = Decimal(cpu_string[:-1]) / 1000 + else: + cpu = Decimal(cpu_string) + except InvalidOperation: + raise AnsibleFilterError("%s: %r" % (ERROR_MSG, cpu_string)) - if cpu_string[-1] == 'm': - cpu = int(cpu_string[:-1])//1000 - else: - cpu = int(cpu_string) + if not cpu.is_finite(): + raise AnsibleFilterError("%s: %r" % (ERROR_MSG, cpu_string)) - return cpu + return int(cpu) From 34a4916e154267fbe818ca9bbf84d403c4b77849 Mon Sep 17 00:00:00 2001 From: eastagiletracker <310448263+eastagiletracker@users.noreply.github.com> Date: Tue, 6 Oct 2026 08:00:25 +0000 Subject: [PATCH 2/2] fix: bound the CPU limit before converting it to an integer Decimal accepts an exponent of any size, so a limit such as "1e1000000" reached int() and spent seconds of controller CPU building an integer with a million digits. With an m suffix the division could instead raise decimal.Overflow, which the InvalidOperation handler did not catch. Work in millicores and reject anything negative or above int64 millicores, the largest CPU limit Kubernetes can apply, before converting. Catch every decimal signal, not only InvalidOperation. Valid limits render the same. --- .../filter_plugins/cpu_string_to_decimal.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/roles/installer/filter_plugins/cpu_string_to_decimal.py b/roles/installer/filter_plugins/cpu_string_to_decimal.py index 9a0745a9..9c985b5d 100644 --- a/roles/installer/filter_plugins/cpu_string_to_decimal.py +++ b/roles/installer/filter_plugins/cpu_string_to_decimal.py @@ -4,7 +4,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from decimal import Decimal, InvalidOperation +from decimal import Decimal, DecimalException from ansible.errors import AnsibleFilterError @@ -12,6 +12,11 @@ # mangled to _FilterModule__ERROR_MSG, which does not exist. ERROR_MSG = "Not a valid cpu value. Cannot process value" +# Kubernetes reads a CPU limit as int64 millicores, so nothing larger is a +# limit it could apply. Checked before int(): an exponent such as "1e1000000" +# would otherwise materialize an enormous integer. +MAX_MILLICORES = Decimal(2**63 - 1) + class FilterModule(object): def filters(self): return { @@ -27,13 +32,13 @@ def cpu_string_to_decimal(self, cpu_string): # which may be fractional ("1.5"). Both round down to whole CPUs. try: if cpu_string.endswith('m'): - cpu = Decimal(cpu_string[:-1]) / 1000 + millicores = Decimal(cpu_string[:-1]) else: - cpu = Decimal(cpu_string) - except InvalidOperation: + millicores = Decimal(cpu_string) * 1000 + except DecimalException: raise AnsibleFilterError("%s: %r" % (ERROR_MSG, cpu_string)) - if not cpu.is_finite(): + if not millicores.is_finite() or not 0 <= millicores <= MAX_MILLICORES: raise AnsibleFilterError("%s: %r" % (ERROR_MSG, cpu_string)) - return int(cpu) + return int(millicores / 1000)