package main import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" "strings" ) // sendToGrafana отправляет дашборд в Grafana // Дашборд будет создан в указанной папке (folder) func sendToGrafana(dashboard map[string]interface{}, config Config) error { // Получить или создать folder ID folderID, err := ensureFolder(config) if err != nil { log.Printf("Warning: Failed to ensure folder exists: %v", err) log.Printf("Dashboard will be created in General folder") folderID = 0 // General folder } // Добавить folder ID в запрос dashboard["folderId"] = folderID if folderID > 0 { log.Printf("Creating dashboard in folder: %s (ID: %d)", config.GrafanaFolder, folderID) } jsonData, err := json.Marshal(dashboard) if err != nil { return fmt.Errorf("failed to marshal dashboard: %w", err) } url := fmt.Sprintf("%s/api/dashboards/db", strings.TrimRight(config.GrafanaURL, "/")) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.GrafanaAPIKey)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("grafana API returned status %d: %s", resp.StatusCode, string(body)) } // Parse response to get dashboard URL var response map[string]interface{} if err := json.Unmarshal(body, &response); err == nil { if url, ok := response["url"].(string); ok { log.Printf("Dashboard URL: %s%s", config.GrafanaURL, url) } } return nil } // ensureFolder создает папку если её нет и возвращает её ID func ensureFolder(config Config) (int, error) { if config.GrafanaFolder == "" { return 0, nil // General folder } // Поиск существующей папки folderID, err := findFolder(config) if err == nil && folderID > 0 { return folderID, nil } // Создать папку если не найдена return createFolder(config) } // findFolder ищет папку по имени func findFolder(config Config) (int, error) { url := fmt.Sprintf("%s/api/folders", strings.TrimRight(config.GrafanaURL, "/")) req, err := http.NewRequest("GET", url, nil) if err != nil { return 0, err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.GrafanaAPIKey)) client := &http.Client{} resp, err := client.Do(req) if err != nil { return 0, err } defer resp.Body.Close() if resp.StatusCode != 200 { return 0, fmt.Errorf("failed to list folders: status %d", resp.StatusCode) } body, _ := io.ReadAll(resp.Body) var folders []map[string]interface{} if err := json.Unmarshal(body, &folders); err != nil { return 0, err } // Поиск по title for _, folder := range folders { if title, ok := folder["title"].(string); ok && title == config.GrafanaFolder { if id, ok := folder["id"].(float64); ok { log.Printf("Found existing folder: %s (ID: %d)", config.GrafanaFolder, int(id)) return int(id), nil } } } return 0, fmt.Errorf("folder not found") } // createFolder создает новую папку func createFolder(config Config) (int, error) { url := fmt.Sprintf("%s/api/folders", strings.TrimRight(config.GrafanaURL, "/")) payload := map[string]interface{}{ "title": config.GrafanaFolder, } jsonData, err := json.Marshal(payload) if err != nil { return 0, err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { return 0, err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.GrafanaAPIKey)) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return 0, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return 0, fmt.Errorf("failed to create folder: status %d: %s", resp.StatusCode, string(body)) } var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { return 0, err } if id, ok := response["id"].(float64); ok { log.Printf("Created new folder: %s (ID: %d)", config.GrafanaFolder, int(id)) return int(id), nil } return 0, fmt.Errorf("failed to get folder ID from response") } func logDryRun(clientTitle string, dashboard map[string]interface{}) { jsonData, _ := json.MarshalIndent(dashboard, "", " ") log.Printf("DRY RUN: Dashboard for %s (%d bytes)", clientTitle, len(jsonData)) // Optionally print first 500 chars if len(jsonData) > 500 { log.Printf("Preview: %s...", string(jsonData[:500])) } else { log.Printf("Preview: %s", string(jsonData)) } }