github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/native/object.go (about) 1 /* 2 Copyright 2022 The Katalyst 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 native 18 19 import ( 20 "fmt" 21 "strings" 22 23 v1 "k8s.io/api/core/v1" 24 apiequality "k8s.io/apimachinery/pkg/api/equality" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 "k8s.io/apimachinery/pkg/labels" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/client-go/tools/cache" 31 "k8s.io/klog/v2" 32 33 "github.com/kubewharf/katalyst-api/pkg/consts" 34 ) 35 36 var ( 37 objectFieldsForLabelSelector = []string{"spec", "selector"} 38 objectFieldsForTemplateAnnotations = []string{"spec", "template", "metadata", "annotations"} 39 objectFieldsForPodTemplate = []string{"spec", "template"} 40 ) 41 42 // GenerateUniqObjectUIDKey generate a uniq key (including UID) for the given object. 43 func GenerateUniqObjectUIDKey(obj metav1.Object) string { 44 return fmt.Sprintf("%s/%s/%s", obj.GetNamespace(), obj.GetName(), obj.GetUID()) 45 } 46 47 // ParseUniqObjectUIDKey parse the given key into namespace, name and uid 48 func ParseUniqObjectUIDKey(key string) (namespace string, name string, uid string, err error) { 49 names := strings.Split(key, "/") 50 if len(names) != 3 { 51 return "", "", "", fmt.Errorf("workload key %s split error", key) 52 } 53 54 return names[0], names[1], names[2], nil 55 } 56 57 // GenerateUniqObjectNameKey generate a uniq key (without UID) for the given object. 58 func GenerateUniqObjectNameKey(obj metav1.Object) string { 59 return GenerateNamespaceNameKey(obj.GetNamespace(), obj.GetName()) 60 } 61 62 // GenerateNamespaceNameKey generate uniq key by concatenating namespace and name. 63 func GenerateNamespaceNameKey(namespace, name string) string { 64 return fmt.Sprintf("%s/%s", namespace, name) 65 } 66 67 // GenerateUniqGVRNameKey generate a uniq key (without UID) for the GVR and its corresponding object. 68 func GenerateUniqGVRNameKey(gvr string, workload metav1.Object) (string, error) { 69 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(workload) 70 if err != nil { 71 return "", err 72 } 73 return gvr + "/" + key, nil 74 } 75 76 // ParseUniqGVRNameKey parse the given key into GVR and namespace/name 77 func ParseUniqGVRNameKey(key string) (gvr string, namespace string, name string, err error) { 78 names := strings.Split(key, "/") 79 if len(names) != 3 { 80 return "", "", "", fmt.Errorf("workload key %s split error", key) 81 } 82 83 return names[0], names[1], names[2], nil 84 } 85 86 // ParseNamespaceNameUIDKey parse the given key into namespace/name/uid 87 func ParseNamespaceNameUIDKey(key string) (string, string, string, error) { 88 if key == "" { 89 return "", "", "", fmt.Errorf("key %s split error", key) 90 } 91 names := strings.Split(key, "/") 92 if len(names) != 3 { 93 return "", "", "", fmt.Errorf("key %s split error", key) 94 } 95 96 return names[0], names[1], names[2], nil 97 } 98 99 // GenerateDynamicResourceByGVR generates dynamic resource by given gvr, the format is such as `resource.version.group`, 100 // which can be input of ParseResourceArg 101 func GenerateDynamicResourceByGVR(gvr schema.GroupVersionResource) string { 102 return fmt.Sprintf("%s.%s.%s", gvr.Resource, gvr.Version, gvr.Group) 103 } 104 105 // ObjectOwnerReferenceIndex is used by informer to index a resource by owner 106 func ObjectOwnerReferenceIndex(o interface{}) ([]string, error) { 107 obj, ok := o.(metav1.Object) 108 if !ok { 109 return nil, fmt.Errorf("failed to reflect a obj to pod") 110 } 111 112 var keys []string 113 for _, ow := range obj.GetOwnerReferences() { 114 keys = append(keys, GenerateObjectOwnerReferenceKey(ow)) 115 } 116 return keys, nil 117 } 118 119 // GenerateObjectOwnerReferenceKey is to generate a unique key by owner reference 120 func GenerateObjectOwnerReferenceKey(reference metav1.OwnerReference) string { 121 return fmt.Sprintf("%s,%s,%s", reference.APIVersion, reference.Kind, reference.Name) 122 } 123 124 func ToSchemaGVR(group, version, resource string) schema.GroupVersionResource { 125 return schema.GroupVersionResource{Group: group, Version: version, Resource: resource} 126 } 127 128 func ToUnstructured(obj interface{}) (*unstructured.Unstructured, error) { 129 ret, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) 130 if err != nil { 131 return nil, err 132 } 133 return &unstructured.Unstructured{Object: ret}, nil 134 } 135 136 func FilterOutDeletingUnstructured(objList []*unstructured.Unstructured) []*unstructured.Unstructured { 137 aliveTargets := make([]*unstructured.Unstructured, 0, len(objList)) 138 for _, obj := range objList { 139 if obj.GetDeletionTimestamp() != nil { 140 continue 141 } 142 aliveTargets = append(aliveTargets, obj) 143 } 144 return aliveTargets 145 } 146 147 // CheckObjectEqual returns true if uid equals or the namespace/name pair equal 148 func CheckObjectEqual(obj1, obj2 metav1.Object) bool { 149 return apiequality.Semantic.DeepEqual(obj1.GetUID(), obj2.GetUID()) || 150 (obj1.GetName() == obj2.GetName() && obj1.GetNamespace() == obj2.GetNamespace()) 151 } 152 153 // GetUnstructuredSelector parse a unstructured object and return its labelSelector (for pods) 154 func GetUnstructuredSelector(object *unstructured.Unstructured) (labels.Selector, error) { 155 anno := object.GetAnnotations() 156 if selectorStr, ok := anno[consts.WorkloadAnnotationVPASelectorKey]; ok { 157 selector, err := labels.Parse(selectorStr) 158 if err != nil { 159 return nil, err 160 } 161 return selector, nil 162 } 163 164 val, ok, err := unstructured.NestedFieldNoCopy(object.UnstructuredContent(), objectFieldsForLabelSelector...) 165 if err != nil { 166 return nil, err 167 } else if !ok || val == nil { 168 return nil, fmt.Errorf("%v doesn't exist", objectFieldsForLabelSelector) 169 } 170 171 selector := &metav1.LabelSelector{} 172 _ = runtime.DefaultUnstructuredConverter.FromUnstructured(val.(map[string]interface{}), selector) 173 174 return metav1.LabelSelectorAsSelector(selector) 175 } 176 177 // GetUnstructuredTemplateAnnotations parse a unstructured object and return its template's annotations (for workload like deployments, statefulsets) 178 func GetUnstructuredTemplateAnnotations(object *unstructured.Unstructured) (map[string]string, error) { 179 val, ok, err := unstructured.NestedFieldCopy(object.UnstructuredContent(), objectFieldsForTemplateAnnotations...) 180 if err != nil { 181 return nil, err 182 } else if !ok || val == nil { 183 return nil, fmt.Errorf("%v doesn't exist", objectFieldsForTemplateAnnotations) 184 } 185 186 annotations := make(map[string]string) 187 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(val.(map[string]interface{}), &annotations); err != nil { 188 return nil, err 189 } 190 191 return annotations, nil 192 } 193 194 func GetUnstructuredPodTemplateSpec(object *unstructured.Unstructured) (*v1.PodTemplateSpec, error) { 195 val, ok, err := unstructured.NestedFieldCopy(object.UnstructuredContent(), objectFieldsForPodTemplate...) 196 if err != nil { 197 return nil, err 198 } else if !ok || val == nil { 199 return nil, fmt.Errorf("%v doesn't exist", objectFieldsForPodTemplate) 200 } 201 202 podTemplateSpec := &v1.PodTemplateSpec{} 203 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(val.(map[string]interface{}), podTemplateSpec); err != nil { 204 return nil, err 205 } 206 207 return podTemplateSpec, nil 208 } 209 210 // VisitUnstructuredAncestors is to walk through all the ancestors of the given object, 211 // during this process, we will try to handle each ancestor with the given util function. 212 // if the handleFunc returns true, it means that we should continue the walking process 213 // for other ancestors, otherwise, we break the process and return. 214 func VisitUnstructuredAncestors(object *unstructured.Unstructured, unstructuredMap map[schema.GroupVersionKind]cache.GenericLister, 215 handleFunc func(owner *unstructured.Unstructured) bool, 216 ) bool { 217 if !handleFunc(object) { 218 return false 219 } 220 221 for _, owner := range object.GetOwnerReferences() { 222 gvk := schema.FromAPIVersionAndKind(owner.APIVersion, owner.Kind) 223 if _, ok := unstructuredMap[gvk]; ok { 224 ownerObj, err := unstructuredMap[gvk].ByNamespace(object.GetNamespace()).Get(owner.Name) 225 if err != nil || ownerObj == nil { 226 klog.Errorf("get object %s/%s owner %s failed: %s", object.GetNamespace(), object.GetName(), owner.Name, err) 227 continue 228 } 229 230 if toContinue := VisitUnstructuredAncestors(ownerObj.(*unstructured.Unstructured), unstructuredMap, handleFunc); !toContinue { 231 return false 232 } 233 } 234 } 235 236 return true 237 }