k8s.io/kubernetes@v1.29.3/pkg/fieldpath/fieldpath.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package fieldpath 18 19 import ( 20 "fmt" 21 "sort" 22 "strconv" 23 "strings" 24 25 "k8s.io/apimachinery/pkg/api/meta" 26 "k8s.io/apimachinery/pkg/util/validation" 27 ) 28 29 // FormatMap formats map[string]string to a string. 30 func FormatMap(m map[string]string) (fmtStr string) { 31 // output with keys in sorted order to provide stable output 32 keys := make([]string, 0, len(m)) 33 var grow int 34 for k, v := range m { 35 keys = append(keys, k) 36 // why add 4: (for =, \n, " and ") 37 grow += len(k) + len(v) + 4 38 } 39 sort.Strings(keys) 40 // allocate space to avoid expansion 41 dst := make([]byte, 0, grow) 42 for _, key := range keys { 43 if len(dst) > 0 { 44 dst = append(dst, '\n') 45 } 46 dst = append(dst, key...) 47 dst = append(dst, '=') 48 dst = strconv.AppendQuote(dst, m[key]) 49 } 50 return string(dst) 51 } 52 53 // ExtractFieldPathAsString extracts the field from the given object 54 // and returns it as a string. The object must be a pointer to an 55 // API type. 56 func ExtractFieldPathAsString(obj interface{}, fieldPath string) (string, error) { 57 accessor, err := meta.Accessor(obj) 58 if err != nil { 59 return "", err 60 } 61 62 if path, subscript, ok := SplitMaybeSubscriptedPath(fieldPath); ok { 63 switch path { 64 case "metadata.annotations": 65 if errs := validation.IsQualifiedName(strings.ToLower(subscript)); len(errs) != 0 { 66 return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";")) 67 } 68 return accessor.GetAnnotations()[subscript], nil 69 case "metadata.labels": 70 if errs := validation.IsQualifiedName(subscript); len(errs) != 0 { 71 return "", fmt.Errorf("invalid key subscript in %s: %s", fieldPath, strings.Join(errs, ";")) 72 } 73 return accessor.GetLabels()[subscript], nil 74 default: 75 return "", fmt.Errorf("fieldPath %q does not support subscript", fieldPath) 76 } 77 } 78 79 switch fieldPath { 80 case "metadata.annotations": 81 return FormatMap(accessor.GetAnnotations()), nil 82 case "metadata.labels": 83 return FormatMap(accessor.GetLabels()), nil 84 case "metadata.name": 85 return accessor.GetName(), nil 86 case "metadata.namespace": 87 return accessor.GetNamespace(), nil 88 case "metadata.uid": 89 return string(accessor.GetUID()), nil 90 } 91 92 return "", fmt.Errorf("unsupported fieldPath: %v", fieldPath) 93 } 94 95 // SplitMaybeSubscriptedPath checks whether the specified fieldPath is 96 // subscripted, and 97 // - if yes, this function splits the fieldPath into path and subscript, and 98 // returns (path, subscript, true). 99 // - if no, this function returns (fieldPath, "", false). 100 // 101 // Example inputs and outputs: 102 // 103 // "metadata.annotations['myKey']" --> ("metadata.annotations", "myKey", true) 104 // "metadata.annotations['a[b]c']" --> ("metadata.annotations", "a[b]c", true) 105 // "metadata.labels['']" --> ("metadata.labels", "", true) 106 // "metadata.labels" --> ("metadata.labels", "", false) 107 func SplitMaybeSubscriptedPath(fieldPath string) (string, string, bool) { 108 if !strings.HasSuffix(fieldPath, "']") { 109 return fieldPath, "", false 110 } 111 s := strings.TrimSuffix(fieldPath, "']") 112 parts := strings.SplitN(s, "['", 2) 113 if len(parts) < 2 { 114 return fieldPath, "", false 115 } 116 if len(parts[0]) == 0 { 117 return fieldPath, "", false 118 } 119 return parts[0], parts[1], true 120 }