feat(persona): wake role runtimes only for bounded sessions
This commit is contained in:
parent
935a880b11
commit
67938c0798
12 changed files with 321 additions and 19 deletions
|
|
@ -2,6 +2,8 @@
|
|||
Description=Chenglu independent persona team handshake
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
PartOf=zhuyuan-persona-team-session.target
|
||||
StopWhenUnneeded=yes
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
|
@ -30,6 +32,3 @@ ReadOnlyPaths=__RELEASE_ROOT__ /var/lib/chenglu-agent/repository /etc/chenglu-ag
|
|||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
LockPersonality=true
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ new_units=(
|
|||
chenglu-team-handshake.service
|
||||
guideng-team-handshake.service
|
||||
kezhou-team-handshake.service
|
||||
zhuyuan-persona-team-session.target
|
||||
zhuyuan-persona-fifth-domain-daily.service
|
||||
zhuyuan-persona-fifth-domain-daily.timer
|
||||
)
|
||||
|
|
@ -179,6 +180,7 @@ for unit in "${handshakes[@]}"; do
|
|||
>"/etc/systemd/system/${unit}"
|
||||
done
|
||||
for unit in \
|
||||
zhuyuan-persona-team-session.target \
|
||||
zhuyuan-persona-fifth-domain-daily.service \
|
||||
zhuyuan-persona-fifth-domain-daily.timer; do
|
||||
sed "s|__RELEASE_ROOT__|${release_root}|g" \
|
||||
|
|
@ -187,9 +189,10 @@ for unit in \
|
|||
done
|
||||
|
||||
systemctl daemon-reload
|
||||
for unit in "${handshakes[@]}"; do
|
||||
systemctl restart "$unit"
|
||||
done
|
||||
systemctl disable "${handshakes[@]}" >/dev/null 2>&1 || true
|
||||
systemctl stop zhuyuan-persona-team-session.target "${handshakes[@]}" \
|
||||
>/dev/null 2>&1 || true
|
||||
systemctl start zhuyuan-persona-team-session.target
|
||||
for port in 3932 3933 3934; do
|
||||
ready=0
|
||||
for attempt in $(seq 1 30); do
|
||||
|
|
@ -209,6 +212,10 @@ before_kezhou="$(git --git-dir="${repository_root}/kezhou.git" rev-parse main)"
|
|||
systemctl enable --now zhuyuan-persona-fifth-domain-daily.timer
|
||||
systemctl start zhuyuan-persona-fifth-domain-daily.service
|
||||
test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.timer)" = "active"
|
||||
systemctl stop zhuyuan-persona-team-session.target
|
||||
for unit in "${handshakes[@]}"; do
|
||||
test "$(systemctl is-active "$unit" || true)" != "active"
|
||||
done
|
||||
|
||||
after_chenglu="$(git --git-dir="${repository_root}/chenglu-agent.git" rev-parse main)"
|
||||
after_guideng="$(git --git-dir="${repository_root}/guideng.git" rev-parse main)"
|
||||
|
|
@ -236,9 +243,7 @@ if ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)3001$'; then
|
|||
exit 41
|
||||
fi
|
||||
test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true"
|
||||
for unit in "${handshakes[@]}"; do
|
||||
test "$(systemctl is-active "$unit")" = "active"
|
||||
done
|
||||
test "$(systemctl is-active zhuyuan-persona-team-session.target || true)" != "active"
|
||||
|
||||
latest_daily_receipt="$(
|
||||
find "${runtime_root}/receipts" -maxdepth 1 -type f \
|
||||
|
|
|
|||
167
server-tools/persona-team-handshake/deploy-on-demand-lifecycle.sh
Executable file
167
server-tools/persona-team-handshake/deploy-on-demand-lifecycle.sh
Executable file
|
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
test "$#" = 1
|
||||
test "$(id -u)" = 0
|
||||
|
||||
expected_machine_id="caa7b1019517470f9d1368b6e79db49e"
|
||||
expected_instance_id="f3d4b730-7f02-452f-975b-7091a4800431"
|
||||
source_commit=$1
|
||||
release_root="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
source_commit_file="${release_root}/SOURCE-COMMIT"
|
||||
receipt_root="/var/lib/guanghu/personas/guanghu/fifth-domain-daily/receipts"
|
||||
backup_root="/var/lib/guanghu/personas/guanghu/fifth-domain-daily/lifecycle-backups/${source_commit}"
|
||||
receipt="${receipt_root}/JD-PERSONA-ON-DEMAND-LIFECYCLE-${source_commit}.json"
|
||||
temporary="$(mktemp -d)"
|
||||
completed=0
|
||||
mutated=0
|
||||
|
||||
handshakes=(
|
||||
chenglu-team-handshake.service
|
||||
guideng-team-handshake.service
|
||||
kezhou-team-handshake.service
|
||||
)
|
||||
units=(
|
||||
"${handshakes[@]}"
|
||||
zhuyuan-persona-team-session.target
|
||||
zhuyuan-persona-fifth-domain-daily.service
|
||||
)
|
||||
|
||||
rollback() {
|
||||
systemctl stop zhuyuan-persona-team-session.target "${handshakes[@]}" \
|
||||
>/dev/null 2>&1 || true
|
||||
for unit in "${units[@]}"; do
|
||||
if test -f "${temporary}/${unit}"; then
|
||||
install -o root -g root -m 0644 \
|
||||
"${temporary}/${unit}" "/etc/systemd/system/${unit}"
|
||||
else
|
||||
rm -f "/etc/systemd/system/${unit}"
|
||||
fi
|
||||
done
|
||||
systemctl daemon-reload
|
||||
for unit in "${handshakes[@]}"; do
|
||||
systemctl enable --now "$unit" >/dev/null 2>&1 || true
|
||||
done
|
||||
}
|
||||
|
||||
on_exit() {
|
||||
status=$?
|
||||
trap - EXIT
|
||||
if test "${completed}" = 0 && test "${mutated}" = 1; then
|
||||
rollback
|
||||
fi
|
||||
rm -rf "${temporary}"
|
||||
exit "${status}"
|
||||
}
|
||||
trap on_exit EXIT
|
||||
|
||||
test "${source_commit}" = "$(tr -d '\n' <"${source_commit_file}")"
|
||||
test "$(basename "${release_root}")" = "${source_commit}"
|
||||
test "$(cat /etc/machine-id)" = "${expected_machine_id}"
|
||||
test "$(tr '[:upper:]' '[:lower:]' </sys/class/dmi/id/product_uuid)" = \
|
||||
"${expected_instance_id}"
|
||||
test "$(systemctl get-default)" = "guanghu-language-primary.target"
|
||||
test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.timer)" = active
|
||||
test ! -e "${backup_root}"
|
||||
test ! -e "${receipt}"
|
||||
|
||||
/guanghu/bin/ghctl authorize /guanghu/current install_world_version |
|
||||
grep -q '^GUANGHU_ACTION_AUTHORIZED$'
|
||||
|
||||
install -d -o root -g root -m 0700 \
|
||||
"${receipt_root}" "${backup_root}"
|
||||
for unit in "${units[@]}"; do
|
||||
if test -f "/etc/systemd/system/${unit}"; then
|
||||
cp "/etc/systemd/system/${unit}" "${temporary}/${unit}"
|
||||
cp "/etc/systemd/system/${unit}" "${backup_root}/${unit}"
|
||||
fi
|
||||
done
|
||||
mutated=1
|
||||
for unit in "${units[@]}"; do
|
||||
sed "s|__RELEASE_ROOT__|${release_root}|g" \
|
||||
"${release_root}/server-tools/persona-team-handshake/${unit}" \
|
||||
>"/etc/systemd/system/${unit}"
|
||||
chmod 0644 "/etc/systemd/system/${unit}"
|
||||
done
|
||||
|
||||
systemctl disable "${handshakes[@]}" >/dev/null 2>&1 || true
|
||||
systemctl stop zhuyuan-persona-team-session.target "${handshakes[@]}" \
|
||||
>/dev/null 2>&1 || true
|
||||
systemctl daemon-reload
|
||||
systemctl start "${handshakes[@]}"
|
||||
|
||||
for port in 3932 3933 3934; do
|
||||
ready=0
|
||||
for _attempt in $(seq 1 30); do
|
||||
if test "$(
|
||||
curl -fsS "http://127.0.0.1:${port}/health" 2>/dev/null |
|
||||
jq -r .ok 2>/dev/null
|
||||
)" = true; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
test "${ready}" = 1
|
||||
done
|
||||
|
||||
systemctl stop "${handshakes[@]}"
|
||||
for unit in "${handshakes[@]}"; do
|
||||
test "$(systemctl is-active "${unit}" || true)" != active
|
||||
test "$(systemctl is-enabled "${unit}" || true)" != enabled
|
||||
done
|
||||
for port in 3932 3933 3934; do
|
||||
! ss -ltnH | awk '{print $4}' | grep -Eq "(^|:)${port}$"
|
||||
done
|
||||
|
||||
systemctl start zhuyuan-persona-fifth-domain-daily.service
|
||||
test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.timer)" = active
|
||||
test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.service || true)" != active
|
||||
test "$(systemctl is-active zhuyuan-persona-team-session.target || true)" != active
|
||||
for unit in "${handshakes[@]}"; do
|
||||
test "$(systemctl is-active "${unit}" || true)" != active
|
||||
done
|
||||
|
||||
latest_daily_receipt="$(
|
||||
find "${receipt_root}" -maxdepth 1 -type f \
|
||||
-name 'ZY-PERSONA-DAILY-*.json' -printf '%T@ %p\n' |
|
||||
sort -nr |
|
||||
head -n 1 |
|
||||
cut -d' ' -f2-
|
||||
)"
|
||||
test -n "${latest_daily_receipt}"
|
||||
test "$(jq -r .result "${latest_daily_receipt}")" = PASS
|
||||
|
||||
jq -n \
|
||||
--arg source_commit "${source_commit}" \
|
||||
--arg daily_receipt "${latest_daily_receipt}" \
|
||||
--arg observed_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
'{
|
||||
schema: "guanghu.persona-on-demand-lifecycle-receipt/v1",
|
||||
receipt_id: ("JD-PERSONA-ON-DEMAND-LIFECYCLE-" + $source_commit),
|
||||
target_node_id: "JD-FD-PRIMARY",
|
||||
source_repository: "REPO-012",
|
||||
source_commit: $source_commit,
|
||||
controller: "ICE-P-ZY001",
|
||||
members: [
|
||||
"CHENGLU-AGENT-001",
|
||||
"GUIDENG-AGENT-001",
|
||||
"ICE-GL-KZ-001"
|
||||
],
|
||||
daily_timer: "ACTIVE",
|
||||
idle_runtime_state: "STOPPED",
|
||||
wake_path: "zhuyuan-persona-fifth-domain-daily.service",
|
||||
session_target: "zhuyuan-persona-team-session.target",
|
||||
daily_receipt: $daily_receipt,
|
||||
identity_and_memory_preserved: true,
|
||||
old_writers_remain_disabled: true,
|
||||
observed_at: $observed_at,
|
||||
result: "PASS_100"
|
||||
}' >"${receipt}.tmp"
|
||||
chmod 0600 "${receipt}.tmp"
|
||||
mv "${receipt}.tmp" "${receipt}"
|
||||
|
||||
completed=1
|
||||
trap - EXIT
|
||||
rm -rf "${temporary}"
|
||||
printf 'PERSONA_ON_DEMAND_LIFECYCLE_PASS_100 receipt=%s\n' "${receipt}"
|
||||
|
|
@ -205,4 +205,61 @@ test("daily dispatcher unit is hardened and restricted to exact persona reposito
|
|||
}),
|
||||
unit,
|
||||
);
|
||||
|
||||
const sessionTarget = fs.readFileSync(
|
||||
path.join(
|
||||
root,
|
||||
"server-tools/persona-team-handshake/zhuyuan-persona-team-session.target",
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(sessionTarget, /^StopWhenUnneeded=yes$/m);
|
||||
for (const member of ["chenglu", "guideng", "kezhou"]) {
|
||||
const serviceName =
|
||||
member === "chenglu"
|
||||
? "chenglu-team-handshake.service"
|
||||
: `${member}-team-handshake.service`;
|
||||
assert.match(sessionTarget, new RegExp(`^Requires=${serviceName}$`, "m"));
|
||||
const memberUnit = fs.readFileSync(
|
||||
path.join(root, "server-tools/persona-team-handshake", serviceName),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(
|
||||
memberUnit,
|
||||
/^PartOf=zhuyuan-persona-team-session\.target$/m,
|
||||
);
|
||||
assert.match(memberUnit, /^StopWhenUnneeded=yes$/m);
|
||||
assert.doesNotMatch(memberUnit, /^WantedBy=multi-user\.target$/m);
|
||||
}
|
||||
assert.match(
|
||||
unit,
|
||||
/^Requires=hlcc-jd-candidate\.service zhuyuan-persona-team-session\.target$/m,
|
||||
);
|
||||
|
||||
const deployScript = fs.readFileSync(
|
||||
path.join(
|
||||
root,
|
||||
"server-tools/persona-team-handshake/deploy-on-demand-lifecycle.sh",
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(deployScript, /caa7b1019517470f9d1368b6e79db49e/u);
|
||||
assert.match(deployScript, /f3d4b730-7f02-452f-975b-7091a4800431/u);
|
||||
assert.match(
|
||||
deployScript,
|
||||
/ghctl authorize \/guanghu\/current install_world_version/u,
|
||||
);
|
||||
assert.match(deployScript, /systemctl disable "\$\{handshakes\[@\]\}"/u);
|
||||
assert.match(
|
||||
deployScript,
|
||||
/systemctl start zhuyuan-persona-fifth-domain-daily\.service/u,
|
||||
);
|
||||
assert.match(
|
||||
deployScript,
|
||||
/guanghu\.persona-on-demand-lifecycle-receipt\/v1/u,
|
||||
);
|
||||
assert.match(
|
||||
deployScript,
|
||||
/if test "\$\{completed\}" = 0 && test "\$\{mutated\}" = 1/u,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
Description=Guideng independent persona team handshake
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
PartOf=zhuyuan-persona-team-session.target
|
||||
StopWhenUnneeded=yes
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
|
@ -31,6 +33,3 @@ ReadWritePaths=/var/lib/guanghu/personas/guideng/team-handshake
|
|||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
LockPersonality=true
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
Description=Kezhou independent persona team handshake
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
PartOf=zhuyuan-persona-team-session.target
|
||||
StopWhenUnneeded=yes
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
|
@ -31,6 +33,3 @@ ReadWritePaths=/var/lib/guanghu/personas/kezhou/team-handshake
|
|||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
LockPersonality=true
|
||||
UMask=0077
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
[Unit]
|
||||
Description=Zhuyuan dispatches three role persona systems through Fifth Domain updates
|
||||
After=network-online.target hlcc-jd-candidate.service chenglu-team-handshake.service guideng-team-handshake.service kezhou-team-handshake.service
|
||||
After=network-online.target hlcc-jd-candidate.service zhuyuan-persona-team-session.target
|
||||
Wants=network-online.target
|
||||
Requires=hlcc-jd-candidate.service chenglu-team-handshake.service guideng-team-handshake.service kezhou-team-handshake.service
|
||||
Requires=hlcc-jd-candidate.service zhuyuan-persona-team-session.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=Bounded runtime session for Zhuyuan managed role persona systems
|
||||
Requires=chenglu-team-handshake.service
|
||||
Requires=guideng-team-handshake.service
|
||||
Requires=kezhou-team-handshake.service
|
||||
After=chenglu-team-handshake.service
|
||||
After=guideng-team-handshake.service
|
||||
After=kezhou-team-handshake.service
|
||||
StopWhenUnneeded=yes
|
||||
Loading…
Reference in a new issue