Merge authoritative main into the task branch
Why: include the current integration baseline before verification. Evidence: fetched origin/main=2c016931044bebdc4a114c1ca1127d1b40d4ab17. Impact: integrate upstream changes without rewriting task history. Refs: task-sync
This commit is contained in:
File diff suppressed because one or more lines are too long
+1
-1
Submodule modules/frontend updated: 1edd872c55...4965de269a
+6
-4
@@ -2000,11 +2000,11 @@ svc_release_adopt_legacy() {
|
||||
c_info "[$ENV_NAME] 领养 legacy 制品为首个 release:releases/$id"
|
||||
}
|
||||
|
||||
# svc_release_id:源标签净化的版本目录名(rdm-ddc09eb-20261012T131500)。
|
||||
# svc_release_id:源标签 + 时钟 + 调用进程身份,同源同秒部署仍使用不同目录。
|
||||
svc_release_id() {
|
||||
local label
|
||||
label="$(printf '%s' "${SRC_LABEL:-manual}" | tr '@' '-' | tr -cd 'a-zA-Z0-9._-' | cut -c1-40)"
|
||||
printf '%s-%s' "$label" "$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
printf '%s-%s-%s' "$label" "$(date -u +%Y%m%dT%H%M%SZ)" "$BASHPID"
|
||||
}
|
||||
|
||||
# svc_release_stage ID SRCBIN SRCPLUGINS [HTDOCS]:候选产物入不可变版本目录。
|
||||
@@ -2012,8 +2012,10 @@ svc_release_id() {
|
||||
svc_release_stage() {
|
||||
local id="$1" srcbin="$2" srcplug="$3" htdocs="${4:-}"
|
||||
local dir="$ENV_DIR/releases/$id"
|
||||
rm -rf "$dir"
|
||||
mkdir -p "$dir/bin" || return 1
|
||||
mkdir -p "$ENV_DIR/releases" || return 1
|
||||
# 排他创建:重复 id 必须拒绝,不能覆盖仍用于运行或回退的历史制品。
|
||||
mkdir "$dir" || { c_err "[$ENV_NAME] release 目录已存在或无法创建:$dir"; return 1; }
|
||||
mkdir "$dir/bin" || return 1
|
||||
cp -f "$srcbin" "$dir/bin/agent-system" || return 1
|
||||
if [ -d "$srcplug" ]; then
|
||||
cp -r "$srcplug" "$dir/plugins" || return 1
|
||||
|
||||
+48
-28
@@ -18,6 +18,7 @@ import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -110,6 +111,52 @@ class RealComboTests(tsr.SvcAutoRecoverTests):
|
||||
("exec", "--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins")),
|
||||
]))
|
||||
self.initialize_store(binary, d / "data")
|
||||
|
||||
def initialize_store(self, binary, data, env=None):
|
||||
"""Create the fixture schema through normal startup before identity CLI.
|
||||
|
||||
identity ensure/verify operate on an existing store and deliberately do
|
||||
not create a missing database or migrate an uninitialized one.
|
||||
"""
|
||||
init_env = dict(self.env if env is None else env)
|
||||
init_env["RDM_DISABLE_EXEC_TOOLS"] = "1"
|
||||
if env is None:
|
||||
init_env.pop("AGENT_SYSTEM_DSN", None)
|
||||
with socket.socket() as sock:
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
port = sock.getsockname()[1]
|
||||
log_path = self.root / ("initialize-%s.log" % data.name)
|
||||
with log_path.open("wb") as log:
|
||||
proc = subprocess.Popen(
|
||||
[str(binary), "serve", "--addr", "127.0.0.1:%d" % port,
|
||||
"--data", str(data), "--auth", "on"],
|
||||
cwd=self.root, env=init_env, stdout=log, stderr=log)
|
||||
try:
|
||||
for _ in range(200):
|
||||
self.assertIsNone(proc.poll(),
|
||||
"fixture startup exited; see %s" % log_path)
|
||||
try:
|
||||
urllib.request.urlopen(
|
||||
"http://127.0.0.1:%d/api/health" % port, timeout=1).close()
|
||||
break
|
||||
except urllib.error.HTTPError as response:
|
||||
status = response.code
|
||||
response.close()
|
||||
if status == 401:
|
||||
break # Authenticated health returning 401 is ready too.
|
||||
self.fail("fixture health returned %d; see %s" % (status, log_path))
|
||||
except OSError:
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
self.fail("fixture startup timed out; see %s" % log_path)
|
||||
finally:
|
||||
proc.terminate()
|
||||
try:
|
||||
proc.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
proc.wait(timeout=10)
|
||||
|
||||
@unittest.skipUnless(REAL and REAL_BIN,
|
||||
"真实基座二进制专项 NOT_RUN:需 SVC_REAL_BIN")
|
||||
@@ -227,34 +274,7 @@ class RealComboTests(tsr.SvcAutoRecoverTests):
|
||||
env["AGENT_SYSTEM_DSN"] = os.environ["AGENT_PG_TEST_DSN"]
|
||||
env["RDM_DISABLE_EXEC_TOOLS"] = "1"
|
||||
env.pop("SVC_STUB_TRACE", None)
|
||||
# 身份命令不建库、不迁移。先让 serve 把库迁到当前 schema,再 ensure/verify。
|
||||
port = 9475
|
||||
log = self.root / "pg-serve.log"
|
||||
serve = subprocess.Popen(
|
||||
[str(rdm_bin), "serve", "--addr", "127.0.0.1:%d" % port,
|
||||
"--data", str(data), "--plugins", str(plugins), "--auth", "auto"],
|
||||
env=env, stdout=log.open("w"), stderr=subprocess.STDOUT)
|
||||
try:
|
||||
up = False
|
||||
for _ in range(50):
|
||||
if serve.poll() is not None:
|
||||
break
|
||||
try:
|
||||
urllib.request.urlopen("http://127.0.0.1:%d/" % port, timeout=1).close()
|
||||
up = True
|
||||
break
|
||||
except urllib.error.HTTPError:
|
||||
up = True
|
||||
break
|
||||
except OSError:
|
||||
time.sleep(0.1)
|
||||
self.assertTrue(up, "serve 未完成迁移:\n%s" % log.read_text(errors="replace")[-1500:])
|
||||
finally:
|
||||
serve.terminate()
|
||||
try:
|
||||
serve.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
serve.kill()
|
||||
self.initialize_store(rdm_bin, data, env)
|
||||
iid = "svc-realcombo-pg-%d" % os.getpid()
|
||||
run = lambda *a: subprocess.run(
|
||||
[str(rdm_bin), "identity", *a, "--instance-id", iid,
|
||||
|
||||
@@ -9,6 +9,7 @@ tests/test_svc_identity.py(IdentityTests:manifest 治理 + 源仓轨道 + no
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import unittest
|
||||
@@ -89,6 +90,21 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
# ---- T1:release 布局 + 启动失败回退 + 期望态恢复 ----
|
||||
|
||||
def test_U2_layout_switch_and_rollback_on_start_failure(self):
|
||||
# Both deployments intentionally share the same clock tick: a failed
|
||||
# candidate must never overwrite the version needed for rollback.
|
||||
clock_bin = self.root / "fixed-clock"
|
||||
clock_bin.mkdir()
|
||||
real_date = shutil.which("date")
|
||||
clock = clock_bin / "date"
|
||||
clock.write_text(
|
||||
'#!/bin/bash\n'
|
||||
'if [[ "$*" == *"+%Y%m%dT%H%M%SZ"* ]]; then\n'
|
||||
' echo 20261012T120000Z\n'
|
||||
'else\n'
|
||||
f' exec {shlex.quote(real_date)} "$@"\n'
|
||||
'fi\n')
|
||||
clock.chmod(0o755)
|
||||
self.env["PATH"] = str(clock_bin) + os.pathsep + self.env["PATH"]
|
||||
self.make_src_repo() # 预克隆 srccache(本用例走 --from local,仅保环境一致)
|
||||
self.seed_agent("u2a", 9461)
|
||||
self.start_ok("u2a") # legacy 运行中(期望态=running)
|
||||
|
||||
Reference in New Issue
Block a user