VictoriaMetrics/lib/promscrape/discovery/hetzner/robot.go
Aliaksandr Valialkin c05982bfa7
lib/promscrape/discovery/hetzner: follow-up after 03a97dc678
- docs/sd_configs.md: moved hetzner_sd_configs docs to the correct place according to alphabetical order of SD names,
  document missing __meta_hetzner_role label.
- lib/promscrape/config.go: added missing MustStop() call for Hetzner SD,
  and moved the code to the correct place according to alphabetical order of SD names.
- lib/promscrape/discovery/hetzner: properly handle pagination for hloud API responses,
  populate missing __meta_hetzner_role label like Prometheus does.
- Properly populate __meta_hetzner_public_ipv6_network label like Prometheus does.
- Remove unused SDConfig.Token.
- Remove "omitempty" annotation from SDConfig.Role field, since this field is mandatory.

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5550
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3154
2024-01-22 00:53:23 +02:00

107 lines
3.2 KiB
Go

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"`
}