netdata/netdata

View on GitHub
src/go/collectors/go.d.plugin/pkg/prometheus/selector/parse.go

Summary

Maintainability
A
0 mins
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package selector

import (
    "fmt"
    "regexp"
    "strings"

    "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher"
)

var (
    reLV = regexp.MustCompile(`^(?P<label_name>[a-zA-Z0-9_]+)(?P<op>=~|!~|=\*|!\*|=|!=)"(?P<pattern>.+)"$`)
)

func Parse(expr string) (Selector, error) {
    var srs []Selector
    lvs := strings.Split(unsugarExpr(expr), ",")

    for _, lv := range lvs {
        sr, err := parseSelector(lv)
        if err != nil {
            return nil, err
        }
        srs = append(srs, sr)
    }

    switch len(srs) {
    case 0:
        return nil, nil
    case 1:
        return srs[0], nil
    default:
        return And(srs[0], srs[1], srs[2:]...), nil
    }
}

func parseSelector(line string) (Selector, error) {
    sub := reLV.FindStringSubmatch(strings.TrimSpace(line))
    if sub == nil {
        return nil, fmt.Errorf("invalid selector syntax: '%s'", line)
    }

    name, op, pattern := sub[1], sub[2], strings.Trim(sub[3], "\"")

    var m matcher.Matcher
    var err error

    switch op {
    case OpEqual, OpNegEqual:
        m, err = matcher.NewStringMatcher(pattern, true, true)
    case OpRegexp, OpNegRegexp:
        m, err = matcher.NewRegExpMatcher(pattern)
    case OpSimplePatterns, OpNegSimplePatterns:
        m, err = matcher.NewSimplePatternsMatcher(pattern)
    default:
        err = fmt.Errorf("unknown matching operator: %s", op)
    }
    if err != nil {
        return nil, err
    }

    sr := labelSelector{
        name: name,
        m:    m,
    }

    if neg := strings.HasPrefix(op, "!"); neg {
        return Not(sr), nil
    }
    return sr, nil
}

func unsugarExpr(expr string) string {
    // name                => __name__=*"name"
    // name{label="value"} => __name__=*"name",label="value"
    // {label="value"}     => label="value"
    expr = strings.TrimSpace(expr)

    switch idx := strings.IndexByte(expr, '{'); true {
    case idx == -1:
        expr = fmt.Sprintf(`__name__%s"%s"`,
            OpSimplePatterns,
            strings.TrimSpace(expr),
        )
    case idx == 0:
        expr = strings.Trim(expr, "{}")
    default:
        expr = fmt.Sprintf(`__name__%s"%s",%s`,
            OpSimplePatterns,
            strings.TrimSpace(expr[:idx]),
            strings.Trim(expr[idx:], "{}"),
        )
    }
    return expr
}