2024-05-13 20:12:45 +00:00
|
|
|
package main
|
2024-05-11 00:37:40 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2024-05-13 12:41:33 +00:00
|
|
|
"log/slog"
|
2024-05-13 20:12:45 +00:00
|
|
|
"oneuptime-infrastructure-agent/model"
|
|
|
|
"oneuptime-infrastructure-agent/utils"
|
2024-05-11 00:37:40 +00:00
|
|
|
"os"
|
|
|
|
"time"
|
2024-05-13 16:49:53 +00:00
|
|
|
|
|
|
|
"github.com/go-co-op/gocron/v2"
|
|
|
|
"github.com/gookit/greq"
|
2024-05-11 00:37:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Agent struct {
|
|
|
|
SecretKey string
|
|
|
|
OneUptimeURL string
|
|
|
|
scheduler gocron.Scheduler
|
|
|
|
mainJob gocron.Job
|
|
|
|
shutdownHook Hook
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAgent(secretKey string, url string) *Agent {
|
|
|
|
ag := &Agent{
|
|
|
|
SecretKey: secretKey,
|
|
|
|
OneUptimeURL: url,
|
|
|
|
}
|
|
|
|
slog.Info("Starting agent...")
|
|
|
|
slog.Info("Agent configuration:")
|
|
|
|
slog.Info("Secret key: " + ag.SecretKey)
|
|
|
|
slog.Info("OneUptime URL: " + ag.OneUptimeURL)
|
|
|
|
if ag.SecretKey == "" || ag.OneUptimeURL == "" {
|
|
|
|
slog.Error("Secret key and OneUptime URL are required")
|
|
|
|
os.Exit(1)
|
|
|
|
return ag
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if secret key is valid
|
|
|
|
if !checkIfSecretKeyIsValid(ag.SecretKey, ag.OneUptimeURL) {
|
|
|
|
slog.Error("Secret key is invalid")
|
|
|
|
os.Exit(1)
|
|
|
|
return ag
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduler, err := gocron.NewScheduler()
|
|
|
|
if err != nil {
|
2024-05-13 12:41:33 +00:00
|
|
|
slog.Error(err.Error())
|
2024-05-11 00:37:40 +00:00
|
|
|
os.Exit(1)
|
|
|
|
return ag
|
|
|
|
}
|
|
|
|
|
|
|
|
job, err := scheduler.NewJob(gocron.DurationJob(time.Minute), gocron.NewTask(collectMetricsJob, ag.SecretKey, ag.OneUptimeURL))
|
|
|
|
if err != nil {
|
|
|
|
slog.Error(err.Error())
|
|
|
|
os.Exit(1)
|
|
|
|
return ag
|
|
|
|
}
|
|
|
|
|
|
|
|
ag.scheduler = scheduler
|
|
|
|
ag.mainJob = job
|
|
|
|
|
|
|
|
return ag
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ag *Agent) Start() {
|
|
|
|
ag.scheduler.Start()
|
|
|
|
err := ag.mainJob.RunNow()
|
|
|
|
if err != nil {
|
|
|
|
slog.Info(err.Error())
|
|
|
|
os.Exit(1)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ag *Agent) Close() {
|
|
|
|
err := ag.scheduler.Shutdown()
|
|
|
|
if err != nil {
|
|
|
|
slog.Error(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func collectMetricsJob(secretKey string, oneuptimeURL string) {
|
2024-05-13 20:12:45 +00:00
|
|
|
memMetrics := utils.GetMemoryMetrics()
|
2024-05-11 00:37:40 +00:00
|
|
|
if memMetrics == nil {
|
|
|
|
slog.Warn("Failed to get memory metrics")
|
|
|
|
}
|
|
|
|
|
2024-05-13 20:12:45 +00:00
|
|
|
cpuMetrics := utils.GetCpuMetrics()
|
2024-05-11 00:37:40 +00:00
|
|
|
if cpuMetrics == nil {
|
|
|
|
slog.Warn("Failed to get CPU metrics")
|
|
|
|
}
|
|
|
|
|
2024-05-13 20:12:45 +00:00
|
|
|
diskMetrics := utils.ListDiskMetrics()
|
2024-05-11 00:37:40 +00:00
|
|
|
if diskMetrics == nil {
|
|
|
|
slog.Warn("Failed to get disk metrics")
|
|
|
|
}
|
|
|
|
|
2024-05-13 20:12:45 +00:00
|
|
|
servProcesses := utils.GetServerProcesses()
|
2024-05-11 00:37:40 +00:00
|
|
|
if servProcesses == nil {
|
|
|
|
slog.Warn("Failed to get server processes")
|
|
|
|
}
|
|
|
|
|
2024-05-13 20:12:45 +00:00
|
|
|
metricsReport := &model.ServerMonitorReport{
|
2024-05-11 00:37:40 +00:00
|
|
|
SecretKey: secretKey,
|
2024-05-13 20:12:45 +00:00
|
|
|
BasicInfrastructureMetrics: &model.BasicInfrastructureMetrics{
|
2024-05-11 00:37:40 +00:00
|
|
|
MemoryMetrics: memMetrics,
|
|
|
|
CpuMetrics: cpuMetrics,
|
|
|
|
DiskMetrics: diskMetrics,
|
|
|
|
},
|
|
|
|
RequestReceivedAt: time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
|
|
|
|
OnlyCheckRequestReceivedAt: false,
|
|
|
|
Processes: servProcesses,
|
2024-07-02 12:04:02 +00:00
|
|
|
Hostname: utils.GetHostname(),
|
2024-05-11 00:37:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reqData := struct {
|
2024-05-13 20:12:45 +00:00
|
|
|
ServerMonitorResponse *model.ServerMonitorReport `json:"serverMonitorResponse"`
|
2024-05-11 00:37:40 +00:00
|
|
|
}{
|
|
|
|
ServerMonitorResponse: metricsReport,
|
|
|
|
}
|
|
|
|
postBuilder := greq.New(oneuptimeURL).Post("/server-monitor/response/ingest/" + secretKey).
|
|
|
|
JSONType().JSONBody(reqData)
|
|
|
|
resp, err := postBuilder.Do()
|
|
|
|
if err != nil {
|
|
|
|
slog.Error(err.Error())
|
|
|
|
}
|
|
|
|
if resp.IsFail() {
|
|
|
|
slog.Error("Failed to ingest metrics with status code ", resp.StatusCode)
|
|
|
|
respJson, _ := json.Marshal(resp)
|
|
|
|
slog.Error("Response: ", string(respJson))
|
|
|
|
}
|
|
|
|
slog.Info("1 minute metrics have been sent to OneUptime.")
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkIfSecretKeyIsValid(secretKey string, baseUrl string) bool {
|
|
|
|
if secretKey == "" {
|
|
|
|
slog.Error("Secret key is empty")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
resp, err := greq.New(baseUrl).JSONType().GetDo("/server-monitor/secret-key/verify/" + secretKey)
|
|
|
|
if err != nil {
|
|
|
|
slog.Error(err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
slog.Error("Secret key verification failed with status code ", resp.StatusCode)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|