VictoriaMetrics/lib/promscrape/discovery/hetzner/hcloud.go
Aliaksandr Valialkin 74448a7e57
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-20 17:01:53 +02:00

264 lines
8.2 KiB
Go

package hetzner
import (
"encoding/json"
"fmt"
"net"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
// getHCloudServerLabels returns labels for hcloud servers obtained from the given cfg
func getHCloudServerLabels(cfg *apiConfig) ([]*promutils.Labels, error) {
networks, err := getHCloudNetworks(cfg)
if err != nil {
return nil, err
}
servers, err := getHCloudServers(cfg)
if err != nil {
return nil, err
}
var ms []*promutils.Labels
for i := range servers {
ms = appendHCloudTargetLabels(ms, &servers[i], networks, cfg.port)
}
return ms, nil
}
func appendHCloudTargetLabels(ms []*promutils.Labels, server *HCloudServer, networks []HCloudNetwork, port int) []*promutils.Labels {
m := promutils.NewLabels(24)
addr := discoveryutils.JoinHostPort(server.PublicNet.IPv4.IP, port)
m.Add("__address__", addr)
m.Add("__meta_hetzner_role", "hcloud")
m.Add("__meta_hetzner_server_id", fmt.Sprintf("%d", server.ID))
m.Add("__meta_hetzner_server_name", server.Name)
m.Add("__meta_hetzner_datacenter", server.Datacenter.Name)
m.Add("__meta_hetzner_public_ipv4", server.PublicNet.IPv4.IP)
if _, n, _ := net.ParseCIDR(server.PublicNet.IPv6.IP); n != nil {
m.Add("__meta_hetzner_public_ipv6_network", n.String())
}
m.Add("__meta_hetzner_server_status", server.Status)
m.Add("__meta_hetzner_hcloud_datacenter_location", server.Datacenter.Location.Name)
m.Add("__meta_hetzner_hcloud_datacenter_location_network_zone", server.Datacenter.Location.NetworkZone)
m.Add("__meta_hetzner_hcloud_server_type", server.ServerType.Name)
m.Add("__meta_hetzner_hcloud_cpu_cores", fmt.Sprintf("%d", server.ServerType.Cores))
m.Add("__meta_hetzner_hcloud_cpu_type", server.ServerType.CPUType)
m.Add("__meta_hetzner_hcloud_memory_size_gb", fmt.Sprintf("%d", int(server.ServerType.Memory)))
m.Add("__meta_hetzner_hcloud_disk_size_gb", fmt.Sprintf("%d", server.ServerType.Disk))
if server.Image != nil {
m.Add("__meta_hetzner_hcloud_image_name", server.Image.Name)
m.Add("__meta_hetzner_hcloud_image_description", server.Image.Description)
m.Add("__meta_hetzner_hcloud_image_os_version", server.Image.OsVersion)
m.Add("__meta_hetzner_hcloud_image_os_flavor", server.Image.OsFlavor)
}
for _, privateNet := range server.PrivateNet {
networkID := privateNet.ID
for _, network := range networks {
if networkID == network.ID {
labelName := discoveryutils.SanitizeLabelName("__meta_hetzner_hcloud_private_ipv4_" + network.Name)
m.Add(labelName, privateNet.IP)
}
}
}
for labelKey, labelValue := range server.Labels {
labelName := discoveryutils.SanitizeLabelName("__meta_hetzner_hcloud_labelpresent_" + labelKey)
m.Add(labelName, "true")
labelName = discoveryutils.SanitizeLabelName("__meta_hetzner_hcloud_label_" + labelKey)
m.Add(labelName, labelValue)
}
ms = append(ms, m)
return ms
}
// getHCloudNetworks returns hcloud networks obtained from the given cfg
func getHCloudNetworks(cfg *apiConfig) ([]HCloudNetwork, error) {
// See https://docs.hetzner.cloud/#networks-get-all-networks
var networks []HCloudNetwork
page := 1
for {
path := fmt.Sprintf("/v1/networks?page=%d", page)
data, err := cfg.client.GetAPIResponse(path)
if err != nil {
return nil, fmt.Errorf("cannot query hcloud api for networks: %w", err)
}
networksPage, nextPage, err := parseHCloudNetworksList(data)
if err != nil {
return nil, err
}
networks = append(networks, networksPage...)
if nextPage <= page {
break
}
page = nextPage
}
return networks, nil
}
func parseHCloudNetworksList(data []byte) ([]HCloudNetwork, int, error) {
var resp HCloudNetworksList
if err := json.Unmarshal(data, &resp); err != nil {
return nil, 0, fmt.Errorf("cannot unmarshal HCloudNetworksList from %q: %w", data, err)
}
return resp.Networks, resp.Meta.Pagination.NextPage, nil
}
// HCloudNetworksList represents the hetzner cloud networks list.
//
// See https://docs.hetzner.cloud/#networks-get-all-networks
type HCloudNetworksList struct {
Meta HCloudMeta `json:"meta"`
Networks []HCloudNetwork `json:"networks"`
}
// HCloudNetwork represents the hetzner cloud network information.
//
// See https://docs.hetzner.cloud/#networks-get-all-networks
type HCloudNetwork struct {
Name string `json:"name"`
ID int `json:"id"`
}
// getHCloudServers returns hcloud servers obtained from the given cfg
func getHCloudServers(cfg *apiConfig) ([]HCloudServer, error) {
// See https://docs.hetzner.cloud/#servers-get-all-servers
var servers []HCloudServer
page := 1
for {
path := fmt.Sprintf("/v1/servers?page=%d", page)
data, err := cfg.client.GetAPIResponse(path)
if err != nil {
return nil, fmt.Errorf("cannot query hcloud api for servers: %w", err)
}
serversPage, nextPage, err := parseHCloudServerList(data)
if err != nil {
return nil, err
}
servers = append(servers, serversPage...)
if nextPage <= page {
break
}
page = nextPage
}
return servers, nil
}
func parseHCloudServerList(data []byte) ([]HCloudServer, int, error) {
var resp HCloudServerList
if err := json.Unmarshal(data, &resp); err != nil {
return nil, 0, fmt.Errorf("cannot unmarshal HCloudServerList from %q: %w", data, err)
}
return resp.Servers, resp.Meta.Pagination.NextPage, nil
}
// HCloudServerList represents a list of servers from Hetzner Cloud API.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudServerList struct {
Meta HCloudMeta `json:"meta"`
Servers []HCloudServer `json:"servers"`
}
// HCloudServer represents the structure of server data.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudServer struct {
ID int `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
PublicNet HCloudPublicNet `json:"public_net"`
PrivateNet []HCloudPrivateNet `json:"private_net"`
ServerType HCloudServerType `json:"server_type"`
Datacenter HCloudDatacenter `json:"datacenter"`
Image *HCloudImage `json:"image"`
Labels map[string]string `json:"labels"`
}
// HCloudServerType represents the server type information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudServerType struct {
Name string `json:"name"`
Cores int `json:"cores"`
CPUType string `json:"cpu_type"`
Memory float32 `json:"memory"`
Disk int `json:"disk"`
}
// HCloudDatacenter represents the Hetzner datacenter.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudDatacenter struct {
Name string `json:"name"`
Location HCloudDatacenterLocation `json:"location"`
}
// HCloudDatacenterLocation represents the datacenter information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudDatacenterLocation struct {
Name string `json:"name"`
NetworkZone string `json:"network_zone"`
}
// HCloudPublicNet represents the public network information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudPublicNet struct {
IPv4 HCloudIPv4 `json:"ipv4"`
IPv6 HCloudIPv6 `json:"ipv6"`
}
// HCloudIPv4 represents the IPv4 information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudIPv4 struct {
IP string `json:"ip"`
}
// HCloudIPv6 represents the IPv6 information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudIPv6 struct {
IP string `json:"ip"`
}
// HCloudPrivateNet represents the private network information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudPrivateNet struct {
ID int `json:"network"`
IP string `json:"ip"`
}
// HCloudImage represents the image information.
//
// See https://docs.hetzner.cloud/#servers-get-all-servers
type HCloudImage struct {
Name string `json:"name"`
Description string `json:"description"`
OsFlavor string `json:"os_flavor"`
OsVersion string `json:"os_version"`
}
// HCloudMeta represents hetzner cloud meta-information.
//
// See https://docs.hetzner.cloud/#pagination
type HCloudMeta struct {
Pagination HCloudPagination `json:"pagination"`
}
// HCloudPagination represents hetzner cloud pagination information.
//
// See https://docs.hetzner.cloud/#pagination
type HCloudPagination struct {
NextPage int `json:"next_page"`
}