github.com/netdata/go.d.plugin@v0.58.1/agent/discovery/sd/pipeline/selector.go (about) 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/go.d.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.Split(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 }