github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/label/selector.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package label 15 16 import ( 17 "regexp" 18 19 "github.com/pingcap/errors" 20 "github.com/pingcap/log" 21 "go.uber.org/atomic" 22 "go.uber.org/zap" 23 ) 24 25 // Op represents the type of operator associated with a selector. 26 type Op string 27 28 const ( 29 // OpEq represents the Equals operator. 30 OpEq = Op("eq") 31 32 // OpNeq represents the Not Equal operator. 33 OpNeq = Op("neq") 34 35 // OpRegex represents the Regular Expression match operator. 36 OpRegex = Op("regex") 37 ) 38 39 // Selector is one selector on a label Set. 40 type Selector struct { 41 // Key is the key of the label that could potentially match the selector. 42 Key Key `json:"label"` 43 // Target is the argument to the operator. In the case of the 44 // Eq operator, it is the exact string that needs to match the value. 45 Target string `json:"target"` 46 // Op is the operator. 47 Op Op `json:"op"` 48 49 // regex stores a compiled Regular Expression. 50 // It is not nil only if Op == OpRegex. 51 regex atomic.Value // *regexp.Regexp 52 } 53 54 // Validate returns whether the selector is valid. 55 // Calling Matches on invalid selectors results in undefined behavior. 56 func (s *Selector) Validate() error { 57 if err := checkLabelStrValid(string(s.Key)); err != nil { 58 return errors.Annotate(err, "validate selector key") 59 } 60 61 if s.Op != OpEq && s.Op != OpNeq && s.Op != OpRegex { 62 return errors.Errorf("invalid selector op: key: %s, op: %s", string(s.Key), string(s.Op)) 63 } 64 65 if s.Op == OpRegex { 66 _, err := s.getRegex() 67 if err != nil { 68 return errors.Annotatef(err, "validating selector key: %s", string(s.Key)) 69 } 70 } 71 return nil 72 } 73 74 // Matches returns whether the given selector matches 75 // the labelSet. 76 // Calling Matches on invalid selectors results in undefined behavior. 77 // Refer to Validate(). 78 func (s *Selector) Matches(labelSet Set) bool { 79 value, exists := labelSet.Get(s.Key) 80 switch s.Op { 81 case OpEq: 82 if !exists { 83 // OpEq should fail if the specified key 84 // does not exist. 85 return false 86 } 87 return s.Target == string(value) 88 case OpNeq: 89 if !exists { 90 // OpNeq succeeds when the specified key 91 // does not exist. 92 return true 93 } 94 return s.Target != string(value) 95 case OpRegex: 96 if !exists { 97 // OpRegex should fail if the specified key 98 // does not exist. 99 return false 100 } 101 regex, err := s.getRegex() 102 if err != nil { 103 log.Warn("illegal regular expression", 104 zap.Any("selector", s)) 105 106 // We only make the selector not match, 107 // because it only complicates things to force the caller to 108 // handle such errors that are preventable with a pre-check. 109 return false 110 } 111 112 return regex.MatchString(string(value)) 113 default: 114 } 115 116 panic("unreachable") 117 } 118 119 func (s *Selector) getRegex() (*regexp.Regexp, error) { 120 if s.Op != OpRegex { 121 panic("unreachable") 122 } 123 124 if s.regex.Load() == nil { 125 regex, err := regexp.Compile(s.Target) 126 if err != nil { 127 // Handles error in case that the regular expression 128 // is not legal. 129 return nil, errors.Trace(err) 130 } 131 s.regex.CompareAndSwap(nil, regex) 132 } 133 134 return s.regex.Load().(*regexp.Regexp), nil 135 }