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  }