master
go 154 lines 3.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package pipeline
4
5 import (
6 "errors"
7 "fmt"
8 "strings"
9
10 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery/sd/model"
11 )
12
13 type selector interface {
14 matches(model.Tags) bool
15 }
16
17 type (
18 exactSelector string
19 trueSelector struct{}
20 negSelector struct{ selector }
21 orSelector struct{ lhs, rhs selector }
22 andSelector struct{ lhs, rhs selector }
23 )
24
25 func (s exactSelector) matches(tags model.Tags) bool { _, ok := tags[string(s)]; return ok }
26 func (s trueSelector) matches(model.Tags) bool { return true }
27 func (s negSelector) matches(tags model.Tags) bool { return !s.selector.matches(tags) }
28 func (s orSelector) matches(tags model.Tags) bool { return s.lhs.matches(tags) || s.rhs.matches(tags) }
29 func (s andSelector) matches(tags model.Tags) bool { return s.lhs.matches(tags) && s.rhs.matches(tags) }
30
31 func (s exactSelector) String() string { return "{" + string(s) + "}" }
32 func (s negSelector) String() string { return "{!" + stringify(s.selector) + "}" }
33 func (s trueSelector) String() string { return "{*}" }
34 func (s orSelector) String() string { return "{" + stringify(s.lhs) + "|" + stringify(s.rhs) + "}" }
35 func (s andSelector) String() string { return "{" + stringify(s.lhs) + ", " + stringify(s.rhs) + "}" }
36 func stringify(sr selector) string { return strings.Trim(fmt.Sprintf("%s", sr), "{}") }
37
38 func parseSelector(line string) (sr selector, err error) {
39 words := strings.Fields(line)
40 if len(words) == 0 {
41 return trueSelector{}, nil
42 }
43
44 var srs []selector
45 for _, word := range words {
46 if idx := strings.IndexByte(word, '|'); idx > 0 {
47 sr, err = parseOrSelectorWord(word)
48 } else {
49 sr, err = parseSingleSelectorWord(word)
50 }
51 if err != nil {
52 return nil, fmt.Errorf("selector '%s' contains selector '%s' with forbidden symbol", line, word)
53 }
54 srs = append(srs, sr)
55 }
56
57 switch len(srs) {
58 case 0:
59 return trueSelector{}, nil
60 case 1:
61 return srs[0], nil
62 default:
63 return newAndSelector(srs[0], srs[1], srs[2:]...), nil
64 }
65 }
66
67 func parseOrSelectorWord(orWord string) (sr selector, err error) {
68 var srs []selector
69 for word := range strings.SplitSeq(orWord, "|") {
70 if sr, err = parseSingleSelectorWord(word); err != nil {
71 return nil, err
72 }
73 srs = append(srs, sr)
74 }
75 switch len(srs) {
76 case 0:
77 return trueSelector{}, nil
78 case 1:
79 return srs[0], nil
80 default:
81 return newOrSelector(srs[0], srs[1], srs[2:]...), nil
82 }
83 }
84
85 func parseSingleSelectorWord(word string) (selector, error) {
86 if len(word) == 0 {
87 return nil, errors.New("empty word")
88 }
89 neg := word[0] == '!'
90 if neg {
91 word = word[1:]
92 }
93 if len(word) == 0 {
94 return nil, errors.New("empty word")
95 }
96 if word != "*" && !isSelectorWordValid(word) {
97 return nil, errors.New("forbidden symbol")
98 }
99
100 var sr selector
101 switch word {
102 case "*":
103 sr = trueSelector{}
104 default:
105 sr = exactSelector(word)
106 }
107 if neg {
108 return negSelector{sr}, nil
109 }
110 return sr, nil
111 }
112
113 func newAndSelector(lhs, rhs selector, others ...selector) selector {
114 m := andSelector{lhs: lhs, rhs: rhs}
115 switch len(others) {
116 case 0:
117 return m
118 default:
119 return newAndSelector(m, others[0], others[1:]...)
120 }
121 }
122
123 func newOrSelector(lhs, rhs selector, others ...selector) selector {
124 m := orSelector{lhs: lhs, rhs: rhs}
125 switch len(others) {
126 case 0:
127 return m
128 default:
129 return newOrSelector(m, others[0], others[1:]...)
130 }
131 }
132
133 func isSelectorWordValid(word string) bool {
134 // valid:
135 // *
136 // ^[a-zA-Z][a-zA-Z0-9=_.]*$
137 if len(word) == 0 {
138 return false
139 }
140 if word == "*" {
141 return true
142 }
143 for i, b := range word {
144 switch {
145 case b >= 'a' && b <= 'z':
146 case b >= 'A' && b <= 'Z':
147 case b >= '0' && b <= '9' && i > 0:
148 case (b == '=' || b == '_' || b == '.') && i > 0:
149 default:
150 return false
151 }
152 }
153 return true
154 }