...

Source file src/github.com/prometheus/alertmanager/pkg/labels/matcher.go

Documentation: github.com/prometheus/alertmanager/pkg/labels

     1  // Copyright 2017 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package labels
    15  
    16  import (
    17  	"bytes"
    18  	"encoding/json"
    19  	"fmt"
    20  	"regexp"
    21  	"strings"
    22  
    23  	"github.com/prometheus/common/model"
    24  )
    25  
    26  // MatchType is an enum for label matching types.
    27  type MatchType int
    28  
    29  // Possible MatchTypes.
    30  const (
    31  	MatchEqual MatchType = iota
    32  	MatchNotEqual
    33  	MatchRegexp
    34  	MatchNotRegexp
    35  )
    36  
    37  func (m MatchType) String() string {
    38  	typeToStr := map[MatchType]string{
    39  		MatchEqual:     "=",
    40  		MatchNotEqual:  "!=",
    41  		MatchRegexp:    "=~",
    42  		MatchNotRegexp: "!~",
    43  	}
    44  	if str, ok := typeToStr[m]; ok {
    45  		return str
    46  	}
    47  	panic("unknown match type")
    48  }
    49  
    50  // Matcher models the matching of a label.
    51  type Matcher struct {
    52  	Type  MatchType
    53  	Name  string
    54  	Value string
    55  
    56  	re *regexp.Regexp
    57  }
    58  
    59  // NewMatcher returns a matcher object.
    60  func NewMatcher(t MatchType, n, v string) (*Matcher, error) {
    61  	m := &Matcher{
    62  		Type:  t,
    63  		Name:  n,
    64  		Value: v,
    65  	}
    66  	if t == MatchRegexp || t == MatchNotRegexp {
    67  		re, err := regexp.Compile("^(?:" + v + ")$")
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		m.re = re
    72  	}
    73  	return m, nil
    74  }
    75  
    76  func (m *Matcher) String() string {
    77  	return fmt.Sprintf(`%s%s"%s"`, m.Name, m.Type, openMetricsEscape(m.Value))
    78  }
    79  
    80  // Matches returns whether the matcher matches the given string value.
    81  func (m *Matcher) Matches(s string) bool {
    82  	switch m.Type {
    83  	case MatchEqual:
    84  		return s == m.Value
    85  	case MatchNotEqual:
    86  		return s != m.Value
    87  	case MatchRegexp:
    88  		return m.re.MatchString(s)
    89  	case MatchNotRegexp:
    90  		return !m.re.MatchString(s)
    91  	}
    92  	panic("labels.Matcher.Matches: invalid match type")
    93  }
    94  
    95  type apiV1Matcher struct {
    96  	Name    string `json:"name"`
    97  	Value   string `json:"value"`
    98  	IsRegex bool   `json:"isRegex"`
    99  	IsEqual bool   `json:"isEqual"`
   100  }
   101  
   102  // MarshalJSON retains backwards compatibility with types.Matcher for the v1 API.
   103  func (m Matcher) MarshalJSON() ([]byte, error) {
   104  	return json.Marshal(apiV1Matcher{
   105  		Name:    m.Name,
   106  		Value:   m.Value,
   107  		IsRegex: m.Type == MatchRegexp || m.Type == MatchNotRegexp,
   108  		IsEqual: m.Type == MatchRegexp || m.Type == MatchEqual,
   109  	})
   110  }
   111  
   112  func (m *Matcher) UnmarshalJSON(data []byte) error {
   113  	v1m := apiV1Matcher{
   114  		IsEqual: true,
   115  	}
   116  
   117  	if err := json.Unmarshal(data, &v1m); err != nil {
   118  		return err
   119  	}
   120  
   121  	var t MatchType
   122  	switch {
   123  	case v1m.IsEqual && !v1m.IsRegex:
   124  		t = MatchEqual
   125  	case !v1m.IsEqual && !v1m.IsRegex:
   126  		t = MatchNotEqual
   127  	case v1m.IsEqual && v1m.IsRegex:
   128  		t = MatchRegexp
   129  	case !v1m.IsEqual && v1m.IsRegex:
   130  		t = MatchNotRegexp
   131  	}
   132  
   133  	matcher, err := NewMatcher(t, v1m.Name, v1m.Value)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	*m = *matcher
   138  	return nil
   139  }
   140  
   141  // openMetricsEscape is similar to the usual string escaping, but more
   142  // restricted. It merely replaces a new-line character with '\n', a double-quote
   143  // character with '\"', and a backslash with '\\', which is the escaping used by
   144  // OpenMetrics.
   145  func openMetricsEscape(s string) string {
   146  	r := strings.NewReplacer(
   147  		`\`, `\\`,
   148  		"\n", `\n`,
   149  		`"`, `\"`,
   150  	)
   151  	return r.Replace(s)
   152  }
   153  
   154  // Matchers is a slice of Matchers that is sortable, implements Stringer, and
   155  // provides a Matches method to match a LabelSet against all Matchers in the
   156  // slice. Note that some users of Matchers might require it to be sorted.
   157  type Matchers []*Matcher
   158  
   159  func (ms Matchers) Len() int      { return len(ms) }
   160  func (ms Matchers) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
   161  
   162  func (ms Matchers) Less(i, j int) bool {
   163  	if ms[i].Name > ms[j].Name {
   164  		return false
   165  	}
   166  	if ms[i].Name < ms[j].Name {
   167  		return true
   168  	}
   169  	if ms[i].Value > ms[j].Value {
   170  		return false
   171  	}
   172  	if ms[i].Value < ms[j].Value {
   173  		return true
   174  	}
   175  	return ms[i].Type < ms[j].Type
   176  }
   177  
   178  // Matches checks whether all matchers are fulfilled against the given label set.
   179  func (ms Matchers) Matches(lset model.LabelSet) bool {
   180  	for _, m := range ms {
   181  		if !m.Matches(string(lset[model.LabelName(m.Name)])) {
   182  			return false
   183  		}
   184  	}
   185  	return true
   186  }
   187  
   188  func (ms Matchers) String() string {
   189  	var buf bytes.Buffer
   190  
   191  	buf.WriteByte('{')
   192  	for i, m := range ms {
   193  		if i > 0 {
   194  			buf.WriteByte(',')
   195  		}
   196  		buf.WriteString(m.String())
   197  	}
   198  	buf.WriteByte('}')
   199  
   200  	return buf.String()
   201  }
   202  

View as plain text