VictoriaMetrics/lib/protoparser/common/extra_labels.go
Alexander Marshalov 5dba522eaa
fixed label values decoding for pushgateway compatibility (#4727)
Fixed decoding of label values with slash for pushgateway and prometheus golang client compatibility + added some tests. (#4962)
2023-08-12 13:23:14 -07:00

83 lines
2.1 KiB
Go

package common
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
)
// GetExtraLabels extracts name:value labels from `extra_label=name=value` query args from req.
//
// It also extracts Pushgateways-compatible extra labels from req.URL.Path
// according to https://github.com/prometheus/pushgateway#url .
func GetExtraLabels(req *http.Request) ([]prompbmarshal.Label, error) {
labels, err := getPushgatewayLabels(req.URL.Path)
if err != nil {
return nil, fmt.Errorf("cannot parse pushgateway-style labels from %q: %w", req.URL.Path, err)
}
q := req.URL.Query()
for _, label := range q["extra_label"] {
tmp := strings.SplitN(label, "=", 2)
if len(tmp) != 2 {
return nil, fmt.Errorf("`extra_label` query arg must have the format `name=value`; got %q", label)
}
labels = append(labels, prompbmarshal.Label{
Name: tmp[0],
Value: tmp[1],
})
}
return labels, nil
}
func getPushgatewayLabels(path string) ([]prompbmarshal.Label, error) {
n := strings.Index(path, "/metrics/job")
if n < 0 {
return nil, nil
}
s := path[n+len("/metrics/"):]
if !strings.HasPrefix(s, "job/") && !strings.HasPrefix(s, "job@base64/") {
return nil, nil
}
labelsCount := (strings.Count(s, "/") + 1) / 2
labels := make([]prompbmarshal.Label, 0, labelsCount)
for len(s) > 0 {
n := strings.IndexByte(s, '/')
if n < 0 {
return nil, fmt.Errorf("missing value for label %q", s)
}
name := s[:n]
s = s[n+1:]
isBase64 := strings.HasSuffix(name, "@base64")
if isBase64 {
name = name[:len(name)-len("@base64")]
}
var value string
n = strings.IndexByte(s, '/')
if n < 0 {
value = s
s = ""
} else {
value = s[:n]
s = s[n+1:]
}
if isBase64 {
data, err := base64.RawURLEncoding.DecodeString(strings.TrimRight(value, "="))
if err != nil {
return nil, fmt.Errorf("cannot base64-decode value=%q for label=%q: %w", value, name, err)
}
value = string(data)
}
if len(value) == 0 {
// Skip labels with empty values
continue
}
labels = append(labels, prompbmarshal.Label{
Name: name,
Value: value,
})
}
return labels, nil
}