feat: mock gateway
This commit is contained in:
752
internal/mock/server.go
Normal file
752
internal/mock/server.go
Normal file
@@ -0,0 +1,752 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"spore-gateway/internal/discovery"
|
||||
"spore-gateway/pkg/client"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// MockHTTPServer represents the mock HTTP server
|
||||
type MockHTTPServer struct {
|
||||
port string
|
||||
router *mux.Router
|
||||
discovery *MockNodeDiscovery
|
||||
wsServer *MockWebSocketServer
|
||||
server *http.Server
|
||||
enableWS bool
|
||||
firmwareStore map[string][]byte // Simple in-memory firmware storage
|
||||
}
|
||||
|
||||
// NewMockHTTPServer creates a new mock HTTP server instance
|
||||
func NewMockHTTPServer(port string, discovery *MockNodeDiscovery, enableWS bool) *MockHTTPServer {
|
||||
ms := &MockHTTPServer{
|
||||
port: port,
|
||||
router: mux.NewRouter(),
|
||||
discovery: discovery,
|
||||
enableWS: enableWS,
|
||||
firmwareStore: make(map[string][]byte),
|
||||
}
|
||||
|
||||
// Initialize WebSocket server if enabled
|
||||
if enableWS {
|
||||
ms.wsServer = NewMockWebSocketServer(discovery)
|
||||
}
|
||||
|
||||
ms.setupRoutes()
|
||||
ms.setupMiddleware()
|
||||
|
||||
ms.server = &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: ms.router,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
return ms
|
||||
}
|
||||
|
||||
// setupMiddleware configures middleware for the server
|
||||
func (ms *MockHTTPServer) setupMiddleware() {
|
||||
ms.router.Use(ms.corsMiddleware)
|
||||
ms.router.Use(ms.jsonMiddleware)
|
||||
ms.router.Use(ms.loggingMiddleware)
|
||||
}
|
||||
|
||||
// corsMiddleware handles CORS headers
|
||||
func (ms *MockHTTPServer) corsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept")
|
||||
w.Header().Set("Access-Control-Expose-Headers", "Content-Type, Content-Length")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// jsonMiddleware sets JSON content type
|
||||
func (ms *MockHTTPServer) jsonMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// loggingMiddleware logs HTTP requests
|
||||
func (ms *MockHTTPServer) loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
next.ServeHTTP(w, r)
|
||||
log.WithFields(log.Fields{
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"remote_addr": r.RemoteAddr,
|
||||
"duration": time.Since(start),
|
||||
}).Debug("HTTP request")
|
||||
})
|
||||
}
|
||||
|
||||
// setupRoutes configures all the API routes
|
||||
func (ms *MockHTTPServer) setupRoutes() {
|
||||
// API routes
|
||||
api := ms.router.PathPrefix("/api").Subrouter()
|
||||
api.Use(ms.corsMiddleware)
|
||||
|
||||
// Discovery endpoints
|
||||
api.HandleFunc("/discovery/nodes", ms.getDiscoveryNodes).Methods("GET")
|
||||
api.HandleFunc("/discovery/refresh", ms.refreshDiscovery).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/discovery/random-primary", ms.selectRandomPrimary).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/discovery/primary/{ip}", ms.setPrimaryNode).Methods("POST", "OPTIONS")
|
||||
|
||||
// Cluster endpoints
|
||||
api.HandleFunc("/cluster/members", ms.getClusterMembers).Methods("GET")
|
||||
api.HandleFunc("/cluster/refresh", ms.refreshCluster).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/cluster/node/versions", ms.getClusterNodeVersions).Methods("GET")
|
||||
api.HandleFunc("/rollout", ms.startRollout).Methods("POST", "OPTIONS")
|
||||
|
||||
// Task endpoints
|
||||
api.HandleFunc("/tasks/status", ms.getTaskStatus).Methods("GET")
|
||||
|
||||
// Node endpoints
|
||||
api.HandleFunc("/node/status", ms.getNodeStatus).Methods("GET")
|
||||
api.HandleFunc("/node/status/{ip}", ms.getNodeStatusByIP).Methods("GET")
|
||||
api.HandleFunc("/node/endpoints", ms.getNodeEndpoints).Methods("GET")
|
||||
api.HandleFunc("/node/update", ms.updateNodeFirmware).Methods("POST", "OPTIONS")
|
||||
|
||||
// Proxy endpoints
|
||||
api.HandleFunc("/proxy-call", ms.proxyCall).Methods("POST", "OPTIONS")
|
||||
|
||||
// Registry proxy endpoints
|
||||
api.HandleFunc("/registry/health", ms.getRegistryHealth).Methods("GET")
|
||||
api.HandleFunc("/registry/firmware", ms.listRegistryFirmware).Methods("GET")
|
||||
api.HandleFunc("/registry/firmware", ms.uploadRegistryFirmware).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/registry/firmware/{name}/{version}", ms.downloadRegistryFirmware).Methods("GET")
|
||||
api.HandleFunc("/registry/firmware/{name}/{version}", ms.updateRegistryFirmware).Methods("PUT", "OPTIONS")
|
||||
api.HandleFunc("/registry/firmware/{name}/{version}", ms.deleteRegistryFirmware).Methods("DELETE", "OPTIONS")
|
||||
|
||||
// Monitoring endpoints
|
||||
api.HandleFunc("/monitoring/resources", ms.getMonitoringResources).Methods("GET")
|
||||
|
||||
// Test endpoints
|
||||
api.HandleFunc("/test/websocket", ms.testWebSocket).Methods("POST", "OPTIONS")
|
||||
|
||||
// Health check
|
||||
api.HandleFunc("/health", ms.healthCheck).Methods("GET")
|
||||
|
||||
// WebSocket endpoint
|
||||
if ms.enableWS {
|
||||
ms.router.HandleFunc("/ws", ms.corsMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := ms.wsServer.HandleWebSocket(w, r); err != nil {
|
||||
log.WithError(err).Error("WebSocket connection failed")
|
||||
http.Error(w, "WebSocket upgrade failed", http.StatusBadRequest)
|
||||
}
|
||||
})).ServeHTTP)
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the HTTP server
|
||||
func (ms *MockHTTPServer) Start() error {
|
||||
log.WithField("port", ms.port).Info("Starting mock HTTP server")
|
||||
return ms.server.ListenAndServe()
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the HTTP server
|
||||
func (ms *MockHTTPServer) Shutdown(ctx context.Context) error {
|
||||
log.Info("Shutting down mock HTTP server")
|
||||
|
||||
// Shutdown WebSocket server if enabled
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
if err := ms.wsServer.Shutdown(ctx); err != nil {
|
||||
log.WithError(err).Error("WebSocket server shutdown error")
|
||||
}
|
||||
}
|
||||
|
||||
return ms.server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// API endpoint handlers
|
||||
|
||||
// GET /api/discovery/nodes
|
||||
func (ms *MockHTTPServer) getDiscoveryNodes(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
primaryNode := ms.discovery.GetPrimaryNode()
|
||||
clusterStatus := ms.discovery.GetClusterStatus()
|
||||
|
||||
type NodeResponse struct {
|
||||
*discovery.NodeInfo
|
||||
IsPrimary bool `json:"isPrimary"`
|
||||
}
|
||||
|
||||
response := struct {
|
||||
PrimaryNode string `json:"primaryNode"`
|
||||
TotalNodes int `json:"totalNodes"`
|
||||
Nodes []NodeResponse `json:"nodes"`
|
||||
ClientInitialized bool `json:"clientInitialized"`
|
||||
ClientBaseURL string `json:"clientBaseUrl"`
|
||||
ClusterStatus discovery.ClusterStatus `json:"clusterStatus"`
|
||||
}{
|
||||
PrimaryNode: primaryNode,
|
||||
TotalNodes: len(nodes),
|
||||
Nodes: make([]NodeResponse, 0, len(nodes)),
|
||||
ClientInitialized: primaryNode != "",
|
||||
ClientBaseURL: fmt.Sprintf("http://%s", primaryNode),
|
||||
ClusterStatus: clusterStatus,
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeResponse := NodeResponse{
|
||||
NodeInfo: node,
|
||||
IsPrimary: node.IP == primaryNode,
|
||||
}
|
||||
response.Nodes = append(response.Nodes, nodeResponse)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/discovery/refresh
|
||||
func (ms *MockHTTPServer) refreshDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PrimaryNode string `json:"primaryNode"`
|
||||
TotalNodes int `json:"totalNodes"`
|
||||
ClientInitialized bool `json:"clientInitialized"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: "Mock cluster refresh completed",
|
||||
PrimaryNode: ms.discovery.GetPrimaryNode(),
|
||||
TotalNodes: len(ms.discovery.GetNodes()),
|
||||
ClientInitialized: ms.discovery.GetPrimaryNode() != "",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/discovery/random-primary
|
||||
func (ms *MockHTTPServer) selectRandomPrimary(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
if len(nodes) == 0 {
|
||||
http.Error(w, `{"error": "No nodes available"}`, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
newPrimary := ms.discovery.SelectRandomPrimaryNode()
|
||||
if newPrimary == "" {
|
||||
http.Error(w, `{"error": "Selection failed"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PrimaryNode string `json:"primaryNode"`
|
||||
TotalNodes int `json:"totalNodes"`
|
||||
ClientInitialized bool `json:"clientInitialized"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Randomly selected new primary node: %s", newPrimary),
|
||||
PrimaryNode: newPrimary,
|
||||
TotalNodes: len(nodes),
|
||||
ClientInitialized: true,
|
||||
Timestamp: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/discovery/primary/{ip}
|
||||
func (ms *MockHTTPServer) setPrimaryNode(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
requestedIP := vars["ip"]
|
||||
|
||||
if err := ms.discovery.SetPrimaryNode(requestedIP); err != nil {
|
||||
http.Error(w, fmt.Sprintf(`{"error": "Node not found", "message": "Node with IP %s not found"}`, requestedIP), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PrimaryNode string `json:"primaryNode"`
|
||||
ClientInitialized bool `json:"clientInitialized"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Primary node set to %s", requestedIP),
|
||||
PrimaryNode: requestedIP,
|
||||
ClientInitialized: true,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/cluster/members
|
||||
func (ms *MockHTTPServer) getClusterMembers(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
|
||||
// Convert to mock format
|
||||
mockNodes := make(map[string]*NodeInfo)
|
||||
for ip, node := range nodes {
|
||||
mockNodes[ip] = &NodeInfo{
|
||||
IP: node.IP,
|
||||
Hostname: node.Hostname,
|
||||
Status: string(node.Status),
|
||||
Latency: node.Latency,
|
||||
LastSeen: node.LastSeen,
|
||||
Labels: node.Labels,
|
||||
}
|
||||
}
|
||||
|
||||
members := GenerateMockClusterMembers(mockNodes)
|
||||
|
||||
response := &client.ClusterStatusResponse{
|
||||
Members: members,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/cluster/refresh
|
||||
func (ms *MockHTTPServer) refreshCluster(w http.ResponseWriter, r *http.Request) {
|
||||
var requestBody struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil && err.Error() != "EOF" {
|
||||
requestBody.Reason = "manual_refresh"
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Reason string `json:"reason"`
|
||||
WSclients int `json:"wsClients"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: "Mock cluster refresh triggered",
|
||||
Reason: requestBody.Reason,
|
||||
}
|
||||
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
response.WSclients = ms.wsServer.GetClientCount()
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/cluster/node/versions
|
||||
func (ms *MockHTTPServer) getClusterNodeVersions(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
|
||||
type NodeVersionInfo struct {
|
||||
IP string `json:"ip"`
|
||||
Version string `json:"version"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
var nodeVersions []NodeVersionInfo
|
||||
for _, node := range nodes {
|
||||
version := "unknown"
|
||||
if v, exists := node.Labels["version"]; exists {
|
||||
version = v
|
||||
}
|
||||
|
||||
nodeVersions = append(nodeVersions, NodeVersionInfo{
|
||||
IP: node.IP,
|
||||
Version: version,
|
||||
Labels: node.Labels,
|
||||
})
|
||||
}
|
||||
|
||||
response := struct {
|
||||
Nodes []NodeVersionInfo `json:"nodes"`
|
||||
}{
|
||||
Nodes: nodeVersions,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// RolloutNode represents a node in a rollout request
|
||||
type RolloutNode struct {
|
||||
IP string `json:"ip"`
|
||||
Version string `json:"version"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// POST /api/rollout
|
||||
func (ms *MockHTTPServer) startRollout(w http.ResponseWriter, r *http.Request) {
|
||||
var request struct {
|
||||
Firmware struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
} `json:"firmware"`
|
||||
Nodes []RolloutNode `json:"nodes"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rolloutID := fmt.Sprintf("rollout_%d", time.Now().Unix())
|
||||
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
RolloutID string `json:"rolloutId"`
|
||||
TotalNodes int `json:"totalNodes"`
|
||||
FirmwareURL string `json:"firmwareUrl"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("Mock rollout started for %d nodes", len(request.Nodes)),
|
||||
RolloutID: rolloutID,
|
||||
TotalNodes: len(request.Nodes),
|
||||
FirmwareURL: fmt.Sprintf("http://localhost:3002/firmware/%s/%s", request.Firmware.Name, request.Firmware.Version),
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
|
||||
// Simulate rollout progress in background
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
go ms.simulateRollout(rolloutID, request.Nodes, request.Firmware.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// simulateRollout simulates a rollout process with progress updates
|
||||
func (ms *MockHTTPServer) simulateRollout(rolloutID string, nodes []RolloutNode, newVersion string) {
|
||||
for i, node := range nodes {
|
||||
// Simulate updating labels
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if ms.wsServer != nil {
|
||||
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "updating_labels", i+1, len(nodes))
|
||||
}
|
||||
|
||||
// Simulate uploading
|
||||
time.Sleep(1 * time.Second)
|
||||
if ms.wsServer != nil {
|
||||
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "uploading", i+1, len(nodes))
|
||||
}
|
||||
|
||||
// Update node version in discovery
|
||||
ms.discovery.UpdateNodeVersion(node.IP, strings.TrimPrefix(newVersion, "v"))
|
||||
|
||||
// Simulate completion
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if ms.wsServer != nil {
|
||||
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "completed", i+1, len(nodes))
|
||||
}
|
||||
}
|
||||
|
||||
log.WithField("rollout_id", rolloutID).Info("Mock rollout completed")
|
||||
}
|
||||
|
||||
// GET /api/tasks/status
|
||||
func (ms *MockHTTPServer) getTaskStatus(w http.ResponseWriter, r *http.Request) {
|
||||
response := GenerateMockTaskStatus()
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/node/status
|
||||
func (ms *MockHTTPServer) getNodeStatus(w http.ResponseWriter, r *http.Request) {
|
||||
primaryNode := ms.discovery.GetPrimaryNode()
|
||||
nodes := ms.discovery.GetNodes()
|
||||
|
||||
var labels map[string]string
|
||||
if primaryNode != "" && nodes[primaryNode] != nil {
|
||||
labels = nodes[primaryNode].Labels
|
||||
} else {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
|
||||
response := GenerateMockSystemStatus(labels)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/node/status/{ip}
|
||||
func (ms *MockHTTPServer) getNodeStatusByIP(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
nodeIP := vars["ip"]
|
||||
|
||||
nodes := ms.discovery.GetNodes()
|
||||
node, exists := nodes[nodeIP]
|
||||
if !exists {
|
||||
http.Error(w, fmt.Sprintf(`{"error": "Node not found", "message": "Node with IP %s not found"}`, nodeIP), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
response := GenerateMockSystemStatus(node.Labels)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/node/endpoints
|
||||
func (ms *MockHTTPServer) getNodeEndpoints(w http.ResponseWriter, r *http.Request) {
|
||||
response := GenerateMockCapabilities()
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/node/update
|
||||
func (ms *MockHTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
nodeIP := r.URL.Query().Get("ip")
|
||||
if nodeIP == "" {
|
||||
nodeIP = r.Header.Get("X-Node-IP")
|
||||
}
|
||||
|
||||
if nodeIP == "" {
|
||||
http.Error(w, `{"error": "Node IP required"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse multipart form
|
||||
err := r.ParseMultipartForm(50 << 20) // 50MB limit
|
||||
if err != nil {
|
||||
http.Error(w, `{"error": "Failed to parse form"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, fileHeader, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
http.Error(w, `{"error": "No file received"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename := fileHeader.Filename
|
||||
if filename == "" {
|
||||
filename = "firmware.bin"
|
||||
}
|
||||
|
||||
// Read file data
|
||||
fileData, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
http.Error(w, `{"error": "Failed to read file"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"node_ip": nodeIP,
|
||||
"file_size": len(fileData),
|
||||
"filename": filename,
|
||||
}).Info("Mock firmware upload received")
|
||||
|
||||
// Store firmware in memory
|
||||
ms.firmwareStore[nodeIP] = fileData
|
||||
|
||||
// Broadcast status if WebSocket enabled
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "uploading", filename, len(fileData))
|
||||
}
|
||||
|
||||
// Send immediate response
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
NodeIP string `json:"nodeIp"`
|
||||
FileSize int `json:"fileSize"`
|
||||
Filename string `json:"filename"`
|
||||
Status string `json:"status"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: "Mock firmware upload received",
|
||||
NodeIP: nodeIP,
|
||||
FileSize: len(fileData),
|
||||
Filename: filename,
|
||||
Status: "processing",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
|
||||
// Simulate firmware update in background
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
// Randomly succeed or fail (90% success rate)
|
||||
if rand.Float32() < 0.9 {
|
||||
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "completed", filename, len(fileData))
|
||||
} else {
|
||||
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "failed", filename, len(fileData))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// POST /api/proxy-call
|
||||
func (ms *MockHTTPServer) proxyCall(w http.ResponseWriter, r *http.Request) {
|
||||
var requestBody struct {
|
||||
IP string `json:"ip"`
|
||||
Method string `json:"method"`
|
||||
URI string `json:"uri"`
|
||||
Params []map[string]interface{} `json:"params"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate mock response based on URI
|
||||
mockResponse := GenerateMockProxyResponse(requestBody.Method, requestBody.URI)
|
||||
|
||||
// Wrap in data field for consistency
|
||||
response := map[string]interface{}{
|
||||
"data": mockResponse,
|
||||
"status": http.StatusOK,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/registry/health
|
||||
func (ms *MockHTTPServer) getRegistryHealth(w http.ResponseWriter, r *http.Request) {
|
||||
response := map[string]interface{}{
|
||||
"status": "healthy",
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
"service": "mock-registry",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/registry/firmware
|
||||
func (ms *MockHTTPServer) listRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
firmwareList := GenerateMockFirmwareList()
|
||||
json.NewEncoder(w).Encode(firmwareList)
|
||||
}
|
||||
|
||||
// POST /api/registry/firmware
|
||||
func (ms *MockHTTPServer) uploadRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Mock firmware uploaded successfully",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/registry/firmware/{name}/{version}
|
||||
func (ms *MockHTTPServer) downloadRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
version := vars["version"]
|
||||
|
||||
// Generate mock firmware binary
|
||||
firmwareData := GenerateMockFirmwareBinary(524288) // 512KB
|
||||
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s-%s.bin\"", name, version))
|
||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(firmwareData)))
|
||||
|
||||
w.Write(firmwareData)
|
||||
}
|
||||
|
||||
// PUT /api/registry/firmware/{name}/{version}
|
||||
func (ms *MockHTTPServer) updateRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Mock firmware metadata updated",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// DELETE /api/registry/firmware/{name}/{version}
|
||||
func (ms *MockHTTPServer) deleteRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Mock firmware deleted",
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// POST /api/test/websocket
|
||||
func (ms *MockHTTPServer) testWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
response := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
WSclients int `json:"websocketClients"`
|
||||
TotalNodes int `json:"totalNodes"`
|
||||
}{
|
||||
Success: true,
|
||||
Message: "Mock WebSocket test broadcast sent",
|
||||
TotalNodes: len(ms.discovery.GetNodes()),
|
||||
}
|
||||
|
||||
if ms.enableWS && ms.wsServer != nil {
|
||||
response.WSclients = ms.wsServer.GetClientCount()
|
||||
// Trigger a test broadcast
|
||||
ms.wsServer.BroadcastClusterUpdate()
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/monitoring/resources
|
||||
func (ms *MockHTTPServer) getMonitoringResources(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
|
||||
// Convert to mock format
|
||||
mockNodes := make(map[string]*NodeInfo)
|
||||
for ip, node := range nodes {
|
||||
mockNodes[ip] = &NodeInfo{
|
||||
IP: node.IP,
|
||||
Hostname: node.Hostname,
|
||||
Status: string(node.Status),
|
||||
Latency: node.Latency,
|
||||
LastSeen: node.LastSeen,
|
||||
Labels: node.Labels,
|
||||
}
|
||||
}
|
||||
|
||||
response := GenerateMockMonitoringResources(mockNodes)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GET /api/health
|
||||
func (ms *MockHTTPServer) healthCheck(w http.ResponseWriter, r *http.Request) {
|
||||
nodes := ms.discovery.GetNodes()
|
||||
primaryNode := ms.discovery.GetPrimaryNode()
|
||||
|
||||
health := struct {
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Services map[string]bool `json:"services"`
|
||||
Cluster map[string]interface{} `json:"cluster"`
|
||||
Mock bool `json:"mock"`
|
||||
}{
|
||||
Status: "healthy",
|
||||
Timestamp: time.Now().Format(time.RFC3339),
|
||||
Services: map[string]bool{
|
||||
"http": true,
|
||||
"websocket": ms.enableWS,
|
||||
"discovery": true,
|
||||
"mockClient": primaryNode != "",
|
||||
},
|
||||
Cluster: map[string]interface{}{
|
||||
"totalNodes": len(nodes),
|
||||
"primaryNode": primaryNode,
|
||||
},
|
||||
Mock: true,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(health)
|
||||
}
|
||||
Reference in New Issue
Block a user