github.com/gocrane/crane@v0.11.0/pkg/utils/target/fetcher.go (about)

     1  package target
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	appsv1 "k8s.io/api/apps/v1"
     9  	batchv1 "k8s.io/api/batch/v1"
    10  	corev1 "k8s.io/api/core/v1"
    11  	"k8s.io/apimachinery/pkg/api/meta"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"k8s.io/apimachinery/pkg/labels"
    15  	"k8s.io/apimachinery/pkg/runtime"
    16  	"k8s.io/apimachinery/pkg/runtime/schema"
    17  	"k8s.io/apimachinery/pkg/util/sets"
    18  	"k8s.io/client-go/scale"
    19  	"sigs.k8s.io/controller-runtime/pkg/client"
    20  )
    21  
    22  // SelectorFetcher gets a labelSelector used to gather Pods controlled by the given targetRef.
    23  type SelectorFetcher interface {
    24  	// Fetch returns a labelSelector used to gather Pods controlled by the given targetRef.
    25  	Fetch(targetRef *corev1.ObjectReference) (labels.Selector, error)
    26  }
    27  
    28  const (
    29  	daemonSet             string = "DaemonSet"
    30  	deployment            string = "Deployment"
    31  	replicaSet            string = "ReplicaSet"
    32  	statefulSet           string = "StatefulSet"
    33  	replicationController string = "ReplicationController"
    34  	job                   string = "Job"
    35  	cronJob               string = "CronJob"
    36  )
    37  
    38  var wellKnownControllers = sets.NewString(daemonSet, deployment, replicaSet, statefulSet, replicationController, job, cronJob)
    39  
    40  // NewSelectorFetcher returns new instance of SelectorFetcher
    41  func NewSelectorFetcher(scheme *runtime.Scheme, restMapper meta.RESTMapper, scaleClient scale.ScalesGetter, kubeClient client.Client) SelectorFetcher {
    42  	return &targetSelectorFetcher{
    43  		Scheme:      scheme,
    44  		RestMapper:  restMapper,
    45  		ScaleClient: scaleClient,
    46  		KubeClient:  kubeClient,
    47  	}
    48  }
    49  
    50  type targetSelectorFetcher struct {
    51  	Scheme      *runtime.Scheme
    52  	RestMapper  meta.RESTMapper
    53  	ScaleClient scale.ScalesGetter
    54  	KubeClient  client.Client
    55  }
    56  
    57  func (f *targetSelectorFetcher) Fetch(target *corev1.ObjectReference) (labels.Selector, error) {
    58  	if wellKnownControllers.Has(target.Kind) {
    59  		return f.getLabelSelector(target)
    60  	}
    61  
    62  	// not on a list of known controllers, use scale sub-resource
    63  	groupVersion, err := schema.ParseGroupVersion(target.APIVersion)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	groupKind := schema.GroupKind{
    68  		Group: groupVersion.Group,
    69  		Kind:  target.Kind,
    70  	}
    71  
    72  	selector, err := f.getLabelSelectorFromScale(groupKind, target.Namespace, target.Name)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("unhandled targetRef %+v,  error: %v", *target, err)
    75  	}
    76  	return selector, nil
    77  }
    78  
    79  func (f *targetSelectorFetcher) getLabelSelector(target *corev1.ObjectReference) (labels.Selector, error) {
    80  	unstructured := &unstructured.Unstructured{}
    81  	unstructured.SetKind(target.Kind)
    82  	unstructured.SetAPIVersion(target.APIVersion)
    83  
    84  	if err := f.KubeClient.Get(context.TODO(), client.ObjectKey{Namespace: target.Namespace, Name: target.Name}, unstructured); err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	switch strings.ToLower(target.Kind) {
    89  	case strings.ToLower(daemonSet):
    90  		var daemonset appsv1.DaemonSet
    91  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &daemonset); err != nil {
    92  			return nil, err
    93  		}
    94  		return metav1.LabelSelectorAsSelector(daemonset.Spec.Selector)
    95  	case strings.ToLower(deployment):
    96  		var deployment appsv1.Deployment
    97  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &deployment); err != nil {
    98  			return nil, err
    99  		}
   100  		return metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
   101  	case strings.ToLower(statefulSet):
   102  		var sts appsv1.StatefulSet
   103  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &sts); err != nil {
   104  			return nil, err
   105  		}
   106  		return metav1.LabelSelectorAsSelector(sts.Spec.Selector)
   107  	case strings.ToLower(replicaSet):
   108  		var rs appsv1.ReplicaSet
   109  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &rs); err != nil {
   110  			return nil, err
   111  		}
   112  		return metav1.LabelSelectorAsSelector(rs.Spec.Selector)
   113  	case strings.ToLower(job):
   114  		var job batchv1.Job
   115  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &job); err != nil {
   116  			return nil, err
   117  		}
   118  		return metav1.LabelSelectorAsSelector(job.Spec.Selector)
   119  	case strings.ToLower(cronJob):
   120  		var cjob batchv1.CronJob
   121  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &cjob); err != nil {
   122  			return nil, err
   123  		}
   124  		return metav1.LabelSelectorAsSelector(metav1.SetAsLabelSelector(cjob.Spec.JobTemplate.Spec.Template.Labels))
   125  	case strings.ToLower(replicationController):
   126  		var rc corev1.ReplicationController
   127  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), &rc); err != nil {
   128  			return nil, err
   129  		}
   130  		return metav1.LabelSelectorAsSelector(metav1.SetAsLabelSelector(rc.Spec.Selector))
   131  	}
   132  	return nil, fmt.Errorf("unable to fetch label seletor for %+v", *target)
   133  }
   134  
   135  func (f *targetSelectorFetcher) getLabelSelectorFromScale(groupKind schema.GroupKind, namespace, name string) (labels.Selector, error) {
   136  	mappings, err := f.RestMapper.RESTMappings(groupKind)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	var errs []error
   142  	for _, mapping := range mappings {
   143  		groupResource := mapping.Resource.GroupResource()
   144  		scale, err := f.ScaleClient.Scales(namespace).Get(context.TODO(), groupResource, name, metav1.GetOptions{})
   145  		if err == nil {
   146  			if scale.Status.Selector == "" {
   147  				return nil, fmt.Errorf("resource %s/%s has an empty selector for scale sub-resource", namespace, name)
   148  			}
   149  			selector, err := labels.Parse(scale.Status.Selector)
   150  			if err != nil {
   151  				return nil, err
   152  			}
   153  			return selector, nil
   154  		}
   155  		errs = append(errs, err)
   156  	}
   157  	return nil, fmt.Errorf("%+v", errs)
   158  }