Files
codex de3fd88948 ✨ feat(agent): 添加 token 鉴权和流程文档
* 默认监听调整为 0.0.0.0:8080,并要求查询参数 token
* 支持首次 run 自动生成含随机 token 的配置文件
* 补充 HTTP 鉴权、配置校验和相关测试
* 完善代码注释,覆盖缓存、上报、服务和平台采集路径
* 新增架构与开发流程文档,补充一键构建脚本
2026-05-22 06:46:21 +00:00

145 lines
4.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"errors"
"flag"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"mfd-monitor-agent/internal/collector"
"mfd-monitor-agent/internal/config"
"mfd-monitor-agent/internal/httpapi"
"mfd-monitor-agent/internal/reporter"
"mfd-monitor-agent/internal/service"
"mfd-monitor-agent/internal/snapshot"
)
// version 是编译时可通过 -ldflags 覆盖的 agent 版本号。
var version = "0.1.0"
// main 运行 CLI,命令失败时以非零状态退出。
func main() {
if err := runCLI(os.Args[1:]); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// runCLI 分发 agent 子命令,并在调用处理函数前加载配置。
func runCLI(args []string) error {
if len(args) == 0 {
args = []string{"run"}
}
switch args[0] {
case "run":
fs := flag.NewFlagSet("run", flag.ExitOnError)
configPath := fs.String("config", "", "JSON 配置文件路径")
if err := fs.Parse(args[1:]); err != nil {
return err
}
path := config.ResolvePath(*configPath)
cfg, err := config.LoadOrCreate(path)
if err != nil {
return err
}
return runAgent(cfg)
case "install":
cfg, executable, configPath, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Install(executable, cfg, service.InstallOptions{ConfigPath: configPath})
case "uninstall":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Uninstall(cfg)
case "start":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Start(cfg)
case "stop":
cfg, _, _, err := commandConfig(args[1:])
if err != nil {
return err
}
return service.NewManager().Stop(cfg)
case "version":
fmt.Println(version)
return nil
default:
return fmt.Errorf("unknown command %q; expected run, install, uninstall, start, stop, version", args[0])
}
}
// commandConfig 解析服务相关子命令的标志并解析可执行文件路径。
func commandConfig(args []string) (config.Config, string, string, error) {
fs := flag.NewFlagSet("service", flag.ExitOnError)
configPath := fs.String("config", "", "JSON 配置文件路径")
executableFlag := fs.String("exe", "", "agent 可执行文件路径(默认当前二进制)")
if err := fs.Parse(args); err != nil {
return config.Config{}, "", "", err
}
path := config.ResolvePath(*configPath)
cfg, err := config.Load(path)
if err != nil {
return cfg, "", "", err
}
executable := *executableFlag
if executable == "" {
// 服务安装需要写入一个稳定的二进制路径;未显式传入时使用当前进程路径。
executable, err = os.Executable()
if err != nil {
return cfg, "", "", err
}
}
return cfg, executable, path, nil
}
// runAgent 启动采集、可选上报与本地 HTTP API,直至收到退出信号。
func runAgent(cfg config.Config) error {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
startedAt := time.Now().UTC()
cache := snapshot.NewCache(collector.New(cfg.HostID), version, startedAt)
// 采集在后台立即执行一次并周期刷新,HTTP 与上报都只读取这份缓存。
go cache.Run(ctx, cfg.Collect.Interval.Duration(), cfg.Collect.Timeout.Duration())
if cfg.Report.Enabled {
client := reporter.NewClient(cfg.Report.Endpoint, cfg.Report.Timeout.Duration())
// 上报循环独立于 HTTP 服务运行;失败只记录日志并重试,不终止 agent。
go reporter.Run(ctx, cache, client, cfg.Report.Interval.Duration(), logger)
}
server := &http.Server{
Addr: cfg.HTTP.ListenAddress,
Handler: httpapi.New(cache, cfg.HTTP.Token),
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
<-ctx.Done()
// 收到系统信号后给 HTTP 请求一个短暂宽限期,避免直接中断正在返回的快照。
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = server.Shutdown(shutdownCtx)
}()
logger.Info("agent listening", "address", cfg.HTTP.ListenAddress)
err := server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}