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  }