github.com/cilium/cilium@v1.16.2/pkg/hubble/filters/labelparser.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  	"io"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  // translateSelector takes a label selector with colon-based source prefixes
    15  // and translates it to a valid k8s label selector with dot-based prefixes,
    16  // i.e. "k8s:foo in (bar, baz)" becomes "k8s.foo in (bar, baz)".
    17  // It also makes sure that bare keys without an explicit source will get an
    18  // `any` source prefix.
    19  func translateSelector(selector string) (string, error) {
    20  	out := &strings.Builder{}
    21  	in := strings.NewReader(selector)
    22  
    23  	for in.Len() > 0 {
    24  		err := advanceToNextKey(in, out)
    25  		if err != nil {
    26  			return "", err
    27  		}
    28  		err = translateKey(in, out)
    29  		if err != nil {
    30  			return "", err
    31  		}
    32  		err = advanceToNextSelector(in, out)
    33  		if err != nil {
    34  			return "", err
    35  		}
    36  	}
    37  	return out.String(), nil
    38  }
    39  
    40  // advanceToNextKey scans from the beginning of a selector to the next
    41  // key and writes everything before the start of the key from in to out.
    42  func advanceToNextKey(in *strings.Reader, out *strings.Builder) error {
    43  	for {
    44  		r, _, err := in.ReadRune()
    45  		if errors.Is(err, io.EOF) {
    46  			return nil
    47  		}
    48  		if err != nil {
    49  			return err
    50  		}
    51  		if !unicode.IsSpace(r) && r != '!' {
    52  			return in.UnreadRune()
    53  		}
    54  		out.WriteRune(r)
    55  	}
    56  }
    57  
    58  // translateKey takes a reader that point to the start of a key. It reads
    59  // until the end of the key and writes the translated key (with dot prefixes
    60  // instead of colon-based source prefixes) to out
    61  func translateKey(in *strings.Reader, out *strings.Builder) error {
    62  	key := &strings.Builder{}
    63  	defer func() {
    64  		ckey := key.String()
    65  		if !strings.Contains(ckey, ":") {
    66  			ckey = fmt.Sprintf("any:%s", ckey)
    67  		}
    68  		ckey = strings.Replace(ckey, ":", ".", 1)
    69  		out.WriteString(ckey)
    70  	}()
    71  	for {
    72  		r, _, err := in.ReadRune()
    73  		if errors.Is(err, io.EOF) {
    74  			return nil
    75  		}
    76  		if err != nil {
    77  			return err
    78  		}
    79  		if unicode.IsSpace(r) || r == '=' || r == '!' || r == ',' {
    80  			return in.UnreadRune()
    81  		}
    82  		key.WriteRune(r)
    83  	}
    84  }
    85  
    86  // advanceToNextSelector takes a read that points to the end of a key and will
    87  // advance the reader to the beginning of the next selector and writes everything
    88  // it scans to out.
    89  func advanceToNextSelector(in *strings.Reader, out *strings.Builder) error {
    90  	nesting := 0
    91  	for {
    92  		r, _, err := in.ReadRune()
    93  		if errors.Is(err, io.EOF) {
    94  			return nil
    95  		}
    96  		if err != nil {
    97  			return err
    98  		}
    99  		switch r {
   100  		case '(':
   101  			nesting++
   102  		case ')':
   103  			nesting--
   104  		}
   105  		out.WriteRune(r)
   106  		if r == ',' && nesting == 0 {
   107  			return nil
   108  		}
   109  	}
   110  }