Skip to content

fix(up): round fractional cpu limits, and range-check before converting to Int - #160

Open
Mikimoto wants to merge 3 commits into
Mcrich23:mainfrom
Mikimoto:fix/cpu-limit-rounding
Open

Mikimoto wants to merge 3 commits into
Mcrich23:mainfrom
Mikimoto:fix/cpu-limit-rounding

Conversation

@Mikimoto

Copy link
Copy Markdown

Split out of #153 at the maintainer's request.

container run --cpus takes a whole number while Compose allows a fraction, so cpus: 0.5 reached the runtime as-is and was rejected. This rounds up to the smallest expressible limit rather than down — 0.5 would otherwise become 0, and a slightly loose cap fails less destructively than none at all. The change is reported to the caller the same way the memory floor is.

Two seams so the mapping is covered where it is decided: clampCPULimit for the run path, and builderCPUCount for the build path, which had its own fallback of 2 and so silently turned "half a CPU" into four times what was asked for.

Range validation

Int(Double) traps rather than saturating, so the value is range-checked before the conversion. Three shapes a compose file can legally contain crashed the process:

cpus: why
1e400 parses as infinity
nan parses as NaN
1e20 finite, but larger than Int can hold — the case an isFinite check alone misses

A value this tool cannot express is now passed through untouched, so container run rejects it and names the service. That reads better than a fatal error with no service in it.

Verified by removing the guard and running the suite: the test process dies with signal 5, reporting Fatal error: Double value cannot be converted to Int because the result would be greater than Int.max and ... because it is either infinite or NaN. With the guard the same inputs return unchanged.

The ordinary path is untouched: 0.5 → 1, 2.5 → 3, and a whole number is left alone.

Static suite: 255 tests / 24 suites green.

`container run --cpus` takes a whole number; Compose allows a fraction. A
compose file using `cpus: "0.5"` failed outright:

  Error: The value '0.5' is invalid for '--cpus <cpus>'

Rounded up rather than down, because down turns 0.5 into 0, which container also
rejects, and a slightly loose cap fails less destructively than none. Reported
on stdout, the same way the 200 MiB memory floor already is.

Also fixes a quieter instance of the same input on the build path, which read
the limit as `Int64(...) ?? 2`. Int64 cannot parse "0.5", so a service asking
for half a CPU silently got two - four times its request, with no message. Only
the run path failing loudly is why this was noticed.
The regression test added with the previous commit called clampCPULimit and
asserted on its result, which passes whether or not buildService uses it -
mutation-checked: reverting that line to `Int64(...) ?? 2` left the suite
green. Second time this shape appeared in this branch.

Extracting builderCPUCount also separated two things the old expression
conflated: the default for a service that declares no limit, and the fallback
for a limit that cannot be parsed. They are now testable apart.
`Int(Double)` traps rather than saturating, so `clampCPULimit` crashed the
process on three shapes a compose file can legally contain: `cpus: 1e400`
parses as infinity, `cpus: nan` as NaN, and `cpus: 1e20` is finite but larger
than `Int` can hold — the case an `isFinite` check alone still misses.

A value this tool cannot express is now passed through untouched, so
`container run` rejects it and names the service. That is a better outcome than
a fatal error with no service in it, and it leaves the ordinary path unchanged:
0.5 still rounds to 1, 2.5 to 3, and a whole number is left alone.

Six tests, one per shape plus the unchanged path and the builder fallback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant