github.phpd.cn/cilium/cilium@v1.6.12/pkg/labels/labels.go (about) 1 // Copyright 2016-2019 Authors of Cilium 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package labels 16 17 import ( 18 "bytes" 19 "crypto/sha512" 20 "encoding/json" 21 "fmt" 22 "net" 23 "sort" 24 "strings" 25 ) 26 27 const ( 28 // PathDelimiter is the delimiter used in the labels paths. 29 PathDelimiter = "." 30 31 // IDNameHost is the label used for the hostname ID. 32 IDNameHost = "host" 33 34 // IDNameWorld is the label used for the world ID. 35 IDNameWorld = "world" 36 37 // IDNameCluster is the label used to identify an unspecified endpoint 38 // inside the cluster 39 IDNameCluster = "cluster" 40 41 // IDNameHealth is the label used for the local cilium-health endpoint 42 IDNameHealth = "health" 43 44 // IDNameInit is the label used to identify any endpoint that has not 45 // received any labels yet. 46 IDNameInit = "init" 47 48 // IDNameNone is the label used to identify no endpoint or other L3 entity. 49 // It will never be assigned and this "label" is here for consistency with 50 // other Entities. 51 IDNameNone = "none" 52 53 // IDNameUnmanaged is the label used to identify unmanaged endpoints 54 IDNameUnmanaged = "unmanaged" 55 56 // IDNameUnknown is the label used to to identify an endpoint with an 57 // unknown identity. 58 IDNameUnknown = "unknown" 59 ) 60 61 var ( 62 // LabelHealth is the label used for health. 63 LabelHealth = Labels{IDNameHealth: NewLabel(IDNameHealth, "", LabelSourceReserved)} 64 ) 65 66 const ( 67 // LabelSourceUnspec is a label with unspecified source 68 LabelSourceUnspec = "unspec" 69 70 // LabelSourceAny is a label that matches any source 71 LabelSourceAny = "any" 72 73 // LabelSourceAnyKeyPrefix is prefix of a "any" label 74 LabelSourceAnyKeyPrefix = LabelSourceAny + "." 75 76 // LabelSourceK8s is a label imported from Kubernetes 77 LabelSourceK8s = "k8s" 78 79 // LabelSourceMesos is a label imported from Mesos 80 LabelSourceMesos = "mesos" 81 82 // LabelSourceK8sKeyPrefix is prefix of a Kubernetes label 83 LabelSourceK8sKeyPrefix = LabelSourceK8s + "." 84 85 // LabelSourceContainer is a label imported from the container runtime 86 LabelSourceContainer = "container" 87 88 // LabelSourceReserved is the label source for reserved types. 89 LabelSourceReserved = "reserved" 90 91 // LabelSourceCIDR is the label source for generated CIDRs. 92 LabelSourceCIDR = "cidr" 93 94 // LabelSourceReservedKeyPrefix is the prefix of a reserved label 95 LabelSourceReservedKeyPrefix = LabelSourceReserved + "." 96 97 // LabelKeyFixedIdentity is the label that can be used to define a fixed 98 // identity. 99 LabelKeyFixedIdentity = "io.cilium.fixed-identity" 100 101 // LabelSourceCiliumGenerated is for labels auto-generated by cilium without 102 // user input 103 LabelSourceCiliumGenerated = "cilium-generated" 104 ) 105 106 // Label is the cilium's representation of a container label. 107 type Label struct { 108 Key string `json:"key"` 109 Value string `json:"value,omitempty"` 110 // Source can be one of the values present in const.go (e.g.: LabelSourceContainer) 111 Source string `json:"source"` 112 } 113 114 // Labels is a map of labels where the map's key is the same as the label's key. 115 type Labels map[string]Label 116 117 // GetPrintableModel turns the Labels into a sorted list of strings 118 // representing the labels, with CIDRs deduplicated (ie, only provide the most 119 // specific CIDR). 120 func (l Labels) GetPrintableModel() (res []string) { 121 cidr := "" 122 prefixLength := 0 123 for _, v := range l { 124 if v.Source == LabelSourceCIDR { 125 vStr := strings.Replace(v.String(), "-", ":", -1) 126 prefix := strings.Replace(v.Key, "-", ":", -1) 127 _, ipnet, _ := net.ParseCIDR(prefix) 128 ones, _ := ipnet.Mask.Size() 129 if ones > prefixLength { 130 cidr = vStr 131 prefixLength = ones 132 } 133 continue 134 } 135 res = append(res, v.String()) 136 } 137 if cidr != "" { 138 res = append(res, cidr) 139 } 140 141 sort.Strings(res) 142 return res 143 } 144 145 // String returns the map of labels as human readable string 146 func (l Labels) String() string { 147 return strings.Join(l.GetPrintableModel(), ",") 148 } 149 150 // AppendPrefixInKey appends the given prefix to all the Key's of the map and the 151 // respective Labels' Key. 152 func (l Labels) AppendPrefixInKey(prefix string) Labels { 153 newLabels := Labels{} 154 for k, v := range l { 155 newLabels[prefix+k] = Label{ 156 Key: prefix + v.Key, 157 Value: v.Value, 158 Source: v.Source, 159 } 160 } 161 return newLabels 162 } 163 164 // Equals returns true if the two Labels contain the same set of labels. 165 func (l Labels) Equals(other Labels) bool { 166 if len(l) != len(other) { 167 return false 168 } 169 170 for k, lbl1 := range l { 171 if lbl2, ok := other[k]; ok { 172 if lbl1.Source == lbl2.Source && lbl1.Key == lbl2.Key && lbl1.Value == lbl2.Value { 173 continue 174 } 175 } 176 return false 177 } 178 return true 179 } 180 181 // GetFromSource returns all labels that are from the given source. 182 func (l Labels) GetFromSource(source string) Labels { 183 lbls := Labels{} 184 for k, v := range l { 185 if v.Source == source { 186 lbls[k] = v 187 } 188 } 189 return lbls 190 } 191 192 // NewLabel returns a new label from the given key, value and source. If source is empty, 193 // the default value will be LabelSourceUnspec. If key starts with '$', the source 194 // will be overwritten with LabelSourceReserved. If key contains ':', the value 195 // before ':' will be used as source if given source is empty, otherwise the value before 196 // ':' will be deleted and unused. 197 func NewLabel(key string, value string, source string) Label { 198 var src string 199 src, key = parseSource(key, ':') 200 if source == "" { 201 if src == "" { 202 source = LabelSourceUnspec 203 } else { 204 source = src 205 } 206 } 207 if src == LabelSourceReserved && key == "" { 208 key = value 209 value = "" 210 } 211 212 return Label{ 213 Key: key, 214 Value: value, 215 Source: source, 216 } 217 } 218 219 // Equals returns true if source, Key and Value are equal and false otherwise. 220 func (l *Label) Equals(b *Label) bool { 221 if !l.IsAnySource() && l.Source != b.Source { 222 return false 223 } 224 return l.Key == b.Key && l.Value == b.Value 225 } 226 227 // IsAnySource return if the label was set with source "any". 228 func (l *Label) IsAnySource() bool { 229 return l.Source == LabelSourceAny 230 } 231 232 // IsReservedSource return if the label was set with source "Reserved". 233 func (l *Label) IsReservedSource() bool { 234 return l.Source == LabelSourceReserved 235 } 236 237 // matches returns true if l matches the target 238 func (l *Label) matches(target *Label) bool { 239 return l.Equals(target) 240 } 241 242 // String returns the string representation of Label in the for of Source:Key=Value or 243 // Source:Key if Value is empty. 244 func (l *Label) String() string { 245 if len(l.Value) != 0 { 246 return fmt.Sprintf("%s:%s=%s", l.Source, l.Key, l.Value) 247 } 248 return fmt.Sprintf("%s:%s", l.Source, l.Key) 249 } 250 251 // IsValid returns true if Key != "". 252 func (l *Label) IsValid() bool { 253 return l.Key != "" 254 } 255 256 // UnmarshalJSON TODO create better explanation about unmarshall with examples 257 func (l *Label) UnmarshalJSON(data []byte) error { 258 decoder := json.NewDecoder(bytes.NewReader(data)) 259 260 if l == nil { 261 return fmt.Errorf("cannot unmarhshal to nil pointer") 262 } 263 264 if len(data) == 0 { 265 return fmt.Errorf("invalid Label: empty data") 266 } 267 268 var aux struct { 269 Source string `json:"source"` 270 Key string `json:"key"` 271 Value string `json:"value,omitempty"` 272 } 273 274 err := decoder.Decode(&aux) 275 if err != nil { 276 // If parsing of the full representation failed then try the short 277 // form in the format: 278 // 279 // [SOURCE:]KEY[=VALUE] 280 var aux string 281 282 decoder = json.NewDecoder(bytes.NewReader(data)) 283 if err := decoder.Decode(&aux); err != nil { 284 return fmt.Errorf("decode of Label as string failed: %+v", err) 285 } 286 287 if aux == "" { 288 return fmt.Errorf("invalid Label: Failed to parse %s as a string", data) 289 } 290 291 *l = ParseLabel(aux) 292 } else { 293 if aux.Key == "" { 294 return fmt.Errorf("invalid Label: '%s' does not contain label key", data) 295 } 296 297 l.Source = aux.Source 298 l.Key = aux.Key 299 l.Value = aux.Value 300 } 301 302 return nil 303 } 304 305 // GetExtendedKey returns the key of a label with the source encoded. 306 func (l *Label) GetExtendedKey() string { 307 return l.Source + PathDelimiter + l.Key 308 } 309 310 // GetCiliumKeyFrom returns the label's source and key from the an extended key 311 // in the format SOURCE:KEY. 312 func GetCiliumKeyFrom(extKey string) string { 313 i := strings.IndexByte(extKey, PathDelimiter[0]) 314 if i >= 0 { 315 return extKey[:i] + ":" + extKey[i+1:] 316 } 317 return LabelSourceAny + ":" + extKey 318 } 319 320 // GetExtendedKeyFrom returns the extended key of a label string. 321 // For example: 322 // `k8s:foo=bar` returns `k8s.foo` 323 // `container:foo=bar` returns `container.foo` 324 // `foo=bar` returns `any.foo=bar` 325 func GetExtendedKeyFrom(str string) string { 326 src, next := parseSource(str, ':') 327 if src == "" { 328 src = LabelSourceAny 329 } 330 // Remove an eventually value 331 i := strings.IndexByte(next, '=') 332 if i >= 0 { 333 return src + PathDelimiter + next[:i] 334 } 335 return src + PathDelimiter + next 336 } 337 338 // Map2Labels transforms in the form: map[key(string)]value(string) into Labels. The 339 // source argument will overwrite the source written in the key of the given map. 340 // Example: 341 // l := Map2Labels(map[string]string{"k8s:foo": "bar"}, "cilium") 342 // fmt.Printf("%+v\n", l) 343 // map[string]Label{"foo":Label{Key:"foo", Value:"bar", Source:"cilium"}} 344 func Map2Labels(m map[string]string, source string) Labels { 345 o := Labels{} 346 for k, v := range m { 347 l := NewLabel(k, v, source) 348 o[l.Key] = l 349 } 350 return o 351 } 352 353 // StringMap converts Labels into map[string]string 354 func (l Labels) StringMap() map[string]string { 355 o := map[string]string{} 356 for _, v := range l { 357 o[v.Source+":"+v.Key] = v.Value 358 } 359 return o 360 } 361 362 // NewLabelsFromModel creates labels from string array. 363 func NewLabelsFromModel(base []string) Labels { 364 lbls := make(Labels, len(base)) 365 for _, v := range base { 366 if lbl := ParseLabel(v); lbl.Key != "" { 367 lbls[lbl.Key] = lbl 368 } 369 } 370 371 return lbls 372 } 373 374 // NewLabelsFromSortedList returns labels based on the output of SortedList() 375 func NewLabelsFromSortedList(list string) Labels { 376 return NewLabelsFromModel(strings.Split(list, ";")) 377 } 378 379 // NewSelectLabelArrayFromModel parses a slice of strings and converts them 380 // into an array of selecting labels, sorted by the key. 381 func NewSelectLabelArrayFromModel(base []string) LabelArray { 382 lbls := make(LabelArray, 0, len(base)) 383 for i := range base { 384 lbls = append(lbls, ParseSelectLabel(base[i])) 385 } 386 387 return lbls.Sort() 388 } 389 390 // GetModel returns model with all the values of the labels. 391 func (l Labels) GetModel() []string { 392 res := make([]string, 0, len(l)) 393 for _, v := range l { 394 res = append(res, v.String()) 395 } 396 return res 397 } 398 399 // MergeLabels merges labels from into to. It overwrites all labels with the same Key as 400 // from written into to. 401 // Example: 402 // to := Labels{Label{key1, value1, source1}, Label{key2, value3, source4}} 403 // from := Labels{Label{key1, value3, source4}} 404 // to.MergeLabels(from) 405 // fmt.Printf("%+v\n", to) 406 // Labels{Label{key1, value3, source4}, Label{key2, value3, source4}} 407 func (l Labels) MergeLabels(from Labels) { 408 for k, v := range from { 409 l[k] = v 410 } 411 } 412 413 // SHA256Sum calculates l' internal SHA256Sum. For a particular set of labels is 414 // guarantee that it will always have the same SHA256Sum. 415 func (l Labels) SHA256Sum() string { 416 return fmt.Sprintf("%x", sha512.Sum512_256(l.SortedList())) 417 } 418 419 // FormatForKVStore returns the label as a formatted string, ending in 420 // a semicolon 421 // 422 // DO NOT BREAK THE FORMAT OF THIS. THE RETURNED STRING IS USED AS 423 // PART OF THE KEY IN THE KEY-VALUE STORE. 424 // 425 // Non-pointer receiver allows this to be called on a value in a map. 426 func (l Label) FormatForKVStore() string { 427 // We don't care if the values already have a '=' since this method is 428 // only used to calculate a SHA256Sum 429 // 430 // We absolutely care that the final character is a semi-colon. 431 // Identity allocation in the kvstore depends on this (see 432 // kvstore.prefixMatchesKey()) 433 return fmt.Sprintf(`%s:%s=%s;`, l.Source, l.Key, l.Value) 434 } 435 436 // SortedList returns the labels as a sorted list, separated by semicolon 437 // 438 // DO NOT BREAK THE FORMAT OF THIS. THE RETURNED STRING IS USED AS KEY IN 439 // THE KEY-VALUE STORE. 440 func (l Labels) SortedList() []byte { 441 var keys []string 442 for k := range l { 443 keys = append(keys, k) 444 } 445 sort.Strings(keys) 446 447 result := "" 448 for _, k := range keys { 449 result += l[k].FormatForKVStore() 450 } 451 452 return []byte(result) 453 } 454 455 // ToSlice returns a slice of label with the values of the given 456 // Labels' map, sorted by the key. 457 func (l Labels) ToSlice() []Label { 458 return l.LabelArray() 459 } 460 461 // LabelArray returns the labels as label array, sorted by the key. 462 func (l Labels) LabelArray() LabelArray { 463 labels := make(LabelArray, 0, len(l)) 464 for _, v := range l { 465 labels = append(labels, v) 466 } 467 return labels.Sort() 468 } 469 470 // FindReserved locates all labels with reserved source in the labels and 471 // returns a copy of them. If there are no reserved labels, returns nil. 472 // TODO: return LabelArray as it is likely faster 473 func (l Labels) FindReserved() Labels { 474 lbls := Labels{} 475 476 for k, lbl := range l { 477 if lbl.Source == LabelSourceReserved { 478 lbls[k] = lbl 479 } 480 } 481 482 if len(lbls) > 0 { 483 return lbls 484 } 485 return nil 486 } 487 488 // IsReserved returns true if any of the labels has a reserved source. 489 func (l Labels) IsReserved() bool { 490 for _, lbl := range l { 491 if lbl.Source == LabelSourceReserved { 492 return true 493 } 494 } 495 return false 496 } 497 498 // parseSource returns the parsed source of the given str. It also returns the next piece 499 // of text that is after the source. 500 // Example: 501 // src, next := parseSource("foo:bar==value") 502 // Println(src) // foo 503 // Println(next) // bar==value 504 // For Cilium format 'delim' must be passed in as ':' 505 // For k8s format 'delim' must be passed in as '.' 506 func parseSource(str string, delim byte) (src, next string) { 507 if str == "" { 508 return "", "" 509 } 510 if str[0] == '$' { 511 return LabelSourceReserved, str[1:] 512 } 513 i := strings.IndexByte(str, delim) 514 if i < 0 { 515 if delim != '.' && strings.HasPrefix(str, LabelSourceReservedKeyPrefix) { 516 return LabelSourceReserved, strings.TrimPrefix(str, LabelSourceReservedKeyPrefix) 517 } 518 return "", str 519 } 520 return str[:i], str[i+1:] 521 } 522 523 // ParseLabel returns the label representation of the given string. The str should be 524 // in the form of Source:Key=Value or Source:Key if Value is empty. It also parses short 525 // forms, for example: $host will be Label{Key: "host", Source: "reserved", Value: ""}. 526 func ParseLabel(str string) Label { 527 return parseLabel(str, ':') 528 } 529 530 // parseLabel returns the label representation of the given string by value. 531 // For Cilium format 'delim' must be passed in as ':' 532 // For k8s format 'delim' must be passed in as '.' 533 func parseLabel(str string, delim byte) (lbl Label) { 534 src, next := parseSource(str, delim) 535 if src != "" { 536 lbl.Source = src 537 } else { 538 lbl.Source = LabelSourceUnspec 539 } 540 541 i := strings.IndexByte(next, '=') 542 if i < 0 { 543 lbl.Key = next 544 } else { 545 if i == 0 && src == LabelSourceReserved { 546 lbl.Key = next[i+1:] 547 } else { 548 lbl.Key = next[:i] 549 lbl.Value = next[i+1:] 550 } 551 } 552 return lbl 553 } 554 555 // ParseSelectLabel returns a selecting label representation of the given 556 // string. Unlike ParseLabel, if source is unspecified, the source defaults to 557 // LabelSourceAny 558 func ParseSelectLabel(str string) Label { 559 return parseSelectLabel(str, ':') 560 } 561 562 // parseSelectLabel returns a selecting label representation of the given 563 // string by value. 564 // For Cilium format 'delim' must be passed in as ':' 565 // For k8s format 'delim' must be passed in as '.' 566 func parseSelectLabel(str string, delim byte) Label { 567 lbl := parseLabel(str, delim) 568 569 if lbl.Source == LabelSourceUnspec { 570 lbl.Source = LabelSourceAny 571 } 572 573 return lbl 574 } 575 576 // generateLabelString generates the string representation of a label with 577 // the provided source, key, and value in the format "source:key=value". 578 func generateLabelString(source, key, value string) string { 579 return fmt.Sprintf("%s:%s=%s", source, key, value) 580 } 581 582 // GenerateK8sLabelString generates the string representation of a label with 583 // the provided source, key, and value in the format "LabelSourceK8s:key=value". 584 func GenerateK8sLabelString(k, v string) string { 585 return generateLabelString(LabelSourceK8s, k, v) 586 }