package hetzner import ( "encoding/json" "fmt" "net" "strings" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" ) func getRobotServerLabels(cfg *apiConfig) ([]*promutils.Labels, error) { servers, err := getRobotServers(cfg) if err != nil { return nil, err } var ms []*promutils.Labels for i := range servers { ms = appendRobotTargetLabels(ms, &servers[i], cfg.port) } return ms, nil } func appendRobotTargetLabels(ms []*promutils.Labels, server *RobotServer, port int) []*promutils.Labels { m := promutils.NewLabels(16) addr := discoveryutils.JoinHostPort(server.ServerIP, port) m.Add("__address__", addr) m.Add("__meta_hetzner_role", "robot") m.Add("__meta_hetzner_server_id", fmt.Sprintf("%d", server.ServerNumber)) m.Add("__meta_hetzner_server_name", server.ServerName) m.Add("__meta_hetzner_datacenter", strings.ToLower(server.DC)) m.Add("__meta_hetzner_public_ipv4", server.ServerIP) for _, subnet := range server.Subnet { ip := net.ParseIP(subnet.IP) if ip.To4() == nil { m.Add("__meta_hetzner_public_ipv6_network", fmt.Sprintf("%s/%s", subnet.IP, subnet.Mask)) break } } m.Add("__meta_hetzner_server_status", server.Status) m.Add("__meta_hetzner_robot_product", server.Product) m.Add("__meta_hetzner_robot_cancelled", fmt.Sprintf("%t", server.Canceled)) ms = append(ms, m) return ms } func getRobotServers(cfg *apiConfig) ([]RobotServer, error) { // See https://robot.hetzner.com/doc/webservice/en.html#server data, err := cfg.client.GetAPIResponse("/server") if err != nil { return nil, fmt.Errorf("cannot query hetzner robot api for servers: %w", err) } servers, err := parseRobotServers(data) if err != nil { return nil, err } return servers, nil } func parseRobotServers(data []byte) ([]RobotServer, error) { var serverEntries []RobotServerEntry err := json.Unmarshal(data, &serverEntries) if err != nil { return nil, fmt.Errorf("cannot unmarshal RobotServer list from %q: %w", data, err) } servers := make([]RobotServer, len(serverEntries)) for i := range serverEntries { servers[i] = serverEntries[i].Server } return servers, nil } // RobotServerEntry represents a single server entry in hetzner robot server response. // // See https://robot.hetzner.com/doc/webservice/en.html#server type RobotServerEntry struct { Server RobotServer `json:"server"` } // RobotServer represents the structure of hetzner robot server data. // // See https://robot.hetzner.com/doc/webservice/en.html#server type RobotServer struct { ServerIP string `json:"server_ip"` ServerIPV6 string `json:"server_ipv6_net"` ServerNumber int `json:"server_number"` ServerName string `json:"server_name"` DC string `json:"dc"` Status string `json:"status"` Product string `json:"product"` Canceled bool `json:"cancelled"` Subnet []RobotSubnet `json:"subnet"` } // RobotSubnet represents the structure of hetzner robot subnet data. // // See https://robot.hetzner.com/doc/webservice/en.html#server type RobotSubnet struct { IP string `json:"ip"` Mask string `json:"mask"` }