Skip to content

Commit c48a90b

Browse files
committed
fix issues gocrane#898
1 parent 557db96 commit c48a90b

3 files changed

Lines changed: 235 additions & 6 deletions

File tree

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ require (
99
github.com/google/cadvisor v0.41.0
1010
github.com/jaypipes/ghw v0.9.0
1111
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12
12+
github.com/onsi/ginkgo v1.16.5
13+
github.com/onsi/gomega v1.15.0
14+
github.com/pkg/errors v0.9.1
1215
github.com/prometheus/client_golang v1.11.0
1316
github.com/prometheus/common v0.26.0
1417
github.com/shirou/gopsutil v3.21.10+incompatible
@@ -78,6 +81,7 @@ require (
7881
github.com/ghodss/yaml v1.0.0 // indirect
7982
github.com/gin-contrib/sse v0.1.0 // indirect
8083
github.com/go-logr/logr v0.4.0 // indirect
84+
github.com/go-logr/zapr v0.4.0 // indirect
8185
github.com/go-ole/go-ole v1.2.6 // indirect
8286
github.com/go-openapi/jsonpointer v0.19.5 // indirect
8387
github.com/go-openapi/jsonreference v0.19.5 // indirect
@@ -113,12 +117,12 @@ require (
113117
github.com/modern-go/reflect2 v1.0.2 // indirect
114118
github.com/mrunalp/fileutils v0.5.0 // indirect
115119
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
120+
github.com/nxadm/tail v1.4.8 // indirect
116121
github.com/opencontainers/go-digest v1.0.0 // indirect
117122
github.com/opencontainers/image-spec v1.0.1 // indirect
118123
github.com/opencontainers/runc v1.0.2 // indirect
119124
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
120125
github.com/opencontainers/selinux v1.8.2 // indirect
121-
github.com/pkg/errors v0.9.1 // indirect
122126
github.com/pmezard/go-difflib v1.0.0 // indirect
123127
github.com/prometheus/client_model v0.2.0 // indirect
124128
github.com/prometheus/procfs v0.6.0 // indirect
@@ -155,6 +159,7 @@ require (
155159
google.golang.org/appengine v1.6.7 // indirect
156160
gopkg.in/inf.v0 v0.9.1 // indirect
157161
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
162+
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
158163
gopkg.in/warnings.v0 v0.1.2 // indirect
159164
gopkg.in/yaml.v2 v2.4.0 // indirect
160165
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect

pkg/controller/recommendation/recommendation_rule_controller.go

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package recommendation
33
import (
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

5964
func (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+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package recommendation
2+
3+
import (
4+
analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1"
5+
corev1 "k8s.io/api/core/v1"
6+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
"reflect"
8+
"testing"
9+
)
10+
11+
func TestRecommendationIndex_GetRecommendation(t *testing.T) {
12+
type fields struct {
13+
recommendationList analysisv1alph1.RecommendationList
14+
}
15+
type args struct {
16+
id ObjectIdentity
17+
}
18+
19+
tests := []struct {
20+
name string
21+
fields fields
22+
args args
23+
want *analysisv1alph1.Recommendation
24+
}{
25+
{
26+
name: "TestRecommendationIndex_GetRecommendation good case",
27+
fields: fields{
28+
recommendationList: analysisv1alph1.RecommendationList{
29+
Items: []analysisv1alph1.Recommendation{
30+
{
31+
ObjectMeta: v1.ObjectMeta{
32+
Name: "test-recommendation-rule",
33+
Namespace: "test-namespace",
34+
},
35+
Spec: analysisv1alph1.RecommendationSpec{
36+
TargetRef: corev1.ObjectReference{
37+
Namespace: "test-namespace",
38+
Kind: "Deployment",
39+
Name: "test-deployment-bar",
40+
APIVersion: "app/v1",
41+
},
42+
Type: analysisv1alph1.AnalysisTypeResource,
43+
},
44+
},
45+
{
46+
ObjectMeta: v1.ObjectMeta{
47+
Name: "test-recommendation-rule",
48+
Namespace: "test-namespace",
49+
},
50+
Spec: analysisv1alph1.RecommendationSpec{
51+
TargetRef: corev1.ObjectReference{
52+
Namespace: "test-namespace",
53+
Kind: "Deployment",
54+
Name: "test-deployment-foo",
55+
APIVersion: "app/v1",
56+
},
57+
Type: analysisv1alph1.AnalysisTypeResource,
58+
},
59+
},
60+
},
61+
},
62+
},
63+
want: &analysisv1alph1.Recommendation{
64+
ObjectMeta: v1.ObjectMeta{
65+
Name: "test-recommendation-rule",
66+
Namespace: "test-namespace",
67+
},
68+
Spec: analysisv1alph1.RecommendationSpec{
69+
TargetRef: corev1.ObjectReference{
70+
Namespace: "test-namespace",
71+
Kind: "Deployment",
72+
Name: "test-deployment-name",
73+
APIVersion: "app/v1",
74+
},
75+
},
76+
},
77+
args: args{
78+
id: ObjectIdentity{
79+
Name: "test-deployment-name",
80+
Namespace: "test-namespace",
81+
APIVersion: "app/v1",
82+
Kind: "Deployment",
83+
Recommender: "Resource",
84+
},
85+
},
86+
},
87+
{
88+
name: "TestRecommendationIndex_GetRecommendation empty case",
89+
fields: fields{
90+
recommendationList: analysisv1alph1.RecommendationList{
91+
Items: []analysisv1alph1.Recommendation{},
92+
},
93+
},
94+
args: args{
95+
id: ObjectIdentity{
96+
Name: "test-deployment-name",
97+
Namespace: "test-namespace",
98+
APIVersion: "app/v1",
99+
Kind: "Deployment",
100+
Recommender: "Resources",
101+
},
102+
},
103+
},
104+
}
105+
for _, tt := range tests {
106+
t.Run(tt.name, func(t *testing.T) {
107+
idx := NewRecommendationIndex(tt.fields.recommendationList)
108+
if got := idx.GetRecommendation(tt.args.id); !reflect.DeepEqual(got, tt.want) {
109+
t.Errorf("GetRecommendation() = %v, want %v", got, tt.want)
110+
}
111+
})
112+
}
113+
}

0 commit comments

Comments
 (0)