Files
cloud-ip-validator/internal/httpapi/handlers_agent.go
T

142 lines
4.6 KiB
Go

package httpapi
import (
"net/http"
"time"
"cloudipvalidator/internal/db"
)
func (s *Server) handleAgentRegister(w http.ResponseWriter, r *http.Request) {
var req registerAgentRequest
if err := readJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
return
}
if req.ValidatorID == "" {
writeError(w, http.StatusBadRequest, "validator_id is required")
return
}
// os_port_id is supplied via control-api's own config (config.ValidatorConfig),
// not by the agent, so registration only touches hostname/version here;
// RegisterValidator preserves any existing os_port_id row.
existing, _ := s.DB.GetValidator(r.Context(), req.ValidatorID)
osPortID := ""
if existing != nil {
osPortID = existing.OSPortID
}
if err := s.DB.RegisterValidator(r.Context(), req.ValidatorID, req.Hostname, osPortID, req.AgentVersion); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
s.Orch.RecordEvent(r.Context(), "validator-agent", req.ValidatorID, nil, "registered", "")
writeJSON(w, http.StatusOK, registerAgentResponse{OK: true, PollIntervalSeconds: s.Orch.Cfg.PollIntervalSeconds})
}
func (s *Server) handleAgentHeartbeat(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req heartbeatRequest
_ = readJSON(r, &req) // heartbeat body is informational only; tolerate empty/missing
if err := s.DB.Heartbeat(r.Context(), id); err != nil {
writeError(w, http.StatusNotFound, "unknown validator: "+id)
return
}
writeJSON(w, http.StatusOK, okResponse{OK: true})
}
func (s *Server) handleAgentAssignment(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
item, checks, err := s.Orch.AssignmentForValidator(r.Context(), id)
if err != nil {
writeError(w, http.StatusNotFound, "unknown validator: "+id)
return
}
if item == nil {
w.WriteHeader(http.StatusNoContent)
return
}
var cfg []checkConfigDTO
for _, c := range checks {
cfg = append(cfg, checkConfigDTO{Type: c.Type, Targets: c.Targets})
}
writeJSON(w, http.StatusOK, assignmentResponse{
IPID: item.ID, IPAddress: item.IPAddress, Phase: item.State, CheckConfig: cfg,
})
}
func (s *Server) handleAgentSelfCheck(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req selfCheckRequest
if err := readJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
return
}
detail := req.Detail
if req.DetectedEgress != "" {
detail = "detected_egress_ip=" + req.DetectedEgress + " " + detail
}
if err := s.Orch.SelfCheckResult(r.Context(), id, req.IPID, req.Success, detail); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, okResponse{OK: true})
}
func (s *Server) handleAgentEvent(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req agentEventRequest
if err := readJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
return
}
if req.EventType == "" {
writeError(w, http.StatusBadRequest, "event_type is required")
return
}
s.Orch.RecordEvent(r.Context(), "validator-agent", id, req.IPID, req.EventType, req.Payload)
writeJSON(w, http.StatusOK, okResponse{OK: true})
}
func (s *Server) handleAgentResults(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req agentResultsRequest
if err := readJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
return
}
for _, res := range req.Results {
item, err := s.DB.GetIP(r.Context(), res.IPID)
if err != nil {
writeError(w, http.StatusNotFound, "unknown ip_id")
return
}
checkedAt, err := time.Parse(time.RFC3339Nano, res.CheckedAt)
if err != nil {
checkedAt = db.Now()
}
err = s.Orch.RecordCheck(r.Context(), db.Check{
IPID: item.ID, IPAddress: item.IPAddress, AttemptNumber: item.AttemptNumber,
ValidatorID: id, Source: db.SourceEgress, CheckType: res.CheckType, Target: res.Target,
Success: res.Success, LatencyMS: res.LatencyMS, Detail: res.Detail, CheckedAt: checkedAt,
})
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
}
writeJSON(w, http.StatusOK, okResponse{OK: true})
}
func (s *Server) handleAgentComplete(w http.ResponseWriter, r *http.Request) {
var req agentCompleteRequest
if err := readJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid body: "+err.Error())
return
}
if err := s.Orch.MarkEgressComplete(r.Context(), req.IPID); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, okResponse{OK: true})
}