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 }