精简文档并完善跨平台发布、安装与备份测试
This commit is contained in:
1 parent
6e3819dedf
commit
a9b4106966
45 files changed
+1217
-658
No files matched your search
@@ -1,11 +0,0 @@
|
||||
[build.env]
|
||||
passthrough = ["NODEFLARE_VERSION"]
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
runner = "qemu-user"
|
||||
|
||||
# cross 0.2.5 自带的 FreeBSD 镜像 sysroot 漏了 libgeom。上游 main 镜像已
|
||||
# 补齐完整 /lib 及共享库软链,因此为 FreeBSD Agent 固定使用该镜像摘要。
|
||||
# 该镜像以 FreeBSD 13 为 sysroot,所以产物最低支持 FreeBSD 13。
|
||||
[target.x86_64-unknown-freebsd]
|
||||
image = "ghcr.io/cross-rs/x86_64-unknown-freebsd@sha256:d1ecbeb055803f5316607acce26c8af2b8609a04e38a06ba7aed84f9df80042e"
|
||||
+38
-3
@@ -36,6 +36,30 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
detect_glibc_version() {
|
||||
[ -x "$glibc_loader" ] || return
|
||||
LC_ALL=C "$glibc_loader" --version 2>/dev/null | awk '
|
||||
tolower($0) ~ /glibc|gnu libc|gnu c library/ {
|
||||
sub(/\.$/, "")
|
||||
if (match($0, /[0-9]+\.[0-9]+(\.[0-9]+)?$/)) {
|
||||
print substr($0, RSTART, RLENGTH)
|
||||
exit
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
glibc_is_supported() {
|
||||
glibc_major=${1%%.*}
|
||||
glibc_minor=${1#*.}
|
||||
glibc_minor=${glibc_minor%%.*}
|
||||
case "$glibc_major:$glibc_minor" in
|
||||
*[!0-9:]*|:*) return 1 ;;
|
||||
esac
|
||||
[ "$glibc_major" -gt 2 ] \
|
||||
|| { [ "$glibc_major" -eq 2 ] && [ "$glibc_minor" -ge 28 ]; }
|
||||
}
|
||||
|
||||
safe_value() {
|
||||
case "$1" in *[!A-Za-z0-9_./:@-]*|'') return 1 ;; esac
|
||||
}
|
||||
@@ -113,7 +137,9 @@ install_agent() {
|
||||
[ -n "$token" ] && [ -n "$endpoint" ] || { usage; exit 1; }
|
||||
endpoint=${endpoint%/}
|
||||
[ ${#token} -le 512 ] && [ ${#endpoint} -le 2048 ] || fail "安装参数长度超出限制"
|
||||
safe_value "$token" && safe_value "$endpoint" || fail "服务地址或 Agent Token 格式无效"
|
||||
if ! safe_value "$token" || ! safe_value "$endpoint"; then
|
||||
fail "服务地址或 Agent Token 格式无效"
|
||||
fi
|
||||
case "$endpoint" in
|
||||
https://?*|http://localhost|http://localhost/*|http://localhost:*|http://127.0.0.1|http://127.0.0.1/*|http://127.0.0.1:*) ;;
|
||||
*) fail "服务地址必须使用 HTTPS;仅本机调试可使用 HTTP" ;;
|
||||
@@ -132,7 +158,16 @@ install_agent() {
|
||||
esac
|
||||
case "$mirror" in *@*) fail "下载加速前缀不能包含用户信息" ;; esac
|
||||
fi
|
||||
case "$(uname -m)" in x86_64|amd64) arch="x64" ;; aarch64|arm64) arch="aarch64" ;; *) fail "暂不支持当前 CPU 架构:$(uname -m)" ;; esac
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) arch="x64"; glibc_loader=/lib64/ld-linux-x86-64.so.2 ;;
|
||||
aarch64|arm64) arch="aarch64"; glibc_loader=/lib/ld-linux-aarch64.so.1 ;;
|
||||
*) fail "暂不支持当前 CPU 架构:$(uname -m)" ;;
|
||||
esac
|
||||
libc=musl
|
||||
glibc_version=$(detect_glibc_version || true)
|
||||
if [ -n "$glibc_version" ] && glibc_is_supported "$glibc_version"; then
|
||||
libc=glibc
|
||||
fi
|
||||
init_system=$(detect_init_system)
|
||||
[ "$init_system" != "unknown" ] || fail "未检测到正在运行的 systemd 或 OpenRC"
|
||||
|
||||
@@ -142,7 +177,7 @@ install_agent() {
|
||||
chmod 750 "$STATE_DIR"
|
||||
temporary="$INSTALL_DIR/.agent.$$.download"
|
||||
trap 'rm -f "$temporary"' EXIT HUP INT TERM
|
||||
artifact="agent-linux-$arch"
|
||||
artifact="agent-linux-$arch-$libc"
|
||||
release_api="https://api.github.com/repos/imengying/NodeFlare/releases/latest"
|
||||
log "正在获取 GitHub 最新正式版本($artifact)"
|
||||
release_json=$(curl --fail --location --silent --show-error --max-time 30 \
|
||||
|
||||
@@ -27,7 +27,7 @@ NodeFlare Agent FreeBSD 安装脚本
|
||||
install-freebsd.sh --status
|
||||
install-freebsd.sh --uninstall
|
||||
|
||||
仅支持 FreeBSD amd64。Agent Token 请勿泄露。
|
||||
支持 FreeBSD amd64 和 arm64。Agent Token 请勿泄露。
|
||||
-m 为可选的 GitHub 下载加速前缀(形如 https://ghproxy.net),
|
||||
仅作用于 Release 下载,摘要校验不受影响。
|
||||
EOF
|
||||
@@ -87,8 +87,9 @@ fi
|
||||
[ "$(id -u)" -eq 0 ] || fail "请使用 root 权限运行安装"
|
||||
[ "$(uname -s)" = "FreeBSD" ] || fail "此脚本仅支持 FreeBSD"
|
||||
case "$(uname -m)" in
|
||||
amd64|x86_64) ;;
|
||||
*) fail "仅支持 FreeBSD amd64" ;;
|
||||
amd64|x86_64) arch=x64 ;;
|
||||
arm64|aarch64) arch=aarch64 ;;
|
||||
*) fail "仅支持 FreeBSD amd64 和 arm64" ;;
|
||||
esac
|
||||
command -v fetch >/dev/null 2>&1 || command -v curl >/dev/null 2>&1 || fail "缺少 fetch 或 curl"
|
||||
command -v sha256 >/dev/null 2>&1 || fail "缺少 sha256,无法校验下载文件"
|
||||
@@ -121,7 +122,9 @@ done
|
||||
[ -n "$token" ] && [ -n "$endpoint" ] || { usage; exit 1; }
|
||||
endpoint=${endpoint%/}
|
||||
[ ${#token} -le 512 ] && [ ${#endpoint} -le 2048 ] || fail "安装参数长度超出限制"
|
||||
safe_value "$token" && safe_value "$endpoint" || fail "服务地址或 Agent Token 格式无效"
|
||||
if ! safe_value "$token" || ! safe_value "$endpoint"; then
|
||||
fail "服务地址或 Agent Token 格式无效"
|
||||
fi
|
||||
case "$endpoint" in
|
||||
https://?*|http://localhost|http://localhost/*|http://localhost:*|http://127.0.0.1|http://127.0.0.1/*|http://127.0.0.1:*) ;;
|
||||
*) fail "服务地址必须使用 HTTPS;仅本机调试可使用 HTTP" ;;
|
||||
@@ -147,7 +150,7 @@ chmod 750 "$STATE_ROOT"
|
||||
chmod 750 "$STATE_DIR"
|
||||
temporary="$INSTALL_DIR/.agent.$$.download"
|
||||
trap 'rm -f "$temporary"' EXIT HUP INT TERM
|
||||
artifact="agent-freebsd-x64"
|
||||
artifact="agent-freebsd-$arch"
|
||||
release_api="https://api.github.com/repos/imengying/NodeFlare/releases/latest"
|
||||
log "正在获取 GitHub 最新正式版本($artifact)"
|
||||
release_json=$(download_stdout "$release_api")
|
||||
|
||||
@@ -89,7 +89,9 @@ done
|
||||
[ -n "$token" ] && [ -n "$endpoint" ] || { usage; exit 1; }
|
||||
endpoint=${endpoint%/}
|
||||
[ ${#token} -le 512 ] && [ ${#endpoint} -le 2048 ] || fail "安装参数长度超出限制"
|
||||
safe_value "$token" && safe_value "$endpoint" || fail "服务地址或 Agent Token 格式无效"
|
||||
if ! safe_value "$token" || ! safe_value "$endpoint"; then
|
||||
fail "服务地址或 Agent Token 格式无效"
|
||||
fi
|
||||
case "$endpoint" in
|
||||
https://?*|http://localhost|http://localhost/*|http://localhost:*|http://127.0.0.1|http://127.0.0.1/*|http://127.0.0.1:*) ;;
|
||||
*) fail "服务地址必须使用 HTTPS;仅本机调试可使用 HTTP" ;;
|
||||
|
||||
@@ -5,7 +5,6 @@ use super::Report;
|
||||
pub(crate) const MAX_LIVE_BATCH_BYTES: usize = 768 * 1024;
|
||||
|
||||
pub(crate) fn batch_len(queue: &VecDeque<Report>) -> usize {
|
||||
// Allow room for the JSON envelope, commas, and future small protocol fields.
|
||||
let mut encoded_bytes = 64_usize;
|
||||
let mut count = 0_usize;
|
||||
for report in queue {
|
||||
|
||||
+14
-20
@@ -60,8 +60,6 @@ const LIVE_RECONNECT_DELAY: Duration = Duration::from_secs(3);
|
||||
const LIVE_QUEUE_CAPACITY: usize = 720;
|
||||
const MAX_PENDING_SPOOL_BYTES: u64 = 64 * 1024 * 1024;
|
||||
const MAX_PENDING_SPOOL_LINE_BYTES: usize = 1024 * 1024;
|
||||
// Database persistence can take a few hundred milliseconds; allow the ACK to arrive
|
||||
// before continuing with the configured live interval.
|
||||
const LIVE_ACK_READ_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const LIVE_HINT_READ_TIMEOUT: Duration = Duration::from_millis(10);
|
||||
const LIVE_REPORT_DIVISOR: u64 = 15;
|
||||
@@ -250,8 +248,6 @@ impl ClockCalibration {
|
||||
|
||||
type SharedClock = Arc<Mutex<ClockCalibration>>;
|
||||
|
||||
/// 双栈公网地址探测:分别请求按协议族锁死的 icanhazip 端点(Cloudflare 运营,
|
||||
/// 纯文本返回调用方出口 IP)。短暂失败保留最近结果,持续失败则清空过期地址。
|
||||
#[derive(Default)]
|
||||
struct PublicIpValue {
|
||||
address: Option<IpAddr>,
|
||||
@@ -286,7 +282,6 @@ struct PublicIpProbe {
|
||||
}
|
||||
|
||||
impl PublicIpProbe {
|
||||
/// 距上次尝试超过刷新间隔且没有探测在进行时,后台起一轮探测(不阻塞采样)。
|
||||
fn refresh(&self) {
|
||||
let due = self
|
||||
.last_attempt
|
||||
@@ -329,7 +324,6 @@ impl PublicIpProbe {
|
||||
.map(|ip| ip.to_string())
|
||||
}
|
||||
|
||||
/// 一次性模式没有后续采样,等首轮探测落地(或预算耗尽)再出报告。
|
||||
fn wait_initial(&self) {
|
||||
let deadline = Instant::now() + PUBLIC_IP_WAIT_BUDGET;
|
||||
while self.probing.load(Ordering::SeqCst) && Instant::now() < deadline {
|
||||
@@ -358,7 +352,6 @@ fn probe_public_ip(url: &str, ipv6: bool) -> Option<IpAddr> {
|
||||
parse_public_ip(&body, ipv6)
|
||||
}
|
||||
|
||||
/// 端点返回的协议族必须与请求的锁族一致,防止代理或劫持回一个错族地址。
|
||||
fn parse_public_ip(text: &str, ipv6: bool) -> Option<IpAddr> {
|
||||
let ip: IpAddr = text.trim().parse().ok()?;
|
||||
match (ipv6, ip) {
|
||||
@@ -1912,9 +1905,7 @@ fn receive_remote_output(
|
||||
match receiver.recv_timeout(REMOTE_OUTPUT_DRAIN_TIMEOUT) {
|
||||
Ok(output) => output,
|
||||
Err(_) => {
|
||||
// A background descendant can keep the inherited pipe open after
|
||||
// the shell exits. End the task's process group so result
|
||||
// collection itself cannot hang the Agent.
|
||||
// Descendants may keep the pipe open after the shell exits.
|
||||
terminate_remote_process_tree(child);
|
||||
receiver
|
||||
.recv_timeout(REMOTE_OUTPUT_DRAIN_TIMEOUT)
|
||||
@@ -2785,12 +2776,7 @@ fn live_sender_loop(endpoint: &str, token: &str, worker: LiveSenderWorker) {
|
||||
}
|
||||
drop_socket = true;
|
||||
}
|
||||
Ok(LiveRead::RemoteTask(_task)) => {
|
||||
// 在 wait_for_live_ack 中已处理,这里不应该出现
|
||||
}
|
||||
Ok(LiveRead::TaskResultAck(_)) => {
|
||||
// 在 wait_for_live_ack 中已处理,这里不应该出现
|
||||
}
|
||||
Ok(LiveRead::RemoteTask(_) | LiveRead::TaskResultAck(_)) => {}
|
||||
Ok(LiveRead::Pending) => {
|
||||
eprintln!("live ACK timed out");
|
||||
drop_socket = true;
|
||||
@@ -3030,11 +3016,20 @@ fn version_triplet(value: &str) -> Option<(u64, u64, u64)> {
|
||||
|
||||
fn agent_artifact_name() -> Option<&'static str> {
|
||||
match (env::consts::OS, env::consts::ARCH) {
|
||||
("linux", "x86_64") => Some("agent-linux-x64"),
|
||||
("linux", "aarch64") => Some("agent-linux-aarch64"),
|
||||
("linux", "x86_64") => Some(if cfg!(target_env = "musl") {
|
||||
"agent-linux-x64-musl"
|
||||
} else {
|
||||
"agent-linux-x64-glibc"
|
||||
}),
|
||||
("linux", "aarch64") => Some(if cfg!(target_env = "musl") {
|
||||
"agent-linux-aarch64-musl"
|
||||
} else {
|
||||
"agent-linux-aarch64-glibc"
|
||||
}),
|
||||
("windows", "x86_64") => Some("agent-windows-x64.exe"),
|
||||
("macos", "aarch64") => Some("agent-macos-aarch64"),
|
||||
("freebsd", "x86_64") => Some("agent-freebsd-x64"),
|
||||
("freebsd", "aarch64") => Some("agent-freebsd-aarch64"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -3490,7 +3485,6 @@ mod tests {
|
||||
let v6: std::net::IpAddr = "2001:db8::1".parse().unwrap();
|
||||
assert_eq!(parse_public_ip("203.0.113.7\n", false), Some(v4));
|
||||
assert_eq!(parse_public_ip("2001:db8::1", true), Some(v6));
|
||||
// 协议族不匹配或无法解析时一律视为探测失败。
|
||||
assert_eq!(parse_public_ip("2001:db8::1", false), None);
|
||||
assert_eq!(parse_public_ip("203.0.113.7", true), None);
|
||||
assert_eq!(parse_public_ip("not an ip", false), None);
|
||||
@@ -3575,7 +3569,7 @@ mod tests {
|
||||
fn validates_release_asset_digests() {
|
||||
let hash = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
|
||||
let mut asset = GithubReleaseAsset {
|
||||
name: "agent-linux-x64".to_string(),
|
||||
name: "agent-linux-x64-glibc".to_string(),
|
||||
browser_download_url: "https://example.com/agent".to_string(),
|
||||
digest: Some(format!("sha256:{hash}")),
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user