github.com/cilium/cilium@v1.16.2/pkg/labels/array.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package labels
     5  
     6  import (
     7  	"sort"
     8  	"strings"
     9  )
    10  
    11  // LabelArray is an array of labels forming a set
    12  type LabelArray []Label
    13  
    14  // Sort is an internal utility to return all LabelArrays in sorted
    15  // order, when the source material may be unsorted.  'ls' is sorted
    16  // in-place, but also returns the sorted array for convenience.
    17  func (ls LabelArray) Sort() LabelArray {
    18  	sort.Slice(ls, func(i, j int) bool {
    19  		return ls[i].Key < ls[j].Key
    20  	})
    21  	return ls
    22  }
    23  
    24  // ParseLabelArray parses a list of labels and returns a LabelArray
    25  func ParseLabelArray(labels ...string) LabelArray {
    26  	array := make(LabelArray, len(labels))
    27  	for i := range labels {
    28  		array[i] = ParseLabel(labels[i])
    29  	}
    30  	return array.Sort()
    31  }
    32  
    33  // ParseSelectLabelArray parses a list of select labels and returns a LabelArray
    34  func ParseSelectLabelArray(labels ...string) LabelArray {
    35  	array := make(LabelArray, len(labels))
    36  	for i := range labels {
    37  		array[i] = ParseSelectLabel(labels[i])
    38  	}
    39  	return array.Sort()
    40  }
    41  
    42  // ParseLabelArrayFromArray converts an array of strings as labels and returns a LabelArray
    43  func ParseLabelArrayFromArray(base []string) LabelArray {
    44  	array := make(LabelArray, len(base))
    45  	for i := range base {
    46  		array[i] = ParseLabel(base[i])
    47  	}
    48  	return array.Sort()
    49  }
    50  
    51  // NewLabelArrayFromSortedList returns labels based on the output of SortedList()
    52  // Trailing ';' will result in an empty key that must be filtered out.
    53  func NewLabelArrayFromSortedList(list string) LabelArray {
    54  	base := strings.Split(list, ";")
    55  	array := make(LabelArray, 0, len(base))
    56  	for _, v := range base {
    57  		if lbl := ParseLabel(v); lbl.Key != "" {
    58  			array = append(array, lbl)
    59  		}
    60  	}
    61  	return array
    62  }
    63  
    64  // ParseSelectLabelArrayFromArray converts an array of strings as select labels and returns a LabelArray
    65  func ParseSelectLabelArrayFromArray(base []string) LabelArray {
    66  	array := make(LabelArray, len(base))
    67  	for i := range base {
    68  		array[i] = ParseSelectLabel(base[i])
    69  	}
    70  	return array.Sort()
    71  }
    72  
    73  // Labels returns the LabelArray as Labels
    74  func (ls LabelArray) Labels() Labels {
    75  	lbls := Labels{}
    76  	for _, l := range ls {
    77  		lbls[l.Key] = l
    78  	}
    79  	return lbls
    80  }
    81  
    82  // Contains returns true if all ls contains all the labels in needed. If
    83  // needed contains no labels, Contains() will always return true
    84  func (ls LabelArray) Contains(needed LabelArray) bool {
    85  nextLabel:
    86  	for i := range needed {
    87  		for l := range ls {
    88  			if ls[l].Has(&needed[i]) {
    89  				continue nextLabel
    90  			}
    91  		}
    92  
    93  		return false
    94  	}
    95  
    96  	return true
    97  }
    98  
    99  // Intersects returns true if ls contains at least one label in needed.
   100  //
   101  // This has the same matching semantics as Has, namely,
   102  // ["k8s:foo=bar"].Intersects(["any:foo=bar"]) == true
   103  // ["any:foo=bar"].Intersects(["k8s:foo=bar"]) == false
   104  func (ls LabelArray) Intersects(needed LabelArray) bool {
   105  	for _, l := range ls {
   106  		for _, n := range needed {
   107  			if l.Has(&n) {
   108  				return true
   109  			}
   110  		}
   111  	}
   112  	return false
   113  }
   114  
   115  // Lacks is identical to Contains but returns all missing labels
   116  func (ls LabelArray) Lacks(needed LabelArray) LabelArray {
   117  	missing := LabelArray{}
   118  nextLabel:
   119  	for i := range needed {
   120  		for l := range ls {
   121  			if ls[l].Has(&needed[l]) {
   122  				continue nextLabel
   123  			}
   124  		}
   125  
   126  		missing = append(missing, needed[i])
   127  	}
   128  
   129  	return missing
   130  }
   131  
   132  // Has returns whether the provided key exists in the label array.
   133  // Implementation of the
   134  // github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels.Labels interface.
   135  //
   136  // The key can be of source "any", in which case the source is
   137  // ignored. The inverse, however, is not true.
   138  // ["k8s.foo=bar"].Has("any.foo") => true
   139  // ["any.foo=bar"].Has("k8s.foo") => false
   140  //
   141  // If the key is of source "cidr", this will also match
   142  // broader keys.
   143  // ["cidr:1.1.1.1/32"].Has("cidr.1.0.0.0/8") => true
   144  // ["cidr:1.0.0.0/8"].Has("cidr.1.1.1.1/32") => false
   145  func (ls LabelArray) Has(key string) bool {
   146  	// The key is submitted in the form of `source.key=value`
   147  	keyLabel := parseSelectLabel(key, '.')
   148  	for _, l := range ls {
   149  		if l.HasKey(&keyLabel) {
   150  			return true
   151  		}
   152  	}
   153  	return false
   154  }
   155  
   156  // Get returns the value for the provided key.
   157  // Implementation of the
   158  // github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels.Labels interface.
   159  //
   160  // The key can be of source "any", in which case the source is
   161  // ignored. The inverse, however, is not true.
   162  // ["k8s.foo=bar"].Get("any.foo") => "bar"
   163  // ["any.foo=bar"].Get("k8s.foo") => ""
   164  //
   165  // If the key is of source "cidr", this will also match
   166  // broader keys.
   167  // ["cidr:1.1.1.1/32"].Has("cidr.1.0.0.0/8") => true
   168  // ["cidr:1.0.0.0/8"].Has("cidr.1.1.1.1/32") => false
   169  func (ls LabelArray) Get(key string) string {
   170  	keyLabel := parseSelectLabel(key, '.')
   171  	for _, l := range ls {
   172  		if l.HasKey(&keyLabel) {
   173  			return l.Value
   174  		}
   175  	}
   176  	return ""
   177  }
   178  
   179  // DeepCopy returns a deep copy of the labels.
   180  func (ls LabelArray) DeepCopy() LabelArray {
   181  	if ls == nil {
   182  		return nil
   183  	}
   184  
   185  	o := make(LabelArray, len(ls))
   186  	copy(o, ls)
   187  	return o
   188  }
   189  
   190  // GetModel returns the LabelArray as a string array with fully-qualified labels.
   191  // The output is parseable by ParseLabelArrayFromArray
   192  func (ls LabelArray) GetModel() []string {
   193  	res := make([]string, 0, len(ls))
   194  	for l := range ls {
   195  		res = append(res, ls[l].String())
   196  	}
   197  	return res
   198  }
   199  
   200  func (ls LabelArray) String() string {
   201  	var sb strings.Builder
   202  	sb.WriteString("[")
   203  	for l := range ls {
   204  		if l > 0 {
   205  			sb.WriteString(" ")
   206  		}
   207  		sb.WriteString(ls[l].String())
   208  	}
   209  	sb.WriteString("]")
   210  	return sb.String()
   211  }
   212  
   213  // StringMap converts LabelArray into map[string]string
   214  // Note: The source is included in the keys with a ':' separator.
   215  // Note: LabelArray does not deduplicate entries, as it is an array. It is
   216  // possible for the output to contain fewer entries when the source and key are
   217  // repeated in a LabelArray, as that is the key of the output. This scenario is
   218  // not expected.
   219  func (ls LabelArray) StringMap() map[string]string {
   220  	o := map[string]string{}
   221  	for _, v := range ls {
   222  		o[v.Source+":"+v.Key] = v.Value
   223  	}
   224  	return o
   225  }
   226  
   227  // Equals returns true if the label arrays are the same, i.e., have the same labels in the same order.
   228  func (ls LabelArray) Equals(b LabelArray) bool {
   229  	if len(ls) != len(b) {
   230  		return false
   231  	}
   232  	for l := range ls {
   233  		if !ls[l].Equals(&b[l]) {
   234  			return false
   235  		}
   236  	}
   237  	return true
   238  }
   239  
   240  // Less returns true if ls comes before b in the lexicographical order.
   241  // Assumes both ls and b are already sorted.
   242  func (ls LabelArray) Less(b LabelArray) bool {
   243  	lsLen, bLen := len(ls), len(b)
   244  
   245  	minLen := lsLen
   246  	if bLen < minLen {
   247  		minLen = bLen
   248  	}
   249  
   250  	for i := 0; i < minLen; i++ {
   251  		switch {
   252  		// Key
   253  		case ls[i].Key < b[i].Key:
   254  			return true
   255  		case ls[i].Key > b[i].Key:
   256  			return false
   257  		// Value
   258  		case ls[i].Value < b[i].Value:
   259  			return true
   260  		case ls[i].Value > b[i].Value:
   261  			return false
   262  		// Source
   263  		case ls[i].Source < b[i].Source:
   264  			return true
   265  		case ls[i].Source > b[i].Source:
   266  			return false
   267  		}
   268  	}
   269  
   270  	return lsLen < bLen
   271  }