mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-05 01:01:09 +01:00
0bf3ae9559
This simplifies copying service tags to target labels with the following relabeling rule: - action: labelmap regex: __meta_consul_tag_(.+) See https://stackoverflow.com/questions/44339461/relabeling-in-prometheus
163 lines
4.5 KiB
Go
163 lines
4.5 KiB
Go
package consul
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
|
|
)
|
|
|
|
// getServiceNodesLabels returns labels for Consul service nodes with given cfg.
|
|
func getServiceNodesLabels(cfg *apiConfig) []*promutils.Labels {
|
|
sns := cfg.consulWatcher.getServiceNodesSnapshot()
|
|
var ms []*promutils.Labels
|
|
for svc, sn := range sns {
|
|
for i := range sn {
|
|
ms = sn[i].appendTargetLabels(ms, svc, cfg.tagSeparator)
|
|
}
|
|
}
|
|
return ms
|
|
}
|
|
|
|
// ServiceNode is Consul service node.
|
|
//
|
|
// See https://www.consul.io/api/health.html#list-nodes-for-service
|
|
type ServiceNode struct {
|
|
Service Service
|
|
Node Node
|
|
Checks []Check
|
|
}
|
|
|
|
// Service is Consul service.
|
|
//
|
|
// See https://www.consul.io/api/health.html#list-nodes-for-service
|
|
type Service struct {
|
|
ID string
|
|
Service string
|
|
Address string
|
|
Namespace string
|
|
Partition string
|
|
Port int
|
|
Tags []string
|
|
Meta map[string]string
|
|
}
|
|
|
|
// Node is Consul node.
|
|
//
|
|
// See https://www.consul.io/api/health.html#list-nodes-for-service
|
|
type Node struct {
|
|
Address string
|
|
Datacenter string
|
|
Node string
|
|
Meta map[string]string
|
|
TaggedAddresses map[string]string
|
|
}
|
|
|
|
// Check is Consul check.
|
|
//
|
|
// See https://www.consul.io/api/health.html#list-nodes-for-service
|
|
type Check struct {
|
|
CheckID string
|
|
Status string
|
|
}
|
|
|
|
func parseServiceNodes(data []byte) ([]ServiceNode, error) {
|
|
var sns []ServiceNode
|
|
if err := json.Unmarshal(data, &sns); err != nil {
|
|
return nil, fmt.Errorf("cannot unmarshal ServiceNodes from %q: %w", data, err)
|
|
}
|
|
return sns, nil
|
|
}
|
|
|
|
func (sn *ServiceNode) appendTargetLabels(ms []*promutils.Labels, serviceName, tagSeparator string) []*promutils.Labels {
|
|
var addr string
|
|
if sn.Service.Address != "" {
|
|
addr = discoveryutils.JoinHostPort(sn.Service.Address, sn.Service.Port)
|
|
} else {
|
|
addr = discoveryutils.JoinHostPort(sn.Node.Address, sn.Service.Port)
|
|
}
|
|
m := promutils.NewLabels(16)
|
|
m.Add("__address__", addr)
|
|
m.Add("__meta_consul_address", sn.Node.Address)
|
|
m.Add("__meta_consul_dc", sn.Node.Datacenter)
|
|
m.Add("__meta_consul_health", aggregatedStatus(sn.Checks))
|
|
m.Add("__meta_consul_namespace", sn.Service.Namespace)
|
|
m.Add("__meta_consul_partition", sn.Service.Partition)
|
|
m.Add("__meta_consul_node", sn.Node.Node)
|
|
m.Add("__meta_consul_service", serviceName)
|
|
m.Add("__meta_consul_service_address", sn.Service.Address)
|
|
m.Add("__meta_consul_service_id", sn.Service.ID)
|
|
m.Add("__meta_consul_service_port", strconv.Itoa(sn.Service.Port))
|
|
// 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.Add("__meta_consul_tags", tagSeparator+strings.Join(sn.Service.Tags, tagSeparator)+tagSeparator)
|
|
|
|
// Expose individual tags via __meta_consul_tag_* labels, so users could move all the tags
|
|
// into the discovered scrape target with the following relabeling rule in the way similar to kubernetes_sd_configs:
|
|
//
|
|
// - action: labelmap
|
|
// regex: __meta_consul_tag_(.+)
|
|
//
|
|
// This solves https://stackoverflow.com/questions/44339461/relabeling-in-prometheus
|
|
for _, tag := range sn.Service.Tags {
|
|
k := tag
|
|
v := ""
|
|
if n := strings.IndexByte(tag, '='); n >= 0 {
|
|
k = tag[:n]
|
|
v = tag[n+1:]
|
|
}
|
|
m.Add(discoveryutils.SanitizeLabelName("__meta_consul_tag_"+k), v)
|
|
m.Add(discoveryutils.SanitizeLabelName("__meta_consul_tagpresent_"+k), "true")
|
|
}
|
|
|
|
for k, v := range sn.Node.Meta {
|
|
m.Add(discoveryutils.SanitizeLabelName("__meta_consul_metadata_"+k), v)
|
|
}
|
|
for k, v := range sn.Service.Meta {
|
|
m.Add(discoveryutils.SanitizeLabelName("__meta_consul_service_metadata_"+k), v)
|
|
}
|
|
for k, v := range sn.Node.TaggedAddresses {
|
|
m.Add(discoveryutils.SanitizeLabelName("__meta_consul_tagged_address_"+k), v)
|
|
}
|
|
ms = append(ms, m)
|
|
return ms
|
|
}
|
|
|
|
func aggregatedStatus(checks []Check) string {
|
|
// The code has been copy-pasted from HealthChecks.AggregatedStatus in Consul
|
|
var passing, warning, critical, maintenance bool
|
|
for _, check := range checks {
|
|
id := check.CheckID
|
|
if id == "_node_maintenance" || strings.HasPrefix(id, "_service_maintenance:") {
|
|
maintenance = true
|
|
continue
|
|
}
|
|
|
|
switch check.Status {
|
|
case "passing":
|
|
passing = true
|
|
case "warning":
|
|
warning = true
|
|
case "critical":
|
|
critical = true
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
switch {
|
|
case maintenance:
|
|
return "maintenance"
|
|
case critical:
|
|
return "critical"
|
|
case warning:
|
|
return "warning"
|
|
case passing:
|
|
return "passing"
|
|
default:
|
|
return "passing"
|
|
}
|
|
}
|