✨ feat(agent): 添加 token 鉴权和流程文档

* 默认监听调整为 0.0.0.0:8080,并要求查询参数 token
* 支持首次 run 自动生成含随机 token 的配置文件
* 补充 HTTP 鉴权、配置校验和相关测试
* 完善代码注释,覆盖缓存、上报、服务和平台采集路径
* 新增架构与开发流程文档,补充一键构建脚本
This commit is contained in:
codex
2026-05-22 06:46:21 +00:00
parent 877e4cf41d
commit de3fd88948
19 changed files with 875 additions and 125 deletions

View File

@@ -23,7 +23,10 @@ import (
"mfd-monitor-agent/internal/status"
)
// socketInodesFunc 是测试可替换的 Linux socket inode 到进程映射函数。
var socketInodesFunc = socketInodes
// readTCPListenersFunc 是测试可替换的 Linux TCP 表解析函数。
var readTCPListenersFunc = readTCPListeners
// collectSystem 从 Linux 填充主机标识、操作系统、架构与运行时长字段。
@@ -108,6 +111,7 @@ func collectProcesses(_ context.Context, out *status.HostStatus) error {
}
process, err := readProcess(pid)
if err != nil {
// 进程可能在遍历期间退出,或因权限限制无法读取;保留其他进程并降级为 partial。
errs = append(errs, err)
continue
}
@@ -130,6 +134,7 @@ func collectSockets(_ context.Context, out *status.HostStatus) error {
var listeners []status.TCPListenerInfo
var errs []error
if inodeErr != nil {
// inode 映射失败通常意味着端口仍可见但进程归属不完整,后续继续解析 TCP 表。
errs = append(errs, inodeErr)
}
for _, file := range []struct {
@@ -285,6 +290,7 @@ func readDisks() ([]status.DiskInfo, error) {
}
var stat syscall.Statfs_t
if err := syscall.Statfs(fields[1], &stat); err != nil {
// 个别挂载点可能瞬时不可达;磁盘采集保持尽力返回其他挂载。
continue
}
total := stat.Blocks * uint64(stat.Bsize)
@@ -331,6 +337,7 @@ func readProcess(pid int) (status.ProcessInfo, error) {
vms := parseKB(statusValues["VmSize"])
username := ""
if uid := firstField(statusValues["Uid"]); uid != "" {
// 用户名解析失败不影响进程主体信息,保留空用户名表示未知。
if u, err := user.LookupId(uid); err == nil {
username = u.Username
}
@@ -339,6 +346,7 @@ func readProcess(pid int) (status.ProcessInfo, error) {
if data, err := os.ReadFile(filepath.Join(base, "cmdline")); err == nil {
cmdline = strings.Trim(strings.ReplaceAll(string(data), "\x00", " "), " ")
}
// exe 和 stat 都可能因权限或进程退出失败;这些字段按空值/零值降级。
exe, _ := os.Readlink(filepath.Join(base, "exe"))
statValues, _ := readProcessStat(filepath.Join(base, "stat"))
return status.ProcessInfo{
@@ -400,6 +408,7 @@ func socketInodes() (map[string]status.ProcessInfo, error) {
fdDir := filepath.Join(path, "fd")
entries, err := os.ReadDir(fdDir)
if err != nil {
// 无法读取 fd 目录会导致部分端口缺少 PID 映射,因此计数并向上报告 partial。
readErrors++
return nil
}
@@ -495,6 +504,7 @@ func parseAddress(raw string, family string) (string, int, error) {
if family == "ipv6" && len(bytes) == 16 {
reordered := make([]byte, 16)
for i := 0; i < 16; i += 4 {
// /proc/net/tcp6 以 32 位小端字组保存 IPv6 地址,需逐字反转为网络序。
reordered[i], reordered[i+1], reordered[i+2], reordered[i+3] = bytes[i+3], bytes[i+2], bytes[i+1], bytes[i]
}
return net.IP(reordered).String(), int(port64), nil