109 lines
3.4 KiB
Go
109 lines
3.4 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.dws.rip/dubey/kat/internal/store"
|
|
)
|
|
|
|
// NodeStatusRequest represents the data sent by an agent in a heartbeat
|
|
type NodeStatusRequest struct {
|
|
NodeName string `json:"nodeName"`
|
|
NodeUID string `json:"nodeUID"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Resources struct {
|
|
Capacity map[string]string `json:"capacity"`
|
|
Allocatable map[string]string `json:"allocatable"`
|
|
} `json:"resources"`
|
|
WorkloadInstances []struct {
|
|
WorkloadName string `json:"workloadName"`
|
|
Namespace string `json:"namespace"`
|
|
InstanceID string `json:"instanceID"`
|
|
ContainerID string `json:"containerID"`
|
|
ImageID string `json:"imageID"`
|
|
State string `json:"state"`
|
|
ExitCode int `json:"exitCode"`
|
|
HealthStatus string `json:"healthStatus"`
|
|
Restarts int `json:"restarts"`
|
|
} `json:"workloadInstances,omitempty"`
|
|
OverlayNetwork struct {
|
|
Status string `json:"status"`
|
|
LastPeerSync string `json:"lastPeerSync"`
|
|
} `json:"overlayNetwork"`
|
|
}
|
|
|
|
// NewNodeStatusHandler creates a handler for node status updates
|
|
func NewNodeStatusHandler(stateStore store.StateStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Extract node name from URL path
|
|
pathParts := strings.Split(r.URL.Path, "/")
|
|
if len(pathParts) < 4 {
|
|
http.Error(w, "Invalid URL path", http.StatusBadRequest)
|
|
return
|
|
}
|
|
nodeName := pathParts[len(pathParts)-2] // /v1alpha1/nodes/{nodeName}/status
|
|
|
|
log.Printf("Received status update from node: %s", nodeName)
|
|
|
|
// Read and parse the request body
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Printf("Failed to read request body: %v", err)
|
|
http.Error(w, "Failed to read request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
var statusReq NodeStatusRequest
|
|
if err := json.Unmarshal(body, &statusReq); err != nil {
|
|
log.Printf("Failed to parse status request: %v", err)
|
|
http.Error(w, "Failed to parse status request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Validate that the node name in the URL matches the one in the request
|
|
if statusReq.NodeName != nodeName {
|
|
log.Printf("Node name mismatch: %s (URL) vs %s (body)", nodeName, statusReq.NodeName)
|
|
http.Error(w, "Node name mismatch", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Store the node status in etcd
|
|
nodeStatusKey := fmt.Sprintf("/kat/nodes/status/%s", nodeName)
|
|
nodeStatus := map[string]interface{}{
|
|
"lastHeartbeat": time.Now().Unix(),
|
|
"status": "Ready",
|
|
"resources": statusReq.Resources,
|
|
"network": statusReq.OverlayNetwork,
|
|
}
|
|
|
|
// Add workload instances if present
|
|
if len(statusReq.WorkloadInstances) > 0 {
|
|
nodeStatus["workloadInstances"] = statusReq.WorkloadInstances
|
|
}
|
|
|
|
nodeStatusData, err := json.Marshal(nodeStatus)
|
|
if err != nil {
|
|
log.Printf("Failed to marshal node status: %v", err)
|
|
http.Error(w, "Failed to marshal node status", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
log.Printf("Storing node status in etcd at key: %s", nodeStatusKey)
|
|
if err := stateStore.Put(r.Context(), nodeStatusKey, nodeStatusData); err != nil {
|
|
log.Printf("Failed to store node status: %v", err)
|
|
http.Error(w, "Failed to store node status", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
log.Printf("Successfully stored status update for node: %s", nodeName)
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
}
|