fix(chv): P2-20/P2-19/P2-17/P2-18 残留收尾 + NO_PROXY 合并
- P2-20:_guest_env_delivered 加专用锁,读写只走 _delivered_secrets/_mark/_clear 三个入口,并发 hammer 测试钉死 - P2-19:_record_net_entries 失败发 net_registry_write_failed 独立审计并返回 False;_boot_vm 见 False 即拒起(wake 随后清半醒 VM) - P2-17:net.json 删失败发 vm_net_registry_delete_failed 审计; _write_dirty_marker 失败发 dirty_marker_write_failed 审计并返回 False,stop 不抛的契约不变 - P2-18:新增 cloud/chv/atomic_file.py(mkstemp 随机名 + 文件 fsync + rename + 目录 fsync),net.json/known_hosts/ snapshots.json/pool registry/脏标记/spill 全部走它,固定 .tmp 清零 - H1 附带:NO_PROXY 实测确有覆盖 bug(env 重复赋值以后者为准, 调用方 NO_PROXY 被 egress 的 localhost 值静默覆盖),改为合并 单条发出;egress 已设时调用方代理变量(含小写)一律不发 - 测试:新增 cloud/tests/test_chv_residual.py(23 项),旧 closeup 两处钉死旧语义的测试按新语义更新
This commit is contained in:
1 parent
ba5df980bf
commit
c39715659e
7 files changed
+850
-51
No files matched your search
+64
-18
@@ -44,6 +44,7 @@ StrictHostKeyChecking=no + UserKnownHostsFile=/dev/null):
|
||||
import os
|
||||
import shlex
|
||||
|
||||
from cloud.chv.atomic_file import atomic_write_text
|
||||
from cloud.chv.gitea_sync import (
|
||||
GITEA_SYNC_PLAIN_KEYS,
|
||||
GITEA_SYNC_SECRET_KEYS,
|
||||
@@ -93,6 +94,63 @@ def split_guest_env(extra_env: dict | None) -> tuple[dict, dict]:
|
||||
return guest_env, plain
|
||||
|
||||
|
||||
def _merge_no_proxy(extra_env, egress_proxy):
|
||||
"""调用方 NO_PROXY/no_proxy 与 egress 的 localhost、
|
||||
127.0.0.1 去重合并(单条 NO_PROXY 的值)。"""
|
||||
parts = []
|
||||
if extra_env:
|
||||
for key in ("NO_PROXY", "no_proxy"):
|
||||
val = extra_env.get(key)
|
||||
if val:
|
||||
parts += [p.strip() for p in str(val).split(",")
|
||||
if p.strip()]
|
||||
merged = []
|
||||
for part in parts:
|
||||
if part not in merged:
|
||||
merged.append(part)
|
||||
if egress_proxy:
|
||||
for part in ("localhost", "127.0.0.1"):
|
||||
if part not in merged:
|
||||
merged.append(part)
|
||||
return merged
|
||||
|
||||
|
||||
def _env_assignments(extra_env, egress_proxy):
|
||||
"""env 前缀的赋值列表(feat-chv-closeup,H1 附带)。
|
||||
|
||||
NO_PROXY 覆盖顺序已核实并修掉:旧拼装是 extra_env 的
|
||||
赋值在前、egress 的 NO_PROXY 在后,`env` 遇重复赋值以
|
||||
后者为准(实测 env NO_PROXY=a NO_PROXY=b → b),调用方
|
||||
传的 NO_PROXY 被 egress 的 localhost,127.0.0.1 静默覆盖。
|
||||
现改为合并(见 _merge_no_proxy),单条 NO_PROXY 发出。
|
||||
egress 代理本身须赢:egress 已设时调用方的
|
||||
HTTPS_PROXY/HTTP_PROXY(含小写,curl 等工具读小写优先,
|
||||
留着会架空 egress)一律不发出,只发 egress 的三条;
|
||||
无 egress 时调用方的值原样保留(只去重,不丢)。"""
|
||||
skip = {"NO_PROXY", "no_proxy"}
|
||||
if egress_proxy:
|
||||
skip |= {"HTTPS_PROXY", "HTTP_PROXY",
|
||||
"https_proxy", "http_proxy"}
|
||||
assignments = []
|
||||
if extra_env:
|
||||
for key in sorted(extra_env):
|
||||
if key in skip:
|
||||
continue
|
||||
assignments.append("%s=%s"
|
||||
% (key, shlex.quote(
|
||||
str(extra_env[key]))))
|
||||
merged = _merge_no_proxy(extra_env, egress_proxy)
|
||||
if egress_proxy:
|
||||
assignments += [
|
||||
"HTTPS_PROXY=%s" % shlex.quote(egress_proxy),
|
||||
"HTTP_PROXY=%s" % shlex.quote(egress_proxy),
|
||||
"NO_PROXY=%s" % shlex.quote(",".join(merged))]
|
||||
elif merged:
|
||||
assignments += [
|
||||
"NO_PROXY=%s" % shlex.quote(",".join(merged))]
|
||||
return assignments
|
||||
|
||||
|
||||
def remote_command(inner_cmd: list, egress_proxy: str | None = None,
|
||||
extra_env: dict | None = None,
|
||||
guest_env_file: str | None = None) -> list:
|
||||
@@ -104,7 +162,8 @@ def remote_command(inner_cmd: list, egress_proxy: str | None = None,
|
||||
set +a`(把 seed 投递的密钥文件 source 进环境并 export),
|
||||
再接 env 前缀与客内命令;文件路径本身不是密钥。
|
||||
extra_env 含密钥名时 ValueError(调用方须先 split 出
|
||||
密钥子集走 seed 投递,见 split_secret_env)。"""
|
||||
密钥子集走 seed 投递,见 split_secret_env)。env 前缀的
|
||||
拼装见 _env_assignments(NO_PROXY 合并、egress 优先)。"""
|
||||
if extra_env:
|
||||
secrets, _ = split_secret_env(extra_env)
|
||||
if secrets:
|
||||
@@ -115,17 +174,7 @@ def remote_command(inner_cmd: list, egress_proxy: str | None = None,
|
||||
if guest_env_file:
|
||||
tokens += ["set", "-a;", ".",
|
||||
shlex.quote(guest_env_file) + ";", "set", "+a;"]
|
||||
env_assignments = []
|
||||
if extra_env:
|
||||
for key in sorted(extra_env):
|
||||
env_assignments.append("%s=%s"
|
||||
% (key, shlex.quote(
|
||||
str(extra_env[key]))))
|
||||
if egress_proxy:
|
||||
env_assignments += [
|
||||
"HTTPS_PROXY=%s" % shlex.quote(egress_proxy),
|
||||
"HTTP_PROXY=%s" % shlex.quote(egress_proxy),
|
||||
"NO_PROXY=localhost,127.0.0.1"]
|
||||
env_assignments = _env_assignments(extra_env, egress_proxy)
|
||||
inner = [shlex.quote(str(arg)) for arg in inner_cmd]
|
||||
if env_assignments:
|
||||
tokens += ["env"] + env_assignments + inner
|
||||
@@ -198,13 +247,10 @@ def parse_keyscan_output(text: str, host: str = "127.0.0.1") -> list:
|
||||
|
||||
|
||||
def write_known_hosts(path: str, key_lines: list) -> None:
|
||||
"""原子写 known_hosts:先写同目录临时文件再 rename,
|
||||
"""原子写 known_hosts:随机 tmp 名 + fsync 后 rename
|
||||
(feat-chv-closeup,P2-18:旧固定 `<path>.tmp` 无 fsync),
|
||||
文件权限 0600(只含公钥行,仍按敏感文件对待)。"""
|
||||
directory = os.path.dirname(path)
|
||||
if directory:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
tmp_path = path + ".tmp"
|
||||
with open(tmp_path, "w") as fh:
|
||||
fh.write("\n".join(key_lines) + "\n")
|
||||
os.chmod(tmp_path, 0o600)
|
||||
os.replace(tmp_path, path)
|
||||
atomic_write_text(path, "\n".join(key_lines) + "\n", mode=0o600)
|
||||
Reference in new issue
Block a user