github.com/cilium/cilium@v1.16.2/pkg/policy/api/utils.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package api 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 ) 11 12 // Exists returns true if the HTTP rule already exists in the list of rules 13 func (h *PortRuleHTTP) Exists(rules L7Rules) bool { 14 for _, existingRule := range rules.HTTP { 15 if h.Equal(existingRule) { 16 return true 17 } 18 } 19 20 return false 21 } 22 23 // Equal returns true if both HTTP rules are equal 24 func (h *PortRuleHTTP) Equal(o PortRuleHTTP) bool { 25 if h.Path != o.Path || 26 h.Method != o.Method || 27 h.Host != o.Host || 28 len(h.Headers) != len(o.Headers) || 29 len(h.HeaderMatches) != len(o.HeaderMatches) { 30 return false 31 } 32 33 for i, value := range h.Headers { 34 if o.Headers[i] != value { 35 return false 36 } 37 } 38 39 for i, value := range h.HeaderMatches { 40 if !o.HeaderMatches[i].Equal(value) { 41 return false 42 } 43 } 44 return true 45 } 46 47 // Equal returns true if both Secrets are equal 48 func (a *Secret) Equal(b *Secret) bool { 49 return a == nil && b == nil || a != nil && b != nil && *a == *b 50 } 51 52 // Equal returns true if both HeaderMatches are equal 53 func (h *HeaderMatch) Equal(o *HeaderMatch) bool { 54 if h.Mismatch != o.Mismatch || 55 h.Name != o.Name || 56 h.Value != o.Value || 57 !h.Secret.Equal(o.Secret) { 58 return false 59 } 60 return true 61 } 62 63 // Exists returns true if the DNS rule already exists in the list of rules 64 func (d *PortRuleDNS) Exists(rules L7Rules) bool { 65 for _, existingRule := range rules.DNS { 66 if d.Equal(existingRule) { 67 return true 68 } 69 } 70 71 return false 72 } 73 74 // Exists returns true if the L7 rule already exists in the list of rules 75 func (h *PortRuleL7) Exists(rules L7Rules) bool { 76 for _, existingRule := range rules.L7 { 77 if h.Equal(existingRule) { 78 return true 79 } 80 } 81 82 return false 83 } 84 85 // Equal returns true if both rules are equal 86 func (d *PortRuleDNS) Equal(o PortRuleDNS) bool { 87 return d != nil && d.MatchName == o.MatchName && d.MatchPattern == o.MatchPattern 88 } 89 90 // Equal returns true if both L7 rules are equal 91 func (h *PortRuleL7) Equal(o PortRuleL7) bool { 92 if len(*h) != len(o) { 93 return false 94 } 95 for k, v := range *h { 96 if v2, ok := o[k]; !ok || v2 != v { 97 return false 98 } 99 } 100 return true 101 } 102 103 // Validate returns an error if the layer 4 protocol is not valid 104 func (l4 L4Proto) Validate() error { 105 switch l4 { 106 case ProtoAny, ProtoTCP, ProtoUDP, ProtoSCTP: 107 default: 108 return fmt.Errorf("invalid protocol %q, must be { tcp | udp | sctp | any }", l4) 109 } 110 111 return nil 112 } 113 114 // ParseL4Proto parses a string as layer 4 protocol 115 func ParseL4Proto(proto string) (L4Proto, error) { 116 if proto == "" { 117 return ProtoAny, nil 118 } 119 120 p := L4Proto(strings.ToUpper(proto)) 121 return p, p.Validate() 122 } 123 124 // ResourceQualifiedName returns the qualified name of an Envoy resource, 125 // prepending CEC namespace and CEC name to the resource name and using 126 // '/' as a separator. 127 // 128 // If resourceName already has a slash, it must be of the form 'namespace/name', where namespace 129 // usually is equal to 'namespace'. This also applies for clusterwide resources for which 130 // 'namespace' is empty. 131 // 132 // If 'resourceName' has no slash, it will be prepended with 'namespace/cecName' so that the 133 // full name passed to Envoy is 'namespace/cecName/resourceName'. This makes non-qualified resource 134 // names and resource name references local to the given namespace and CiliumEnvoyConfig CRD. 135 // 136 // if 'forceNamespace' is 'true' then resourceName is always prepended with "namespace/cecName/", 137 // even it it already has backslashes, unless the first component of the name is equal to 138 // 'namespace'. 139 // 140 // As a special case pass through an empty resourceName without qualification so that unnamed 141 // resources do not become named. This is important to not transform an invalid Envoy configuration 142 // to a valid one with a fake name. 143 144 type Option int 145 146 const ( 147 ForceNamespace Option = iota 148 ) 149 150 func ResourceQualifiedName(namespace, cecName, resourceName string, options ...Option) (name string, updated bool) { 151 forceNamespace := false 152 for _, option := range options { 153 switch option { 154 case ForceNamespace: 155 forceNamespace = true 156 } 157 } 158 159 idx := strings.IndexRune(resourceName, '/') 160 if resourceName == "" || idx >= 0 && (!forceNamespace || (idx == len(namespace) && strings.HasPrefix(resourceName, namespace))) { 161 return resourceName, false 162 } 163 164 var sb strings.Builder 165 166 sb.WriteString(namespace) 167 sb.WriteRune('/') 168 sb.WriteString(cecName) 169 sb.WriteRune('/') 170 sb.WriteString(resourceName) 171 172 return sb.String(), true 173 } 174 175 // ParseQualifiedName returns the namespace, name, and the resource name of a name qualified with ResourceQualifiedName() 176 func ParseQualifiedName(qualifiedName string) (namespace, name, resourceName string) { 177 parts := strings.SplitN(qualifiedName, "/", 3) 178 if len(parts) < 3 { 179 return "", "", qualifiedName 180 } 181 return parts[0], parts[1], parts[2] 182 } 183 184 // ExtractCidrSet abstracts away some of the logic from the CreateDerivative methods 185 func ExtractCidrSet(ctx context.Context, groups []Groups) ([]CIDRRule, error) { 186 var cidrSet []CIDRRule 187 for _, group := range groups { 188 c, err := group.GetCidrSet(ctx) 189 if err != nil { 190 return cidrSet, err 191 } 192 if len(c) > 0 { 193 cidrSet = append(cidrSet, c...) 194 } 195 } 196 return cidrSet, nil 197 }