mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-30 07:40:06 +01:00
c06e7a142c
Cache sanitized label names and return them next time. This reduces the number of allocations and speeds up the SanitizeLabelName() function for common case when the number of unique label names is smaller than 100k
175 lines
5.2 KiB
Go
175 lines
5.2 KiB
Go
package gce
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
|
|
)
|
|
|
|
// getInstancesLabels returns labels for gce instances obtained from the given cfg
|
|
func getInstancesLabels(cfg *apiConfig) []map[string]string {
|
|
insts := getInstances(cfg)
|
|
var ms []map[string]string
|
|
for _, inst := range insts {
|
|
ms = inst.appendTargetLabels(ms, cfg.project, cfg.tagSeparator, cfg.port)
|
|
}
|
|
return ms
|
|
}
|
|
|
|
func getInstances(cfg *apiConfig) []Instance {
|
|
// Collect instances for each zone in parallel
|
|
type result struct {
|
|
zone string
|
|
insts []Instance
|
|
err error
|
|
}
|
|
ch := make(chan result, len(cfg.zones))
|
|
for _, zone := range cfg.zones {
|
|
go func(zone string) {
|
|
insts, err := getInstancesForProjectAndZone(cfg.client, cfg.project, zone, cfg.filter)
|
|
ch <- result{
|
|
zone: zone,
|
|
insts: insts,
|
|
err: err,
|
|
}
|
|
}(zone)
|
|
}
|
|
var insts []Instance
|
|
for range cfg.zones {
|
|
r := <-ch
|
|
if r.err != nil {
|
|
logger.Errorf("cannot collect instances from zone %q: %s", r.zone, r.err)
|
|
continue
|
|
}
|
|
insts = append(insts, r.insts...)
|
|
}
|
|
return insts
|
|
}
|
|
|
|
func getInstancesForProjectAndZone(client *http.Client, project, zone, filter string) ([]Instance, error) {
|
|
// See https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
instsURL := fmt.Sprintf("https://compute.googleapis.com/compute/v1/projects/%s/zones/%s/instances", project, zone)
|
|
var insts []Instance
|
|
pageToken := ""
|
|
for {
|
|
data, err := getAPIResponse(client, instsURL, filter, pageToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot obtain instances: %w", err)
|
|
}
|
|
il, err := parseInstanceList(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot parse instance list from %q: %w", instsURL, err)
|
|
}
|
|
insts = append(insts, il.Items...)
|
|
if len(il.NextPageToken) == 0 {
|
|
return insts, nil
|
|
}
|
|
pageToken = il.NextPageToken
|
|
}
|
|
}
|
|
|
|
// InstanceList is response to https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type InstanceList struct {
|
|
Items []Instance
|
|
NextPageToken string
|
|
}
|
|
|
|
// Instance is instance from https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type Instance struct {
|
|
ID string `json:"id"`
|
|
Name string
|
|
Status string
|
|
MachineType string
|
|
Zone string
|
|
NetworkInterfaces []NetworkInterface
|
|
Tags TagList
|
|
Metadata MetadataList
|
|
Labels discoveryutils.SortedLabels
|
|
}
|
|
|
|
// NetworkInterface is network interface from https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type NetworkInterface struct {
|
|
Name string
|
|
Network string
|
|
Subnetwork string
|
|
NetworkIP string
|
|
AccessConfigs []AccessConfig
|
|
}
|
|
|
|
// AccessConfig is access config from https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type AccessConfig struct {
|
|
Type string
|
|
NatIP string
|
|
}
|
|
|
|
// TagList is tag list from https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type TagList struct {
|
|
Items []string
|
|
}
|
|
|
|
// MetadataList is metadataList from https://cloud.google.com/compute/docs/reference/rest/v1/instances/list
|
|
type MetadataList struct {
|
|
Items []MetadataEntry
|
|
}
|
|
|
|
// MetadataEntry is a single entry from metadata
|
|
type MetadataEntry struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
// parseInstanceList parses InstanceList from data.
|
|
func parseInstanceList(data []byte) (*InstanceList, error) {
|
|
var il InstanceList
|
|
if err := json.Unmarshal(data, &il); err != nil {
|
|
return nil, fmt.Errorf("cannot unmarshal InstanceList from %q: %w", data, err)
|
|
}
|
|
return &il, nil
|
|
}
|
|
|
|
func (inst *Instance) appendTargetLabels(ms []map[string]string, project, tagSeparator string, port int) []map[string]string {
|
|
if len(inst.NetworkInterfaces) == 0 {
|
|
return ms
|
|
}
|
|
iface := inst.NetworkInterfaces[0]
|
|
addr := discoveryutils.JoinHostPort(iface.NetworkIP, port)
|
|
m := map[string]string{
|
|
"__address__": addr,
|
|
"__meta_gce_instance_id": inst.ID,
|
|
"__meta_gce_instance_status": inst.Status,
|
|
"__meta_gce_instance_name": inst.Name,
|
|
"__meta_gce_machine_type": inst.MachineType,
|
|
"__meta_gce_network": iface.Network,
|
|
"__meta_gce_private_ip": iface.NetworkIP,
|
|
"__meta_gce_project": project,
|
|
"__meta_gce_subnetwork": iface.Subnetwork,
|
|
"__meta_gce_zone": inst.Zone,
|
|
}
|
|
for _, iface := range inst.NetworkInterfaces {
|
|
m[discoveryutils.SanitizeLabelName("__meta_gce_interface_ipv4_"+iface.Name)] = iface.NetworkIP
|
|
}
|
|
if len(inst.Tags.Items) > 0 {
|
|
// We surround the separated list with the separator as well. This way regular expressions
|
|
// in relabeling rules don't have to consider tag positions.
|
|
m["__meta_gce_tags"] = tagSeparator + strings.Join(inst.Tags.Items, tagSeparator) + tagSeparator
|
|
}
|
|
for _, item := range inst.Metadata.Items {
|
|
m[discoveryutils.SanitizeLabelName("__meta_gce_metadata_"+item.Key)] = item.Value
|
|
}
|
|
for _, label := range inst.Labels {
|
|
m[discoveryutils.SanitizeLabelName("__meta_gce_label_"+label.Name)] = label.Value
|
|
}
|
|
if len(iface.AccessConfigs) > 0 {
|
|
ac := iface.AccessConfigs[0]
|
|
if ac.Type == "ONE_TO_ONE_NAT" {
|
|
m["__meta_gce_public_ip"] = ac.NatIP
|
|
}
|
|
}
|
|
ms = append(ms, m)
|
|
return ms
|
|
}
|