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 }