feat: 脚本/定时任务真实执行+输出捕获+执行历史 — 替换pending空壳为本地执行,采集stdout/stderr,更新统计;修复CronJobManager/Automation字段名不匹配(status→enabled,cron→cron_expr,command→content,language→type)

This commit is contained in:
attacker committed 2026-06-14 22:11:18 +08:00
1 parent 85bc2a8e05
commit a4fd5fc178
19 files changed
+2715 -114

No files matched your search

+58 -1
View File
@@ -18,6 +18,7 @@ import (
"ezops/agent/collector"
"ezops/agent/model"
"ezops/agent/reporter"
"ezops/agent/task"
)
// 编译时可注入的版本信息
@@ -65,6 +66,10 @@ func main() {
portCollector := collector.NewPortCollector()
rep := reporter.New(*server, *token, logger)
// 初始化任务执行器与任务客户端(与 reporter 复用相同的 server/token/hostname)
taskExecutor := task.New(logger)
taskClient := task.NewClient(*server, *token, hostname, logger)
// 监听 SIGINT / SIGTERM,支持优雅退出
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
@@ -111,7 +116,59 @@ func main() {
report.CPU.UsagePercent, report.Memory.UsagePercent, len(report.Disks), len(report.Ports))
}
// 启动后立即执行一次,再按 ticker 周期执行
// pollAndExecute 执行单次任务轮询:拉取待执行任务 → 逐个执行 → 回传结果。
// 网络错误只记录日志,不 panic,下一个周期继续重试。
pollAndExecute := func() {
tasks, err := taskClient.Poll(ctx)
if err != nil {
// 上下文已取消(Agent 退出中)属于正常情况,不输出告警
if ctx.Err() != nil {
return
}
logger.Printf("任务轮询失败: %v", err)
return
}
if len(tasks) == 0 {
return // 暂无任务,静默返回
}
logger.Printf("拉取到 %d 个待执行任务", len(tasks))
for _, t := range tasks {
// Agent 退出时跳过后续任务,避免退出延迟
if ctx.Err() != nil {
return
}
result := taskExecutor.ExecuteTask(ctx, t)
if err := taskClient.Submit(ctx, result); err != nil {
if ctx.Err() != nil {
return
}
logger.Printf("任务 %s 结果回传失败: %v", t.TaskID, err)
} else {
logger.Printf("任务 %s 结果回传成功 status=%s", t.TaskID, result.Status)
}
}
}
// 启动任务轮询协程:每 5 秒轮询一次,与指标采集并行运行。
// 使用独立的 5 秒固定间隔,不受 -interval 影响。
const taskPollInterval = 5 * time.Second
go func() {
// 启动后立即轮询一次,再按固定周期执行
pollAndExecute()
ticker := time.NewTicker(taskPollInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
pollAndExecute()
case <-ctx.Done():
return
}
}
}()
// 主协程:启动后立即执行一次指标采集,再按 ticker 周期执行
collectAndReport()
for {
select {