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  }