github.com/cilium/cilium@v1.16.2/pkg/hubble/filters/patterns.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package filters
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"regexp"
    10  	"strings"
    11  )
    12  
    13  var (
    14  	// fqdnRegexpStr matches an FQDN, inluding underscores.
    15  	// FIXME this should not match components that begin or end with hyphens, e.g. -foo-
    16  	fqdnRegexpStr = `(?:[-0-9_a-z]+(?:\.[-0-9_a-z]+)*)`
    17  	_             = regexp.MustCompile(fqdnRegexpStr) // compile regexp to ensure that it is valid
    18  
    19  	errEmptyPattern                  = errors.New("empty pattern")
    20  	errMultipleTrailingDotsInPattern = errors.New("multiple trailing dots in pattern")
    21  	errTooManySlashesInPattern       = errors.New("too many slashes in pattern")
    22  )
    23  
    24  // canonicalizeFQDNPattern canonicalizes fqdnPattern by trimming space, trimming
    25  // up to one trailing dot, and converting it to lowercase.
    26  func canonicalizeFQDNPattern(fqdnPattern string) string {
    27  	fqdnPattern = strings.TrimSpace(fqdnPattern)
    28  	fqdnPattern = strings.TrimSuffix(fqdnPattern, ".")
    29  	fqdnPattern = strings.ToLower(fqdnPattern)
    30  	return fqdnPattern
    31  }
    32  
    33  // appendFQDNPatternRegexp appends the regular expression equivalent to
    34  // fqdnPattern to sb.
    35  func appendFQDNPatternRegexp(sb *strings.Builder, fqdnPattern string) error {
    36  	fqdnPattern = canonicalizeFQDNPattern(fqdnPattern)
    37  	switch {
    38  	case fqdnPattern == "":
    39  		return errEmptyPattern
    40  	case strings.HasSuffix(fqdnPattern, "."):
    41  		return errMultipleTrailingDotsInPattern
    42  	}
    43  	for _, r := range fqdnPattern {
    44  		switch {
    45  		case r == '.':
    46  			sb.WriteString(`\.`)
    47  		case r == '*':
    48  			sb.WriteString(`[-.0-9a-z]*`)
    49  		case r == '-':
    50  			fallthrough
    51  		case '0' <= r && r <= '9':
    52  			fallthrough
    53  		case r == '_':
    54  			fallthrough
    55  		case 'a' <= r && r <= 'z':
    56  			sb.WriteRune(r)
    57  		default:
    58  			return fmt.Errorf("%q: invalid rune in pattern", r)
    59  		}
    60  	}
    61  	return nil
    62  }
    63  
    64  // appendNodeNamePatternRegexp appends the regular expression equivalent to
    65  // nodeNamePattern to sb. The returned regular expression matches node names
    66  // that include a cluster name.
    67  //
    68  // Node name patterns consist of a cluster pattern element and a node pattern
    69  // element separated by a forward slash. Each element is an FQDN pattern, with
    70  // an empty pattern matching everything. If there is no forward slash then the
    71  // pattern is treated as a node pattern and matches all clusters.
    72  func appendNodeNamePatternRegexp(sb *strings.Builder, nodeNamePattern string) error {
    73  	if nodeNamePattern == "" {
    74  		return errEmptyPattern
    75  	}
    76  	clusterPattern := ""
    77  	nodePattern := ""
    78  	elems := strings.Split(nodeNamePattern, "/")
    79  	switch len(elems) {
    80  	case 1:
    81  		nodePattern = elems[0]
    82  	case 2:
    83  		clusterPattern = elems[0]
    84  		nodePattern = elems[1]
    85  	default:
    86  		return errTooManySlashesInPattern
    87  	}
    88  
    89  	if clusterPattern == "" {
    90  		sb.WriteString(fqdnRegexpStr)
    91  	} else if err := appendFQDNPatternRegexp(sb, clusterPattern); err != nil {
    92  		return err
    93  	}
    94  	sb.WriteByte('/')
    95  	if nodePattern == "" {
    96  		sb.WriteString(fqdnRegexpStr)
    97  	} else if err := appendFQDNPatternRegexp(sb, nodePattern); err != nil {
    98  		return err
    99  	}
   100  	return nil
   101  }
   102  
   103  // compileFQDNPattern returns a regular expression equivalent to the FQDN
   104  // patterns in fqdnPatterns.
   105  func compileFQDNPattern(fqdnPatterns []string) (*regexp.Regexp, error) {
   106  	var sb strings.Builder
   107  	sb.WriteString(`\A(?:`)
   108  	for i, fqdnPattern := range fqdnPatterns {
   109  		if i > 0 {
   110  			sb.WriteByte('|')
   111  		}
   112  		if err := appendFQDNPatternRegexp(&sb, fqdnPattern); err != nil {
   113  			return nil, err
   114  		}
   115  	}
   116  	sb.WriteString(`)\z`)
   117  	return regexp.Compile(sb.String())
   118  }
   119  
   120  // compileNodeNamePattern returns a regular expression equivalent to the node
   121  // name patterns in nodeNamePatterns.
   122  func compileNodeNamePattern(nodeNamePatterns []string) (*regexp.Regexp, error) {
   123  	sb := strings.Builder{}
   124  	sb.WriteString(`\A(?:`)
   125  	for i, nodeNamePattern := range nodeNamePatterns {
   126  		if i > 0 {
   127  			sb.WriteByte('|')
   128  		}
   129  		if err := appendNodeNamePatternRegexp(&sb, nodeNamePattern); err != nil {
   130  			return nil, err
   131  		}
   132  	}
   133  	sb.WriteString(`)\z`)
   134  	return regexp.Compile(sb.String())
   135  }