summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/pkg/prometheus/selector/parse.go
blob: 29c1d4fbf6140cc28f3faa8b31093bf18cb4afa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// SPDX-License-Identifier: GPL-3.0-or-later

package selector

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/netdata/netdata/go/go.d.plugin/pkg/matcher"
)

var (
	reLV = regexp.MustCompile(`^(?P<label_name>[a-zA-Z0-9_]+)(?P<op>=~|!~|=\*|!\*|=|!=)"(?P<pattern>.+)"$`)
)

func Parse(expr string) (Selector, error) {
	var srs []Selector
	lvs := strings.Split(unsugarExpr(expr), ",")

	for _, lv := range lvs {
		sr, err := parseSelector(lv)
		if err != nil {
			return nil, err
		}
		srs = append(srs, sr)
	}

	switch len(srs) {
	case 0:
		return nil, nil
	case 1:
		return srs[0], nil
	default:
		return And(srs[0], srs[1], srs[2:]...), nil
	}
}

func parseSelector(line string) (Selector, error) {
	sub := reLV.FindStringSubmatch(strings.TrimSpace(line))
	if sub == nil {
		return nil, fmt.Errorf("invalid selector syntax: '%s'", line)
	}

	name, op, pattern := sub[1], sub[2], strings.Trim(sub[3], "\"")

	var m matcher.Matcher
	var err error

	switch op {
	case OpEqual, OpNegEqual:
		m, err = matcher.NewStringMatcher(pattern, true, true)
	case OpRegexp, OpNegRegexp:
		m, err = matcher.NewRegExpMatcher(pattern)
	case OpSimplePatterns, OpNegSimplePatterns:
		m, err = matcher.NewSimplePatternsMatcher(pattern)
	default:
		err = fmt.Errorf("unknown matching operator: %s", op)
	}
	if err != nil {
		return nil, err
	}

	sr := labelSelector{
		name: name,
		m:    m,
	}

	if neg := strings.HasPrefix(op, "!"); neg {
		return Not(sr), nil
	}
	return sr, nil
}

func unsugarExpr(expr string) string {
	// name                => __name__=*"name"
	// name{label="value"} => __name__=*"name",label="value"
	// {label="value"}     => label="value"
	expr = strings.TrimSpace(expr)

	switch idx := strings.IndexByte(expr, '{'); true {
	case idx == -1:
		expr = fmt.Sprintf(`__name__%s"%s"`,
			OpSimplePatterns,
			strings.TrimSpace(expr),
		)
	case idx == 0:
		expr = strings.Trim(expr, "{}")
	default:
		expr = fmt.Sprintf(`__name__%s"%s",%s`,
			OpSimplePatterns,
			strings.TrimSpace(expr[:idx]),
			strings.Trim(expr[idx:], "{}"),
		)
	}
	return expr
}