mirror of
https://github.com/prometheus/alertmanager
synced 2025-01-01 02:52:06 +00:00
e87985a9a8
The doc comments do not describe the current (arguably buggy) state, but the desired state, as it will be implemented in future commits. Signed-off-by: beorn7 <beorn@grafana.com>
129 lines
4.0 KiB
Go
129 lines
4.0 KiB
Go
// Copyright 2018 Prometheus Team
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package labels
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
re = regexp.MustCompile(`(?:\s?)(\w+)(=|=~|!=|!~)(?:\"([^"=~!]+)\"|([^"=~!]+)|\"\")`)
|
|
typeMap = map[string]MatchType{
|
|
"=": MatchEqual,
|
|
"!=": MatchNotEqual,
|
|
"=~": MatchRegexp,
|
|
"!~": MatchNotRegexp,
|
|
}
|
|
)
|
|
|
|
// ParseMatchers parses a comma-separated list of Matchers. A leading '{' and/or
|
|
// a trailing '}' is optional and will be trimmed before further
|
|
// parsing. Individual Matchers are separated by commas outside of quoted parts
|
|
// of the input string. Those commas may be followed by whitespace. Parts of the
|
|
// string inside unescaped double quotes ('"…"') are considered quoted (and
|
|
// commas don't act as separators there). If double quotes are escaped with a
|
|
// single backslash ('\"'), they are ignored for the purpose of identifying
|
|
// quoted parts of the input string. If the input string, after trimming the
|
|
// optional trailing '}', ends with a comma, followed by optional whitespace,
|
|
// this comma and whitespace will be trimmed.
|
|
//
|
|
// Examples for valid input strings:
|
|
// {foo = "bar", dings != "bums", }
|
|
// foo=bar,dings!=bums
|
|
// {quote="She said: \"Hi, ladies! That's gender-neutral…\""}
|
|
// statuscode=~"5.."
|
|
//
|
|
// See ParseMatcher for details how an individual Matcher is parsed.
|
|
func ParseMatchers(s string) ([]*Matcher, error) {
|
|
matchers := []*Matcher{}
|
|
s = strings.TrimPrefix(s, "{")
|
|
s = strings.TrimSuffix(s, "}")
|
|
|
|
var insideQuotes bool
|
|
var token string
|
|
var tokens []string
|
|
for _, r := range s {
|
|
if !insideQuotes && r == ',' {
|
|
tokens = append(tokens, token)
|
|
token = ""
|
|
continue
|
|
}
|
|
token += string(r)
|
|
if r == '"' {
|
|
insideQuotes = !insideQuotes
|
|
}
|
|
}
|
|
if token != "" {
|
|
tokens = append(tokens, token)
|
|
}
|
|
for _, token := range tokens {
|
|
m, err := ParseMatcher(token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
matchers = append(matchers, m)
|
|
}
|
|
|
|
return matchers, nil
|
|
}
|
|
|
|
// ParseMatcher parses a matcher with a syntax inspired by PromQL and
|
|
// OpenMetrics. This syntax is convenient to describe filters and selectors in
|
|
// UIs and config files. To support the interactive nature of the use cases, the
|
|
// parser is in various aspects fairly tolerant.
|
|
//
|
|
// The syntax of a matcher consists of three tokens: (1) A valid Prometheus
|
|
// label name. (2) One of '=', '!=', '=~', or '!~', with the same meaning as
|
|
// known from PromQL selectors. (3) A UTF-8 string, which may be enclosed in
|
|
// double quotes. Before or after each token, there may be any amount of
|
|
// whitespace, which will be discarded. The 3rd token may be the empty
|
|
// string. Within the 3rd token, OpenMetrics escaping rules apply: '\"' for a
|
|
// double-quote, '\n' for a line feed, '\\' for a literal backslash. Unescaped
|
|
// '"' must not occur inside the 3rd token (only as the 1st or last
|
|
// character). However, literal line feed characters are tolerated, as are
|
|
// single '\' characters not followed by '\', 'n', or '"'. They act as a literal
|
|
// backslash in that case.
|
|
func ParseMatcher(s string) (*Matcher, error) {
|
|
var (
|
|
name, value string
|
|
matchType MatchType
|
|
)
|
|
|
|
ms := re.FindStringSubmatch(s)
|
|
if len(ms) < 4 {
|
|
return nil, errors.Errorf("bad matcher format: %s", s)
|
|
}
|
|
|
|
name = ms[1]
|
|
if name == "" {
|
|
return nil, errors.New("failed to parse label name")
|
|
}
|
|
|
|
matchType, found := typeMap[ms[2]]
|
|
if !found {
|
|
return nil, errors.New("failed to find match operator")
|
|
}
|
|
|
|
if ms[3] != "" {
|
|
value = ms[3]
|
|
} else {
|
|
value = ms[4]
|
|
}
|
|
|
|
return NewMatcher(matchType, name, value)
|
|
}
|