|
6 | 6 | hostqual-artifact-boundary every consumed artifact is proved before it is read |
7 | 7 | hostqual-t0-versioned a qualification is versioned by the T0 it claims |
8 | 8 | hostqual-ci-predicate ci == false, not "the field is absent" |
9 | | - hostqual-provisioning-values shape AND value; a VM that promises nothing fails |
| 9 | + hostqual-provisioning-shape malformed is malformed; a boolean false is not |
| 10 | + hostqual-provisioning-predicate values are judged by the predicate, not the parser |
| 11 | + hostqual-negative-evidence an honest no leaves a record, not a stderr line |
10 | 12 | hostqual-one-environment-id one identity, not three strings that usually agree |
11 | 13 | hostqual-quiesce-window 120 s means 120 s, of which 60 s is measured |
12 | 14 | hostqual-quiesce-arithmetic quiet passes; spike, mean, gap and rewind do not |
@@ -418,37 +420,68 @@ def control_ci_predicate() -> None: |
418 | 420 | ok("hostqual-ci-predicate", "ci == false; absent or non-boolean is refused") |
419 | 421 |
|
420 | 422 |
|
421 | | -def control_provisioning_values() -> None: |
422 | | - if hq.validate_provisioning(provisioning()): |
423 | | - fail("hostqual-provisioning-values", "a valid physical-host declaration was refused") |
424 | | - return |
425 | | - if hq.validate_provisioning(vm_provisioning()): |
426 | | - fail("hostqual-provisioning-values", "a valid VM declaration was refused") |
427 | | - return |
428 | | - cases = [ |
429 | | - ("dedicated_to_p022 false", provisioning(dedicated_to_p022=False)), |
430 | | - ("no_concurrent_user_workload false", provisioning(no_concurrent_user_workload=False)), |
431 | | - ("hosted_ci_runner true", provisioning(hosted_ci_runner=True)), |
432 | | - ("VM fixed_vcpu false", vm_provisioning(fixed_vcpu=False)), |
433 | | - ("VM fixed_ram false", vm_provisioning(fixed_ram=False)), |
434 | | - ("VM live_migration_disabled false", vm_provisioning(live_migration_disabled=False)), |
435 | | - ("VM dynamic_memory_disabled false", vm_provisioning(dynamic_memory_disabled=False)), |
436 | | - ("VM answering n/a", vm_provisioning(fixed_vcpu="n/a: do not ask")), |
437 | | - ("is_vm as n/a", provisioning(virtualization={"is_vm": "n/a: unclear"})), |
| 423 | +def control_provisioning_shape() -> None: |
| 424 | + """Malformed is malformed; `false` is not malformed.""" |
| 425 | + for label, doc in (("a physical-host declaration", provisioning()), |
| 426 | + ("a VM declaration", vm_provisioning()), |
| 427 | + ("an honest negative", provisioning(dedicated_to_p022=False)), |
| 428 | + ("an honest VM negative", vm_provisioning(fixed_vcpu=False))): |
| 429 | + if hq.validate_provisioning(doc): |
| 430 | + fail("hostqual-provisioning-shape", |
| 431 | + f"{label} was refused as malformed: {hq.validate_provisioning(doc)}") |
| 432 | + return |
| 433 | + malformed = [ |
| 434 | + ('the string "false" where a boolean belongs', provisioning(dedicated_to_p022="false")), |
| 435 | + ("a missing required boolean", |
| 436 | + {k: v for k, v in provisioning().items() if k != "dedicated_to_p022"}), |
| 437 | + ("is_vm answered with n/a", provisioning(virtualization={"is_vm": "n/a: unclear"})), |
| 438 | + ("a VM leaving a VM-only field out", |
| 439 | + provisioning(virtualization={"is_vm": True, "fixed_vcpu": True, "fixed_ram": True, |
| 440 | + "live_migration_disabled": True})), |
| 441 | + ("a VM answering a VM-only field with n/a", vm_provisioning(fixed_vcpu="n/a: do not ask")), |
438 | 442 | ] |
439 | | - for label, doc in cases: |
| 443 | + physical_bare = provisioning() |
| 444 | + physical_bare["virtualization"]["fixed_vcpu"] = True |
| 445 | + malformed.append(("a physical host answering VM questions with bare booleans", physical_bare)) |
| 446 | + for label, doc in malformed: |
440 | 447 | if not hq.validate_provisioning(doc): |
441 | | - fail("hostqual-provisioning-values", f"{label} was accepted") |
| 448 | + fail("hostqual-provisioning-shape", f"{label} was accepted as a valid artifact") |
| 449 | + return |
| 450 | + ok("hostqual-provisioning-shape", |
| 451 | + "wrong types, missing keys and inapplicable answers are malformed; a boolean `false` is " |
| 452 | + "not, because a declaration that this host does not qualify is still a declaration") |
| 453 | + |
| 454 | + |
| 455 | +def control_provisioning_predicate() -> None: |
| 456 | + if hq.provisioning_predicate(provisioning()) or hq.provisioning_predicate(vm_provisioning()): |
| 457 | + fail("hostqual-provisioning-predicate", "a compliant declaration failed the predicate") |
| 458 | + return |
| 459 | + for label, doc in (("dedicated_to_p022 false", provisioning(dedicated_to_p022=False)), |
| 460 | + ("no_concurrent_user_workload false", |
| 461 | + provisioning(no_concurrent_user_workload=False)), |
| 462 | + ("hosted_ci_runner true", provisioning(hosted_ci_runner=True)), |
| 463 | + ("VM fixed_vcpu false", vm_provisioning(fixed_vcpu=False)), |
| 464 | + ("VM fixed_ram false", vm_provisioning(fixed_ram=False)), |
| 465 | + ("VM live_migration_disabled false", |
| 466 | + vm_provisioning(live_migration_disabled=False)), |
| 467 | + ("VM dynamic_memory_disabled false", |
| 468 | + vm_provisioning(dynamic_memory_disabled=False))): |
| 469 | + if not hq.provisioning_predicate(doc): |
| 470 | + fail("hostqual-provisioning-predicate", f"{label} satisfied the predicate") |
| 471 | + return |
| 472 | + if hq.check_single_tenant(doc, manifest())["result"] != "fail": |
| 473 | + fail("hostqual-provisioning-predicate", f"{label} still qualified the host") |
442 | 474 | return |
443 | | - physical_hole = provisioning() |
444 | | - physical_hole["virtualization"]["fixed_vcpu"] = True |
445 | | - if not hq.validate_provisioning(physical_hole): |
446 | | - fail("hostqual-provisioning-values", |
447 | | - "a physical host answering the VM questions with bare booleans was accepted") |
| 475 | + if hq.declaration_predicate(declaration()): |
| 476 | + fail("hostqual-provisioning-predicate", "a compliant session declaration failed") |
448 | 477 | return |
449 | | - ok("hostqual-provisioning-values", |
450 | | - "every required boolean is checked by VALUE: a VM that cannot promise fixed vCPU, fixed " |
451 | | - "RAM, no live migration or no dynamic memory fails, and 'n/a' is unavailable to it") |
| 478 | + for key in hq.DECLARATION_REQUIRED_TRUE: |
| 479 | + if not hq.declaration_predicate(declaration(**{key: False})): |
| 480 | + fail("hostqual-provisioning-predicate", f"session declaration {key} false passed") |
| 481 | + return |
| 482 | + ok("hostqual-provisioning-predicate", |
| 483 | + "every required value is judged by the predicate rather than by the parser, so each " |
| 484 | + "failure can reach a record instead of a stream of errors") |
452 | 485 |
|
453 | 486 |
|
454 | 487 | def control_one_environment_id() -> None: |
@@ -831,6 +864,91 @@ def control_execbinding_no_overwrite() -> None: |
831 | 864 | # --- the template and this file itself --------------------------------------- |
832 | 865 |
|
833 | 866 |
|
| 867 | +def control_negative_evidence() -> None: |
| 868 | + """A refused host leaves a record saying so. Erasing negative attempts is how |
| 869 | + a laboratory ends up with machines that pass on the first try because the |
| 870 | + other tries were never artifacts.""" |
| 871 | + with tempfile.TemporaryDirectory() as raw: |
| 872 | + tmp = Path(raw) |
| 873 | + repo, commit, _digest, t0_path = instrument_repo(tmp) |
| 874 | + mpath = write(tmp, "m.json", manifest()) |
| 875 | + |
| 876 | + def qualify_cli(name: str, prov: dict) -> tuple[int, Path]: |
| 877 | + ppath = write(tmp, f"{name}-p.json", prov) |
| 878 | + out = tmp / f"{name}-q.json" |
| 879 | + with fixed_power(COMPLIANT_POWER): |
| 880 | + rc = hq.main(["--qualify", "--stratum", "linux", "--repo", str(repo), |
| 881 | + "--t0-path", t0_path, "--t0-commit", commit, |
| 882 | + "--provisioning", str(ppath), "--manifest", str(mpath), |
| 883 | + "--emit", str(out)]) |
| 884 | + return rc, out |
| 885 | + |
| 886 | + # N1 and N2: honest provisioning negatives |
| 887 | + for label, prov, expect_key in ( |
| 888 | + ("n1", provisioning(dedicated_to_p022=False), "single_tenant"), |
| 889 | + ("n1b", provisioning(no_concurrent_user_workload=False), "single_tenant"), |
| 890 | + ("n2", vm_provisioning(fixed_vcpu=False), "single_tenant")): |
| 891 | + rc, out = qualify_cli(label, prov) |
| 892 | + if not out.is_file(): |
| 893 | + fail("hostqual-negative-evidence", |
| 894 | + f"{label}: a valid negative declaration produced no artifact at all") |
| 895 | + return |
| 896 | + record = json.loads(out.read_text(encoding="utf-8")) |
| 897 | + if record.get("qualified") is not False: |
| 898 | + fail("hostqual-negative-evidence", f"{label}: the record does not say qualified " |
| 899 | + f"false: {record.get('qualified')!r}") |
| 900 | + return |
| 901 | + if record.get("predicate", {}).get(expect_key) != "fail": |
| 902 | + fail("hostqual-negative-evidence", |
| 903 | + f"{label}: {expect_key} is {record.get('predicate', {}).get(expect_key)!r}, " |
| 904 | + "so the record does not say WHY") |
| 905 | + return |
| 906 | + if rc != 1: |
| 907 | + fail("hostqual-negative-evidence", |
| 908 | + f"{label}: exit code {rc}; a valid artifact with a negative outcome is 1") |
| 909 | + return |
| 910 | + |
| 911 | + # N4 and N5: malformed input produces no artifact and a different code |
| 912 | + rc, out = qualify_cli("n4", provisioning(dedicated_to_p022="false")) |
| 913 | + if out.is_file(): |
| 914 | + fail("hostqual-negative-evidence", "a malformed declaration produced an artifact") |
| 915 | + return |
| 916 | + if rc != 2: |
| 917 | + fail("hostqual-negative-evidence", |
| 918 | + f"a malformed declaration exited {rc}; malformed input is 2, not 1") |
| 919 | + return |
| 920 | + |
| 921 | + # N3: an honest session negative still records eligibility |
| 922 | + qpath = write(tmp, "q.json", qualification()) |
| 923 | + wpath = write(tmp, "qw.json", qualification("windows")) |
| 924 | + bpath = write(tmp, "b.json", binding_doc(hq.sha256_file(qpath), hq.sha256_file(wpath))) |
| 925 | + dpath = write(tmp, "d.json", declaration(no_prohibited_background_job_active=False)) |
| 926 | + cpath = tmp / "cand.bin" |
| 927 | + cpath.write_bytes(LINUX_CANDIDATE) |
| 928 | + quiet = {"eligible": True, "reason": "", "samples": [0.01], "mean": 0.01, "max": 0.01} |
| 929 | + with fixed_power(COMPLIANT_POWER): |
| 930 | + record = hq.session_eligibility(bpath, qpath, mpath, dpath, cpath, |
| 931 | + quiesce_result=quiet) |
| 932 | + if record["eligible"]: |
| 933 | + fail("hostqual-negative-evidence", "a declared prohibited job left the session eligible") |
| 934 | + return |
| 935 | + if not any("declaration" in r for r in record["reasons"]): |
| 936 | + fail("hostqual-negative-evidence", |
| 937 | + f"the eligibility record does not say why: {record['reasons']}") |
| 938 | + return |
| 939 | + malformed_d = write(tmp, "d-bad.json", declaration(no_campaign_workload="false")) |
| 940 | + with fixed_power(COMPLIANT_POWER): |
| 941 | + message = refuses(lambda: hq.session_eligibility(bpath, qpath, mpath, malformed_d, |
| 942 | + cpath, quiesce_result=quiet)) |
| 943 | + if message is None: |
| 944 | + fail("hostqual-negative-evidence", "a malformed session declaration was consumed") |
| 945 | + return |
| 946 | + ok("hostqual-negative-evidence", |
| 947 | + "an honest negative — host, VM or session — leaves a real artifact saying qualified/" |
| 948 | + "eligible false and naming the reason, and exits 1; malformed input leaves nothing and " |
| 949 | + "exits 2. The two classes never share a code") |
| 950 | + |
| 951 | + |
834 | 952 | def control_provisioning_example() -> None: |
835 | 953 | if not EXAMPLE.is_file(): |
836 | 954 | fail("provisioning-example-validates", f"{EXAMPLE} is missing") |
@@ -899,7 +1017,9 @@ def control_tools_do_not_import_harness() -> None: |
899 | 1017 | ("hostqual-artifact-boundary", control_artifact_boundary), |
900 | 1018 | ("hostqual-t0-versioned", control_t0_versioned), |
901 | 1019 | ("hostqual-ci-predicate", control_ci_predicate), |
902 | | - ("hostqual-provisioning-values", control_provisioning_values), |
| 1020 | + ("hostqual-provisioning-shape", control_provisioning_shape), |
| 1021 | + ("hostqual-provisioning-predicate", control_provisioning_predicate), |
| 1022 | + ("hostqual-negative-evidence", control_negative_evidence), |
903 | 1023 | ("hostqual-one-environment-id", control_one_environment_id), |
904 | 1024 | ("hostqual-quiesce-window", control_quiesce_window), |
905 | 1025 | ("hostqual-quiesce-arithmetic", control_quiesce_arithmetic), |
|
0 commit comments