Skip to content

fix: accept a fractional CPU limit on the task container - #61

Open
eastagiletracker wants to merge 2 commits into
ctrliq:develfrom
eastagiletracker:agile-board/fractional-cpu-limit
Open

eastagiletracker wants to merge 2 commits into
ctrliq:develfrom
eastagiletracker:agile-board/fractional-cpu-limit

Conversation

@eastagiletracker

@eastagiletracker eastagiletracker commented Sep 25, 2026 •

Copy link
Copy Markdown

This PR proposes a fix so that a fractional CPU limit on the task container (for example task_resource_requirements.limits.cpu: "1.5") no longer stops the installer from rendering the settings ConfigMap. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/625. You can sign in with your GitHub ID to claim ownership of the project.

The defect

configmaps/config.yaml.j2 passes the task container's CPU limit through the cpu_string_to_decimal filter to size JOB_EVENT_WORKERS. The filter handles millicores ("2000m") and whole cores ("6"), but a fractional core count such as "1.5", "2.5" or "0.5" goes straight into int() and raises ValueError. Those values are valid Kubernetes quantities, the CRD accepts them (the field is type: string), and Ascender itself reads SYSTEM_TASK_ABS_CPU = '1.5' fine, so the only thing that breaks is the operator: the template fails, the ConfigMap is never applied, and every reconcile of that deployment fails. The filter's own error path is broken too: __ERROR_MSG is read inside the class, so Python name-mangles it to _FilterModule__ERROR_MSG and a non-string value raises NameError instead of the intended message.

Reproduced at current devel (649823e) by rendering the real template with the role defaults and the repo's filter plugin, passing the limit the way the operator does (as an extra var):

ANSIBLE_FILTER_PLUGINS=roles/installer/filter_plugins ansible-playbook render.yml \
  -e '{"task_resource_requirements":{"limits":{"cpu":"1.5"}}}'

fatal: [localhost]: FAILED! => ... The filter plugin 'cpu_string_to_decimal' failed: invalid literal for int() with base 10: '1.5'

render.yml is a five-line play that loads roles/common/defaults/main.yml and roles/installer/defaults/main.yml and does lookup('template', 'roles/installer/templates/configmaps/config.yaml.j2').

The change

One file, roles/installer/filter_plugins/cpu_string_to_decimal.py. The quantity is now parsed as a Decimal (millicores divided by 1000), and the result still rounds down to whole CPUs exactly as before, so every value that worked keeps its output. Anything that is not a CPU quantity ("abc", "", a non-string, NaN) now raises the intended AnsibleFilterError naming the bad value, with the message moved to module level so it resolves.

Verification

Filter output before and after, for the same inputs:

limit before after
100m, 2000m, 4500m 0, 2, 4 0, 2, 4
6, 8 6, 8 6, 8
1.5, 2.5, 0.5 ValueError 1, 2, 0
1500.5m ValueError 1
abc ValueError AnsibleFilterError: Not a valid cpu value. Cannot process value: 'abc'
6 as an int, None NameError: name '_FilterModule__ERROR_MSG' is not defined AnsibleFilterError

Rendering the full ConfigMap after the change: 1.5, 2.5 and 0.5 render with SYSTEM_TASK_ABS_CPU and no JOB_EVENT_WORKERS (at or below the default of 4), 5.5 renders JOB_EVENT_WORKERS = 5, 6 and 6500m render JOB_EVENT_WORKERS = 6 as before, and the default spec with no limits renders unchanged. yamllint roles and ansible-lint --offline -p roles/installer report the same findings before and after the change, with nothing new. I did not run the molecule scenario, which needs a kind cluster; the table above can be replayed with just ansible-core from the repo root.

How this was managed

This fix was tracked as its own story on the Ascender Operator board, which was imported from this repository's 59 issues and pull requests and used to manage this work.

board

If you'd rather not receive contributions like this, reply no-more-prs on this pull request and we won't open any further ones on your repositories.


Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/

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.
@ciq-it-service-account

ciq-it-service-account commented Sep 25, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cigamit
cigamit requested a balanced review from Copilot October 5, 2026 16:34
@cigamit cigamit self-assigned this Oct 5, 2026
@cigamit cigamit added the bug Something isn't working label Oct 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unbounded decimal exponents can cause excessive controller resource consumption during integer conversion.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Updates CPU parsing to support fractional Kubernetes CPU limits while preserving whole-core rounding.

Changes:

  • Parses CPU values using Decimal.
  • Corrects filter error handling for invalid values.
File Description
roles/​installer/​filter_plugins/​cpu_string_to_decimal.py Adds fractional CPU parsing and validation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

raise AnsibleFilterError("%s: %r" % (ERROR_MSG, cpu_string))

return cpu
return int(cpu)
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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Development

Successfully merging this pull request may close these issues.

4 participants