feat(xceadmin): validate percent-based limit values are in [0.0, 1.0] (bug-1833)
Adds an if/then JSON Schema (draft 2020-12) conditional constraint to limits.schema.json's Limit definition: when percent is true, value must be a number in [0.0, 1.0]. This is the class of authoring mistake bug-1831/bug-1832 fixed manually (night_mem_pct: value: 50 instead of 0.5) -- now caught automatically at real `xceadmin config build` time (the actual cluster.bin generation path deploy-ha.sh uses), not merely by a unit test. Empirically verified against the real jsonschema library this repo uses: a violation surfaces with the exact same file/json_pointer/ constraint contract validate_yaml()'s existing ValidationError already provides, so zero changes were needed to its error-reporting code. While implementing this, the new constraint caught an identical pre-existing defect in xceadmin's own checked-in "full" golden test fixture (fixtures/full/configs/limits.yaml, night_mem_pct: value: 50) -- fixed the same way, and regenerated the checked-in expected.bin golden artifact to match the corrected input. Added 3 tests to test_validation.py: reject an out-of-range percent value, accept an in-range one, and confirm a non-percent value is never range-checked regardless of magnitude. Build gotcha discovered and documented (cerebrum.md): this repo has two copies of every bundled schema -- src/configs/schema/ (canonical) and src/xceadmin/src/xcecommon/schema_files/json/ (a gitignored local mirror meson-build.sh always overwrites from the canonical copy on every build). Only the canonical copy needed a source-controlled edit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
a8fb768526
commit
7982f28557
6 changed files with 111 additions and 3 deletions
|
|
@ -19,13 +19,15 @@ limits:
|
|||
value: 4
|
||||
per_user: true
|
||||
|
||||
# queue-scoped memory cap, percentage of total mem resource
|
||||
# queue-scoped memory cap, percentage of currently-available mem resource
|
||||
# (bug-1831/1832: percent-based value is a 0-1 fraction, not a 0-100
|
||||
# number -- 0.5 = 50%, matching src/configs/limits.yaml's own fix)
|
||||
- name: night_mem_pct
|
||||
queues:
|
||||
members:
|
||||
- night
|
||||
resname: mem
|
||||
value: 50
|
||||
value: 0.5
|
||||
percent: true
|
||||
|
||||
# host-scoped cap, applied per host in hg1
|
||||
|
|
|
|||
BIN
src/xceadmin/tests/fixtures/full/expected.bin
vendored
BIN
src/xceadmin/tests/fixtures/full/expected.bin
vendored
Binary file not shown.
|
|
@ -139,6 +139,72 @@ def test_validate_yaml_reports_missing_required_field(tmp_path: Path) -> None:
|
|||
assert exc_info.value.constraint == "required"
|
||||
|
||||
|
||||
def test_validate_yaml_rejects_percent_limit_value_above_one(tmp_path: Path) -> None:
|
||||
"""bug-1832: xce_limit_val_t.val is a 0-1 fraction convention whenever
|
||||
percent is set (0.5 = 50%), not a 0-100 number -- confirmed end to end
|
||||
against the real capacity computation, with zero /100 scaling anywhere
|
||||
in the config->capnp->runtime pipeline. A percent-based limit authored
|
||||
with a 0-100-scale value (e.g. 50, meaning "50%") silently computed a
|
||||
non-functional 5000%-of-resource cap in production before this schema
|
||||
constraint existed. This must now be a hard config-build failure."""
|
||||
bad_limits = tmp_path / "limits.yaml"
|
||||
bad_limits.write_text(
|
||||
"limits:\n"
|
||||
" - name: bad_pct_limit\n"
|
||||
" resname: mem\n"
|
||||
" value: 50\n"
|
||||
" percent: true\n"
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
validate_yaml(str(bad_limits), "limits")
|
||||
|
||||
caught = exc_info.value
|
||||
assert caught.file == str(bad_limits)
|
||||
assert "value" in caught.json_pointer
|
||||
assert caught.constraint == "maximum"
|
||||
|
||||
|
||||
def test_validate_yaml_accepts_percent_limit_value_in_fraction_range(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""The corrected, 0-1-fraction convention (0.5 = 50%) must validate
|
||||
cleanly -- this is exactly the fix bug-1832 applied to the real
|
||||
night_mem_pct limit in src/configs/limits.yaml."""
|
||||
good_limits = tmp_path / "limits.yaml"
|
||||
good_limits.write_text(
|
||||
"limits:\n"
|
||||
" - name: good_pct_limit\n"
|
||||
" resname: mem\n"
|
||||
" value: 0.5\n"
|
||||
" percent: true\n"
|
||||
)
|
||||
|
||||
data = validate_yaml(str(good_limits), "limits")
|
||||
|
||||
assert data["limits"][0]["value"] == 0.5
|
||||
|
||||
|
||||
def test_validate_yaml_does_not_range_check_non_percent_limit_value(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""A non-percent (absolute-value) limit's value is an ordinary
|
||||
resource amount (e.g. slots=10, or a large byte count for mem) --
|
||||
the 0-1 fraction constraint must apply ONLY when percent is true, never
|
||||
to an absolute value, however large."""
|
||||
absolute_limits = tmp_path / "limits.yaml"
|
||||
absolute_limits.write_text(
|
||||
"limits:\n"
|
||||
" - name: absolute_value_limit\n"
|
||||
" resname: mem\n"
|
||||
" value: 5000000000\n"
|
||||
)
|
||||
|
||||
data = validate_yaml(str(absolute_limits), "limits")
|
||||
|
||||
assert data["limits"][0]["value"] == 5000000000
|
||||
|
||||
|
||||
def test_validate_yaml_missing_yaml_file_is_not_this_functions_concern(tmp_path: Path) -> None:
|
||||
"""A missing individual YAML file is handled by the caller (build
|
||||
orchestration treats it as an empty section, Requirement 1.5) — but if
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue