Files
oneuptime/InfrastructureAgent/agent.go

194 lines
4.8 KiB
Go

package main
import (
"bytes"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/url"
"oneuptime-infrastructure-agent/model"
"oneuptime-infrastructure-agent/utils"
"os"
"strconv"
"time"
"github.com/go-co-op/gocron/v2"
)
type Agent struct {
SecretKey string
OneUptimeURL string
ProxyURL string
scheduler gocron.Scheduler
mainJob gocron.Job
shutdownHook Hook
}
func NewAgent(secretKey string, oneuptimeUrl string, proxyUrl string) *Agent {
ag := &Agent{
SecretKey: secretKey,
OneUptimeURL: oneuptimeUrl,
ProxyURL: proxyUrl,
}
slog.Info("Starting agent...")
slog.Info("Agent configuration:")
slog.Info("Secret key: " + ag.SecretKey)
slog.Info("OneUptime URL: " + ag.OneUptimeURL)
slog.Info("Proxy URL: " + ag.ProxyURL)
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, ag.ProxyURL) {
slog.Error("Secret key is invalid. If you are sure that the secret key is correct, please check your network connection, OneUptime URL (" + ag.OneUptimeURL + "), Proxy URL (" + ag.ProxyURL + ") and try again.")
os.Exit(1)
return ag
}
scheduler, err := gocron.NewScheduler()
if err != nil {
slog.Error(err.Error())
os.Exit(1)
return ag
}
job, err := scheduler.NewJob(gocron.DurationJob(30*time.Second), gocron.NewTask(collectMetricsJob, ag.SecretKey, ag.OneUptimeURL, ag.ProxyURL))
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, proxyUrl string) {
memMetrics := utils.GetMemoryMetrics()
if memMetrics == nil {
slog.Warn("Failed to get memory metrics")
}
cpuMetrics := utils.GetCpuMetrics()
if cpuMetrics == nil {
slog.Warn("Failed to get CPU metrics")
}
diskMetrics := utils.ListDiskMetrics()
if diskMetrics == nil {
slog.Warn("Failed to get disk metrics")
}
servProcesses := utils.GetServerProcesses()
if servProcesses == nil {
slog.Warn("Failed to get server processes")
}
metricsReport := &model.ServerMonitorReport{
SecretKey: secretKey,
BasicInfrastructureMetrics: &model.BasicInfrastructureMetrics{
MemoryMetrics: memMetrics,
CpuMetrics: cpuMetrics,
DiskMetrics: diskMetrics,
},
RequestReceivedAt: time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
OnlyCheckRequestReceivedAt: false,
Processes: servProcesses,
Hostname: utils.GetHostname(),
}
reqData := struct {
ServerMonitorResponse *model.ServerMonitorReport `json:"serverMonitorResponse"`
}{
ServerMonitorResponse: metricsReport,
}
reqBody, err := json.Marshal(reqData)
if err != nil {
slog.Error("Failed to marshal request data", "error", err)
return
}
client := &http.Client{}
if proxyUrl != "" {
proxyURL, _ := url.Parse(proxyUrl)
transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client = &http.Client{Transport: transport}
slog.Info("Using proxy to send request:" + proxyUrl)
}
resp, err := client.Post(oneuptimeUrl+"/server-monitor/response/ingest/"+secretKey, "application/json", bytes.NewBuffer(reqBody))
if err != nil {
slog.Error("Failed to send request to server", "error", err)
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
slog.Info("Metrics successfully pushed to OneUptime server", "status", resp.StatusCode, "endpoint", oneuptimeUrl+"/server-monitor/response/ingest/"+secretKey)
} else {
slog.Error("Failed to ingest metrics", "status_code", resp.StatusCode)
respBody, err := io.ReadAll(resp.Body)
if err != nil {
slog.Error("Failed to read error response body", "error", err)
} else {
slog.Error("Server response", "body", string(respBody))
}
}
}
func checkIfSecretKeyIsValid(secretKey string, oneuptimeUrl string, proxyUrl string) bool {
// if we have a proxy, we need to use that to make the request
client := &http.Client{}
if proxyUrl != "" {
proxyURL, _ := url.Parse(proxyUrl)
transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client = &http.Client{Transport: transport}
slog.Info("Using proxy to send request:" + proxyUrl)
}
if secretKey == "" {
slog.Error("Secret key is empty")
return false
}
resp, err := client.Get(oneuptimeUrl + "/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 " + strconv.Itoa(resp.StatusCode))
return false
}
return true
}