@@ -15,12 +15,16 @@ import (
1515 "k8s.io/apimachinery/pkg/api/meta"
1616 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1717 unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
18+ "k8s.io/apimachinery/pkg/labels"
1819 "k8s.io/apimachinery/pkg/runtime"
20+ "k8s.io/apimachinery/pkg/runtime/schema"
1921 "k8s.io/apimachinery/pkg/types"
2022 "k8s.io/client-go/discovery"
2123 "k8s.io/client-go/dynamic"
24+ "k8s.io/client-go/dynamic/dynamicinformer"
2225 "k8s.io/client-go/kubernetes"
2326 "k8s.io/client-go/scale"
27+ "k8s.io/client-go/tools/cache"
2428 "k8s.io/client-go/tools/record"
2529 "k8s.io/client-go/util/retry"
2630 "k8s.io/klog/v2"
@@ -30,7 +34,6 @@ import (
3034 "sigs.k8s.io/controller-runtime/pkg/predicate"
3135
3236 analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1"
33-
3437 "github.com/gocrane/crane/pkg/known"
3538 "github.com/gocrane/crane/pkg/metrics"
3639 "github.com/gocrane/crane/pkg/oom"
@@ -54,6 +57,7 @@ type RecommendationRuleController struct {
5457 dynamicClient dynamic.Interface
5558 discoveryClient discovery.DiscoveryInterface
5659 Provider providers.History
60+ dynamicLister DynamicLister
5761}
5862
5963func (c * RecommendationRuleController ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
@@ -147,9 +151,10 @@ func (c *RecommendationRuleController) doReconcile(ctx context.Context, recommen
147151 keys = append (keys , k )
148152 }
149153 sort .Strings (keys ) // sort key to get a certain order
154+ recommendationIndex := NewRecommendationIndex (currRecommendations )
150155 for _ , key := range keys {
151156 id := identities [key ]
152- id .Recommendation = GetRecommendationFromIdentity ( identities [ key ], currRecommendations )
157+ id .Recommendation = recommendationIndex . GetRecommendation ( id )
153158 identitiesArray = append (identitiesArray , id )
154159 }
155160
@@ -243,6 +248,8 @@ func (c *RecommendationRuleController) SetupWithManager(mgr ctrl.Manager) error
243248 c .kubeClient = kubernetes .NewForConfigOrDie (mgr .GetConfig ())
244249 c .discoveryClient = discovery .NewDiscoveryClientForConfigOrDie (mgr .GetConfig ())
245250 c .dynamicClient = dynamic .NewForConfigOrDie (mgr .GetConfig ())
251+ dynamicInformerFactory := dynamicinformer .NewDynamicSharedInformerFactory (c .dynamicClient , 0 )
252+ c .dynamicLister = NewDynamicInformerLister (dynamicInformerFactory )
246253
247254 return ctrl .NewControllerManagedBy (mgr ).
248255 For (& analysisv1alph1.RecommendationRule {}, builder .WithPredicates (predicate.GenerationChangedPredicate {})).
@@ -264,19 +271,19 @@ func (c *RecommendationRuleController) getIdentities(ctx context.Context, recomm
264271
265272 var unstructureds []unstructuredv1.Unstructured
266273 if recommendationRule .Spec .NamespaceSelector .Any {
267- unstructuredList , err := c .dynamicClient . Resource ( * gvr ). List (ctx , metav1. ListOptions {} )
274+ unstructuredList , err := c .dynamicLister . List (ctx , * gvr , "" )
268275 if err != nil {
269276 return nil , err
270277 }
271- unstructureds = append (unstructureds , unstructuredList .Items . .. )
278+ unstructureds = append (unstructureds , unstructuredList ... )
272279 } else {
273280 for _ , namespace := range recommendationRule .Spec .NamespaceSelector .MatchNames {
274- unstructuredList , err := c .dynamicClient . Resource ( * gvr ). Namespace ( namespace ). List (ctx , metav1. ListOptions {} )
281+ unstructuredList , err := c .dynamicLister . List (ctx , * gvr , namespace )
275282 if err != nil {
276283 return nil , err
277284 }
278285
279- unstructureds = append (unstructureds , unstructuredList .Items . .. )
286+ unstructureds = append (unstructureds , unstructuredList ... )
280287 }
281288 }
282289
@@ -453,6 +460,7 @@ func executeIdentity(ctx context.Context, wg *sync.WaitGroup, recommenderMgr rec
453460 defer func () {
454461 if wg != nil {
455462 wg .Done ()
463+ metrics .RecommendationExecutionCounter .WithLabelValues (id .APIVersion , id .Kind , id .Namespace , id .Name , id .Recommender ).Inc ()
456464 }
457465 }()
458466 var message string
@@ -528,3 +536,106 @@ func IsConvertFromAnalytics(recommendationRule *analysisv1alph1.RecommendationRu
528536
529537 return false , ""
530538}
539+
540+ // DynamicLister is a lister for dynamic resources.
541+ type DynamicLister interface {
542+ // List returns a list of resources matching the given groupVersionResource.
543+ List (ctx context.Context , gvk schema.GroupVersionResource , namespace string ) ([]unstructuredv1.Unstructured , error )
544+ }
545+
546+ type dynamicInformerLister struct {
547+ dynamicLister map [schema.GroupVersionResource ]cache.GenericLister
548+ dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory
549+ stopCh <- chan struct {}
550+ }
551+
552+ func NewDynamicInformerLister (dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory ) DynamicLister {
553+ return & dynamicInformerLister {
554+ dynamicLister : map [schema.GroupVersionResource ]cache.GenericLister {},
555+ dynamicInformerFactory : dynamicInformerFactory ,
556+ stopCh : make (chan struct {}),
557+ }
558+ }
559+
560+ func (d * dynamicInformerLister ) List (ctx context.Context , gvr schema.GroupVersionResource , namespace string ) ([]unstructuredv1.Unstructured , error ) {
561+ var (
562+ objects []runtime.Object
563+ err error
564+ )
565+
566+ lister , exists := d .dynamicLister [gvr ]
567+ if ! exists {
568+ lister = d .dynamicInformerFactory .ForResource (gvr ).Lister ()
569+ d .dynamicLister [gvr ] = lister
570+ d .dynamicInformerFactory .Start (d .stopCh )
571+ if ! d .dynamicInformerFactory .WaitForCacheSync (d .stopCh )[gvr ] {
572+ return nil , fmt .Errorf ("failed to sync informer for %s" , gvr )
573+ }
574+ }
575+ if namespace != "" {
576+ objects , err = lister .ByNamespace (namespace ).List (labels .Everything ())
577+ } else {
578+ objects , err = lister .List (labels .Everything ())
579+ }
580+ if err != nil {
581+ return nil , err
582+ }
583+
584+ var unstructuredObjects []unstructuredv1.Unstructured
585+ for _ , obj := range objects {
586+ unstructuredObj , err := runtime .DefaultUnstructuredConverter .ToUnstructured (obj )
587+ if err != nil {
588+ return nil , err
589+ }
590+ unstructuredObjects = append (unstructuredObjects , unstructuredv1.Unstructured {Object : unstructuredObj })
591+ }
592+ return unstructuredObjects , nil
593+ }
594+
595+ type IndexKey struct {
596+ Namespace string
597+ APIVersion string
598+ Kind string
599+ Name string
600+ Recommender string
601+ }
602+
603+ type RecommendationIndex struct {
604+ mtx sync.RWMutex
605+ idx map [IndexKey ]* analysisv1alph1.Recommendation
606+ }
607+
608+ func NewRecommendationIndex (recommendations analysisv1alph1.RecommendationList ) * RecommendationIndex {
609+ idx := make (map [IndexKey ]* analysisv1alph1.Recommendation , len (recommendations .Items ))
610+ for i := range recommendations .Items {
611+ r := & recommendations .Items [i ]
612+ idx [createIndexKey (r )] = r
613+ }
614+
615+ return & RecommendationIndex {
616+ idx : idx ,
617+ }
618+ }
619+
620+ func createIndexKey (r * analysisv1alph1.Recommendation ) IndexKey {
621+ return IndexKey {
622+ Kind : r .Spec .TargetRef .Kind ,
623+ APIVersion : r .Spec .TargetRef .APIVersion ,
624+ Namespace : r .Spec .TargetRef .Namespace ,
625+ Name : r .Spec .TargetRef .Name ,
626+ Recommender : string (r .Spec .Type ),
627+ }
628+ }
629+
630+ func (idx * RecommendationIndex ) GetRecommendation (id ObjectIdentity ) * analysisv1alph1.Recommendation {
631+ key := IndexKey {
632+ Kind : id .Kind ,
633+ APIVersion : id .APIVersion ,
634+ Namespace : id .Namespace ,
635+ Name : id .Name ,
636+ Recommender : id .Recommender ,
637+ }
638+ idx .mtx .RLock ()
639+ defer idx .mtx .RUnlock ()
640+ return idx .idx [key ]
641+ }
0 commit comments