package main import ( "context" "encoding/json" "flag" "fmt" "io" "net/http" "net/url" "os" "strings" "time" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/capacity" ) const defaultEndpoints = "gateway=http://127.0.0.1:20211,history=http://127.0.0.1:20212,stat=http://127.0.0.1:20213,bridge=http://127.0.0.1:20214,realtime=http://127.0.0.1:20200,fast-writer=http://127.0.0.1:20215" type endpoint struct { Name string URL string } func main() { var rawEndpoints string var timeout time.Duration flag.StringVar(&rawEndpoints, "endpoints", defaultEndpoints, "comma separated name=url endpoints; /metrics is appended when no path is provided") flag.DurationVar(&timeout, "timeout", 2*time.Second, "per endpoint timeout") flag.Parse() ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() metricsByService, findings := collectMetrics(ctx, parseEndpoints(rawEndpoints), timeout) report := capacity.Evaluate(metricsByService) report.Findings = append(report.Findings, findings...) if len(report.Findings) > 0 { report.Status = capacity.StatusDegraded } encoder := json.NewEncoder(os.Stdout) encoder.SetIndent("", " ") _ = encoder.Encode(report) if report.Status != capacity.StatusOK { os.Exit(2) } } func parseEndpoints(value string) []endpoint { var endpoints []endpoint for _, item := range strings.Split(value, ",") { item = strings.TrimSpace(item) if item == "" { continue } name, url, ok := strings.Cut(item, "=") if !ok { continue } name = strings.TrimSpace(name) url = withMetricsPath(strings.TrimSpace(url)) if name == "" || url == "" { continue } endpoints = append(endpoints, endpoint{Name: name, URL: url}) } return endpoints } func withMetricsPath(value string) string { parsed, err := url.Parse(value) if err != nil || parsed.Scheme == "" || parsed.Host == "" { return value } if parsed.Path == "" || parsed.Path == "/" { parsed.Path = "/metrics" } return parsed.String() } func collectMetrics(ctx context.Context, endpoints []endpoint, timeout time.Duration) (map[string]string, []string) { client := &http.Client{Timeout: timeout} metricsByService := make(map[string]string, len(endpoints)) var findings []string for _, ep := range endpoints { request, err := http.NewRequestWithContext(ctx, http.MethodGet, ep.URL, nil) if err != nil { findings = append(findings, fmt.Sprintf("%s metrics request invalid: %v", ep.Name, err)) continue } response, err := client.Do(request) if err != nil { findings = append(findings, fmt.Sprintf("%s metrics fetch failed: %v", ep.Name, err)) continue } body, readErr := io.ReadAll(response.Body) _ = response.Body.Close() if readErr != nil { findings = append(findings, fmt.Sprintf("%s metrics read failed: %v", ep.Name, readErr)) continue } if response.StatusCode < 200 || response.StatusCode >= 300 { findings = append(findings, fmt.Sprintf("%s metrics status %d", ep.Name, response.StatusCode)) continue } metricsByService[ep.Name] = string(body) } return metricsByService, findings }