Repository navigation
Conversation
hidb4ai
left a comment
There was a problem hiding this comment.
Two recovery failures remain in the managed installer. The inline comments describe the reproductions and required recovery behavior. These probes used real protected files and a simulated service manager; the port-conflict case also used a real loopback listener.
| facts = previous["plan"].get("installation") or {} | ||
| if ( | ||
| facts.get("registration_present") is True | ||
| or state.get("stage") in {"registration_dispatching", "complete"} |
There was a problem hiding this comment.
[P2] Keep first-time registration failures recoverable
registration_dispatching is persisted before controller.install(), so it does not prove that registration ever existed. I reproduced a temporary port conflict during installation: the controller rejected it before any native mutation, but after releasing the listener, install preflight and resume still returned registration_missing. Interrupting immediately after the dispatching checkpoint produces the same result. Please distinguish incomplete first registration from explicit removal so a transient failure does not leave installation without a supported recovery path.
There was a problem hiding this comment.
复核 head 37036d1f(fix(ops): recover incomplete managed installations):本条 P2 已闭合。
ManagedInstaller._pending现在按「是否有过真实注册证据」而不是「是否写过派发检查点」判定:只有registration_present is True、stage == complete、components.registration == installed、或本 attempt 计划里的registration_removal与当前移除身份不一致(即之后发生明确移除)时才registration_missing;旧记录缺该键时仍按registration_dispatching/native_command兜底要求人工检查,不会被自动重放。- 当前移除身份来自
ServiceController.registration_removal_path(受保护文件,format_version/UUID 校验失败即ServiceError),并进入facts["registration_removal"];protocol._validate_installation_plan放行该键并用identifier()校验(_IDENTIFIER允许连字符,UUID 可通过)。 - exact head 树跑
tests/test_ops_installer.py:29 passed(452 s)。变异验证:删掉installer.py中"registration_removal": snapshot["registration_removal"],一行后,test_first_registration_failure_can_resume的两个参数化用例同时失败(rejected而非succeeded)⇒ 新用例确实保护了这次修改。
一点相邻事实,不作要求:registration_removal 记录损坏时 _registration_removal() 抛 ServiceError,而 snapshot() 会把它带进所有 Ops 命令的前置事实,等于让全部操作失败;这与「旧记录仍可读」的文档措辞不同,若预期是仅阻断依赖注册判定的操作,需要再确认。
| state["prepared"] = {"directory": str(result.directory), "identity": result.identity} | ||
| checkpoint("prepared") | ||
| else: | ||
| self._verify_prepared(state["prepared"], cancelled=resource_cancelled) |
There was a problem hiding this comment.
[P2] Allow reacquiring corrupt owned staging resources
Cancel at the prepared checkpoint, corrupt a regular staged file, then resume. Verification returns resource_corrupt on every attempt because the existing directory is always reused. A fresh install operation inherits the same corrupt staging state and fails too, while repair returns installation_missing. Please provide a supported way to prepare the resource again after confirming ownership of the damaged staging tree; rejecting corrupt bytes should not leave installation permanently dependent on that tree.
There was a problem hiding this comment.
复核 head 37036d1f:本条 P2 已闭合。
- 现在
resource_state != "verified"且state["prepared"]指向的目录仍存在时,先_verify_prepared;只有resource_corrupt才进入重建,其余错误照旧抛出。重建前先核三类归属:路径必须是绝对路径、resolve().parent必须等于programs/staging的 resolve 结果、validate_owned_tree(path)必须等于记录里的identity;任一不符即OpsError("resource_identity_changed")并保留现场,之后才rmtree并把state["prepared"]置空重新准备。 - 覆盖用例:
test_corrupt_owned_staging_can_be_prepared_again[True/False](resume 与新 install 两条路径)与反向的test_replaced_staging_is_preserved_and_not_reacquired(被替换的目录含user-owned文件时不清理、返回resource_identity_changed)。 - exact head 树跑
tests/test_ops_installer.py:29 passed(452 s)。变异验证:把if prepared is not None and (改成if False and ((等价于旧行为:不复检、直接复用)后,test_corrupt_owned_staging_can_be_prepared_again两个参数化用例同时失败(failed而非succeeded)⇒ 两条路径都有回归保护。
一点相邻事实,不作要求:新增的 shutil.rmtree(path) 若因只读文件或占用而抛 OSError(Windows 上较常见),会被 ops/__main__.py 的 except (OSError, ValueError) 归为 invalid_input_or_binding,与真实原因不符;如果希望恢复路径给出可判断的错误码,可在此处收敛成独立的 Ops 错误。
29f5e65 to
09614b0
Compare
09614b0 to
37036d1
Compare
Which issue or RFC does this PR close?
Refs #2115 (10-A and 10-B), #2114, and RFC #1733.
Builds on the independent Ops boundary merged in #2190. This PR contains the installer-specific changes and does not close the end-to-end acceptance issue.
Rationale for this change
The independent Ops tool can manage a prepared Server, but cannot yet acquire or repair its runtime. This change adds fixed-resource installation and same-version repair through the existing approval, operation journal, cancellation, and native service boundaries. A damaged Server interpreter is not needed to run the repair.
What changes are included in this PR?
installandrepairplans bind resource, configuration, database, service, destination, and space observations. Apply revalidates the plan; checkpoints reuse the existing operation/attempt journal and locks. Recovery preserves ownership across interruption and later stop operations, and cannot recreate an explicitly removed registration.-I -B; old service definitions retain their serialization and startup behavior.Are there any user-facing changes?
The trusted launcher can select managed mode with
--catalog-fileand--catalog-sha256; the existing prepared-runtime--target-filemode remains available. Requests cannot select arbitrary download URLs, executable paths, or versions. Results report the actual endpoint and installation/resource identities without claiming connection compatibility or a default Scope.Current limitations:
start/restartafter the first initialization and configuration changes that would actually restart an active service. Same-version repair can finish while stopped; it does not authorize database use. This is not yet a complete daily restart workflow.isolated_runtimedefinition field cannot manage these new registrations; use the matching independent Ops tool.How was this change tested?
Local validation used Windows and Python 3.12.13.
uv run --no-sync python -m pytest -n 4 --basetemp D:/t/pr2203-installer-tests tests/test_ops_installer.py tests/test_ops_resources.py tests/test_ops_local_policy.py tests/test_service_runtime_repair.py tests/e2e/test_managed_runtime_repair.py -q --tb=short: 89 passed, 1 skipped. This includes public Memory revision/history retention through same-version resource repair.SKIP=ty-check UV_NO_SYNC=1 uv run --no-sync prek run -apassed.ty check --python-platform linuxand affected-source/test Windows type checks passed. The default whole-repository Windows type check still reports existing POSIX-only attributes in unrelated code.uv lock --locked --offlineanduv lock --locked --offline --directory e2e/bubpassed.AI usage statement
Implemented and tested with OpenAI Codex. Existing review findings guided the registration and staging recovery fixes. Validation distinguishes real protected-file, socket and process behavior from simulated native service managers.