From 0ea56cc2fd9fbe540e800bfe113fe2cf144aa712 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 06:18:53 +0000 Subject: [PATCH 01/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect.dag | 3 + dag/gunbc/host_effect_realize.dag | 16 ++ dag/gunbc/srv3_host_effect_realize.dag | 242 +++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 dag/gunbc/srv3_host_effect_realize.dag diff --git a/dag/gunbc/host_effect.dag b/dag/gunbc/host_effect.dag index b3345ba052a..88e3441cfff 100644 --- a/dag/gunbc/host_effect.dag +++ b/dag/gunbc/host_effect.dag @@ -7,6 +7,7 @@ import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.bmc.types { RedfishSystemAction } import gunbc.fleet_intent { ComputeHost } import gunbc.host_converge { HostConverge } +import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } import std.effects { EffectShape, ReadEffect, @@ -21,6 +22,8 @@ type HostEffect = ShellCommand { script: String } | RedfishAction { action: RedfishSystemAction } | ConvergePlan { policy: HostConverge } + | Srv3InstallDiagnosticObserve { intent: OsInstallAttemptIntent } + | OsInstallActuatorToolchainEnsure {} type NodeControlPlane = HostOs { node: ComputeHost } diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index b4cbf22cc69..44e7bef01f0 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -2,6 +2,7 @@ module gunbc.host_effect_realize import gunbc.host_effect { HostEffect, ShellCommand, RedfishAction, ConvergePlan, + Srv3InstallDiagnosticObserve, OsInstallActuatorToolchainEnsure, NodeControlPlane, HostOs, BmcController, Drive, OneShot, ConvergeLoop, HostEffectIntent, HostEffectEvidence, ShellEffectApplied, ConvergePlanApplied, @@ -26,18 +27,25 @@ import gunbc.host_converge_realize { ConvergeHostInterpreted, converge_host_no_knobs_reason, } +import gunbc.srv3_host_effect_realize { + realize_srv3_install_diagnostic_observe, + realize_os_install_actuator_toolchain_ensure, +} import std.access { AccessDecision, Permit, Deny } import gunbc.ci_deploy_access { DeployAccess, DeployAccessActor, deploy_access_check, deploy_access_check_observed, deploy_access_decision_reason, } +import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } import std.types { String, NonEmptyStr, List } type ResolvedHostEffectCell = ShellOnHost { node: ComputeHost, script: String } | RedfishOnBmc { node: ComputeHost, action: RedfishSystemAction } | ConvergeOnHost { node: ComputeHost, host_converge: HostConverge } + | Srv3DiagnosticObserveOnHost { node: ComputeHost, intent: OsInstallAttemptIntent } + | OsInstallActuatorToolchainOnHost { node: ComputeHost } | IncompatibleCell { reason: String } fn converge_plan_identity_mismatch_reason(node: ComputeHost, policy: HostConverge) -> String { @@ -63,6 +71,10 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res } else { ConvergeOnHost { node: n, host_converge: p } } + Srv3InstallDiagnosticObserve { intent: i } => + Srv3DiagnosticObserveOnHost { node: n, intent: i } + OsInstallActuatorToolchainEnsure {} => + OsInstallActuatorToolchainOnHost { node: n } } BmcController { node: n } => match effect { @@ -71,6 +83,10 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } ConvergePlan { policy: _ } => IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + Srv3InstallDiagnosticObserve { intent: _ } => + IncompatibleCell { reason: "host_effect: Srv3InstallDiagnosticObserve is in-band (actuator host shell observe) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + OsInstallActuatorToolchainEnsure {} => + IncompatibleCell { reason: "host_effect: OsInstallActuatorToolchainEnsure is in-band (actuator host toolchain) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } } } } diff --git a/dag/gunbc/srv3_host_effect_realize.dag b/dag/gunbc/srv3_host_effect_realize.dag new file mode 100644 index 00000000000..7bb10463d02 --- /dev/null +++ b/dag/gunbc/srv3_host_effect_realize.dag @@ -0,0 +1,242 @@ +module gunbc.srv3_host_effect_realize + +import extdeps.shell +import extdeps.gunbc +import extdeps.apt { package_curl, package_nbdkit, package_socat } +import extdeps.tools.curl { curl_cli_tool } +import extdeps.tools.nbdkit { nbdkit_cli_tool } +import extdeps.tools.socat { socat_cli_tool } +import extdeps.tools.websocat { + websocat_cli_tool, + websocat_github_tag, + websocat_asset_aarch64, + websocat_install_path, + websocat_github_release_download_url, +} +import extdeps.diagnostic.ssh { ssh_session_exec_script } +import gunbc.shell_bash_runner { shell_exec_via_bash } +import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_observe_script } +import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } +import gunbc.srv3_os_install_actuator_toolchain_ensure { gunbc_bin_dir } +import gunbc.ci_deploy_access { deploy_access_apt_get_binary_path } +import gunbc.host_effect { + HostEffectIntent, + HostEffectEvidence, + HostEffectTransport, + LocalShell, + SshShell, + EmitArtifactThenThinRun, + ShellEffectApplied, +} +import std.realization_reconcile { + Reconciliation, + Converged, + NotConverged, + ReadObserved, + Grounds, + DoesNotGround, + reconcile_grounded, +} +import std.types { String, NonEmptyStr, Bool, List, Int } + +type Srv3ShellExecObserved { + exit_code: Int + success: Bool + stdout: String + stderr: String +} + +fn srv3_shell_exec_failure_reason(observed: Srv3ShellExecObserved) -> String { + let header = "srv3_host_effect: shell exec returned a nonzero exit code; fail-closed" + if observed.stdout == "" && observed.stderr == "" { + header + } else { + concat( + concat(concat(header, "\n--- stdout ---\n"), observed.stdout), + concat("\n--- stderr ---\n", observed.stderr) + ) + } +} + +fn srv3_run_shell_transport(script: String, transport: HostEffectTransport) -> Srv3ShellExecObserved { + match transport { + LocalShell => { + let result = shell.Exec.Run(script: script) + Srv3ShellExecObserved { + exit_code: result.exit_code, + success: result.success, + stdout: result.stdout, + stderr: result.stderr, + } + } + SshShell { ssh_host: h } => { + if script == "" { + Srv3ShellExecObserved { + exit_code: 1, + success: false, + stdout: "srv3_host_effect: empty shell script over SshShell; fail-closed", + stderr: "", + } + } else { + let result = ssh_session_exec_script(host: h, script: script) + Srv3ShellExecObserved { + exit_code: result.exit_code, + success: result.success, + stdout: result.stdout, + stderr: result.stderr, + } + } + } + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + Srv3ShellExecObserved { + exit_code: 1, + success: false, + stdout: "srv3_host_effect: srv3 observe/toolchain effects cannot use EmitArtifactThenThinRun transport; typed mismatch.", + stderr: "", + } + } +} + +fn srv3_run_multiline_bash(script: String, transport: HostEffectTransport) -> Srv3ShellExecObserved { + srv3_run_shell_transport(script: shell_exec_via_bash(body: script), transport: transport) +} + +fn srv3_shell_reconcile( + observed: Srv3ShellExecObserved, + intent: HostEffectIntent, +) -> Reconciliation { + let grounding = if observed.success { + Grounds { evidence: ShellEffectApplied { exit_code: observed.exit_code, stdout: observed.stdout } } + } else { + DoesNotGround { reason: srv3_shell_exec_failure_reason(observed: observed) } + } + reconcile_grounded( + apply_effect: intent, + show_read: ReadObserved { value: observed }, + grounding: grounding + ) +} + +fn realize_srv3_install_diagnostic_observe( + intent: OsInstallAttemptIntent, + transport: HostEffectTransport, + effect_intent: HostEffectIntent, +) -> Reconciliation { + let script = srv3_install_diagnostic_observe_script(intent: intent) + let observed = srv3_run_multiline_bash(script: script, transport: transport) + srv3_shell_reconcile(observed: observed, intent: effect_intent) +} + +fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { + shell.Which.Check(command: tool_name).exists +} + +fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { + let probe = concat(tool_name as String, " --version >/dev/null 2>&1") + shell.Exec.Check(command: probe).exists +} + +fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: [ + "-n", + deploy_access_apt_get_binary_path as String, + "install", + "-y", + package as String, + ], + ).success +} + +fn srv3_ensure_apt_tool( + tool_name: NonEmptyStr, + package: NonEmptyStr, + require_version_probe: Bool, +) -> Bool { + if srv3_apt_tool_present(tool_name: tool_name) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + { + true + } else { + srv3_privileged_apt_install(package: package) + && srv3_apt_tool_present(tool_name: tool_name) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + } +} + +fn srv3_ensure_websocat() -> Bool { + let install_path = websocat_install_path as String + if shell.Test.IsExecutable(path: install_path).executable { + true + } else { + let bin_dir = gunbc_bin_dir as String + let url = websocat_github_release_download_url( + tag: websocat_github_tag, + asset: websocat_asset_aarch64, + ) + let mkdir = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["mkdir", "-p", bin_dir], + ) + let chown = shell.Exec.Run( + script: concat(concat("sudo -n chown \"$(id -un):$(id -gn)\" ", bin_dir), ""), + ) + let curl = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/curl", + args: ["-fsSL", url, "-o", install_path], + ) + let chmod = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/bin/chmod", + args: ["+x", install_path], + ) + mkdir.success + && chown.success + && curl.success + && chmod.success + && shell.Test.IsExecutable(path: install_path).executable + } +} + +fn realize_os_install_actuator_toolchain_ensure( + effect_intent: HostEffectIntent, +) -> Reconciliation { + let curl_ok = srv3_ensure_apt_tool( + tool_name: curl_cli_tool.name, + package: package_curl, + require_version_probe: true, + ) + let nbdkit_ok = srv3_ensure_apt_tool( + tool_name: nbdkit_cli_tool.name, + package: package_nbdkit, + require_version_probe: true, + ) + let socat_ok = srv3_ensure_apt_tool( + tool_name: socat_cli_tool.name, + package: package_socat, + require_version_probe: false, + ) + let websocat_ok = srv3_ensure_websocat() + let success = curl_ok && nbdkit_ok && socat_ok && websocat_ok + let stdout = concat( + concat( + concat( + concat("OsInstallActuatorToolchainReceipt: curl=", if curl_ok { "ok" } else { "fail" }), + concat(" nbdkit=", if nbdkit_ok { "ok" } else { "fail" }), + ), + concat(" socat=", if socat_ok { "ok" } else { "fail" }), + ), + concat(" websocat=", if websocat_ok { "ok" } else { "fail" }), + ) + let observed = Srv3ShellExecObserved { + exit_code: if success { 0 } else { 1 }, + success: success, + stdout: stdout, + stderr: if success { "" } else { "actuator-toolchain-ensure: one or more tools failed ensure" }, + } + srv3_shell_reconcile(observed: observed, intent: effect_intent) +} From aa975534f371b92743b6e7040f90d9423e9b164d Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 06:29:26 +0000 Subject: [PATCH 02/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bmc/os_install_actuator_toolchain.dag | 2 + dag/gunbc/host_effect_realize.dag | 29 +- dag/gunbc/srv3_host_effect_realize.dag | 19 +- .../srv3_install_diagnostic_checklist.dag | 442 +++--------------- ...srv3_install_diagnostic_observe_script.dag | 344 ++++++++++++++ ...3_os_install_actuator_toolchain_ensure.dag | 57 ++- dag/gunbc/srv3_os_install_reconcile_apply.dag | 2 +- .../srv3_os_install_reconcile_dry_run.dag | 2 +- .../srv3_host_effect_realize_witness_test.dag | 45 ++ ...tall_diagnostic_checklist_witness_test.dag | 6 +- ...actuator_toolchain_ensure_witness_test.dag | 6 +- src/v2/lens/medium_structure_containment.dag | 3 - 12 files changed, 538 insertions(+), 419 deletions(-) create mode 100644 dag/gunbc/srv3_install_diagnostic_observe_script.dag create mode 100644 dag/test/claim/srv3_host_effect_realize_witness_test.dag diff --git a/dag/extdeps/bmc/os_install_actuator_toolchain.dag b/dag/extdeps/bmc/os_install_actuator_toolchain.dag index 618cd2816d0..faf5c762655 100644 --- a/dag/extdeps/bmc/os_install_actuator_toolchain.dag +++ b/dag/extdeps/bmc/os_install_actuator_toolchain.dag @@ -31,6 +31,8 @@ data os_install_actuator_toolchain_requirement: ActuatorToolchainRequirement = A data srv3_os_install_actuator_host_label: NonEmptyStr = "install-actuator-host" +data gunbc_bin_dir: NonEmptyStr = "/var/lib/gunbc/bin" + data srv3_os_install_actuator_toolchain_grant: ActuatorToolchainRequirement = os_install_actuator_toolchain_requirement fn actuator_toolchain_requirement_includes_tool( diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 44e7bef01f0..3e006960737 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -37,14 +37,14 @@ import gunbc.ci_deploy_access { deploy_access_check, deploy_access_check_observed, deploy_access_decision_reason, } -import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } +import gunbc.srv3_install_diagnostic_observe_script { srv3_bmcweb_token_path_for_observe, srv3_sol_console_log_path_for_observe } import std.types { String, NonEmptyStr, List } type ResolvedHostEffectCell = ShellOnHost { node: ComputeHost, script: String } | RedfishOnBmc { node: ComputeHost, action: RedfishSystemAction } | ConvergeOnHost { node: ComputeHost, host_converge: HostConverge } - | Srv3DiagnosticObserveOnHost { node: ComputeHost, intent: OsInstallAttemptIntent } + | Srv3DiagnosticObserveOnHost { node: ComputeHost, bmc: String, port: String, iso_path: String, intent_content_hash: String } | OsInstallActuatorToolchainOnHost { node: ComputeHost } | IncompatibleCell { reason: String } @@ -72,7 +72,13 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res ConvergeOnHost { node: n, host_converge: p } } Srv3InstallDiagnosticObserve { intent: i } => - Srv3DiagnosticObserveOnHost { node: n, intent: i } + Srv3DiagnosticObserveOnHost { + node: n, + bmc: i.bmc_endpoint as String, + port: i.serve_port as String, + iso_path: i.iso_path as String, + intent_content_hash: i.iso_content_hash as String, + } OsInstallActuatorToolchainEnsure {} => OsInstallActuatorToolchainOnHost { node: n } } @@ -214,6 +220,17 @@ fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: Ide ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) + Srv3DiagnosticObserveOnHost { node: _, bmc: b, port: p, iso_path: ip, intent_content_hash: h } => + realize_srv3_install_diagnostic_observe( + bmc: b, + port: p, + iso_path: ip, + intent_content_hash: h, + transport: transport, + effect_intent: intent, + ) + OsInstallActuatorToolchainOnHost { node: _ } => + realize_os_install_actuator_toolchain_ensure(effect_intent: intent) RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } @@ -252,6 +269,10 @@ fn host_effect_apply_gated( ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) + Srv3DiagnosticObserveOnHost { node: _, intent: i } => + realize_srv3_install_diagnostic_observe(intent: i, transport: transport, effect_intent: intent) + OsInstallActuatorToolchainOnHost { node: _ } => + realize_os_install_actuator_toolchain_ensure(effect_intent: intent) RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } @@ -291,6 +312,8 @@ fn host_effect_drive_directive(r: Reconciliation Refused { reason: inner_why } ShellOnHost { node: _, script: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) ConvergeOnHost { node: _, host_converge: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) + Srv3DiagnosticObserveOnHost { node: _, bmc: _, port: _, iso_path: _, intent_content_hash: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) + OsInstallActuatorToolchainOnHost { node: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) RedfishOnBmc { node: _, action: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) } } diff --git a/dag/gunbc/srv3_host_effect_realize.dag b/dag/gunbc/srv3_host_effect_realize.dag index 7bb10463d02..857dfa33a30 100644 --- a/dag/gunbc/srv3_host_effect_realize.dag +++ b/dag/gunbc/srv3_host_effect_realize.dag @@ -15,9 +15,8 @@ import extdeps.tools.websocat { } import extdeps.diagnostic.ssh { ssh_session_exec_script } import gunbc.shell_bash_runner { shell_exec_via_bash } -import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_observe_script } -import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } -import gunbc.srv3_os_install_actuator_toolchain_ensure { gunbc_bin_dir } +import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, srv3_bmcweb_token_path_for_observe, srv3_sol_console_log_path_for_observe } +import extdeps.bmc.os_install_actuator_toolchain { gunbc_bin_dir } import gunbc.ci_deploy_access { deploy_access_apt_get_binary_path } import gunbc.host_effect { HostEffectIntent, @@ -118,11 +117,21 @@ fn srv3_shell_reconcile( } fn realize_srv3_install_diagnostic_observe( - intent: OsInstallAttemptIntent, + bmc: String, + port: String, + iso_path: String, + intent_content_hash: String, transport: HostEffectTransport, effect_intent: HostEffectIntent, ) -> Reconciliation { - let script = srv3_install_diagnostic_observe_script(intent: intent) + let script = srv3_install_diagnostic_observe_script( + token_path: srv3_bmcweb_token_path_for_observe, + bmc: bmc, + port: port, + iso_path: iso_path, + intent_content_hash: intent_content_hash, + sol_log: srv3_sol_console_log_path_for_observe as String, + ) let observed = srv3_run_multiline_bash(script: script, transport: transport) srv3_shell_reconcile(observed: observed, intent: effect_intent) } diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index 770eb2a95b3..a4ccfe10150 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -2,8 +2,11 @@ module gunbc.srv3_install_diagnostic_checklist import extdeps.shell import extdeps.filesystem.filesystem_io { Filesystem } -import gunbc.shell_bash_runner { shell_exec_via_bash, shell_printf_line } -import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } +import gunbc.shell_bash_runner { shell_printf_line } +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script, + srv3_install_diagnostic_observe_script_declares_five_surfaces, +} import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, OsInstallAttemptIntent, @@ -21,356 +24,32 @@ import gunbc.srv3_os_install_diagnostic { srv3_install_diagnostic_recommended_action_wire, parse_srv3_install_debug_observation, } -import std.process { ProcessExit, ExitSuccess, exit_failure } -import std.types { String, NonEmptyStr } -import std.disposition { Disposition, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } - -data srv3_install_diagnostic_checklist_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "gunbc.srv3_install_diagnostic_checklist", - decl_name: "srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger", - field: WholeDeclaration, - } +import gunbc.host_effect { + Srv3InstallDiagnosticObserve, + HostOs, + host_effect_read_evidence, + LocalShell, + ShellEffectApplied, + ConvergePlanApplied, } - -data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "๐ŸŸก dissolve-on: srv3_install_diagnostic_checklist โ€” observe script via shell_exec_via_bash + stdout parse into Srv3InstallDebugObservation then diagnose_srv3_install; curl/redfish/sol concat tails in this module are TERMINAL for the reconcile spine โ€” FROZEN effective at merge (no further concat-shell growth here; this PR's tail expansion is pre-freeze forward-fix for TOKEN_PATH/SOL/NBDKIT markers); receipt echo via shell_printf_line; DISSOLVES WHEN host_effect_apply binds Srv3InstallDiagnosticObserve without concat runner glue; retire medium_structure_exception_roster entry for dag/gunbc/srv3_install_diagnostic_checklist.dag" - -data srv3_install_diagnostic_observe_script_path: String = "/tmp/srv3_install_diagnostic_observe.sh" - -data srv3_sol_console_log_path_for_observe: NonEmptyStr = "/var/lib/gunbc/logs/srv3-install-sol-console.log" as NonEmptyStr - -fn bash_var_ref(name: String) -> String { - concat("$", concat("{", concat(name, "}"))) -} - -fn curl_http_code_writeout() -> String { - concat("%", concat("{", concat("http_code", "}"))) -} - -fn srv3_install_diagnostic_observe_script_curl_tail() -> String { - concat( - "if test -s \"", - concat( - bash_var_ref(name: "TOKEN_PATH"), - concat( - "\"; then\n HTTP_CODE=", - concat( - concat("$", "(curl -sk -o /dev/null -w \""), - concat( - curl_http_code_writeout(), - concat( - "\" -H \"Connection: Upgrade\" -H \"Upgrade: websocket\" -H \"Sec-WebSocket-Version: 13\" -H \"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\" -H \"Authorization: Token ", - concat( - concat("$", "(cat \""), - concat( - bash_var_ref(name: "TOKEN_PATH"), - concat( - "\")\" \"https://", - concat( - bash_var_ref(name: "BMC"), - concat( - "/vm/0/0\" 2>/dev/null || echo 000)\nelse\n HTTP_CODE=000\nfi\necho \"Srv3InstallObserveReceipt: surface=bmc_websocket http_code=", - concat( - bash_var_ref(name: "HTTP_CODE"), - "\"\nif test -s \"", - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ) +import gunbc.host_effect_realize { host_effect_apply } +import gunbc.os_install_actuator_selection { srv3_os_install_actuator_host } +import std.realization_reconcile { + Reconciliation, + Converged, + NotConverged, } +import std.process { ProcessExit, ExitSuccess, exit_failure } +import std.types { String } +import std.disposition { Disposition, Terminal } -fn srv3_install_diagnostic_observe_script_redfish_tail() -> String { - concat( - bash_var_ref(name: "TOKEN_PATH"), - concat( - "\"; then\n RF=", - concat( - concat("$", "(curl -sk -H \"Authorization: Token "), - concat( - concat("$", "(cat \""), - concat( - bash_var_ref(name: "TOKEN_PATH"), - concat( - "\")\" \"https://", - concat( - bash_var_ref(name: "BMC"), - concat( - "/redfish/v1/Systems/system\")\n POWER=", - concat( - concat("$", "(echo \""), - concat( - bash_var_ref(name: "RF"), - concat( - "\" | jq -r .PowerState)\n BOOT_ENABLED=", - concat( - concat("$", "(echo \""), - concat( - bash_var_ref(name: "RF"), - concat( - "\" | jq -r .Boot.BootSourceOverrideEnabled)\n BOOT_TARGET=", - concat( - concat("$", "(echo \""), - concat( - bash_var_ref(name: "RF"), - concat( - "\" | jq -r .Boot.BootSourceOverrideTarget)\n echo \"Srv3InstallObserveReceipt: surface=redfish power=", - concat( - bash_var_ref(name: "POWER"), - concat( - " boot_enabled=", - concat( - bash_var_ref(name: "BOOT_ENABLED"), - concat( - " boot_target=", - concat( - bash_var_ref(name: "BOOT_TARGET"), - "\"\nelse\n echo \"Srv3InstallObserveReceipt: surface=redfish status=token_missing\"\nfi\nif test -s \"", - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ) +data srv3_install_diagnostic_checklist_scaffold: Disposition = Terminal { + reason: "host_effect_apply binds Srv3InstallDiagnosticObserve โ€” frozen observe script realized via srv3_host_effect_realize without checklist-level shell_exec_via_bash runner glue" } -fn srv3_install_diagnostic_observe_script_sol_tail() -> String { - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\"; then\n echo \"Srv3InstallObserveReceipt: surface=sol status=present\"\n grep -qi grub \"", - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=grub\" || true\n grep -qi subiquity \"", - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=subiquity\" || true\n grep -qi autoinstall \"", - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=autoinstall\" || true\n grep -qiE 'UEFI|Setup|Firmware|BIOS' \"", - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=firmware_menu\" || true\n grep -qi -E 'error|fail' \"", - concat( - bash_var_ref(name: "SOL_LOG"), - concat( - "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=error\" || true\nelse\n echo \"Srv3InstallObserveReceipt: surface=sol status=silent\"\nfi\nNBDKIT_PID=$(pgrep -n -x nbdkit 2>/dev/null || true)\nif [ -n \"", - concat( - bash_var_ref(name: "NBDKIT_PID"), - concat( - "\" ]; then\n ELAPSED_MS=$(( $(ps -o etimes= -p \"", - concat( - bash_var_ref(name: "NBDKIT_PID"), - concat( - "\" 2>/dev/null | tr -d ' ') * 1000 ))\nelse\n ELAPSED_MS=0\nfi\necho \"Srv3InstallObserveReceipt: surface=elapsed boot_once_ms=", - concat( - bash_var_ref(name: "ELAPSED_MS"), - "\"\necho \"Srv3InstallObserveReceipt: surface=router status=operator_required\"\n", - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ) -} +data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "dissolved: srv3_install_diagnostic_checklist observe path routes through host_effect_apply Srv3InstallDiagnosticObserve; retire medium_structure_exception_roster entry for dag/gunbc/srv3_install_diagnostic_checklist.dag" -fn srv3_install_diagnostic_observe_script(intent: OsInstallAttemptIntent) -> String { - let token_path = srv3_bmcweb_token_path - let bmc = intent.bmc_endpoint as String - let port = intent.serve_port as String - let iso_path = intent.iso_path as String - let intent_content_hash = intent.iso_content_hash as String - let sol_log = srv3_sol_console_log_path_for_observe as String - let serve_body = concat( - concat( - concat( - concat( - "#!/usr/bin/env bash\nset -uo pipefail\nTOKEN_PATH=", - concat( - token_path, - concat( - "\nBMC=", - concat( - bmc, - concat( - "\nPORT=", - concat( - port, - concat( - "\nISO_PATH=", - concat( - iso_path, - concat( - "\nINTENT_CONTENT_HASH=", - concat( - intent_content_hash, - concat("\nSOL_LOG=", sol_log), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - "\nif pgrep -x nbdkit >/dev/null 2>&1; then NBDKIT=present; else NBDKIT=absent; fi\nif pgrep -x websocat >/dev/null 2>&1; then WSOCAT=present; else WSOCAT=absent; fi\nPORT_OWNER=absent\nif ss -ltnp 2>/dev/null | grep -q \":", - ), - concat( - bash_var_ref(name: "PORT"), - " \"; then\n if ss -ltnp 2>/dev/null | grep \":", - ), - ), - concat( - concat( - concat( - concat( - bash_var_ref(name: "PORT"), - "\" | grep -q nbdkit; then\n PORT_OWNER=expected\n else\n PORT_OWNER=foreign\n fi\nfi\nISO_STATE=missing\nISO_FILE_SHA256=none\nCONTENT_HASH_OBSERVED=none\nif test -f \"", - ), - concat( - bash_var_ref(name: "ISO_PATH"), - "\"; then\n ISO_FILE_SHA256=", - ), - ), - concat( - concat("$", "(sha256sum \""), - concat( - bash_var_ref(name: "ISO_PATH"), - concat( - "\" | cut -d' ' -f1)\n SIDE=\"", - concat( - bash_var_ref(name: "ISO_PATH"), - ".gunbc-content-hash\"\n if test -f \"", - ), - ), - ), - ), - ), - concat( - concat( - concat( - concat( - bash_var_ref(name: "SIDE"), - "\"; then\n CONTENT_HASH_OBSERVED=", - ), - concat("$", "(cat \""), - ), - concat( - bash_var_ref(name: "SIDE"), - concat( - "\")\n if [ \"", - concat( - bash_var_ref(name: "CONTENT_HASH_OBSERVED"), - concat( - "\" = \"", - concat( - bash_var_ref(name: "INTENT_CONTENT_HASH"), - "\" ]; then\n ISO_STATE=match\n else\n ISO_STATE=mismatch\n fi\n else\n ISO_STATE=unverified\n fi\nfi\nif test -s \"", - ), - ), - ), - ), - ), - ), - concat( - concat( - concat( - concat( - bash_var_ref(name: "TOKEN_PATH"), - "\"; then TOKEN=present; else TOKEN=absent; fi\necho \"Srv3InstallObserveReceipt: surface=serve nbdkit=", - ), - concat( - concat( - concat( - bash_var_ref(name: "NBDKIT"), - " websocat=", - ), - bash_var_ref(name: "WSOCAT"), - ), - " port_owner=", - ), - ), - bash_var_ref(name: "PORT_OWNER"), - ), - concat( - " token=", - concat( - bash_var_ref(name: "TOKEN"), - concat( - " iso=", - concat( - bash_var_ref(name: "ISO_STATE"), - concat( - " iso_file_sha256=", - concat( - bash_var_ref(name: "ISO_FILE_SHA256"), - concat( - " content_hash_observed=", - concat(bash_var_ref(name: "CONTENT_HASH_OBSERVED"), "\"\n"), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ) - concat( - serve_body, - concat( - srv3_install_diagnostic_observe_script_curl_tail(), - concat( - srv3_install_diagnostic_observe_script_redfish_tail(), - srv3_install_diagnostic_observe_script_sol_tail(), - ), - ), - ) -} +data srv3_install_diagnostic_observe_script_path: String = "/tmp/srv3_install_diagnostic_observe.sh" func srv3_install_diagnostic_emit_observe_script() -> ProcessExit { let script = srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent) @@ -382,29 +61,47 @@ func srv3_install_diagnostic_emit_observe_script() -> ProcessExit { } } +fn srv3_install_diagnostic_observe_stdout( + intent: OsInstallAttemptIntent, +) -> String? { + let target = HostOs { node: srv3_os_install_actuator_host } + match host_effect_apply( + target: target, + effect: Srv3InstallDiagnosticObserve { intent: intent }, + evidence: host_effect_read_evidence, + transport: LocalShell, + ) { + Converged { evidence: ShellEffectApplied { exit_code: _, stdout: out }, applied: _ } => + Present { value: out } + Converged { evidence: ConvergePlanApplied { exit_code: _, stdout: _ }, applied: _ } => none + NotConverged { reason: _, applied: _ } => none + } +} + func srv3_install_diagnostic_checklist() -> ProcessExit { let intent = srv3_os_install_attempt_intent - let observe_script = srv3_install_diagnostic_observe_script(intent: intent) - let observe_run = shell.Exec.Run(script: shell_exec_via_bash(body: observe_script)) - if !observe_run.success { - exit_failure(reason: concat("srv3-install-diagnostic-observe: ", observe_run.stderr)) - } - let observation = parse_srv3_install_debug_observation( - stdout: observe_run.stdout, - intent: intent, - ) - let verdict = diagnose_srv3_install(intent: intent, observation: observation) - let receipt = srv3_install_diagnostic_receipt_line(verdict: verdict, observation: observation) - let action = srv3_install_diagnostic_recommended_action_wire(verdict: verdict) - let echo_body = concat( - shell_printf_line(line: receipt), - shell_printf_line(line: concat("Srv3InstallDiagnosticAction: ", action)), - ) - let echo_run = shell.Exec.Run(script: shell_exec_via_bash(body: echo_body)) - if echo_run.success { - ExitSuccess - } else { - exit_failure(reason: concat("srv3-install-diagnostic-receipt: ", echo_run.stderr)) + match srv3_install_diagnostic_observe_stdout(intent: intent) { + Present { value: stdout } => { + let observation = parse_srv3_install_debug_observation( + stdout: stdout, + intent: intent, + ) + let verdict = diagnose_srv3_install(intent: intent, observation: observation) + let receipt = srv3_install_diagnostic_receipt_line(verdict: verdict, observation: observation) + let action = srv3_install_diagnostic_recommended_action_wire(verdict: verdict) + let echo_body = concat( + shell_printf_line(line: receipt), + shell_printf_line(line: concat("Srv3InstallDiagnosticAction: ", action)), + ) + let echo_run = shell.Exec.Run(script: echo_body) + if echo_run.success { + ExitSuccess + } else { + exit_failure(reason: concat("srv3-install-diagnostic-receipt: ", echo_run.stderr)) + } + } + Absent => + exit_failure(reason: "srv3-install-diagnostic-observe: host_effect_apply Srv3InstallDiagnosticObserve did not converge") } } @@ -443,14 +140,5 @@ fn srv3_install_diagnostic_websocket_wire(observation: BmcWebsocketObservation) } fn srv3_install_diagnostic_checklist_entrypoint_is_declared() -> Bool { - string_contains(s: srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger, pattern: "diagnose_srv3_install") -} - -fn srv3_install_diagnostic_observe_script_declares_five_surfaces() -> Bool { - let script = srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent) - string_contains(s: script, pattern: "surface=serve") - && string_contains(s: script, pattern: "surface=bmc_websocket") - && string_contains(s: script, pattern: "surface=redfish") - && string_contains(s: script, pattern: "surface=sol") - && string_contains(s: script, pattern: "surface=router") + string_contains(s: srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger, pattern: "Srv3InstallDiagnosticObserve") } diff --git a/dag/gunbc/srv3_install_diagnostic_observe_script.dag b/dag/gunbc/srv3_install_diagnostic_observe_script.dag new file mode 100644 index 00000000000..52171e36575 --- /dev/null +++ b/dag/gunbc/srv3_install_diagnostic_observe_script.dag @@ -0,0 +1,344 @@ +module gunbc.srv3_install_diagnostic_observe_script + +import std.types { String, NonEmptyStr } +import std.disposition { Disposition, Terminal } + +data srv3_install_diagnostic_observe_script_scope_disposition: Disposition = Terminal { + reason: "FROZEN terminal observe-script concat โ€” no further shell growth; realized via host_effect_apply Srv3InstallDiagnosticObserve in srv3_host_effect_realize" +} + +data srv3_bmcweb_token_path_for_observe: String = "/tmp/srv3_bmcweb_token" + +data srv3_sol_console_log_path_for_observe: NonEmptyStr = "/var/lib/gunbc/logs/srv3-install-sol-console.log" as NonEmptyStr + +fn bash_var_ref(name: String) -> String { + concat("$", concat("{", concat(name, "}"))) +} + +fn curl_http_code_writeout() -> String { + concat("%", concat("{", concat("http_code", "}"))) +} + +fn srv3_install_diagnostic_observe_script_curl_tail() -> String { + concat( + "if test -s \"", + concat( + bash_var_ref(name: "TOKEN_PATH"), + concat( + "\"; then\n HTTP_CODE=", + concat( + concat("$", "(curl -sk -o /dev/null -w \""), + concat( + curl_http_code_writeout(), + concat( + "\" -H \"Connection: Upgrade\" -H \"Upgrade: websocket\" -H \"Sec-WebSocket-Version: 13\" -H \"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\" -H \"Authorization: Token ", + concat( + concat("$", "(cat \""), + concat( + bash_var_ref(name: "TOKEN_PATH"), + concat( + "\")\" \"https://", + concat( + bash_var_ref(name: "BMC"), + concat( + "/vm/0/0\" 2>/dev/null || echo 000)\nelse\n HTTP_CODE=000\nfi\necho \"Srv3InstallObserveReceipt: surface=bmc_websocket http_code=", + concat( + bash_var_ref(name: "HTTP_CODE"), + "\"\nif test -s \"", + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ) +} + +fn srv3_install_diagnostic_observe_script_redfish_tail() -> String { + concat( + bash_var_ref(name: "TOKEN_PATH"), + concat( + "\"; then\n RF=", + concat( + concat("$", "(curl -sk -H \"Authorization: Token "), + concat( + concat("$", "(cat \""), + concat( + bash_var_ref(name: "TOKEN_PATH"), + concat( + "\")\" \"https://", + concat( + bash_var_ref(name: "BMC"), + concat( + "/redfish/v1/Systems/system\")\n POWER=", + concat( + concat("$", "(echo \""), + concat( + bash_var_ref(name: "RF"), + concat( + "\" | jq -r .PowerState)\n BOOT_ENABLED=", + concat( + concat("$", "(echo \""), + concat( + bash_var_ref(name: "RF"), + concat( + "\" | jq -r .Boot.BootSourceOverrideEnabled)\n BOOT_TARGET=", + concat( + concat("$", "(echo \""), + concat( + bash_var_ref(name: "RF"), + concat( + "\" | jq -r .Boot.BootSourceOverrideTarget)\n echo \"Srv3InstallObserveReceipt: surface=redfish power=", + concat( + bash_var_ref(name: "POWER"), + concat( + " boot_enabled=", + concat( + bash_var_ref(name: "BOOT_ENABLED"), + concat( + " boot_target=", + concat( + bash_var_ref(name: "BOOT_TARGET"), + "\"\nelse\n echo \"Srv3InstallObserveReceipt: surface=redfish status=token_missing\"\nfi\nif test -s \"", + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ) +} + +fn srv3_install_diagnostic_observe_script_sol_tail() -> String { + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\"; then\n echo \"Srv3InstallObserveReceipt: surface=sol status=present\"\n grep -qi grub \"", + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=grub\" || true\n grep -qi subiquity \"", + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=subiquity\" || true\n grep -qi autoinstall \"", + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=autoinstall\" || true\n grep -qiE 'UEFI|Setup|Firmware|BIOS' \"", + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=firmware_menu\" || true\n grep -qi -E 'error|fail' \"", + concat( + bash_var_ref(name: "SOL_LOG"), + concat( + "\" && echo \"Srv3InstallObserveReceipt: surface=sol marker=error\" || true\nelse\n echo \"Srv3InstallObserveReceipt: surface=sol status=silent\"\nfi\nNBDKIT_PID=$(pgrep -n -x nbdkit 2>/dev/null || true)\nif [ -n \"", + concat( + bash_var_ref(name: "NBDKIT_PID"), + concat( + "\" ]; then\n ELAPSED_MS=$(( $(ps -o etimes= -p \"", + concat( + bash_var_ref(name: "NBDKIT_PID"), + concat( + "\" 2>/dev/null | tr -d ' ') * 1000 ))\nelse\n ELAPSED_MS=0\nfi\necho \"Srv3InstallObserveReceipt: surface=elapsed boot_once_ms=", + concat( + bash_var_ref(name: "ELAPSED_MS"), + "\"\necho \"Srv3InstallObserveReceipt: surface=router status=operator_required\"\n", + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ) +} + +fn srv3_install_diagnostic_observe_script( + token_path: String, + bmc: String, + port: String, + iso_path: String, + intent_content_hash: String, + sol_log: String, +) -> String { + let serve_body = concat( + concat( + concat( + concat( + "#!/usr/bin/env bash\nset -uo pipefail\nTOKEN_PATH=", + concat( + token_path, + concat( + "\nBMC=", + concat( + bmc, + concat( + "\nPORT=", + concat( + port, + concat( + "\nISO_PATH=", + concat( + iso_path, + concat( + "\nINTENT_CONTENT_HASH=", + concat( + intent_content_hash, + concat("\nSOL_LOG=", sol_log), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + "\nif pgrep -x nbdkit >/dev/null 2>&1; then NBDKIT=present; else NBDKIT=absent; fi\nif pgrep -x websocat >/dev/null 2>&1; then WSOCAT=present; else WSOCAT=absent; fi\nPORT_OWNER=absent\nif ss -ltnp 2>/dev/null | grep -q \":", + ), + concat( + bash_var_ref(name: "PORT"), + " \"; then\n if ss -ltnp 2>/dev/null | grep \":", + ), + ), + concat( + concat( + concat( + concat( + bash_var_ref(name: "PORT"), + "\" | grep -q nbdkit; then\n PORT_OWNER=expected\n else\n PORT_OWNER=foreign\n fi\nfi\nISO_STATE=missing\nISO_FILE_SHA256=none\nCONTENT_HASH_OBSERVED=none\nif test -f \"", + ), + concat( + bash_var_ref(name: "ISO_PATH"), + "\"; then\n ISO_FILE_SHA256=", + ), + ), + concat( + concat("$", "(sha256sum \""), + concat( + bash_var_ref(name: "ISO_PATH"), + concat( + "\" | cut -d' ' -f1)\n SIDE=\"", + concat( + bash_var_ref(name: "ISO_PATH"), + ".gunbc-content-hash\"\n if test -f \"", + ), + ), + ), + ), + ), + concat( + concat( + concat( + concat( + bash_var_ref(name: "SIDE"), + "\"; then\n CONTENT_HASH_OBSERVED=", + ), + concat("$", "(cat \""), + ), + concat( + bash_var_ref(name: "SIDE"), + concat( + "\")\n if [ \"", + concat( + bash_var_ref(name: "CONTENT_HASH_OBSERVED"), + concat( + "\" = \"", + concat( + bash_var_ref(name: "INTENT_CONTENT_HASH"), + "\" ]; then\n ISO_STATE=match\n else\n ISO_STATE=mismatch\n fi\n else\n ISO_STATE=unverified\n fi\nfi\nif test -s \"", + ), + ), + ), + ), + ), + ), + concat( + concat( + concat( + concat( + bash_var_ref(name: "TOKEN_PATH"), + "\"; then TOKEN=present; else TOKEN=absent; fi\necho \"Srv3InstallObserveReceipt: surface=serve nbdkit=", + ), + concat( + concat( + concat( + bash_var_ref(name: "NBDKIT"), + " websocat=", + ), + bash_var_ref(name: "WSOCAT"), + ), + " port_owner=", + ), + ), + bash_var_ref(name: "PORT_OWNER"), + ), + concat( + " token=", + concat( + bash_var_ref(name: "TOKEN"), + concat( + " iso=", + concat( + bash_var_ref(name: "ISO_STATE"), + concat( + " iso_file_sha256=", + concat( + bash_var_ref(name: "ISO_FILE_SHA256"), + concat( + " content_hash_observed=", + concat(bash_var_ref(name: "CONTENT_HASH_OBSERVED"), "\"\n"), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ) + concat( + serve_body, + concat( + srv3_install_diagnostic_observe_script_curl_tail(), + concat( + srv3_install_diagnostic_observe_script_redfish_tail(), + srv3_install_diagnostic_observe_script_sol_tail(), + ), + ), + ) +} diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 4a032586be6..0c3316786fc 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -1,6 +1,6 @@ module gunbc.srv3_os_install_actuator_toolchain_ensure -import extdeps.bmc.os_install_actuator_toolchain { os_install_actuator_toolchain_requirement } +import extdeps.bmc.os_install_actuator_toolchain { os_install_actuator_toolchain_requirement, gunbc_bin_dir } import extdeps.tools.curl { curl_cli_tool } import extdeps.tools.nbdkit { nbdkit_cli_tool } import extdeps.tools.socat { socat_cli_tool } @@ -18,34 +18,27 @@ import gunbc.ci_deploy_access { deploy_access_apt_get_privileged_command, deploy_access_sudo_n_apt_get_install_command, } -import gunbc.shell_bash_runner { shell_exec_via_bash } -import extdeps.shell -import std.disposition { Disposition, RealizationDispatch, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } +import gunbc.host_effect { + OsInstallActuatorToolchainEnsure, + HostOs, + host_effect_identity_evidence, +} +import gunbc.host_effect_realize { host_effect_apply_gated } +import gunbc.os_install_actuator_selection { srv3_os_install_actuator_host } +import std.realization_reconcile { reconciliation_converged } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { NonEmptyStr, String, Bool } import std.resources { Network } +import std.disposition { Disposition, Terminal } -data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "gunbc.srv3_os_install_actuator_toolchain_ensure", - decl_name: "os_install_actuator_toolchain_ensure_dissolution_trigger", - field: WholeDeclaration, - } +data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal { + reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via typed apt/curl/websocat argv effects in srv3_host_effect_realize" } -data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Scaffold { - dissolves_to: RealizationDispatch, - bind: DeclarationRef { - module_path: "gunbc.srv3_os_install_actuator_toolchain_ensure", - decl_name: "os_install_actuator_toolchain_ensure_script", - field: WholeDeclaration - } +data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Terminal { + reason: "dissolved: os_install_actuator_toolchain_ensure routes through host_effect_apply OsInstallActuatorToolchainEnsure; retire medium_structure_exception_roster entry for dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag" } -data gunbc_bin_dir: NonEmptyStr = "/var/lib/gunbc/bin" - fn os_install_actuator_apt_tool_ensure_block(tool_name: String, package: NonEmptyStr) -> String { os_install_actuator_apt_tool_ensure_block_with_check( tool_name: tool_name, @@ -166,12 +159,19 @@ fn os_install_actuator_toolchain_ensure_script() -> String { func srv3_os_install_actuator_toolchain_ensure() -> ProcessExit uses net: Network { - let script = os_install_actuator_toolchain_ensure_script() - let run = shell.Exec.Run(script: shell_exec_via_bash(body: script)) - if run.success { + let target = HostOs { node: srv3_os_install_actuator_host } + let access = ci_deploy_srv1_operator_access + if reconciliation_converged(r: host_effect_apply_gated( + access: access, + target: target, + effect: OsInstallActuatorToolchainEnsure {}, + evidence: host_effect_identity_evidence, + transport: access.transport, + fixture_actor: none, + )) { ExitSuccess } else { - exit_failure(reason: concat("actuator-toolchain-ensure: ", run.stderr)) + exit_failure(reason: "actuator-toolchain-ensure: host_effect_apply OsInstallActuatorToolchainEnsure did not converge") } } @@ -185,3 +185,10 @@ fn os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_w fn os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt() -> Bool { string_contains(s: os_install_actuator_toolchain_ensure_script(), pattern: "sudo -n /usr/bin/apt-get install -y") } + +fn os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() -> Bool { + string_contains( + s: srv3_os_install_actuator_toolchain_ensure_scaffold.reason, + pattern: "OsInstallActuatorToolchainEnsure", + ) +} diff --git a/dag/gunbc/srv3_os_install_reconcile_apply.dag b/dag/gunbc/srv3_os_install_reconcile_apply.dag index 94d2a9be108..8097b3d30da 100644 --- a/dag/gunbc/srv3_os_install_reconcile_apply.dag +++ b/dag/gunbc/srv3_os_install_reconcile_apply.dag @@ -8,7 +8,7 @@ import std.types { NonEmptyStr, Timestamp } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.measure { second_count } -import gunbc.srv3_install_diagnostic_checklist { +import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, } import gunbc.srv3_os_install_diagnostic { diff --git a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag index c916b62ac7d..2855461bcb2 100644 --- a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag +++ b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag @@ -6,7 +6,7 @@ import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { String, NonEmptyStr } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } -import gunbc.srv3_install_diagnostic_checklist { +import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, } import gunbc.srv3_os_install_diagnostic { diff --git a/dag/test/claim/srv3_host_effect_realize_witness_test.dag b/dag/test/claim/srv3_host_effect_realize_witness_test.dag new file mode 100644 index 00000000000..07a1e755ed4 --- /dev/null +++ b/dag/test/claim/srv3_host_effect_realize_witness_test.dag @@ -0,0 +1,45 @@ +module test.claim.srv3_host_effect_realize_witness + +import std.logic { Bool } +import gunbc.host_effect { + Srv3InstallDiagnosticObserve, + OsInstallActuatorToolchainEnsure, + HostOs, + BmcController, + host_effect_read_evidence, + host_effect_identity_evidence, + LocalShell, +} +import gunbc.host_effect_realize { host_effect_apply } +import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent } +import gunbc.fleet_intent { srv1_host } +import std.realization_reconcile { NotConverged, reconciliation_converged } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +test fn srv3_diagnostic_observe_rejects_bmc_controller_target() -> Bool { + match host_effect_apply( + target: BmcController { node: srv1_host }, + effect: Srv3InstallDiagnosticObserve { intent: srv3_os_install_attempt_intent }, + evidence: host_effect_read_evidence, + transport: LocalShell, + ) { + NotConverged { reason: why, applied: _ } => + string_contains(s: why, pattern: "Srv3InstallDiagnosticObserve") + _ => false + } +} + +test fn os_install_actuator_toolchain_ensure_rejects_bmc_controller_target() -> Bool { + match host_effect_apply( + target: BmcController { node: srv1_host }, + effect: OsInstallActuatorToolchainEnsure {}, + evidence: host_effect_identity_evidence, + transport: LocalShell, + ) { + NotConverged { reason: why, applied: _ } => + string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") + _ => false + } +} diff --git a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag index 2d2e600cf43..e0fc7e17f73 100644 --- a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag +++ b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag @@ -1,10 +1,12 @@ module test.claim.srv3_install_diagnostic_checklist import std.logic { Bool } -import gunbc.srv3_install_diagnostic_checklist { +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script, srv3_install_diagnostic_observe_script_declares_five_surfaces, +} +import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_checklist_entrypoint_is_declared, - srv3_install_diagnostic_observe_script, } import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, diff --git a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag index 9e12f6f26d8..1a596a8c417 100644 --- a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag +++ b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag @@ -7,6 +7,7 @@ import gunbc.srv3_os_install_actuator_toolchain_ensure { os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_websocat, os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt, os_install_actuator_toolchain_ensure_dissolution_trigger, + os_install_actuator_toolchain_ensure_routes_through_host_effect_apply, } import extdeps.tools.websocat { websocat_install_path } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } @@ -27,7 +28,8 @@ test fn srv3_actuator_toolchain_ensure_websocat_installs_to_modeled_path_with_na test fn srv3_actuator_toolchain_ensure_dissolution_trigger_names_roster_retirement() -> Bool { match os_install_actuator_toolchain_ensure_dissolution_trigger { - Scaffold { dissolves_to: _, bind: _ } => true - Terminal { reason: _ } => false + Terminal { reason: why } => string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") + Scaffold { dissolves_to: _, bind: _ } => false } + && os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() } diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index 15ded5edaf7..488d40dc4a1 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -193,14 +193,11 @@ data medium_structure_exception_roster: List = [ "dag/gunbc/shell_bash_runner.dag", - "dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag", - "dag/extdeps/os/ubuntu_install_media_fetch.dag", "dag/gunbc/srv3_install_media_fetch.dag", "dag/extdeps/os/ubuntu_seeded_install_media_remaster.dag", "dag/gunbc/srv3_seeded_install_media.dag", "dag/extdeps/bmc/serial_console.dag", - "dag/gunbc/srv3_install_diagnostic_checklist.dag", "dag/gunbc/srv3_os_install_reconcile.dag", "dag/gunbc/srv3_os_install_reconcile_types.dag", "dag/gunbc/srv3_os_install_reconcile_receipt.dag", From d65e4521111b307f974ceb36a7283f951feee257 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 06:39:47 +0000 Subject: [PATCH 03/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/extdeps/package_managers/apt.dag | 2 + dag/gunbc/host_effect.dag | 10 +- dag/gunbc/host_effect_realize.dag | 209 ++++++++++++++- dag/gunbc/srv3_host_effect_realize.dag | 251 ------------------ .../srv3_install_diagnostic_checklist.dag | 16 +- ...srv3_install_diagnostic_observe_script.dag | 2 +- ...3_os_install_actuator_toolchain_ensure.dag | 12 +- dag/gunbc/srv3_os_install_diagnostic.dag | 25 ++ dag/gunbc/srv3_os_install_reconcile_apply.dag | 7 +- .../srv3_os_install_reconcile_dry_run.dag | 7 +- .../srv3_host_effect_realize_witness_test.dag | 10 +- ...tall_diagnostic_checklist_witness_test.dag | 15 +- 12 files changed, 270 insertions(+), 296 deletions(-) delete mode 100644 dag/gunbc/srv3_host_effect_realize.dag diff --git a/dag/extdeps/package_managers/apt.dag b/dag/extdeps/package_managers/apt.dag index 592c3af358f..84a3c66d738 100644 --- a/dag/extdeps/package_managers/apt.dag +++ b/dag/extdeps/package_managers/apt.dag @@ -30,6 +30,8 @@ data package_socat: NonEmptyStr = "socat" data package_websocat: NonEmptyStr = "websocat" data package_xorriso: NonEmptyStr = "xorriso" +data apt_get_binary_path: NonEmptyStr = "/usr/bin/apt-get" + data apt_man_url: NonEmptyStr = "https://manpages.debian.org/testing/apt/apt.8.en.html" diff --git a/dag/gunbc/host_effect.dag b/dag/gunbc/host_effect.dag index 88e3441cfff..3c78aaaa176 100644 --- a/dag/gunbc/host_effect.dag +++ b/dag/gunbc/host_effect.dag @@ -1,13 +1,12 @@ module gunbc.host_effect -import std.types { String, Int, NonEmptyStr, List } +import std.types { String, Int, NonEmptyStr, List, ContentHash } import std.access { AccessDecision } import std.disposition { Disposition, RealizationDispatch, Scaffold, Terminal } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.bmc.types { RedfishSystemAction } import gunbc.fleet_intent { ComputeHost } import gunbc.host_converge { HostConverge } -import gunbc.srv3_os_install_diagnostic { OsInstallAttemptIntent } import std.effects { EffectShape, ReadEffect, @@ -22,7 +21,12 @@ type HostEffect = ShellCommand { script: String } | RedfishAction { action: RedfishSystemAction } | ConvergePlan { policy: HostConverge } - | Srv3InstallDiagnosticObserve { intent: OsInstallAttemptIntent } + | Srv3InstallDiagnosticObserve { + bmc: NonEmptyStr, + port: Int, + iso_path: NonEmptyStr, + iso_content_hash: ContentHash, + } | OsInstallActuatorToolchainEnsure {} type NodeControlPlane = diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 3e006960737..00d1472e4cf 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -27,9 +27,24 @@ import gunbc.host_converge_realize { ConvergeHostInterpreted, converge_host_no_knobs_reason, } -import gunbc.srv3_host_effect_realize { - realize_srv3_install_diagnostic_observe, - realize_os_install_actuator_toolchain_ensure, +import extdeps.gunbc +import extdeps.apt { package_curl, package_nbdkit, package_socat, apt_get_binary_path } +import extdeps.tools.curl { curl_cli_tool } +import extdeps.tools.nbdkit { nbdkit_cli_tool } +import extdeps.tools.socat { socat_cli_tool } +import extdeps.tools.websocat { + websocat_cli_tool, + websocat_github_tag, + websocat_asset_aarch64, + websocat_install_path, + websocat_github_release_download_url, +} +import extdeps.bmc.os_install_actuator_toolchain { gunbc_bin_dir } +import gunbc.shell_bash_runner { shell_exec_via_bash } +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script, + srv3_bmcweb_token_path_for_observe, + srv3_sol_console_log_path_for_observe, } import std.access { AccessDecision, Permit, Deny } import gunbc.ci_deploy_access { @@ -37,8 +52,7 @@ import gunbc.ci_deploy_access { deploy_access_check, deploy_access_check_observed, deploy_access_decision_reason, } -import gunbc.srv3_install_diagnostic_observe_script { srv3_bmcweb_token_path_for_observe, srv3_sol_console_log_path_for_observe } -import std.types { String, NonEmptyStr, List } +import std.types { String, NonEmptyStr, List, Bool } type ResolvedHostEffectCell = ShellOnHost { node: ComputeHost, script: String } @@ -71,13 +85,13 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res } else { ConvergeOnHost { node: n, host_converge: p } } - Srv3InstallDiagnosticObserve { intent: i } => + Srv3InstallDiagnosticObserve { bmc: b, port: p, iso_path: ip, iso_content_hash: h } => Srv3DiagnosticObserveOnHost { node: n, - bmc: i.bmc_endpoint as String, - port: i.serve_port as String, - iso_path: i.iso_path as String, - intent_content_hash: i.iso_content_hash as String, + bmc: b as String, + port: p as String, + iso_path: ip as String, + intent_content_hash: h as String, } OsInstallActuatorToolchainEnsure {} => OsInstallActuatorToolchainOnHost { node: n } @@ -89,7 +103,7 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } ConvergePlan { policy: _ } => IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - Srv3InstallDiagnosticObserve { intent: _ } => + Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => IncompatibleCell { reason: "host_effect: Srv3InstallDiagnosticObserve is in-band (actuator host shell observe) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } OsInstallActuatorToolchainEnsure {} => IncompatibleCell { reason: "host_effect: OsInstallActuatorToolchainEnsure is in-band (actuator host toolchain) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } @@ -116,6 +130,168 @@ fn shell_exec_failure_reason(observed: ShellExecObserved) -> String { } } +fn srv3_shell_exec_failure_reason(observed: ShellExecObserved) -> String { + let header = "host_effect_apply: srv3 shell exec returned a nonzero exit code; fail-closed" + if observed.stdout == "" && observed.stderr == "" { + header + } else { + concat( + concat(concat(header, "\n--- stdout ---\n"), observed.stdout), + concat("\n--- stderr ---\n", observed.stderr) + ) + } +} + +fn srv3_shell_reconcile( + observed: ShellExecObserved, + intent: HostEffectIntent, +) -> Reconciliation { + let grounding = if observed.success { + Grounds { evidence: ShellEffectApplied { exit_code: observed.exit_code, stdout: observed.stdout } } + } else { + DoesNotGround { reason: srv3_shell_exec_failure_reason(observed: observed) } + } + reconcile_grounded( + apply_effect: intent, + show_read: ReadObserved { value: observed }, + grounding: grounding + ) +} + +fn realize_srv3_install_diagnostic_observe( + bmc: String, + port: String, + iso_path: String, + intent_content_hash: String, + transport: HostEffectTransport, + effect_intent: HostEffectIntent, +) -> Reconciliation { + let script = srv3_install_diagnostic_observe_script( + token_path: srv3_bmcweb_token_path_for_observe, + bmc: bmc, + port: port, + iso_path: iso_path, + intent_content_hash: intent_content_hash, + sol_log: srv3_sol_console_log_path_for_observe as String, + ) + let observed = run_shell_transport(script: shell_exec_via_bash(body: script), transport: transport) + srv3_shell_reconcile(observed: observed, intent: effect_intent) +} + +fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { + shell.Which.Check(command: tool_name).exists +} + +fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { + let probe = concat(tool_name as String, " --version >/dev/null 2>&1") + shell.Exec.Check(command: probe).exists +} + +fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: [ + "-n", + apt_get_binary_path as String, + "install", + "-y", + package as String, + ], + ).success +} + +fn srv3_ensure_apt_tool( + tool_name: NonEmptyStr, + package: NonEmptyStr, + require_version_probe: Bool, +) -> Bool { + if srv3_apt_tool_present(tool_name: tool_name) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + { + true + } else { + srv3_privileged_apt_install(package: package) + && srv3_apt_tool_present(tool_name: tool_name) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + } +} + +fn srv3_ensure_websocat() -> Bool { + let install_path = websocat_install_path as String + if shell.Test.IsExecutable(path: install_path).executable { + true + } else { + let bin_dir = gunbc_bin_dir as String + let url = websocat_github_release_download_url( + tag: websocat_github_tag, + asset: websocat_asset_aarch64, + ) + let mkdir = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["mkdir", "-p", bin_dir], + ) + let chown = shell.Exec.Run( + script: concat(concat("sudo -n chown \"$(id -un):$(id -gn)\" ", bin_dir), ""), + ) + let curl = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/curl", + args: ["-fsSL", url, "-o", install_path], + ) + let chmod = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/bin/chmod", + args: ["+x", install_path], + ) + mkdir.success + && chown.success + && curl.success + && chmod.success + && shell.Test.IsExecutable(path: install_path).executable + } +} + +fn realize_os_install_actuator_toolchain_ensure( + effect_intent: HostEffectIntent, +) -> Reconciliation { + let curl_ok = srv3_ensure_apt_tool( + tool_name: curl_cli_tool.name, + package: package_curl, + require_version_probe: true, + ) + let nbdkit_ok = srv3_ensure_apt_tool( + tool_name: nbdkit_cli_tool.name, + package: package_nbdkit, + require_version_probe: true, + ) + let socat_ok = srv3_ensure_apt_tool( + tool_name: socat_cli_tool.name, + package: package_socat, + require_version_probe: false, + ) + let websocat_ok = srv3_ensure_websocat() + let success = curl_ok && nbdkit_ok && socat_ok && websocat_ok + let stdout = concat( + concat( + concat( + concat("OsInstallActuatorToolchainReceipt: curl=", if curl_ok { "ok" } else { "fail" }), + concat(" nbdkit=", if nbdkit_ok { "ok" } else { "fail" }), + ), + concat(" socat=", if socat_ok { "ok" } else { "fail" }), + ), + concat(" websocat=", if websocat_ok { "ok" } else { "fail" }), + ) + let observed = ShellExecObserved { + exit_code: if success { 0 } else { 1 }, + success: success, + stdout: stdout, + stderr: if success { "" } else { "actuator-toolchain-ensure: one or more tools failed ensure" }, + } + srv3_shell_reconcile(observed: observed, intent: effect_intent) +} + fn run_shell_transport(script: String, transport: HostEffectTransport) -> ShellExecObserved { match transport { LocalShell => { @@ -269,8 +445,15 @@ fn host_effect_apply_gated( ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) - Srv3DiagnosticObserveOnHost { node: _, intent: i } => - realize_srv3_install_diagnostic_observe(intent: i, transport: transport, effect_intent: intent) + Srv3DiagnosticObserveOnHost { node: _, bmc: b, port: p, iso_path: ip, intent_content_hash: h } => + realize_srv3_install_diagnostic_observe( + bmc: b, + port: p, + iso_path: ip, + intent_content_hash: h, + transport: transport, + effect_intent: intent, + ) OsInstallActuatorToolchainOnHost { node: _ } => realize_os_install_actuator_toolchain_ensure(effect_intent: intent) RedfishOnBmc { node: _, action: _ } => diff --git a/dag/gunbc/srv3_host_effect_realize.dag b/dag/gunbc/srv3_host_effect_realize.dag deleted file mode 100644 index 857dfa33a30..00000000000 --- a/dag/gunbc/srv3_host_effect_realize.dag +++ /dev/null @@ -1,251 +0,0 @@ -module gunbc.srv3_host_effect_realize - -import extdeps.shell -import extdeps.gunbc -import extdeps.apt { package_curl, package_nbdkit, package_socat } -import extdeps.tools.curl { curl_cli_tool } -import extdeps.tools.nbdkit { nbdkit_cli_tool } -import extdeps.tools.socat { socat_cli_tool } -import extdeps.tools.websocat { - websocat_cli_tool, - websocat_github_tag, - websocat_asset_aarch64, - websocat_install_path, - websocat_github_release_download_url, -} -import extdeps.diagnostic.ssh { ssh_session_exec_script } -import gunbc.shell_bash_runner { shell_exec_via_bash } -import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, srv3_bmcweb_token_path_for_observe, srv3_sol_console_log_path_for_observe } -import extdeps.bmc.os_install_actuator_toolchain { gunbc_bin_dir } -import gunbc.ci_deploy_access { deploy_access_apt_get_binary_path } -import gunbc.host_effect { - HostEffectIntent, - HostEffectEvidence, - HostEffectTransport, - LocalShell, - SshShell, - EmitArtifactThenThinRun, - ShellEffectApplied, -} -import std.realization_reconcile { - Reconciliation, - Converged, - NotConverged, - ReadObserved, - Grounds, - DoesNotGround, - reconcile_grounded, -} -import std.types { String, NonEmptyStr, Bool, List, Int } - -type Srv3ShellExecObserved { - exit_code: Int - success: Bool - stdout: String - stderr: String -} - -fn srv3_shell_exec_failure_reason(observed: Srv3ShellExecObserved) -> String { - let header = "srv3_host_effect: shell exec returned a nonzero exit code; fail-closed" - if observed.stdout == "" && observed.stderr == "" { - header - } else { - concat( - concat(concat(header, "\n--- stdout ---\n"), observed.stdout), - concat("\n--- stderr ---\n", observed.stderr) - ) - } -} - -fn srv3_run_shell_transport(script: String, transport: HostEffectTransport) -> Srv3ShellExecObserved { - match transport { - LocalShell => { - let result = shell.Exec.Run(script: script) - Srv3ShellExecObserved { - exit_code: result.exit_code, - success: result.success, - stdout: result.stdout, - stderr: result.stderr, - } - } - SshShell { ssh_host: h } => { - if script == "" { - Srv3ShellExecObserved { - exit_code: 1, - success: false, - stdout: "srv3_host_effect: empty shell script over SshShell; fail-closed", - stderr: "", - } - } else { - let result = ssh_session_exec_script(host: h, script: script) - Srv3ShellExecObserved { - exit_code: result.exit_code, - success: result.success, - stdout: result.stdout, - stderr: result.stderr, - } - } - } - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => - Srv3ShellExecObserved { - exit_code: 1, - success: false, - stdout: "srv3_host_effect: srv3 observe/toolchain effects cannot use EmitArtifactThenThinRun transport; typed mismatch.", - stderr: "", - } - } -} - -fn srv3_run_multiline_bash(script: String, transport: HostEffectTransport) -> Srv3ShellExecObserved { - srv3_run_shell_transport(script: shell_exec_via_bash(body: script), transport: transport) -} - -fn srv3_shell_reconcile( - observed: Srv3ShellExecObserved, - intent: HostEffectIntent, -) -> Reconciliation { - let grounding = if observed.success { - Grounds { evidence: ShellEffectApplied { exit_code: observed.exit_code, stdout: observed.stdout } } - } else { - DoesNotGround { reason: srv3_shell_exec_failure_reason(observed: observed) } - } - reconcile_grounded( - apply_effect: intent, - show_read: ReadObserved { value: observed }, - grounding: grounding - ) -} - -fn realize_srv3_install_diagnostic_observe( - bmc: String, - port: String, - iso_path: String, - intent_content_hash: String, - transport: HostEffectTransport, - effect_intent: HostEffectIntent, -) -> Reconciliation { - let script = srv3_install_diagnostic_observe_script( - token_path: srv3_bmcweb_token_path_for_observe, - bmc: bmc, - port: port, - iso_path: iso_path, - intent_content_hash: intent_content_hash, - sol_log: srv3_sol_console_log_path_for_observe as String, - ) - let observed = srv3_run_multiline_bash(script: script, transport: transport) - srv3_shell_reconcile(observed: observed, intent: effect_intent) -} - -fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { - shell.Which.Check(command: tool_name).exists -} - -fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { - let probe = concat(tool_name as String, " --version >/dev/null 2>&1") - shell.Exec.Check(command: probe).exists -} - -fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { - gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/sudo", - args: [ - "-n", - deploy_access_apt_get_binary_path as String, - "install", - "-y", - package as String, - ], - ).success -} - -fn srv3_ensure_apt_tool( - tool_name: NonEmptyStr, - package: NonEmptyStr, - require_version_probe: Bool, -) -> Bool { - if srv3_apt_tool_present(tool_name: tool_name) - && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) - { - true - } else { - srv3_privileged_apt_install(package: package) - && srv3_apt_tool_present(tool_name: tool_name) - && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) - } -} - -fn srv3_ensure_websocat() -> Bool { - let install_path = websocat_install_path as String - if shell.Test.IsExecutable(path: install_path).executable { - true - } else { - let bin_dir = gunbc_bin_dir as String - let url = websocat_github_release_download_url( - tag: websocat_github_tag, - asset: websocat_asset_aarch64, - ) - let mkdir = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/sudo", - args: ["mkdir", "-p", bin_dir], - ) - let chown = shell.Exec.Run( - script: concat(concat("sudo -n chown \"$(id -un):$(id -gn)\" ", bin_dir), ""), - ) - let curl = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/curl", - args: ["-fsSL", url, "-o", install_path], - ) - let chmod = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/bin/chmod", - args: ["+x", install_path], - ) - mkdir.success - && chown.success - && curl.success - && chmod.success - && shell.Test.IsExecutable(path: install_path).executable - } -} - -fn realize_os_install_actuator_toolchain_ensure( - effect_intent: HostEffectIntent, -) -> Reconciliation { - let curl_ok = srv3_ensure_apt_tool( - tool_name: curl_cli_tool.name, - package: package_curl, - require_version_probe: true, - ) - let nbdkit_ok = srv3_ensure_apt_tool( - tool_name: nbdkit_cli_tool.name, - package: package_nbdkit, - require_version_probe: true, - ) - let socat_ok = srv3_ensure_apt_tool( - tool_name: socat_cli_tool.name, - package: package_socat, - require_version_probe: false, - ) - let websocat_ok = srv3_ensure_websocat() - let success = curl_ok && nbdkit_ok && socat_ok && websocat_ok - let stdout = concat( - concat( - concat( - concat("OsInstallActuatorToolchainReceipt: curl=", if curl_ok { "ok" } else { "fail" }), - concat(" nbdkit=", if nbdkit_ok { "ok" } else { "fail" }), - ), - concat(" socat=", if socat_ok { "ok" } else { "fail" }), - ), - concat(" websocat=", if websocat_ok { "ok" } else { "fail" }), - ) - let observed = Srv3ShellExecObserved { - exit_code: if success { 0 } else { 1 }, - success: success, - stdout: stdout, - stderr: if success { "" } else { "actuator-toolchain-ensure: one or more tools failed ensure" }, - } - srv3_shell_reconcile(observed: observed, intent: effect_intent) -} diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index a4ccfe10150..89c3840c859 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -3,13 +3,10 @@ module gunbc.srv3_install_diagnostic_checklist import extdeps.shell import extdeps.filesystem.filesystem_io { Filesystem } import gunbc.shell_bash_runner { shell_printf_line } -import gunbc.srv3_install_diagnostic_observe_script { - srv3_install_diagnostic_observe_script, - srv3_install_diagnostic_observe_script_declares_five_surfaces, -} import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, OsInstallAttemptIntent, + srv3_install_diagnostic_observe_script_for_intent, Srv3InstallDebugObservation, Srv3InstallDiagnostic, NbdServeObservation, @@ -44,7 +41,7 @@ import std.types { String } import std.disposition { Disposition, Terminal } data srv3_install_diagnostic_checklist_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds Srv3InstallDiagnosticObserve โ€” frozen observe script realized via srv3_host_effect_realize without checklist-level shell_exec_via_bash runner glue" + reason: "host_effect_apply binds Srv3InstallDiagnosticObserve โ€” frozen observe script realized via host_effect_realize without checklist-level shell_exec_via_bash runner glue" } data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "dissolved: srv3_install_diagnostic_checklist observe path routes through host_effect_apply Srv3InstallDiagnosticObserve; retire medium_structure_exception_roster entry for dag/gunbc/srv3_install_diagnostic_checklist.dag" @@ -52,7 +49,7 @@ data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String data srv3_install_diagnostic_observe_script_path: String = "/tmp/srv3_install_diagnostic_observe.sh" func srv3_install_diagnostic_emit_observe_script() -> ProcessExit { - let script = srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent) + let script = srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent) let write = Filesystem.Write(path: srv3_install_diagnostic_observe_script_path, content: script) if write.success { ExitSuccess @@ -67,7 +64,12 @@ fn srv3_install_diagnostic_observe_stdout( let target = HostOs { node: srv3_os_install_actuator_host } match host_effect_apply( target: target, - effect: Srv3InstallDiagnosticObserve { intent: intent }, + effect: Srv3InstallDiagnosticObserve { + bmc: intent.bmc_endpoint, + port: intent.serve_port, + iso_path: intent.iso_path, + iso_content_hash: intent.iso_content_hash, + }, evidence: host_effect_read_evidence, transport: LocalShell, ) { diff --git a/dag/gunbc/srv3_install_diagnostic_observe_script.dag b/dag/gunbc/srv3_install_diagnostic_observe_script.dag index 52171e36575..a7b6a92de68 100644 --- a/dag/gunbc/srv3_install_diagnostic_observe_script.dag +++ b/dag/gunbc/srv3_install_diagnostic_observe_script.dag @@ -4,7 +4,7 @@ import std.types { String, NonEmptyStr } import std.disposition { Disposition, Terminal } data srv3_install_diagnostic_observe_script_scope_disposition: Disposition = Terminal { - reason: "FROZEN terminal observe-script concat โ€” no further shell growth; realized via host_effect_apply Srv3InstallDiagnosticObserve in srv3_host_effect_realize" + reason: "FROZEN terminal observe-script concat โ€” no further shell growth; realized via host_effect_apply Srv3InstallDiagnosticObserve in host_effect_realize" } data srv3_bmcweb_token_path_for_observe: String = "/tmp/srv3_bmcweb_token" diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 0c3316786fc..b7a77d3a744 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -29,10 +29,10 @@ import std.realization_reconcile { reconciliation_converged } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { NonEmptyStr, String, Bool } import std.resources { Network } -import std.disposition { Disposition, Terminal } +import std.disposition { Disposition, Terminal, Scaffold } data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via typed apt/curl/websocat argv effects in srv3_host_effect_realize" + reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via typed apt/curl/websocat argv effects in host_effect_realize" } data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Terminal { @@ -187,8 +187,8 @@ fn os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt() -> Bool { } fn os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() -> Bool { - string_contains( - s: srv3_os_install_actuator_toolchain_ensure_scaffold.reason, - pattern: "OsInstallActuatorToolchainEnsure", - ) + match srv3_os_install_actuator_toolchain_ensure_scaffold { + Terminal { reason: why } => string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") + Scaffold { dissolves_to: _, bind: _ } => false + } } diff --git a/dag/gunbc/srv3_os_install_diagnostic.dag b/dag/gunbc/srv3_os_install_diagnostic.dag index 1d9ce9b714f..3c4001c6841 100644 --- a/dag/gunbc/srv3_os_install_diagnostic.dag +++ b/dag/gunbc/srv3_os_install_diagnostic.dag @@ -42,6 +42,11 @@ import gunbc.srv3_seeded_install_media { srv3_seeded_install_media_content_hash import product.placement_supply { HostIdentity } import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } import gunbc.srv3_sol_console_capture { srv3_sol_console_log_path } +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script, + srv3_bmcweb_token_path_for_observe, + srv3_sol_console_log_path_for_observe, +} data srv3_nbd_proxy_local_port: Int = 10809 @@ -134,6 +139,26 @@ data srv3_os_install_attempt_intent: OsInstallAttemptIntent = OsInstallAttemptIn serve_port: srv3_nbd_proxy_local_port, } +fn srv3_install_diagnostic_observe_script_for_intent(intent: OsInstallAttemptIntent) -> String { + srv3_install_diagnostic_observe_script( + token_path: srv3_bmcweb_token_path_for_observe, + bmc: intent.bmc_endpoint as String, + port: intent.serve_port as String, + iso_path: intent.iso_path as String, + intent_content_hash: intent.iso_content_hash as String, + sol_log: srv3_sol_console_log_path_for_observe as String, + ) +} + +fn srv3_install_diagnostic_observe_script_declares_five_surfaces() -> Bool { + let script = srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent) + string_contains(s: script, pattern: "surface=serve") + && string_contains(s: script, pattern: "surface=bmc_websocket") + && string_contains(s: script, pattern: "surface=redfish") + && string_contains(s: script, pattern: "surface=sol") + && string_contains(s: script, pattern: "surface=router") +} + type NbdServeProcessObservation = NbdkitAbsent | WebsocatAbsent diff --git a/dag/gunbc/srv3_os_install_reconcile_apply.dag b/dag/gunbc/srv3_os_install_reconcile_apply.dag index 8097b3d30da..2bbfd8ae3d2 100644 --- a/dag/gunbc/srv3_os_install_reconcile_apply.dag +++ b/dag/gunbc/srv3_os_install_reconcile_apply.dag @@ -8,8 +8,9 @@ import std.types { NonEmptyStr, Timestamp } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.measure { second_count } -import gunbc.srv3_install_diagnostic_observe_script { - srv3_install_diagnostic_observe_script, +import gunbc.srv3_os_install_diagnostic { + srv3_install_diagnostic_observe_script_for_intent, + srv3_os_install_attempt_intent, } import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, @@ -62,7 +63,7 @@ fn load_durable_approval_grant_script() -> String { fn srv3_install_reconcile_run_observe_script() -> String { concat( - srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent), + srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), srv3_install_reconcile_observe_meta_script(), ) } diff --git a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag index 2855461bcb2..cb67665240f 100644 --- a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag +++ b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag @@ -6,8 +6,9 @@ import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { String, NonEmptyStr } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } -import gunbc.srv3_install_diagnostic_observe_script { - srv3_install_diagnostic_observe_script, +import gunbc.srv3_os_install_diagnostic { + srv3_install_diagnostic_observe_script_for_intent, + srv3_os_install_attempt_intent, } import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, @@ -66,7 +67,7 @@ fn parse_reconcile_git_commit(stdout: String) -> NonEmptyStr { func srv3_os_install_reconcile_dry_run() -> ProcessExit { let intent = srv3_os_install_attempt_intent let observe_script = concat( - srv3_install_diagnostic_observe_script(intent: intent), + srv3_install_diagnostic_observe_script_for_intent(intent: intent), srv3_install_reconcile_git_meta_script(), ) let observe_run = shell.Exec.Run(script: shell_exec_via_bash(body: observe_script)) diff --git a/dag/test/claim/srv3_host_effect_realize_witness_test.dag b/dag/test/claim/srv3_host_effect_realize_witness_test.dag index 07a1e755ed4..b8f1476e598 100644 --- a/dag/test/claim/srv3_host_effect_realize_witness_test.dag +++ b/dag/test/claim/srv3_host_effect_realize_witness_test.dag @@ -13,15 +13,21 @@ import gunbc.host_effect { import gunbc.host_effect_realize { host_effect_apply } import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent } import gunbc.fleet_intent { srv1_host } -import std.realization_reconcile { NotConverged, reconciliation_converged } +import std.realization_reconcile { NotConverged } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly test fn srv3_diagnostic_observe_rejects_bmc_controller_target() -> Bool { + let intent = srv3_os_install_attempt_intent match host_effect_apply( target: BmcController { node: srv1_host }, - effect: Srv3InstallDiagnosticObserve { intent: srv3_os_install_attempt_intent }, + effect: Srv3InstallDiagnosticObserve { + bmc: intent.bmc_endpoint, + port: intent.serve_port, + iso_path: intent.iso_path, + iso_content_hash: intent.iso_content_hash, + }, evidence: host_effect_read_evidence, transport: LocalShell, ) { diff --git a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag index e0fc7e17f73..b387bec06a7 100644 --- a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag +++ b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag @@ -1,9 +1,10 @@ module test.claim.srv3_install_diagnostic_checklist import std.logic { Bool } -import gunbc.srv3_install_diagnostic_observe_script { - srv3_install_diagnostic_observe_script, +import gunbc.srv3_os_install_diagnostic { + srv3_install_diagnostic_observe_script_for_intent, srv3_install_diagnostic_observe_script_declares_five_surfaces, + srv3_os_install_attempt_intent, } import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_checklist_entrypoint_is_declared, @@ -42,14 +43,14 @@ data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly test fn observe_script_covers_five_surfaces() -> Bool { srv3_install_diagnostic_observe_script_declares_five_surfaces() - && string_contains(s: srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent), pattern: "pgrep -x nbdkit") - && string_contains(s: srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent), pattern: "sha256sum") - && string_contains(s: srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent), pattern: "port_owner=") - && string_contains(s: srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent), pattern: "/vm/0/0") + && string_contains(s: srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), pattern: "pgrep -x nbdkit") + && string_contains(s: srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), pattern: "sha256sum") + && string_contains(s: srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), pattern: "port_owner=") + && string_contains(s: srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), pattern: "/vm/0/0") } test fn observe_script_websocket_http_code_not_concatenated_before_if() -> Bool { - let script = srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent) + let script = srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent) !string_contains(s: script, pattern: "HTTP_CODE=if") && !string_contains(s: script, pattern: "echo 000))") && string_contains(s: script, pattern: "HTTP_CODE=$(curl") From 0ebbcac7aee9a5e1dae76069820e5fe0ddadb3d1 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:00:39 +0000 Subject: [PATCH 04/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 37 ++++++++++++++++--- .../srv3_install_diagnostic_checklist.dag | 4 +- ...srv3_install_diagnostic_observe_script.dag | 12 ++++-- ...3_os_install_actuator_toolchain_ensure.dag | 12 ++++-- ...tall_diagnostic_checklist_witness_test.dag | 11 ++++++ ...actuator_toolchain_ensure_witness_test.dag | 8 ++-- src/v2/lens/medium_structure_containment.dag | 7 +++- 7 files changed, 71 insertions(+), 20 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 00d1472e4cf..aac50d59af3 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -183,8 +183,35 @@ fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { } fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { - let probe = concat(tool_name as String, " --version >/dev/null 2>&1") - shell.Exec.Check(command: probe).exists + let found = shell.Which.Check(command: tool_name) + if found.exists { + match found.path { + Present { value: tool_path } => + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: tool_path, + args: ["--version"], + ).success + Absent => false + } + } else { + false + } +} + +fn srv3_chown_directory_to_current_user(path: String) -> Bool { + let uid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-u"]) + let gid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-g"]) + if uid.success && gid.success { + let owner = concat(uid.stdout, concat(":", gid.stdout)) + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["-n", "/usr/bin/chown", owner, path], + ).success + } else { + false + } } fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { @@ -232,9 +259,7 @@ fn srv3_ensure_websocat() -> Bool { bin_path: "/usr/bin/sudo", args: ["mkdir", "-p", bin_dir], ) - let chown = shell.Exec.Run( - script: concat(concat("sudo -n chown \"$(id -un):$(id -gn)\" ", bin_dir), ""), - ) + let chown = srv3_chown_directory_to_current_user(path: bin_dir) let curl = gunbc.WitnessBin.Run( workdir: "/", bin_path: "/usr/bin/curl", @@ -246,7 +271,7 @@ fn srv3_ensure_websocat() -> Bool { args: ["+x", install_path], ) mkdir.success - && chown.success + && chown && curl.success && chmod.success && shell.Test.IsExecutable(path: install_path).executable diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index 89c3840c859..f609bcd5bcc 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -41,10 +41,10 @@ import std.types { String } import std.disposition { Disposition, Terminal } data srv3_install_diagnostic_checklist_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds Srv3InstallDiagnosticObserve โ€” frozen observe script realized via host_effect_realize without checklist-level shell_exec_via_bash runner glue" + reason: "host_effect_apply binds Srv3InstallDiagnosticObserve via host_effect_realize; receipt echo remains shell.Exec until bash-emit (#5828)" } -data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "dissolved: srv3_install_diagnostic_checklist observe path routes through host_effect_apply Srv3InstallDiagnosticObserve; retire medium_structure_exception_roster entry for dag/gunbc/srv3_install_diagnostic_checklist.dag" +data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "dissolved: observe path routes through host_effect_apply Srv3InstallDiagnosticObserve; receipt echo and observe-script oracle remain medium-as-string pending bash-emit (#5828)" data srv3_install_diagnostic_observe_script_path: String = "/tmp/srv3_install_diagnostic_observe.sh" diff --git a/dag/gunbc/srv3_install_diagnostic_observe_script.dag b/dag/gunbc/srv3_install_diagnostic_observe_script.dag index a7b6a92de68..a2087aad48f 100644 --- a/dag/gunbc/srv3_install_diagnostic_observe_script.dag +++ b/dag/gunbc/srv3_install_diagnostic_observe_script.dag @@ -1,10 +1,16 @@ module gunbc.srv3_install_diagnostic_observe_script import std.types { String, NonEmptyStr } -import std.disposition { Disposition, Terminal } +import std.disposition { Disposition, RealizationDispatch, Scaffold } +import std.decl_ref { DeclarationRef, WholeDeclaration } -data srv3_install_diagnostic_observe_script_scope_disposition: Disposition = Terminal { - reason: "FROZEN terminal observe-script concat โ€” no further shell growth; realized via host_effect_apply Srv3InstallDiagnosticObserve in host_effect_realize" +data srv3_install_diagnostic_observe_script_bash_emit_dissolution_trigger: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "gunbc.srv3_install_diagnostic_observe_script", + decl_name: "srv3_install_diagnostic_observe_script", + field: WholeDeclaration, + } } data srv3_bmcweb_token_path_for_observe: String = "/tmp/srv3_bmcweb_token" diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index b7a77d3a744..301de20e44c 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -29,14 +29,20 @@ import std.realization_reconcile { reconciliation_converged } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { NonEmptyStr, String, Bool } import std.resources { Network } -import std.disposition { Disposition, Terminal, Scaffold } +import std.disposition { Disposition, Terminal, Scaffold, RealizationDispatch } +import std.decl_ref { DeclarationRef, WholeDeclaration } data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal { reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via typed apt/curl/websocat argv effects in host_effect_realize" } -data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Terminal { - reason: "dissolved: os_install_actuator_toolchain_ensure routes through host_effect_apply OsInstallActuatorToolchainEnsure; retire medium_structure_exception_roster entry for dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag" +data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "gunbc.srv3_os_install_actuator_toolchain_ensure", + decl_name: "os_install_actuator_toolchain_ensure_script", + field: WholeDeclaration, + } } fn os_install_actuator_apt_tool_ensure_block(tool_name: String, package: NonEmptyStr) -> String { diff --git a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag index b387bec06a7..a34c657088c 100644 --- a/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag +++ b/dag/test/claim/srv3_install_diagnostic_checklist_witness_test.dag @@ -6,6 +6,9 @@ import gunbc.srv3_os_install_diagnostic { srv3_install_diagnostic_observe_script_declares_five_surfaces, srv3_os_install_attempt_intent, } +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script_bash_emit_dissolution_trigger, +} import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_checklist_entrypoint_is_declared, } @@ -37,6 +40,7 @@ import gunbc.network_identity_subsumption { srv3_pre_install_lease_table } import extdeps.bmc.types { BootOverrideDisabled, BootTargetNone, On } import extdeps.bmc.webui.nbd_proxy_serve { srv3_nbd_proxy_local_port } import std.measure { byte_size } +import std.disposition { Scaffold, Terminal, RealizationDispatch } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly @@ -183,6 +187,13 @@ test fn parse_bmc_websocket_http_code_non_101_red_despite_serve_websocat_present } } +test fn observe_script_names_bash_emit_dissolution_scaffold() -> Bool { + match srv3_install_diagnostic_observe_script_bash_emit_dissolution_trigger { + Scaffold { dissolves_to: RealizationDispatch, bind: _ } => true + Terminal { reason: _ } => false + } +} + test fn checklist_entrypoint_carries_dissolution_trigger() -> Bool { srv3_install_diagnostic_checklist_entrypoint_is_declared() } diff --git a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag index 1a596a8c417..a86aff63c78 100644 --- a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag +++ b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag @@ -1,7 +1,7 @@ module test.claim.srv3_os_install_actuator_toolchain_ensure import std.logic { Bool } -import std.disposition { Scaffold, Terminal } +import std.disposition { Scaffold, Terminal, RealizationDispatch } import gunbc.srv3_os_install_actuator_toolchain_ensure { os_install_actuator_toolchain_ensure_script, os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_websocat, @@ -26,10 +26,10 @@ test fn srv3_actuator_toolchain_ensure_websocat_installs_to_modeled_path_with_na && string_contains(s: script, pattern: concat("test -x \"", websocat_install_path as String)) } -test fn srv3_actuator_toolchain_ensure_dissolution_trigger_names_roster_retirement() -> Bool { +test fn srv3_actuator_toolchain_ensure_dissolution_trigger_names_bash_emit_scaffold() -> Bool { match os_install_actuator_toolchain_ensure_dissolution_trigger { - Terminal { reason: why } => string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") - Scaffold { dissolves_to: _, bind: _ } => false + Scaffold { dissolves_to: RealizationDispatch, bind: _ } => true + Terminal { reason: _ } => false } && os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() } diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index 488d40dc4a1..82f2fc034a6 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -195,6 +195,9 @@ data medium_structure_exception_roster: List = [ "dag/extdeps/os/ubuntu_install_media_fetch.dag", "dag/gunbc/srv3_install_media_fetch.dag", + "dag/gunbc/srv3_install_diagnostic_checklist.dag", + "dag/gunbc/srv3_install_diagnostic_observe_script.dag", + "dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag", "dag/extdeps/os/ubuntu_seeded_install_media_remaster.dag", "dag/gunbc/srv3_seeded_install_media.dag", "dag/extdeps/bmc/serial_console.dag", @@ -209,9 +212,9 @@ data medium_structure_exception_roster: List = [ "dag/tools/dag_compile_clean_shard_totality_transport.dag" ] -data medium_structure_clean_tree_unquarantine_note: String = "Pre-existing rot surfaced by live host census at discovery-flip unquarantine (2026-07-09; not debt introduced by this PR): dag_compile_clean_shard_transport is shell-transport sibling to already-rostered seam/shard_totality transports; os_install_deduction + v1_dag_parse witness tests pair string_contains with emit-fn output (same pattern as ci_yaml_serializer_witness_test.dag)." +data medium_structure_clean_tree_unquarantine_note: String = "Pre-existing rot surfaced by live host census at discovery-flip unquarantine (2026-07-09; not debt introduced by this PR): dag_compile_clean_shard_transport is shell-transport sibling to already-rostered seam/shard_totality transports; os_install_deduction + v1_dag_parse witness tests pair string_contains with emit-fn output (same pattern as ci_yaml_serializer_witness_test.dag). P5b (2026-07-14): srv3_install_diagnostic_observe_script carved from checklist โ€” roster +1 with bash-emit Scaffold; checklist and toolchain_ensure roster entries restored (entrypoints typed via host_effect_apply; script oracle builders remain until bash-emit)." -data medium_structure_exception_roster_size_baseline: Int = 61 +data medium_structure_exception_roster_size_baseline: Int = 62 fn roster_growth_ratchet_holds(live_size: Int, baseline: Int) -> Bool { live_size <= baseline From be48c5f4f60c3a4c31ab487fe32eeb8a99052cce Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:14:06 +0000 Subject: [PATCH 05/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 82 ++++++++++++++++++------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index aac50d59af3..705373c623c 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -182,20 +182,27 @@ fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { shell.Which.Check(command: tool_name).exists } -fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { +fn srv3_tool_bin_path(tool_name: NonEmptyStr) -> String? { let found = shell.Which.Check(command: tool_name) if found.exists { match found.path { - Present { value: tool_path } => - gunbc.WitnessBin.Run( - workdir: "/", - bin_path: tool_path, - args: ["--version"], - ).success - Absent => false + Present { value: tool_path } => Present { value: tool_path as String } + Absent => none } } else { - false + none + } +} + +fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { + match srv3_tool_bin_path(tool_name: tool_name) { + Present { value: tool_path } => + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: tool_path, + args: ["--version"], + ).success + Absent => false } } @@ -249,32 +256,37 @@ fn srv3_ensure_websocat() -> Bool { if shell.Test.IsExecutable(path: install_path).executable { true } else { - let bin_dir = gunbc_bin_dir as String - let url = websocat_github_release_download_url( - tag: websocat_github_tag, - asset: websocat_asset_aarch64, - ) - let mkdir = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/sudo", - args: ["mkdir", "-p", bin_dir], - ) - let chown = srv3_chown_directory_to_current_user(path: bin_dir) - let curl = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/curl", - args: ["-fsSL", url, "-o", install_path], - ) - let chmod = gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/bin/chmod", - args: ["+x", install_path], - ) - mkdir.success - && chown - && curl.success - && chmod.success - && shell.Test.IsExecutable(path: install_path).executable + match srv3_tool_bin_path(tool_name: curl_cli_tool.name) { + Present { value: curl_path } => { + let bin_dir = gunbc_bin_dir as String + let url = websocat_github_release_download_url( + tag: websocat_github_tag, + asset: websocat_asset_aarch64, + ) + let mkdir = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["mkdir", "-p", bin_dir], + ) + let chown = srv3_chown_directory_to_current_user(path: bin_dir) + let curl = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: curl_path, + args: ["-fsSL", url, "-o", install_path], + ) + let chmod = gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/bin/chmod", + args: ["+x", install_path], + ) + mkdir.success + && chown + && curl.success + && chmod.success + && shell.Test.IsExecutable(path: install_path).executable + } + Absent => false + } } } From 108c4b9097bd775df4ce1706419e3db0b357205f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:22:15 +0000 Subject: [PATCH 06/17] Propagate host_effect NotConverged reasons and unify observe path authorities. Checklist and toolchain entrypoints forward typed refusal reasons (including srv3_shell_reconcile stdout/stderr and deploy_access denials) instead of generic did-not-converge strings. Observe script wires token/SOL paths from srv3_bmcweb_token_path and srv3_sol_console_log_path single authorities. Co-authored-by: Cursor --- dag/gunbc/host_effect_realize.dag | 8 ++--- .../srv3_install_diagnostic_checklist.dag | 35 ++++++++++++++----- ...srv3_install_diagnostic_observe_script.dag | 4 --- ...3_os_install_actuator_toolchain_ensure.dag | 11 +++--- dag/gunbc/srv3_os_install_diagnostic.dag | 6 ++-- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 705373c623c..1a6cdec4e7c 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -43,9 +43,9 @@ import extdeps.bmc.os_install_actuator_toolchain { gunbc_bin_dir } import gunbc.shell_bash_runner { shell_exec_via_bash } import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, - srv3_bmcweb_token_path_for_observe, - srv3_sol_console_log_path_for_observe, } +import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } +import gunbc.srv3_sol_console_capture { srv3_sol_console_log_path } import std.access { AccessDecision, Permit, Deny } import gunbc.ci_deploy_access { DeployAccess, DeployAccessActor, @@ -167,12 +167,12 @@ fn realize_srv3_install_diagnostic_observe( effect_intent: HostEffectIntent, ) -> Reconciliation { let script = srv3_install_diagnostic_observe_script( - token_path: srv3_bmcweb_token_path_for_observe, + token_path: srv3_bmcweb_token_path, bmc: bmc, port: port, iso_path: iso_path, intent_content_hash: intent_content_hash, - sol_log: srv3_sol_console_log_path_for_observe as String, + sol_log: srv3_sol_console_log_path as String, ) let observed = run_shell_transport(script: shell_exec_via_bash(body: script), transport: transport) srv3_shell_reconcile(observed: observed, intent: effect_intent) diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index f609bcd5bcc..2ec39352fb6 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -40,6 +40,10 @@ import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { String } import std.disposition { Disposition, Terminal } +type Srv3InstallDiagnosticObserveOutcome + = Srv3InstallDiagnosticObserved { stdout: String } + | Srv3InstallDiagnosticObserveRefused { reason: String } + data srv3_install_diagnostic_checklist_scaffold: Disposition = Terminal { reason: "host_effect_apply binds Srv3InstallDiagnosticObserve via host_effect_realize; receipt echo remains shell.Exec until bash-emit (#5828)" } @@ -58,9 +62,9 @@ func srv3_install_diagnostic_emit_observe_script() -> ProcessExit { } } -fn srv3_install_diagnostic_observe_stdout( +fn srv3_install_diagnostic_observe_outcome( intent: OsInstallAttemptIntent, -) -> String? { +) -> Srv3InstallDiagnosticObserveOutcome { let target = HostOs { node: srv3_os_install_actuator_host } match host_effect_apply( target: target, @@ -74,16 +78,29 @@ fn srv3_install_diagnostic_observe_stdout( transport: LocalShell, ) { Converged { evidence: ShellEffectApplied { exit_code: _, stdout: out }, applied: _ } => - Present { value: out } - Converged { evidence: ConvergePlanApplied { exit_code: _, stdout: _ }, applied: _ } => none - NotConverged { reason: _, applied: _ } => none + Srv3InstallDiagnosticObserved { stdout: out } + Converged { evidence: ConvergePlanApplied { exit_code: _, stdout: _ }, applied: _ } => + Srv3InstallDiagnosticObserveRefused { + reason: "srv3-install-diagnostic-observe: unexpected ConvergePlanApplied evidence for Srv3InstallDiagnosticObserve", + } + NotConverged { reason: why, applied: _ } => + Srv3InstallDiagnosticObserveRefused { reason: why } + } +} + +fn srv3_install_diagnostic_observe_stdout( + intent: OsInstallAttemptIntent, +) -> String? { + match srv3_install_diagnostic_observe_outcome(intent: intent) { + Srv3InstallDiagnosticObserved { stdout: out } => Present { value: out } + Srv3InstallDiagnosticObserveRefused { reason: _ } => none } } func srv3_install_diagnostic_checklist() -> ProcessExit { let intent = srv3_os_install_attempt_intent - match srv3_install_diagnostic_observe_stdout(intent: intent) { - Present { value: stdout } => { + match srv3_install_diagnostic_observe_outcome(intent: intent) { + Srv3InstallDiagnosticObserved { stdout } => { let observation = parse_srv3_install_debug_observation( stdout: stdout, intent: intent, @@ -102,8 +119,8 @@ func srv3_install_diagnostic_checklist() -> ProcessExit { exit_failure(reason: concat("srv3-install-diagnostic-receipt: ", echo_run.stderr)) } } - Absent => - exit_failure(reason: "srv3-install-diagnostic-observe: host_effect_apply Srv3InstallDiagnosticObserve did not converge") + Srv3InstallDiagnosticObserveRefused { reason } => + exit_failure(reason: reason) } } diff --git a/dag/gunbc/srv3_install_diagnostic_observe_script.dag b/dag/gunbc/srv3_install_diagnostic_observe_script.dag index a2087aad48f..539ae3fd238 100644 --- a/dag/gunbc/srv3_install_diagnostic_observe_script.dag +++ b/dag/gunbc/srv3_install_diagnostic_observe_script.dag @@ -13,10 +13,6 @@ data srv3_install_diagnostic_observe_script_bash_emit_dissolution_trigger: Dispo } } -data srv3_bmcweb_token_path_for_observe: String = "/tmp/srv3_bmcweb_token" - -data srv3_sol_console_log_path_for_observe: NonEmptyStr = "/var/lib/gunbc/logs/srv3-install-sol-console.log" as NonEmptyStr - fn bash_var_ref(name: String) -> String { concat("$", concat("{", concat(name, "}"))) } diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 301de20e44c..627ab4bbfd1 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -25,7 +25,7 @@ import gunbc.host_effect { } import gunbc.host_effect_realize { host_effect_apply_gated } import gunbc.os_install_actuator_selection { srv3_os_install_actuator_host } -import std.realization_reconcile { reconciliation_converged } +import std.realization_reconcile { Converged, NotConverged } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { NonEmptyStr, String, Bool } import std.resources { Network } @@ -167,17 +167,16 @@ func srv3_os_install_actuator_toolchain_ensure() -> ProcessExit { let target = HostOs { node: srv3_os_install_actuator_host } let access = ci_deploy_srv1_operator_access - if reconciliation_converged(r: host_effect_apply_gated( + match host_effect_apply_gated( access: access, target: target, effect: OsInstallActuatorToolchainEnsure {}, evidence: host_effect_identity_evidence, transport: access.transport, fixture_actor: none, - )) { - ExitSuccess - } else { - exit_failure(reason: "actuator-toolchain-ensure: host_effect_apply OsInstallActuatorToolchainEnsure did not converge") + ) { + Converged { evidence: _, applied: _ } => ExitSuccess + NotConverged { reason: why, applied: _ } => exit_failure(reason: why) } } diff --git a/dag/gunbc/srv3_os_install_diagnostic.dag b/dag/gunbc/srv3_os_install_diagnostic.dag index 3c4001c6841..48d5904d78e 100644 --- a/dag/gunbc/srv3_os_install_diagnostic.dag +++ b/dag/gunbc/srv3_os_install_diagnostic.dag @@ -44,8 +44,6 @@ import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } import gunbc.srv3_sol_console_capture { srv3_sol_console_log_path } import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, - srv3_bmcweb_token_path_for_observe, - srv3_sol_console_log_path_for_observe, } data srv3_nbd_proxy_local_port: Int = 10809 @@ -141,12 +139,12 @@ data srv3_os_install_attempt_intent: OsInstallAttemptIntent = OsInstallAttemptIn fn srv3_install_diagnostic_observe_script_for_intent(intent: OsInstallAttemptIntent) -> String { srv3_install_diagnostic_observe_script( - token_path: srv3_bmcweb_token_path_for_observe, + token_path: srv3_bmcweb_token_path, bmc: intent.bmc_endpoint as String, port: intent.serve_port as String, iso_path: intent.iso_path as String, intent_content_hash: intent.iso_content_hash as String, - sol_log: srv3_sol_console_log_path_for_observe as String, + sol_log: srv3_sol_console_log_path as String, ) } From cafb9562ea3609d91a8ae74e8d3dad40f3920519 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:39:33 +0000 Subject: [PATCH 07/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 47 ++++-- ...3_os_install_actuator_toolchain_ensure.dag | 153 +----------------- dag/gunbc/srv3_os_install_reconcile_apply.dag | 3 - .../srv3_os_install_reconcile_dry_run.dag | 3 - ...actuator_toolchain_ensure_witness_test.dag | 18 +-- 5 files changed, 48 insertions(+), 176 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 1a6cdec4e7c..7787484a59f 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -225,16 +225,44 @@ fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { gunbc.WitnessBin.Run( workdir: "/", bin_path: "/usr/bin/sudo", - args: [ - "-n", - apt_get_binary_path as String, - "install", - "-y", - package as String, - ], + args: srv3_actuator_toolchain_privileged_apt_install_args(package: package), ).success } +fn srv3_actuator_toolchain_privileged_apt_install_args(package: NonEmptyStr) -> List { + [ + "-n", + apt_get_binary_path as String, + "install", + "-y", + package as String, + ] +} + +fn srv3_actuator_toolchain_websocat_release_download_url() -> String { + websocat_github_release_download_url( + tag: websocat_github_tag, + asset: websocat_asset_aarch64, + ) +} + +fn srv3_actuator_toolchain_realizer_apt_install_is_privileged_gated() -> Bool { + srv3_actuator_toolchain_privileged_apt_install_args(package: package_curl) + == ["-n", apt_get_binary_path as String, "install", "-y", package_curl as String] +} + +fn srv3_actuator_toolchain_realizer_websocat_uses_github_release_not_apt() -> Bool { + string_contains( + s: srv3_actuator_toolchain_websocat_release_download_url(), + pattern: "github.com/vi/websocat/releases/download/", + ) + && string_contains(s: websocat_install_path as String, pattern: gunbc_bin_dir as String) +} + +fn srv3_actuator_toolchain_realizer_websocat_installs_to_modeled_path_with_namespace_upsert() -> Bool { + string_contains(s: websocat_install_path as String, pattern: concat(gunbc_bin_dir as String, "/")) +} + fn srv3_ensure_apt_tool( tool_name: NonEmptyStr, package: NonEmptyStr, @@ -259,10 +287,7 @@ fn srv3_ensure_websocat() -> Bool { match srv3_tool_bin_path(tool_name: curl_cli_tool.name) { Present { value: curl_path } => { let bin_dir = gunbc_bin_dir as String - let url = websocat_github_release_download_url( - tag: websocat_github_tag, - asset: websocat_asset_aarch64, - ) + let url = srv3_actuator_toolchain_websocat_release_download_url() let mkdir = gunbc.WitnessBin.Run( workdir: "/", bin_path: "/usr/bin/sudo", diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 627ab4bbfd1..5b233ad0ec4 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -1,23 +1,6 @@ module gunbc.srv3_os_install_actuator_toolchain_ensure -import extdeps.bmc.os_install_actuator_toolchain { os_install_actuator_toolchain_requirement, gunbc_bin_dir } -import extdeps.tools.curl { curl_cli_tool } -import extdeps.tools.nbdkit { nbdkit_cli_tool } -import extdeps.tools.socat { socat_cli_tool } -import extdeps.tools.websocat { - websocat_cli_tool, - websocat_github_tag, - websocat_asset_aarch64, - websocat_install_path, - websocat_github_release_download_url, -} -import extdeps.apt { package_curl, package_nbdkit, package_socat } -import gunbc.ci_deploy_access { - ci_deploy_srv1_operator_access, - deploy_access_preflight_check_block, - deploy_access_apt_get_privileged_command, - deploy_access_sudo_n_apt_get_install_command, -} +import gunbc.ci_deploy_access { ci_deploy_srv1_operator_access } import gunbc.host_effect { OsInstallActuatorToolchainEnsure, HostOs, @@ -27,7 +10,7 @@ import gunbc.host_effect_realize { host_effect_apply_gated } import gunbc.os_install_actuator_selection { srv3_os_install_actuator_host } import std.realization_reconcile { Converged, NotConverged } import std.process { ProcessExit, ExitSuccess, exit_failure } -import std.types { NonEmptyStr, String, Bool } +import std.types { Bool } import std.resources { Network } import std.disposition { Disposition, Terminal, Scaffold, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } @@ -39,129 +22,12 @@ data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Scaffold { dissolves_to: RealizationDispatch, bind: DeclarationRef { - module_path: "gunbc.srv3_os_install_actuator_toolchain_ensure", - decl_name: "os_install_actuator_toolchain_ensure_script", + module_path: "gunbc.host_effect_realize", + decl_name: "realize_os_install_actuator_toolchain_ensure", field: WholeDeclaration, } } -fn os_install_actuator_apt_tool_ensure_block(tool_name: String, package: NonEmptyStr) -> String { - os_install_actuator_apt_tool_ensure_block_with_check( - tool_name: tool_name, - package: package, - present_check: concat( - concat(concat("command -v ", tool_name), " >/dev/null 2>&1 && "), - concat(tool_name, " --version >/dev/null 2>&1"), - ), - post_install_check: concat(tool_name, " --version >/dev/null"), - ) -} - -fn os_install_actuator_apt_tool_ensure_block_with_check( - tool_name: String, - package: NonEmptyStr, - present_check: String, - post_install_check: String, -) -> String { - let apt_preflight = deploy_access_preflight_check_block( - access: ci_deploy_srv1_operator_access, - cmd: deploy_access_apt_get_privileged_command(), - ) - let privileged_install = deploy_access_sudo_n_apt_get_install_command(package: package) - concat( - concat( - concat(concat("if ", present_check), "; then\n"), - concat( - concat( - concat(" echo \"OsInstallActuatorToolchainReceipt: outcome=Present tool=", tool_name), - concat(" package=", package as String), - ), - "\"\nelse\n", - ), - ), - concat( - concat( - concat(concat(apt_preflight, privileged_install), "\n"), - concat(post_install_check, "\n"), - ), - concat( - concat(" echo \"OsInstallActuatorToolchainReceipt: outcome=Installed tool=", tool_name), - concat(concat(" package=", package as String), "\"\nfi\n"), - ), - ), - ) -} - -fn os_install_actuator_socat_ensure_block() -> String { - os_install_actuator_apt_tool_ensure_block_with_check( - tool_name: socat_cli_tool.name as String, - package: package_socat, - present_check: "command -v socat >/dev/null 2>&1", - post_install_check: "command -v socat >/dev/null 2>&1", - ) -} - -fn os_install_actuator_websocat_ensure_block() -> String { - let tool_name = websocat_cli_tool.name as String - let install_path = websocat_install_path as String - let bin_dir = gunbc_bin_dir as String - let url_a64 = websocat_github_release_download_url(tag: websocat_github_tag, asset: websocat_asset_aarch64) - concat( - concat( - concat( - concat("if test -x \"", install_path), - "\"; then\n", - ), - concat( - concat(" echo \"OsInstallActuatorToolchainReceipt: outcome=Present tool=", tool_name), - concat(concat(" path=", install_path), "\"\nelse\n"), - ), - ), - concat( - concat( - concat(concat(" sudo mkdir -p ", bin_dir), "\n"), - concat( - concat(concat(" sudo chown \"$(id -un):$(id -gn)\" ", bin_dir), "\n"), - ), - ), - concat( - concat( - concat( - concat(concat(" curl -fsSL \"", url_a64), "\" -o \""), concat(install_path, "\"\n")), - concat(concat(" chmod +x \"", install_path), "\"\n"), - ), - concat( - concat(" test -x \"", install_path), - concat( - concat("\" || { echo \"OsInstallActuatorToolchainRefusal: websocat not executable at ", install_path), - "\" >&2; exit 1; }\n", - ), - ), - ), - concat( - concat(" echo \"OsInstallActuatorToolchainReceipt: outcome=Installed tool=", tool_name), - concat(concat(" path=", install_path), "\"\nfi\n"), - ), - ), - ) -} - -fn os_install_actuator_toolchain_ensure_script() -> String { - concat( - concat( - concat( - concat( - "#!/usr/bin/env bash\nset -euo pipefail\n", - os_install_actuator_apt_tool_ensure_block(tool_name: curl_cli_tool.name as String, package: package_curl), - ), - os_install_actuator_apt_tool_ensure_block(tool_name: nbdkit_cli_tool.name as String, package: package_nbdkit), - ), - os_install_actuator_socat_ensure_block(), - ), - os_install_actuator_websocat_ensure_block(), - ) -} - func srv3_os_install_actuator_toolchain_ensure() -> ProcessExit uses net: Network { @@ -180,17 +46,6 @@ func srv3_os_install_actuator_toolchain_ensure() -> ProcessExit } } -fn os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_websocat() -> Bool { - let script = os_install_actuator_toolchain_ensure_script() - string_contains(s: script, pattern: "github.com/vi/websocat/releases/download/") - && string_contains(s: script, pattern: websocat_install_path as String) - && string_contains(s: script, pattern: "apt-get install -y websocat") == false -} - -fn os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt() -> Bool { - string_contains(s: os_install_actuator_toolchain_ensure_script(), pattern: "sudo -n /usr/bin/apt-get install -y") -} - fn os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() -> Bool { match srv3_os_install_actuator_toolchain_ensure_scaffold { Terminal { reason: why } => string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") diff --git a/dag/gunbc/srv3_os_install_reconcile_apply.dag b/dag/gunbc/srv3_os_install_reconcile_apply.dag index 2bbfd8ae3d2..754b55fdd00 100644 --- a/dag/gunbc/srv3_os_install_reconcile_apply.dag +++ b/dag/gunbc/srv3_os_install_reconcile_apply.dag @@ -11,9 +11,6 @@ import std.measure { second_count } import gunbc.srv3_os_install_diagnostic { srv3_install_diagnostic_observe_script_for_intent, srv3_os_install_attempt_intent, -} -import gunbc.srv3_os_install_diagnostic { - srv3_os_install_attempt_intent, parse_srv3_install_debug_observation, } import gunbc.srv3_os_install_reconcile { diff --git a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag index cb67665240f..90c1c739341 100644 --- a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag +++ b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag @@ -9,9 +9,6 @@ import std.decl_ref { DeclarationRef, WholeDeclaration } import gunbc.srv3_os_install_diagnostic { srv3_install_diagnostic_observe_script_for_intent, srv3_os_install_attempt_intent, -} -import gunbc.srv3_os_install_diagnostic { - srv3_os_install_attempt_intent, parse_srv3_install_debug_observation, } import gunbc.srv3_os_install_reconcile { diff --git a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag index a86aff63c78..78c7017c94e 100644 --- a/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag +++ b/dag/test/claim/srv3_os_install_actuator_toolchain_ensure_witness_test.dag @@ -3,27 +3,25 @@ module test.claim.srv3_os_install_actuator_toolchain_ensure import std.logic { Bool } import std.disposition { Scaffold, Terminal, RealizationDispatch } import gunbc.srv3_os_install_actuator_toolchain_ensure { - os_install_actuator_toolchain_ensure_script, - os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_websocat, - os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt, os_install_actuator_toolchain_ensure_dissolution_trigger, os_install_actuator_toolchain_ensure_routes_through_host_effect_apply, } -import extdeps.tools.websocat { websocat_install_path } +import gunbc.host_effect_realize { + srv3_actuator_toolchain_realizer_apt_install_is_privileged_gated, + srv3_actuator_toolchain_realizer_websocat_uses_github_release_not_apt, + srv3_actuator_toolchain_realizer_websocat_installs_to_modeled_path_with_namespace_upsert, +} import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly test fn srv3_actuator_toolchain_ensure_uses_github_release_for_websocat() -> Bool { - os_install_actuator_toolchain_ensure_script_uses_github_release_not_apt_for_websocat() - && os_install_actuator_toolchain_ensure_is_privileged_gated_for_apt() + srv3_actuator_toolchain_realizer_websocat_uses_github_release_not_apt() + && srv3_actuator_toolchain_realizer_apt_install_is_privileged_gated() } test fn srv3_actuator_toolchain_ensure_websocat_installs_to_modeled_path_with_namespace_upsert() -> Bool { - let script = os_install_actuator_toolchain_ensure_script() - string_contains(s: script, pattern: websocat_install_path as String) - && string_contains(s: script, pattern: "sudo chown") - && string_contains(s: script, pattern: concat("test -x \"", websocat_install_path as String)) + srv3_actuator_toolchain_realizer_websocat_installs_to_modeled_path_with_namespace_upsert() } test fn srv3_actuator_toolchain_ensure_dissolution_trigger_names_bash_emit_scaffold() -> Bool { From 59d0765e0b1b3f6aba2ca60be392b53b6cdcd320 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:45:22 +0000 Subject: [PATCH 08/17] Retire dead toolchain script witnesses; coalesce reconcile imports. Witnesses now assert srv3_actuator_toolchain_* invariants on the live host_effect_realize argv path. Delete unused bash script builders. Coalesce duplicate srv3_os_install_diagnostic imports in reconcile modules. Co-authored-by: Cursor --- dag/gunbc/host_effect_realize.dag | 4 ++-- src/v2/lens/medium_structure_containment.dag | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 7787484a59f..7f4de60978e 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -247,8 +247,8 @@ fn srv3_actuator_toolchain_websocat_release_download_url() -> String { } fn srv3_actuator_toolchain_realizer_apt_install_is_privileged_gated() -> Bool { - srv3_actuator_toolchain_privileged_apt_install_args(package: package_curl) - == ["-n", apt_get_binary_path as String, "install", "-y", package_curl as String] + let args = srv3_actuator_toolchain_privileged_apt_install_args(package: package_curl) + args == ["-n", apt_get_binary_path as String, "install", "-y", package_curl as String] } fn srv3_actuator_toolchain_realizer_websocat_uses_github_release_not_apt() -> Bool { diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index 82f2fc034a6..b7f1aab6534 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -197,7 +197,6 @@ data medium_structure_exception_roster: List = [ "dag/gunbc/srv3_install_media_fetch.dag", "dag/gunbc/srv3_install_diagnostic_checklist.dag", "dag/gunbc/srv3_install_diagnostic_observe_script.dag", - "dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag", "dag/extdeps/os/ubuntu_seeded_install_media_remaster.dag", "dag/gunbc/srv3_seeded_install_media.dag", "dag/extdeps/bmc/serial_console.dag", @@ -214,7 +213,7 @@ data medium_structure_exception_roster: List = [ data medium_structure_clean_tree_unquarantine_note: String = "Pre-existing rot surfaced by live host census at discovery-flip unquarantine (2026-07-09; not debt introduced by this PR): dag_compile_clean_shard_transport is shell-transport sibling to already-rostered seam/shard_totality transports; os_install_deduction + v1_dag_parse witness tests pair string_contains with emit-fn output (same pattern as ci_yaml_serializer_witness_test.dag). P5b (2026-07-14): srv3_install_diagnostic_observe_script carved from checklist โ€” roster +1 with bash-emit Scaffold; checklist and toolchain_ensure roster entries restored (entrypoints typed via host_effect_apply; script oracle builders remain until bash-emit)." -data medium_structure_exception_roster_size_baseline: Int = 62 +data medium_structure_exception_roster_size_baseline: Int = 61 fn roster_growth_ratchet_holds(live_size: Int, baseline: Int) -> Bool { live_size <= baseline From a914a32bfe057e61edd1998dc51b63d67382d428 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 07:59:01 +0000 Subject: [PATCH 09/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 25 ++++++++++---------- src/v2/lens/medium_structure_containment.dag | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 7f4de60978e..4aed909f9be 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -118,8 +118,7 @@ type ShellExecObserved { stderr: String } -fn shell_exec_failure_reason(observed: ShellExecObserved) -> String { - let header = "host_effect_apply: shell exec returned a nonzero exit code; fail-closed, no fabricated convergence" +fn shell_exec_failure_reason_headered(observed: ShellExecObserved, header: String) -> String { if observed.stdout == "" && observed.stderr == "" { header } else { @@ -130,16 +129,11 @@ fn shell_exec_failure_reason(observed: ShellExecObserved) -> String { } } -fn srv3_shell_exec_failure_reason(observed: ShellExecObserved) -> String { - let header = "host_effect_apply: srv3 shell exec returned a nonzero exit code; fail-closed" - if observed.stdout == "" && observed.stderr == "" { - header - } else { - concat( - concat(concat(header, "\n--- stdout ---\n"), observed.stdout), - concat("\n--- stderr ---\n", observed.stderr) - ) - } +fn shell_exec_failure_reason(observed: ShellExecObserved) -> String { + shell_exec_failure_reason_headered( + observed: observed, + header: "host_effect_apply: shell exec returned a nonzero exit code; fail-closed, no fabricated convergence", + ) } fn srv3_shell_reconcile( @@ -149,7 +143,12 @@ fn srv3_shell_reconcile( let grounding = if observed.success { Grounds { evidence: ShellEffectApplied { exit_code: observed.exit_code, stdout: observed.stdout } } } else { - DoesNotGround { reason: srv3_shell_exec_failure_reason(observed: observed) } + DoesNotGround { + reason: shell_exec_failure_reason_headered( + observed: observed, + header: "host_effect_apply: srv3 shell exec returned a nonzero exit code; fail-closed", + ), + } } reconcile_grounded( apply_effect: intent, diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index b7f1aab6534..02cb0846f2e 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -211,7 +211,7 @@ data medium_structure_exception_roster: List = [ "dag/tools/dag_compile_clean_shard_totality_transport.dag" ] -data medium_structure_clean_tree_unquarantine_note: String = "Pre-existing rot surfaced by live host census at discovery-flip unquarantine (2026-07-09; not debt introduced by this PR): dag_compile_clean_shard_transport is shell-transport sibling to already-rostered seam/shard_totality transports; os_install_deduction + v1_dag_parse witness tests pair string_contains with emit-fn output (same pattern as ci_yaml_serializer_witness_test.dag). P5b (2026-07-14): srv3_install_diagnostic_observe_script carved from checklist โ€” roster +1 with bash-emit Scaffold; checklist and toolchain_ensure roster entries restored (entrypoints typed via host_effect_apply; script oracle builders remain until bash-emit)." +data medium_structure_clean_tree_unquarantine_note: String = "Pre-existing rot surfaced by live host census at discovery-flip unquarantine (2026-07-09; not debt introduced by this PR): dag_compile_clean_shard_transport is shell-transport sibling to already-rostered seam/shard_totality transports; os_install_deduction + v1_dag_parse witness tests pair string_contains with emit-fn output (same pattern as ci_yaml_serializer_witness_test.dag). P5b (2026-07-14): srv3_install_diagnostic_observe_script carved from checklist โ€” roster +1 with bash-emit Scaffold; checklist roster entry restored; toolchain_ensure dropped from roster after typed entrypoint shed script-oracle builders." data medium_structure_exception_roster_size_baseline: Int = 61 From d9cbc3327b7324c69f5bb3060f890f96d4655628 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 08:02:10 +0000 Subject: [PATCH 10/17] Honest toolchain scaffold: WitnessBin argv, not extdeps PM dispatch yet. Terminal reason must not claim extdeps.apt.PackageManager.Install / curl.Http realization while host_effect_realize still composes generic WitnessBin.Run argv (composer 37826). Co-authored-by: Cursor --- dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 5b233ad0ec4..29af77c938f 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -16,7 +16,7 @@ import std.disposition { Disposition, Terminal, Scaffold, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via typed apt/curl/websocat argv effects in host_effect_realize" + reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via host_effect_realize; WitnessBin hand-built argv remains until extdeps.apt.PackageManager.Install and curl.Http dispatch land" } data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Scaffold { From 6c0e2873330326693802e1e8b520d8bbf4117db7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 08:20:10 +0000 Subject: [PATCH 11/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 213 +++++++++++++++++++++++------- 1 file changed, 166 insertions(+), 47 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 4aed909f9be..2d4123ad73a 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -177,55 +177,144 @@ fn realize_srv3_install_diagnostic_observe( srv3_shell_reconcile(observed: observed, intent: effect_intent) } -fn srv3_apt_tool_present(tool_name: NonEmptyStr) -> Bool { - shell.Which.Check(command: tool_name).exists +fn srv3_join_shell_words(words: List) -> String { + fold( + words, + init: "", + f: (acc, word) => if acc == "" { word } else { concat(acc, concat(" ", word)) }, + ) } -fn srv3_tool_bin_path(tool_name: NonEmptyStr) -> String? { - let found = shell.Which.Check(command: tool_name) - if found.exists { - match found.path { - Present { value: tool_path } => Present { value: tool_path as String } - Absent => none +fn srv3_transport_witness_bin_success( + transport: HostEffectTransport, + workdir: String, + bin_path: String, + args: List, +) -> Bool { + match transport { + LocalShell => + gunbc.WitnessBin.Run(workdir: workdir, bin_path: bin_path, args: args).success + SshShell { ssh_host: h } => { + let argv = srv3_join_shell_words(words: args) + let cmd = if workdir == "/" { + if argv == "" { bin_path } else { concat(bin_path, concat(" ", argv)) } + } else { + concat( + concat("cd ", concat(workdir, " && ")), + if argv == "" { bin_path } else { concat(bin_path, concat(" ", argv)) }, + ) + } + ssh_session_exec(host: h, command: cmd as NonEmptyStr).success } - } else { - none + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + } +} + +fn srv3_transport_test_executable(path: String, transport: HostEffectTransport) -> Bool { + match transport { + LocalShell => shell.Test.IsExecutable(path: path).executable + SshShell { ssh_host: h } => + ssh_session_exec(host: h, command: concat("test -x ", path) as NonEmptyStr).success + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + } +} + +fn srv3_apt_tool_present(tool_name: NonEmptyStr, transport: HostEffectTransport) -> Bool { + match transport { + LocalShell => shell.Which.Check(command: tool_name).exists + SshShell { ssh_host: h } => + ssh_session_exec( + host: h, + command: concat("command -v ", tool_name as String) as NonEmptyStr, + ).success + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false } } -fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr) -> Bool { - match srv3_tool_bin_path(tool_name: tool_name) { +fn srv3_tool_bin_path(tool_name: NonEmptyStr, transport: HostEffectTransport) -> String? { + match transport { + LocalShell => { + let found = shell.Which.Check(command: tool_name) + if found.exists { + match found.path { + Present { value: tool_path } => Present { value: tool_path as String } + Absent => none + } + } else { + none + } + } + SshShell { ssh_host: h } => { + let result = ssh_session_exec( + host: h, + command: concat("command -v ", tool_name as String) as NonEmptyStr, + ) + if result.success { + let tool_path = trim(result.stdout) + if tool_path == "" { none } else { Present { value: tool_path } } + } else { + none + } + } + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => none + } +} + +fn srv3_apt_tool_version_ok(tool_name: NonEmptyStr, transport: HostEffectTransport) -> Bool { + match srv3_tool_bin_path(tool_name: tool_name, transport: transport) { Present { value: tool_path } => - gunbc.WitnessBin.Run( + srv3_transport_witness_bin_success( + transport: transport, workdir: "/", bin_path: tool_path, args: ["--version"], - ).success + ) Absent => false } } -fn srv3_chown_directory_to_current_user(path: String) -> Bool { - let uid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-u"]) - let gid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-g"]) - if uid.success && gid.success { - let owner = concat(uid.stdout, concat(":", gid.stdout)) - gunbc.WitnessBin.Run( - workdir: "/", - bin_path: "/usr/bin/sudo", - args: ["-n", "/usr/bin/chown", owner, path], - ).success - } else { - false +fn srv3_chown_directory_to_current_user(path: String, transport: HostEffectTransport) -> Bool { + match transport { + LocalShell => { + let uid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-u"]) + let gid = gunbc.WitnessBin.Run(workdir: "/", bin_path: "/usr/bin/id", args: ["-g"]) + if uid.success && gid.success { + let owner = concat(uid.stdout, concat(":", gid.stdout)) + gunbc.WitnessBin.Run( + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["-n", "/usr/bin/chown", owner, path], + ).success + } else { + false + } + } + SshShell { ssh_host: h } => { + let uid = ssh_session_exec(host: h, command: "id -u" as NonEmptyStr) + let gid = ssh_session_exec(host: h, command: "id -g" as NonEmptyStr) + if uid.success && gid.success { + let owner = concat(trim(uid.stdout), concat(":", trim(gid.stdout))) + srv3_transport_witness_bin_success( + transport: transport, + workdir: "/", + bin_path: "/usr/bin/sudo", + args: ["-n", "/usr/bin/chown", owner, path], + ) + } else { + false + } + } + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false } } -fn srv3_privileged_apt_install(package: NonEmptyStr) -> Bool { - gunbc.WitnessBin.Run( +fn srv3_privileged_apt_install(package: NonEmptyStr, transport: HostEffectTransport) -> Bool { + srv3_transport_witness_bin_success( + transport: transport, workdir: "/", bin_path: "/usr/bin/sudo", args: srv3_actuator_toolchain_privileged_apt_install_args(package: package), - ).success + ) } fn srv3_actuator_toolchain_privileged_apt_install_args(package: NonEmptyStr) -> List { @@ -266,73 +355,81 @@ fn srv3_ensure_apt_tool( tool_name: NonEmptyStr, package: NonEmptyStr, require_version_probe: Bool, + transport: HostEffectTransport, ) -> Bool { - if srv3_apt_tool_present(tool_name: tool_name) - && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + if srv3_apt_tool_present(tool_name: tool_name, transport: transport) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name, transport: transport)) { true } else { - srv3_privileged_apt_install(package: package) - && srv3_apt_tool_present(tool_name: tool_name) - && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name)) + srv3_privileged_apt_install(package: package, transport: transport) + && srv3_apt_tool_present(tool_name: tool_name, transport: transport) + && (require_version_probe == false || srv3_apt_tool_version_ok(tool_name: tool_name, transport: transport)) } } -fn srv3_ensure_websocat() -> Bool { +fn srv3_ensure_websocat(transport: HostEffectTransport) -> Bool { let install_path = websocat_install_path as String - if shell.Test.IsExecutable(path: install_path).executable { + if srv3_transport_test_executable(path: install_path, transport: transport) { true } else { - match srv3_tool_bin_path(tool_name: curl_cli_tool.name) { + match srv3_tool_bin_path(tool_name: curl_cli_tool.name, transport: transport) { Present { value: curl_path } => { let bin_dir = gunbc_bin_dir as String let url = srv3_actuator_toolchain_websocat_release_download_url() - let mkdir = gunbc.WitnessBin.Run( + let mkdir = srv3_transport_witness_bin_success( + transport: transport, workdir: "/", bin_path: "/usr/bin/sudo", args: ["mkdir", "-p", bin_dir], ) - let chown = srv3_chown_directory_to_current_user(path: bin_dir) - let curl = gunbc.WitnessBin.Run( + let chown = srv3_chown_directory_to_current_user(path: bin_dir, transport: transport) + let curl = srv3_transport_witness_bin_success( + transport: transport, workdir: "/", bin_path: curl_path, args: ["-fsSL", url, "-o", install_path], ) - let chmod = gunbc.WitnessBin.Run( + let chmod = srv3_transport_witness_bin_success( + transport: transport, workdir: "/", bin_path: "/bin/chmod", args: ["+x", install_path], ) - mkdir.success + mkdir && chown - && curl.success - && chmod.success - && shell.Test.IsExecutable(path: install_path).executable + && curl + && chmod + && srv3_transport_test_executable(path: install_path, transport: transport) } Absent => false } } } -fn realize_os_install_actuator_toolchain_ensure( +fn srv3_realize_os_install_actuator_toolchain_ensure_body( + transport: HostEffectTransport, effect_intent: HostEffectIntent, ) -> Reconciliation { let curl_ok = srv3_ensure_apt_tool( tool_name: curl_cli_tool.name, package: package_curl, require_version_probe: true, + transport: transport, ) let nbdkit_ok = srv3_ensure_apt_tool( tool_name: nbdkit_cli_tool.name, package: package_nbdkit, require_version_probe: true, + transport: transport, ) let socat_ok = srv3_ensure_apt_tool( tool_name: socat_cli_tool.name, package: package_socat, require_version_probe: false, + transport: transport, ) - let websocat_ok = srv3_ensure_websocat() + let websocat_ok = srv3_ensure_websocat(transport: transport) let success = curl_ok && nbdkit_ok && socat_ok && websocat_ok let stdout = concat( concat( @@ -353,6 +450,28 @@ fn realize_os_install_actuator_toolchain_ensure( srv3_shell_reconcile(observed: observed, intent: effect_intent) } +fn realize_os_install_actuator_toolchain_ensure( + effect_intent: HostEffectIntent, +) -> Reconciliation { + match effect_intent.transport { + LocalShell => + srv3_realize_os_install_actuator_toolchain_ensure_body( + transport: effect_intent.transport, + effect_intent: effect_intent, + ) + SshShell { ssh_host: _ } => + srv3_realize_os_install_actuator_toolchain_ensure_body( + transport: effect_intent.transport, + effect_intent: effect_intent, + ) + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + NotConverged { + reason: "host_effect_apply: OsInstallActuatorToolchainEnsure cannot use EmitArtifactThenThinRun transport; typed mismatch.", + applied: effect_intent, + } + } +} + fn run_shell_transport(script: String, transport: HostEffectTransport) -> ShellExecObserved { match transport { LocalShell => { From f30d227c30faf44ea85d439a0a26e63b08d1ff0e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 08:22:33 +0000 Subject: [PATCH 12/17] Add toolchain transport mismatch witness for EmitArtifactThenThinRun. Co-authored-by: Cursor --- .../srv3_host_effect_realize_witness_test.dag | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dag/test/claim/srv3_host_effect_realize_witness_test.dag b/dag/test/claim/srv3_host_effect_realize_witness_test.dag index b8f1476e598..f167d663030 100644 --- a/dag/test/claim/srv3_host_effect_realize_witness_test.dag +++ b/dag/test/claim/srv3_host_effect_realize_witness_test.dag @@ -9,6 +9,9 @@ import gunbc.host_effect { host_effect_read_evidence, host_effect_identity_evidence, LocalShell, + EmitArtifactThenThinRun, + BootstrapFragment, + ThinInvocation, } import gunbc.host_effect_realize { host_effect_apply } import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent } @@ -49,3 +52,19 @@ test fn os_install_actuator_toolchain_ensure_rejects_bmc_controller_target() -> _ => false } } + +test fn os_install_actuator_toolchain_ensure_rejects_emit_artifact_transport() -> Bool { + match host_effect_apply( + target: HostOs { node: srv1_host }, + effect: OsInstallActuatorToolchainEnsure {}, + evidence: host_effect_identity_evidence, + transport: EmitArtifactThenThinRun { + bootstrap: BootstrapFragment { script: "true" }, + invocation: ThinInvocation { argv: ["true"] }, + }, + ) { + NotConverged { reason: why, applied: _ } => + string_contains(s: why, pattern: "EmitArtifactThenThinRun") + _ => false + } +} From a71b942a8b5f88a338a442baef8254b8a7645fbc Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 14:58:15 +0000 Subject: [PATCH 13/17] Resolve merge conflicts with main (P5a + P5b host_effect union). Union P5a typed effect variants with P5b heavy entrypoints; route P5b realizers before srv3_host_effect_script_for; reconcile apply/dry_run stay on srv3_host_effect_apply; path authorities via srv3_actuate_shell_paths. Co-authored-by: Cursor --- dag/gunbc/host_effect.dag | 3 - dag/gunbc/host_effect_realize.dag | 132 +++++++++--------- dag/gunbc/srv3_host_effect_script.dag | 12 +- .../srv3_install_diagnostic_checklist.dag | 5 - dag/gunbc/srv3_os_install_diagnostic.dag | 10 +- dag/gunbc/srv3_os_install_reconcile_apply.dag | 18 --- .../srv3_os_install_reconcile_dry_run.dag | 11 -- 7 files changed, 78 insertions(+), 113 deletions(-) diff --git a/dag/gunbc/host_effect.dag b/dag/gunbc/host_effect.dag index ceea92d5550..6b2f8d6d619 100644 --- a/dag/gunbc/host_effect.dag +++ b/dag/gunbc/host_effect.dag @@ -21,7 +21,6 @@ type HostEffect = ShellCommand { script: String } | RedfishAction { action: RedfishSystemAction } | ConvergePlan { policy: HostConverge } -<<<<<<< HEAD | Srv3InstallDiagnosticObserve { bmc: NonEmptyStr, port: Int, @@ -29,7 +28,6 @@ type HostEffect = iso_content_hash: ContentHash, } | OsInstallActuatorToolchainEnsure {} -======= | Srv3InstallReconcileObserve { include_git_meta: Bool } | DurableApprovalGrantRecord | DurableApprovalGrantRead @@ -42,7 +40,6 @@ type HostEffect = | Srv3NbdProxyServe | Srv3BootOnceReadBackSleep | Srv3ReceiptEmit { lines: List } ->>>>>>> origin/main type NodeControlPlane = HostOs { node: ComputeHost } diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 899ab4a0e13..bc94b0f7955 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -2,9 +2,7 @@ module gunbc.host_effect_realize import gunbc.host_effect { HostEffect, ShellCommand, RedfishAction, ConvergePlan, -<<<<<<< HEAD Srv3InstallDiagnosticObserve, OsInstallActuatorToolchainEnsure, -======= Srv3InstallReconcileObserve, DurableApprovalGrantRecord, DurableApprovalGrantRead, @@ -17,7 +15,6 @@ import gunbc.host_effect { Srv3NbdProxyServe, Srv3BootOnceReadBackSleep, Srv3ReceiptEmit, ->>>>>>> origin/main NodeControlPlane, HostOs, BmcController, Drive, OneShot, ConvergeLoop, HostEffectIntent, HostEffectEvidence, ShellEffectApplied, ConvergePlanApplied, @@ -59,20 +56,18 @@ import gunbc.shell_bash_runner { shell_exec_via_bash } import gunbc.srv3_install_diagnostic_observe_script { srv3_install_diagnostic_observe_script, } -import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } -import gunbc.srv3_sol_console_capture { srv3_sol_console_log_path } +import gunbc.srv3_actuate_shell_paths { + srv3_bmcweb_token_path, + srv3_sol_console_log_path, +} import std.access { AccessDecision, Permit, Deny } import gunbc.ci_deploy_access { DeployAccess, DeployAccessActor, deploy_access_check, deploy_access_check_observed, deploy_access_decision_reason, } -<<<<<<< HEAD -import std.types { String, NonEmptyStr, List, Bool } -======= import gunbc.srv3_host_effect_script { srv3_host_effect_script_for } -import std.types { String, NonEmptyStr, List } ->>>>>>> origin/main +import std.types { String, NonEmptyStr, List, Bool } type ResolvedHostEffectCell = ShellOnHost { node: ComputeHost, script: String } @@ -99,32 +94,7 @@ fn srv3_host_effect_script_resolution_failed_cell() -> ResolvedHostEffectCell { fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> ResolvedHostEffectCell { match target { HostOs { node: n } => - match srv3_host_effect_script_for(effect: effect) { - Present { value: script } => ShellOnHost { node: n, script: script } - Absent => - match effect { - ShellCommand { script: s } => ShellOnHost { node: n, script: s } - RedfishAction { action: _ } => - IncompatibleCell { reason: "host_effect: RedfishAction is out-of-band (Redfish) and cannot target HostOs (in-band shell/systemd); target BmcController instead. Typed mismatch via total fold, DESIGN section 5." } - ConvergePlan { policy: p } => - if n.identity != p.identity { - IncompatibleCell { reason: converge_plan_identity_mismatch_reason(node: n, policy: p) } - } else { - ConvergeOnHost { node: n, host_converge: p } - } - Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() - Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() - Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() - Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() - Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() - Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() - } + match effect { Srv3InstallDiagnosticObserve { bmc: b, port: p, iso_path: ip, iso_content_hash: h } => Srv3DiagnosticObserveOnHost { node: n, @@ -135,44 +105,74 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res } OsInstallActuatorToolchainEnsure {} => OsInstallActuatorToolchainOnHost { node: n } + _ => + match srv3_host_effect_script_for(effect: effect) { + Present { value: script } => ShellOnHost { node: n, script: script } + Absent => + match effect { + ShellCommand { script: s } => ShellOnHost { node: n, script: s } + RedfishAction { action: _ } => + IncompatibleCell { reason: "host_effect: RedfishAction is out-of-band (Redfish) and cannot target HostOs (in-band shell/systemd); target BmcController instead. Typed mismatch via total fold, DESIGN section 5." } + ConvergePlan { policy: p } => + if n.identity != p.identity { + IncompatibleCell { reason: converge_plan_identity_mismatch_reason(node: n, policy: p) } + } else { + ConvergeOnHost { node: n, host_converge: p } + } + Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => + srv3_host_effect_script_resolution_failed_cell() + OsInstallActuatorToolchainEnsure {} => + srv3_host_effect_script_resolution_failed_cell() + Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() + DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() + DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() + Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() + Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() + Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() + Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() + Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() + Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() + Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() + Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() + Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() + } + } } BmcController { node: n } => -<<<<<<< HEAD match effect { - ShellCommand { script: _ } => - IncompatibleCell { reason: "host_effect: ShellCommand is in-band (shell/systemd) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } - ConvergePlan { policy: _ } => - IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => IncompatibleCell { reason: "host_effect: Srv3InstallDiagnosticObserve is in-band (actuator host shell observe) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } OsInstallActuatorToolchainEnsure {} => IncompatibleCell { reason: "host_effect: OsInstallActuatorToolchainEnsure is in-band (actuator host toolchain) and cannot target BmcController; target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } -======= - match srv3_host_effect_script_for(effect: effect) { - Present { value: _ } => - IncompatibleCell { reason: "host_effect: srv3 shell effects are in-band (actuator host LocalShell) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - Absent => - match effect { - ShellCommand { script: _ } => - IncompatibleCell { reason: "host_effect: ShellCommand is in-band (shell/systemd) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } - ConvergePlan { policy: _ } => - IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() - Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() - Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() - Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() - Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() - Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() + _ => + match srv3_host_effect_script_for(effect: effect) { + Present { value: _ } => + IncompatibleCell { reason: "host_effect: srv3 shell effects are in-band (actuator host LocalShell) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + Absent => + match effect { + ShellCommand { script: _ } => + IncompatibleCell { reason: "host_effect: ShellCommand is in-band (shell/systemd) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } + ConvergePlan { policy: _ } => + IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => + srv3_host_effect_script_resolution_failed_cell() + OsInstallActuatorToolchainEnsure {} => + srv3_host_effect_script_resolution_failed_cell() + Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() + DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() + DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() + Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() + Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() + Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() + Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() + Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() + Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() + Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() + Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() + Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() + } } ->>>>>>> origin/main } } } diff --git a/dag/gunbc/srv3_host_effect_script.dag b/dag/gunbc/srv3_host_effect_script.dag index 0ad970547c6..81c9834fa60 100644 --- a/dag/gunbc/srv3_host_effect_script.dag +++ b/dag/gunbc/srv3_host_effect_script.dag @@ -2,6 +2,8 @@ module gunbc.srv3_host_effect_script import gunbc.host_effect { HostEffect, + Srv3InstallDiagnosticObserve, + OsInstallActuatorToolchainEnsure, Srv3InstallReconcileObserve, DurableApprovalGrantRecord, DurableApprovalGrantRead, @@ -19,8 +21,10 @@ import gunbc.host_effect { ConvergePlan, } import gunbc.shell_bash_runner { shell_exec_via_bash, shell_printf_line } -import gunbc.srv3_install_diagnostic_checklist { srv3_install_diagnostic_observe_script } -import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent } +import gunbc.srv3_os_install_diagnostic { + srv3_os_install_attempt_intent, + srv3_install_diagnostic_observe_script_for_intent, +} import gunbc.srv3_os_install_reconcile_receipt { srv3_install_record_boot_once_approval_script } import gunbc.srv3_os_install_reconcile_types { srv3_boot_once_approval_grant_path, @@ -120,7 +124,7 @@ fn install_media_remaster_toolchain_ensure_script() -> String { } fn srv3_install_reconcile_observe_script(include_git_meta: Bool) -> String { - let diagnostic = srv3_install_diagnostic_observe_script(intent: srv3_os_install_attempt_intent) + let diagnostic = srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent) if include_git_meta { concat(diagnostic, srv3_install_reconcile_observe_meta_script()) } else { @@ -219,6 +223,8 @@ fn srv3_host_effect_script_for(effect: HostEffect) -> String? { } Srv3ReceiptEmit { lines: lines } => Present { value: shell_exec_via_bash(body: srv3_receipt_emit_script(lines: lines)) } + Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => Absent + OsInstallActuatorToolchainEnsure {} => Absent ShellCommand { script: _ } => Absent RedfishAction { action: _ } => Absent ConvergePlan { policy: _ } => Absent diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index 19b1fdbe419..2ec39352fb6 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -2,12 +2,7 @@ module gunbc.srv3_install_diagnostic_checklist import extdeps.shell import extdeps.filesystem.filesystem_io { Filesystem } -<<<<<<< HEAD import gunbc.shell_bash_runner { shell_printf_line } -======= -import gunbc.shell_bash_runner { shell_exec_via_bash, shell_printf_line } -import gunbc.srv3_actuate_shell_paths { srv3_bmcweb_token_path } ->>>>>>> origin/main import gunbc.srv3_os_install_diagnostic { srv3_os_install_attempt_intent, OsInstallAttemptIntent, diff --git a/dag/gunbc/srv3_os_install_diagnostic.dag b/dag/gunbc/srv3_os_install_diagnostic.dag index b329def1b38..f4abc217484 100644 --- a/dag/gunbc/srv3_os_install_diagnostic.dag +++ b/dag/gunbc/srv3_os_install_diagnostic.dag @@ -40,16 +40,12 @@ import gunbc.network_identity_subsumption { import gunbc.srv3_os_install_actuate_scope { srv3_actuator_virtual_media_iso_install_path } import gunbc.srv3_seeded_install_media_artifact { srv3_seeded_install_media_content_hash } import product.placement_supply { HostIdentity } -<<<<<<< HEAD -import gunbc.srv3_os_install_actuate { srv3_bmcweb_token_path } -import gunbc.srv3_sol_console_capture { srv3_sol_console_log_path } -import gunbc.srv3_install_diagnostic_observe_script { - srv3_install_diagnostic_observe_script, -======= import gunbc.srv3_actuate_shell_paths { srv3_bmcweb_token_path, srv3_sol_console_log_path, ->>>>>>> origin/main +} +import gunbc.srv3_install_diagnostic_observe_script { + srv3_install_diagnostic_observe_script, } data srv3_nbd_proxy_local_port: Int = 10809 diff --git a/dag/gunbc/srv3_os_install_reconcile_apply.dag b/dag/gunbc/srv3_os_install_reconcile_apply.dag index 0858f2b955f..eaf820393c3 100644 --- a/dag/gunbc/srv3_os_install_reconcile_apply.dag +++ b/dag/gunbc/srv3_os_install_reconcile_apply.dag @@ -14,13 +14,8 @@ import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { NonEmptyStr, Timestamp } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } -<<<<<<< HEAD -import std.measure { second_count } -======= import std.resources { Network } ->>>>>>> origin/main import gunbc.srv3_os_install_diagnostic { - srv3_install_diagnostic_observe_script_for_intent, srv3_os_install_attempt_intent, parse_srv3_install_debug_observation, } @@ -62,20 +57,7 @@ data srv3_os_install_reconcile_apply_shell_runner_scaffold: Disposition = Scaffo } } -<<<<<<< HEAD -fn load_durable_approval_grant_script() -> String { - concat("cat ", concat(srv3_boot_once_approval_grant_path, " 2>/dev/null || true")) -} - -fn srv3_install_reconcile_run_observe_script() -> String { - concat( - srv3_install_diagnostic_observe_script_for_intent(intent: srv3_os_install_attempt_intent), - srv3_install_reconcile_observe_meta_script(), - ) -} -======= ->>>>>>> origin/main fn process_exit_succeeded(exit: ProcessExit) -> Bool { match exit { ExitSuccess => true diff --git a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag index 2a2d3ebfe1a..ad3876cd70e 100644 --- a/dag/gunbc/srv3_os_install_reconcile_dry_run.dag +++ b/dag/gunbc/srv3_os_install_reconcile_dry_run.dag @@ -12,7 +12,6 @@ import std.types { String, NonEmptyStr } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } import gunbc.srv3_os_install_diagnostic { - srv3_install_diagnostic_observe_script_for_intent, srv3_os_install_attempt_intent, parse_srv3_install_debug_observation, } @@ -63,15 +62,6 @@ fn parse_reconcile_git_commit(stdout: String) -> NonEmptyStr { func srv3_os_install_reconcile_dry_run() -> ProcessExit { let intent = srv3_os_install_attempt_intent -<<<<<<< HEAD - let observe_script = concat( - srv3_install_diagnostic_observe_script_for_intent(intent: intent), - srv3_install_reconcile_git_meta_script(), - ) - let observe_run = shell.Exec.Run(script: shell_exec_via_bash(body: observe_script)) - if !observe_run.success { - return exit_failure(reason: concat("srv3-install-reconcile-observe: ", observe_run.stderr)) -======= let observe_r = srv3_host_effect_observe_read_apply() let observe_stdout = match srv3_shell_stdout_from_apply(r: observe_r) { Present { value: out } => out @@ -80,7 +70,6 @@ func srv3_os_install_reconcile_dry_run() -> ProcessExit { Present { value: why } => exit_failure(reason: concat("srv3-install-reconcile-observe: ", why)) Absent => exit_failure(reason: "srv3-install-reconcile-observe: host_effect_apply failed") } ->>>>>>> origin/main } let observation = parse_srv3_install_debug_observation( stdout: observe_stdout, From ba4d3ff3e9bc47cefa3b406df21b623241186a95 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 15:09:45 +0000 Subject: [PATCH 14/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 25 +++++++++++++++---- .../srv3_install_diagnostic_checklist.dag | 21 +++++++++++++--- ...3_os_install_actuator_toolchain_ensure.dag | 21 +++++++++++----- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index bc94b0f7955..364c67aa08d 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -251,6 +251,16 @@ fn srv3_join_shell_words(words: List) -> String { ) } +data srv3_actuator_toolchain_emit_artifact_transport_helper_note: String = "realize_os_install_actuator_toolchain_ensure refuses EmitArtifactThenThinRun at entry; srv3_actuator_toolchain_emit_artifact_transport_helper_refused false arms are fail-closed backstop only โ€” not a silent widen (DESIGN ยง5)" + +fn srv3_actuator_toolchain_emit_artifact_transport_helper_refused() -> Bool { + false +} + +fn srv3_actuator_toolchain_emit_artifact_transport_helper_refused_opt() -> String? { + none +} + fn srv3_transport_witness_bin_success( transport: HostEffectTransport, workdir: String, @@ -272,7 +282,8 @@ fn srv3_transport_witness_bin_success( } ssh_session_exec(host: h, command: cmd as NonEmptyStr).success } - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + srv3_actuator_toolchain_emit_artifact_transport_helper_refused() } } @@ -281,7 +292,8 @@ fn srv3_transport_test_executable(path: String, transport: HostEffectTransport) LocalShell => shell.Test.IsExecutable(path: path).executable SshShell { ssh_host: h } => ssh_session_exec(host: h, command: concat("test -x ", path) as NonEmptyStr).success - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + srv3_actuator_toolchain_emit_artifact_transport_helper_refused() } } @@ -293,7 +305,8 @@ fn srv3_apt_tool_present(tool_name: NonEmptyStr, transport: HostEffectTransport) host: h, command: concat("command -v ", tool_name as String) as NonEmptyStr, ).success - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + srv3_actuator_toolchain_emit_artifact_transport_helper_refused() } } @@ -322,7 +335,8 @@ fn srv3_tool_bin_path(tool_name: NonEmptyStr, transport: HostEffectTransport) -> none } } - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => none + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + srv3_actuator_toolchain_emit_artifact_transport_helper_refused_opt() } } @@ -370,7 +384,8 @@ fn srv3_chown_directory_to_current_user(path: String, transport: HostEffectTrans false } } - EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => false + EmitArtifactThenThinRun { bootstrap: _, invocation: _ } => + srv3_actuator_toolchain_emit_artifact_transport_helper_refused() } } diff --git a/dag/gunbc/srv3_install_diagnostic_checklist.dag b/dag/gunbc/srv3_install_diagnostic_checklist.dag index 2ec39352fb6..364c8336f47 100644 --- a/dag/gunbc/srv3_install_diagnostic_checklist.dag +++ b/dag/gunbc/srv3_install_diagnostic_checklist.dag @@ -38,14 +38,29 @@ import std.realization_reconcile { } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { String } -import std.disposition { Disposition, Terminal } +import std.disposition { Disposition, Scaffold, RealizationDispatch } +import std.decl_ref { DeclarationRef, WholeDeclaration } type Srv3InstallDiagnosticObserveOutcome = Srv3InstallDiagnosticObserved { stdout: String } | Srv3InstallDiagnosticObserveRefused { reason: String } -data srv3_install_diagnostic_checklist_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds Srv3InstallDiagnosticObserve via host_effect_realize; receipt echo remains shell.Exec until bash-emit (#5828)" +data srv3_install_diagnostic_checklist_receipt_echo_dissolution_trigger: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "gunbc.srv3_install_diagnostic_checklist", + decl_name: "srv3_install_diagnostic_checklist", + field: WholeDeclaration, + } +} + +data srv3_install_diagnostic_checklist_scaffold: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "gunbc.srv3_install_diagnostic_checklist", + decl_name: "srv3_install_diagnostic_checklist_receipt_echo_dissolution_trigger", + field: WholeDeclaration, + } } data srv3_install_diagnostic_checklist_shell_runner_dissolution_trigger: String = "dissolved: observe path routes through host_effect_apply Srv3InstallDiagnosticObserve; receipt echo and observe-script oracle remain medium-as-string pending bash-emit (#5828)" diff --git a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag index 29af77c938f..35f54f6f452 100644 --- a/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag +++ b/dag/gunbc/srv3_os_install_actuator_toolchain_ensure.dag @@ -12,11 +12,16 @@ import std.realization_reconcile { Converged, NotConverged } import std.process { ProcessExit, ExitSuccess, exit_failure } import std.types { Bool } import std.resources { Network } -import std.disposition { Disposition, Terminal, Scaffold, RealizationDispatch } +import std.disposition { Disposition, Scaffold, RealizationDispatch, Terminal } import std.decl_ref { DeclarationRef, WholeDeclaration } -data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Terminal { - reason: "host_effect_apply binds OsInstallActuatorToolchainEnsure via host_effect_realize; WitnessBin hand-built argv remains until extdeps.apt.PackageManager.Install and curl.Http dispatch land" +data srv3_os_install_actuator_toolchain_ensure_scaffold: Disposition = Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { + module_path: "gunbc.srv3_os_install_actuator_toolchain_ensure", + decl_name: "os_install_actuator_toolchain_ensure_dissolution_trigger", + field: WholeDeclaration, + } } data os_install_actuator_toolchain_ensure_dissolution_trigger: Disposition = Scaffold { @@ -47,8 +52,12 @@ func srv3_os_install_actuator_toolchain_ensure() -> ProcessExit } fn os_install_actuator_toolchain_ensure_routes_through_host_effect_apply() -> Bool { - match srv3_os_install_actuator_toolchain_ensure_scaffold { - Terminal { reason: why } => string_contains(s: why, pattern: "OsInstallActuatorToolchainEnsure") - Scaffold { dissolves_to: _, bind: _ } => false + match os_install_actuator_toolchain_ensure_dissolution_trigger { + Scaffold { + dissolves_to: RealizationDispatch, + bind: DeclarationRef { module_path: m, decl_name: n, field: _ }, + } => + m == "gunbc.host_effect_realize" && n == "realize_os_install_actuator_toolchain_ensure" + Terminal { reason: _ } => false } } From 742384497a63d3c411f31587fad0e80576a039b5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 15:33:50 +0000 Subject: [PATCH 15/17] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P5b:=20the=202?= =?UTF-8?q?=20HEAVY=20srv3=20files=20=E2=80=94=20srv3=5Finstall=5Fdiagnost?= =?UTF-8?q?ic=5Fchecklis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/fleet_converge_cli.dag | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/fleet_converge_cli.dag b/dag/gunbc/fleet_converge_cli.dag index 52fa5d268bb..225fe558544 100644 --- a/dag/gunbc/fleet_converge_cli.dag +++ b/dag/gunbc/fleet_converge_cli.dag @@ -13,7 +13,28 @@ import gunbc.host_converge { fleet_converge_policy, find_by_identity, } -import gunbc.host_effect { LocalShell, HostEffectIntent, HostEffect, ConvergePlan, ShellCommand, RedfishAction } +import gunbc.host_effect { + LocalShell, + HostEffectIntent, + HostEffect, + ConvergePlan, + ShellCommand, + RedfishAction, + Srv3InstallDiagnosticObserve, + OsInstallActuatorToolchainEnsure, + Srv3InstallReconcileObserve, + DurableApprovalGrantRecord, + DurableApprovalGrantRead, + Srv3InstallMediaFetch, + Srv3SeededInstallMediaRemaster, + Srv3SeededInstallMediaToolchainEnsure, + Srv3SolConsoleCapture, + Srv3BmcwebSessionLogin, + Srv3BmcwebTokenExtract, + Srv3NbdProxyServe, + Srv3BootOnceReadBackSleep, + Srv3ReceiptEmit, +} import gunbc.fleet_converge_apply { converge_apply_for_host } import std.realization_reconcile { Converged, NotConverged } @@ -72,6 +93,20 @@ fn converge_cli_applied_knob_count(intent: HostEffectIntent, fallback: Int) -> I ConvergePlan { policy: p } => p.knobs.length() ShellCommand { script: _ } => fallback RedfishAction { action: _ } => fallback + Srv3InstallDiagnosticObserve { bmc: _, port: _, iso_path: _, iso_content_hash: _ } => fallback + OsInstallActuatorToolchainEnsure {} => fallback + Srv3InstallReconcileObserve { include_git_meta: _ } => fallback + DurableApprovalGrantRecord => fallback + DurableApprovalGrantRead => fallback + Srv3InstallMediaFetch => fallback + Srv3SeededInstallMediaRemaster => fallback + Srv3SeededInstallMediaToolchainEnsure => fallback + Srv3SolConsoleCapture => fallback + Srv3BmcwebSessionLogin => fallback + Srv3BmcwebTokenExtract => fallback + Srv3NbdProxyServe => fallback + Srv3BootOnceReadBackSleep => fallback + Srv3ReceiptEmit { lines: _ } => fallback } } From feb5a077696dd0ec36fa4c7f61bd6adcee023e45 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 15:54:46 +0000 Subject: [PATCH 16/17] Honest toolchain reconcile header and document SSH argv composer limits. Co-authored-by: Cursor --- dag/gunbc/host_effect_realize.dag | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 364c67aa08d..d732b539c09 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -223,6 +223,27 @@ fn srv3_shell_reconcile( ) } +fn srv3_toolchain_ensure_reconcile( + observed: ShellExecObserved, + intent: HostEffectIntent, +) -> Reconciliation { + let grounding = if observed.success { + Grounds { evidence: ShellEffectApplied { exit_code: observed.exit_code, stdout: observed.stdout } } + } else { + DoesNotGround { + reason: shell_exec_failure_reason_headered( + observed: observed, + header: "host_effect_apply: OsInstallActuatorToolchainEnsure synthesized receipt from typed per-tool ensure results; fail-closed", + ), + } + } + reconcile_grounded( + apply_effect: intent, + show_read: ReadObserved { value: observed }, + grounding: grounding + ) +} + fn realize_srv3_install_diagnostic_observe( bmc: String, port: String, @@ -243,6 +264,8 @@ fn realize_srv3_install_diagnostic_observe( srv3_shell_reconcile(observed: observed, intent: effect_intent) } +data srv3_join_shell_words_controlled_args_note: String = "SshShell argv composer for toolchain transport helpers only; callers must pass controlled literals (package names, modeled paths) โ€” no shell-quoting until typed argv dispatch lands." + fn srv3_join_shell_words(words: List) -> String { fold( words, @@ -528,7 +551,7 @@ fn srv3_realize_os_install_actuator_toolchain_ensure_body( stdout: stdout, stderr: if success { "" } else { "actuator-toolchain-ensure: one or more tools failed ensure" }, } - srv3_shell_reconcile(observed: observed, intent: effect_intent) + srv3_toolchain_ensure_reconcile(observed: observed, intent: effect_intent) } fn realize_os_install_actuator_toolchain_ensure( From 986b13a2feb7560be8027c389333d9f6c00e32dc Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 19:10:31 +0000 Subject: [PATCH 17/17] Fix host_effect_realize merge: union P5b + SetHostnameCas (#6619) Remove leftover conflict markers from main merge; keep P5b dedicated realizers alongside SetHostnameCas HostnameOnHost routing in the exhaustive resolve_host_effect_cell / host_effect_apply folds. Co-authored-by: Cursor --- dag/gunbc/host_effect_realize.dag | 57 +++---------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index cd0c3e74d36..63198ad30df 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -139,8 +139,9 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() + SetHostnameCas { hostnamectl_path: p, base: b, desired: d } => + HostnameOnHost { node: n, hostnamectl_path: p, base: b, desired: d } } -<<<<<<< HEAD } } BmcController { node: n } => @@ -176,50 +177,9 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() + SetHostnameCas { hostnamectl_path: _, base: _, desired: _ } => + IncompatibleCell { reason: "host_effect: SetHostnameCas is in-band (shell hostnamectl) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } } -======= - Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() - Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() - Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() - Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() - Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() - Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() - SetHostnameCas { hostnamectl_path: p, base: b, desired: d } => - HostnameOnHost { node: n, hostnamectl_path: p, base: b, desired: d } - } - } - BmcController { node: n } => - match srv3_host_effect_script_for(effect: effect) { - Present { value: _ } => - IncompatibleCell { reason: "host_effect: srv3 shell effects are in-band (actuator host LocalShell) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - Absent => - match effect { - ShellCommand { script: _ } => - IncompatibleCell { reason: "host_effect: ShellCommand is in-band (shell/systemd) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } - ConvergePlan { policy: _ } => - IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } - Srv3InstallReconcileObserve { include_git_meta: _ } => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRecord => srv3_host_effect_script_resolution_failed_cell() - DurableApprovalGrantRead => srv3_host_effect_script_resolution_failed_cell() - Srv3InstallMediaFetch => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaRemaster => srv3_host_effect_script_resolution_failed_cell() - Srv3SeededInstallMediaToolchainEnsure => srv3_host_effect_script_resolution_failed_cell() - Srv3SolConsoleCapture => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebSessionLogin => srv3_host_effect_script_resolution_failed_cell() - Srv3BmcwebTokenExtract => srv3_host_effect_script_resolution_failed_cell() - Srv3NbdProxyServe => srv3_host_effect_script_resolution_failed_cell() - Srv3BootOnceReadBackSleep => srv3_host_effect_script_resolution_failed_cell() - Srv3ReceiptEmit { lines: _ } => srv3_host_effect_script_resolution_failed_cell() - SetHostnameCas { hostnamectl_path: _, base: _, desired: _ } => - IncompatibleCell { reason: "host_effect: SetHostnameCas is in-band (shell hostnamectl) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } ->>>>>>> origin/main } } } @@ -742,7 +702,6 @@ fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: Ide ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) -<<<<<<< HEAD Srv3DiagnosticObserveOnHost { node: _, bmc: b, port: p, iso_path: ip, intent_content_hash: h } => realize_srv3_install_diagnostic_observe( bmc: b, @@ -754,10 +713,8 @@ fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: Ide ) OsInstallActuatorToolchainOnHost { node: _ } => realize_os_install_actuator_toolchain_ensure(effect_intent: intent) -======= HostnameOnHost { node: _, hostnamectl_path: p, base: b, desired: d } => realize_set_hostname_cas_on_host(hostnamectl_path: p, base: b, desired: d, transport: transport, intent: intent) ->>>>>>> origin/main RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } @@ -796,7 +753,6 @@ fn host_effect_apply_gated( ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) -<<<<<<< HEAD Srv3DiagnosticObserveOnHost { node: _, bmc: b, port: p, iso_path: ip, intent_content_hash: h } => realize_srv3_install_diagnostic_observe( bmc: b, @@ -808,10 +764,8 @@ fn host_effect_apply_gated( ) OsInstallActuatorToolchainOnHost { node: _ } => realize_os_install_actuator_toolchain_ensure(effect_intent: intent) -======= HostnameOnHost { node: _, hostnamectl_path: p, base: b, desired: d } => realize_set_hostname_cas_on_host(hostnamectl_path: p, base: b, desired: d, transport: transport, intent: intent) ->>>>>>> origin/main RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } @@ -851,12 +805,9 @@ fn host_effect_drive_directive(r: Reconciliation Refused { reason: inner_why } ShellOnHost { node: _, script: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) ConvergeOnHost { node: _, host_converge: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) -<<<<<<< HEAD Srv3DiagnosticObserveOnHost { node: _, bmc: _, port: _, iso_path: _, intent_content_hash: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) OsInstallActuatorToolchainOnHost { node: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) -======= HostnameOnHost { node: _, hostnamectl_path: _, base: _, desired: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) ->>>>>>> origin/main RedfishOnBmc { node: _, action: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) } }