@@ -3,6 +3,10 @@ package recommendation
33import (
44 "context"
55 "fmt"
6+ "k8s.io/apimachinery/pkg/labels"
7+ "k8s.io/apimachinery/pkg/runtime/schema"
8+ "k8s.io/client-go/dynamic/dynamicinformer"
9+ "k8s.io/client-go/tools/cache"
610 "sort"
711 "strconv"
812 "strings"
@@ -54,6 +58,7 @@ type RecommendationRuleController struct {
5458 dynamicClient dynamic.Interface
5559 discoveryClient discovery.DiscoveryInterface
5660 Provider providers.History
61+ dynamicLister DynamicLister
5762}
5863
5964func (c * RecommendationRuleController ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
@@ -147,9 +152,10 @@ func (c *RecommendationRuleController) doReconcile(ctx context.Context, recommen
147152 keys = append (keys , k )
148153 }
149154 sort .Strings (keys ) // sort key to get a certain order
155+ recommendationIndex := NewRecommendationIndex (currRecommendations )
150156 for _ , key := range keys {
151157 id := identities [key ]
152- id .Recommendation = GetRecommendationFromIdentity ( identities [ key ], currRecommendations )
158+ id .Recommendation = recommendationIndex . GetRecommendation ( id )
153159 identitiesArray = append (identitiesArray , id )
154160 }
155161
@@ -243,6 +249,8 @@ func (c *RecommendationRuleController) SetupWithManager(mgr ctrl.Manager) error
243249 c .kubeClient = kubernetes .NewForConfigOrDie (mgr .GetConfig ())
244250 c .discoveryClient = discovery .NewDiscoveryClientForConfigOrDie (mgr .GetConfig ())
245251 c .dynamicClient = dynamic .NewForConfigOrDie (mgr .GetConfig ())
252+ dynamicInformerFactory := dynamicinformer .NewDynamicSharedInformerFactory (c .dynamicClient , 0 )
253+ c .dynamicLister = NewDynamicInformerLister (dynamicInformerFactory )
246254
247255 return ctrl .NewControllerManagedBy (mgr ).
248256 For (& analysisv1alph1.RecommendationRule {}, builder .WithPredicates (predicate.GenerationChangedPredicate {})).
@@ -264,19 +272,19 @@ func (c *RecommendationRuleController) getIdentities(ctx context.Context, recomm
264272
265273 var unstructureds []unstructuredv1.Unstructured
266274 if recommendationRule .Spec .NamespaceSelector .Any {
267- unstructuredList , err := c .dynamicClient . Resource ( * gvr ). List (ctx , metav1. ListOptions {} )
275+ unstructuredList , err := c .dynamicLister . List (ctx , * gvr , "" )
268276 if err != nil {
269277 return nil , err
270278 }
271- unstructureds = append (unstructureds , unstructuredList .Items . .. )
279+ unstructureds = append (unstructureds , unstructuredList ... )
272280 } else {
273281 for _ , namespace := range recommendationRule .Spec .NamespaceSelector .MatchNames {
274- unstructuredList , err := c .dynamicClient . Resource ( * gvr ). Namespace ( namespace ). List (ctx , metav1. ListOptions {} )
282+ unstructuredList , err := c .dynamicLister . List (ctx , * gvr , namespace )
275283 if err != nil {
276284 return nil , err
277285 }
278286
279- unstructureds = append (unstructureds , unstructuredList .Items . .. )
287+ unstructureds = append (unstructureds , unstructuredList ... )
280288 }
281289 }
282290
@@ -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