You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							152 lines
						
					
					
						
							3.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							152 lines
						
					
					
						
							3.7 KiB
						
					
					
				| package api | |
| 
 | |
| import ( | |
| 	"encoding/json" | |
| 	"io" | |
| 	"net/http" | |
| 	"strconv" | |
| 	"time" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/telemetry/proto" | |
| 	"github.com/seaweedfs/seaweedfs/telemetry/server/storage" | |
| 	protobuf "google.golang.org/protobuf/proto" | |
| ) | |
| 
 | |
| type Handler struct { | |
| 	storage *storage.PrometheusStorage | |
| } | |
| 
 | |
| func NewHandler(storage *storage.PrometheusStorage) *Handler { | |
| 	return &Handler{storage: storage} | |
| } | |
| 
 | |
| func (h *Handler) CollectTelemetry(w http.ResponseWriter, r *http.Request) { | |
| 	if r.Method != http.MethodPost { | |
| 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 		return | |
| 	} | |
| 
 | |
| 	contentType := r.Header.Get("Content-Type") | |
| 
 | |
| 	// Only accept protobuf content type | |
| 	if contentType != "application/x-protobuf" && contentType != "application/protobuf" { | |
| 		http.Error(w, "Content-Type must be application/x-protobuf", http.StatusUnsupportedMediaType) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Read protobuf request | |
| 	body, err := io.ReadAll(r.Body) | |
| 	if err != nil { | |
| 		http.Error(w, "Failed to read request body", http.StatusBadRequest) | |
| 		return | |
| 	} | |
| 
 | |
| 	req := &proto.TelemetryRequest{} | |
| 	if err := protobuf.Unmarshal(body, req); err != nil { | |
| 		http.Error(w, "Invalid protobuf data", http.StatusBadRequest) | |
| 		return | |
| 	} | |
| 
 | |
| 	data := req.Data | |
| 	if data == nil { | |
| 		http.Error(w, "Missing telemetry data", http.StatusBadRequest) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Validate required fields | |
| 	if data.ClusterId == "" || data.Version == "" || data.Os == "" { | |
| 		http.Error(w, "Missing required fields", http.StatusBadRequest) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Set timestamp if not provided | |
| 	if data.Timestamp == 0 { | |
| 		data.Timestamp = time.Now().Unix() | |
| 	} | |
| 
 | |
| 	// Store the telemetry data | |
| 	if err := h.storage.StoreTelemetry(data); err != nil { | |
| 		http.Error(w, "Failed to store data", http.StatusInternalServerError) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Return protobuf response | |
| 	resp := &proto.TelemetryResponse{ | |
| 		Success: true, | |
| 		Message: "Telemetry data received", | |
| 	} | |
| 
 | |
| 	respData, err := protobuf.Marshal(resp) | |
| 	if err != nil { | |
| 		http.Error(w, "Failed to marshal response", http.StatusInternalServerError) | |
| 		return | |
| 	} | |
| 
 | |
| 	w.Header().Set("Content-Type", "application/x-protobuf") | |
| 	w.WriteHeader(http.StatusOK) | |
| 	w.Write(respData) | |
| } | |
| 
 | |
| func (h *Handler) GetStats(w http.ResponseWriter, r *http.Request) { | |
| 	if r.Method != http.MethodGet { | |
| 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 		return | |
| 	} | |
| 
 | |
| 	stats, err := h.storage.GetStats() | |
| 	if err != nil { | |
| 		http.Error(w, "Failed to get stats", http.StatusInternalServerError) | |
| 		return | |
| 	} | |
| 
 | |
| 	w.Header().Set("Content-Type", "application/json") | |
| 	json.NewEncoder(w).Encode(stats) | |
| } | |
| 
 | |
| func (h *Handler) GetInstances(w http.ResponseWriter, r *http.Request) { | |
| 	if r.Method != http.MethodGet { | |
| 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 		return | |
| 	} | |
| 
 | |
| 	limitStr := r.URL.Query().Get("limit") | |
| 	limit := 100 // default | |
| 	if limitStr != "" { | |
| 		if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 1000 { | |
| 			limit = l | |
| 		} | |
| 	} | |
| 
 | |
| 	instances, err := h.storage.GetInstances(limit) | |
| 	if err != nil { | |
| 		http.Error(w, "Failed to get instances", http.StatusInternalServerError) | |
| 		return | |
| 	} | |
| 
 | |
| 	w.Header().Set("Content-Type", "application/json") | |
| 	json.NewEncoder(w).Encode(instances) | |
| } | |
| 
 | |
| func (h *Handler) GetMetrics(w http.ResponseWriter, r *http.Request) { | |
| 	if r.Method != http.MethodGet { | |
| 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 		return | |
| 	} | |
| 
 | |
| 	daysStr := r.URL.Query().Get("days") | |
| 	days := 30 // default | |
| 	if daysStr != "" { | |
| 		if d, err := strconv.Atoi(daysStr); err == nil && d > 0 && d <= 365 { | |
| 			days = d | |
| 		} | |
| 	} | |
| 
 | |
| 	metrics, err := h.storage.GetMetrics(days) | |
| 	if err != nil { | |
| 		http.Error(w, "Failed to get metrics", http.StatusInternalServerError) | |
| 		return | |
| 	} | |
| 
 | |
| 	w.Header().Set("Content-Type", "application/json") | |
| 	json.NewEncoder(w).Encode(metrics) | |
| }
 |