Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7091f7a

Browse files
committedSep 3, 2024·
add test for pkg/controllers/cluster
Signed-off-by: xovoxy <xilovele@gmail.com>
1 parent 704a873 commit 7091f7a

File tree

2 files changed

+893
-0
lines changed

2 files changed

+893
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,465 @@
1+
/*
2+
Copyright 2024 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cluster
18+
19+
import (
20+
"context"
21+
"reflect"
22+
"testing"
23+
24+
corev1 "k8s.io/api/core/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/types"
27+
"k8s.io/client-go/tools/record"
28+
controllerruntime "sigs.k8s.io/controller-runtime"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
31+
32+
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
33+
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
34+
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
35+
"github.com/karmada-io/karmada/pkg/util"
36+
"github.com/karmada-io/karmada/pkg/util/gclient"
37+
"github.com/karmada-io/karmada/pkg/util/names"
38+
)
39+
40+
func newClusterController() *Controller {
41+
rbIndexerFunc := func(obj client.Object) []string {
42+
rb, ok := obj.(*workv1alpha2.ResourceBinding)
43+
if !ok {
44+
return nil
45+
}
46+
return util.GetBindingClusterNames(&rb.Spec)
47+
}
48+
49+
crbIndexerFunc := func(obj client.Object) []string {
50+
crb, ok := obj.(*workv1alpha2.ClusterResourceBinding)
51+
if !ok {
52+
return nil
53+
}
54+
return util.GetBindingClusterNames(&crb.Spec)
55+
}
56+
client := fake.NewClientBuilder().WithScheme(gclient.NewSchema()).
57+
WithIndex(&workv1alpha2.ResourceBinding{}, rbClusterKeyIndex, rbIndexerFunc).
58+
WithIndex(&workv1alpha2.ClusterResourceBinding{}, crbClusterKeyIndex, crbIndexerFunc).
59+
WithStatusSubresource(&clusterv1alpha1.Cluster{}).Build()
60+
return &Controller{
61+
Client: client,
62+
EventRecorder: record.NewFakeRecorder(1024),
63+
clusterHealthMap: newClusterHealthMap(),
64+
EnableTaintManager: true,
65+
}
66+
}
67+
68+
func TestController_Reconcile(t *testing.T) {
69+
req := controllerruntime.Request{NamespacedName: types.NamespacedName{Name: "test-cluster"}}
70+
tests := []struct {
71+
name string
72+
cluster *clusterv1alpha1.Cluster
73+
ns *corev1.Namespace
74+
work *workv1alpha1.Work
75+
del bool
76+
wCluster *clusterv1alpha1.Cluster
77+
want controllerruntime.Result
78+
wantErr bool
79+
}{
80+
{
81+
name: "cluster without status",
82+
cluster: &clusterv1alpha1.Cluster{
83+
ObjectMeta: controllerruntime.ObjectMeta{
84+
Name: "test-cluster",
85+
Finalizers: []string{util.ClusterControllerFinalizer},
86+
},
87+
},
88+
wCluster: &clusterv1alpha1.Cluster{
89+
ObjectMeta: controllerruntime.ObjectMeta{
90+
Name: "test-cluster",
91+
Finalizers: []string{util.ClusterControllerFinalizer},
92+
},
93+
Spec: clusterv1alpha1.ClusterSpec{
94+
Taints: []corev1.Taint{{
95+
Key: clusterv1alpha1.TaintClusterNotReady,
96+
Effect: corev1.TaintEffectNoSchedule,
97+
}},
98+
},
99+
Status: clusterv1alpha1.ClusterStatus{
100+
Conditions: []metav1.Condition{},
101+
},
102+
},
103+
want: controllerruntime.Result{},
104+
wantErr: false,
105+
},
106+
{
107+
name: "cluster with ready condition",
108+
cluster: &clusterv1alpha1.Cluster{
109+
ObjectMeta: controllerruntime.ObjectMeta{
110+
Name: "test-cluster",
111+
Finalizers: []string{util.ClusterControllerFinalizer},
112+
},
113+
Spec: clusterv1alpha1.ClusterSpec{
114+
Taints: []corev1.Taint{{
115+
Key: clusterv1alpha1.TaintClusterNotReady,
116+
Effect: corev1.TaintEffectNoSchedule,
117+
}},
118+
},
119+
Status: clusterv1alpha1.ClusterStatus{
120+
Conditions: []metav1.Condition{
121+
{
122+
Type: clusterv1alpha1.ClusterConditionReady,
123+
Status: metav1.ConditionTrue,
124+
},
125+
},
126+
},
127+
},
128+
wCluster: &clusterv1alpha1.Cluster{
129+
ObjectMeta: controllerruntime.ObjectMeta{
130+
Name: "test-cluster",
131+
Finalizers: []string{util.ClusterControllerFinalizer},
132+
},
133+
Spec: clusterv1alpha1.ClusterSpec{Taints: []corev1.Taint{}},
134+
Status: clusterv1alpha1.ClusterStatus{
135+
Conditions: []metav1.Condition{
136+
{
137+
Type: clusterv1alpha1.ClusterConditionReady,
138+
Status: metav1.ConditionTrue,
139+
},
140+
},
141+
},
142+
},
143+
want: controllerruntime.Result{},
144+
wantErr: false,
145+
},
146+
{
147+
name: "cluster with unknown condition",
148+
cluster: &clusterv1alpha1.Cluster{
149+
ObjectMeta: controllerruntime.ObjectMeta{
150+
Name: "test-cluster",
151+
Finalizers: []string{util.ClusterControllerFinalizer},
152+
},
153+
Spec: clusterv1alpha1.ClusterSpec{
154+
Taints: []corev1.Taint{{
155+
Key: clusterv1alpha1.TaintClusterNotReady,
156+
Effect: corev1.TaintEffectNoSchedule,
157+
}},
158+
},
159+
Status: clusterv1alpha1.ClusterStatus{
160+
Conditions: []metav1.Condition{
161+
{
162+
Type: clusterv1alpha1.ClusterConditionReady,
163+
Status: metav1.ConditionUnknown,
164+
},
165+
},
166+
},
167+
},
168+
wCluster: &clusterv1alpha1.Cluster{
169+
ObjectMeta: controllerruntime.ObjectMeta{
170+
Name: "test-cluster",
171+
Finalizers: []string{util.ClusterControllerFinalizer},
172+
},
173+
Spec: clusterv1alpha1.ClusterSpec{Taints: []corev1.Taint{
174+
{
175+
Key: clusterv1alpha1.TaintClusterUnreachable,
176+
Effect: corev1.TaintEffectNoSchedule,
177+
},
178+
}},
179+
Status: clusterv1alpha1.ClusterStatus{
180+
Conditions: []metav1.Condition{
181+
{
182+
Type: clusterv1alpha1.ClusterConditionReady,
183+
Status: metav1.ConditionUnknown,
184+
},
185+
},
186+
},
187+
},
188+
want: controllerruntime.Result{},
189+
wantErr: false,
190+
},
191+
{
192+
name: "cluster with false condition",
193+
cluster: &clusterv1alpha1.Cluster{
194+
ObjectMeta: controllerruntime.ObjectMeta{
195+
Name: "test-cluster",
196+
Finalizers: []string{util.ClusterControllerFinalizer},
197+
},
198+
Spec: clusterv1alpha1.ClusterSpec{
199+
Taints: []corev1.Taint{{
200+
Key: clusterv1alpha1.TaintClusterUnreachable,
201+
Effect: corev1.TaintEffectNoSchedule,
202+
}},
203+
},
204+
Status: clusterv1alpha1.ClusterStatus{
205+
Conditions: []metav1.Condition{
206+
{
207+
Type: clusterv1alpha1.ClusterConditionReady,
208+
Status: metav1.ConditionFalse,
209+
},
210+
},
211+
},
212+
},
213+
wCluster: &clusterv1alpha1.Cluster{
214+
ObjectMeta: controllerruntime.ObjectMeta{
215+
Name: "test-cluster",
216+
Finalizers: []string{util.ClusterControllerFinalizer},
217+
},
218+
Spec: clusterv1alpha1.ClusterSpec{Taints: []corev1.Taint{
219+
{
220+
Key: clusterv1alpha1.TaintClusterNotReady,
221+
Effect: corev1.TaintEffectNoSchedule,
222+
},
223+
}},
224+
Status: clusterv1alpha1.ClusterStatus{
225+
Conditions: []metav1.Condition{
226+
{
227+
Type: clusterv1alpha1.ClusterConditionReady,
228+
Status: metav1.ConditionFalse,
229+
},
230+
},
231+
},
232+
},
233+
want: controllerruntime.Result{},
234+
wantErr: false,
235+
},
236+
{
237+
name: "cluster not found",
238+
cluster: &clusterv1alpha1.Cluster{
239+
ObjectMeta: controllerruntime.ObjectMeta{
240+
Name: "test-cluster-noexist",
241+
Finalizers: []string{util.ClusterControllerFinalizer},
242+
},
243+
},
244+
want: controllerruntime.Result{},
245+
wantErr: false,
246+
},
247+
{
248+
name: "remove cluster failed",
249+
cluster: &clusterv1alpha1.Cluster{
250+
ObjectMeta: controllerruntime.ObjectMeta{
251+
Name: "test-cluster",
252+
Finalizers: []string{util.ClusterControllerFinalizer},
253+
},
254+
Spec: clusterv1alpha1.ClusterSpec{
255+
SyncMode: clusterv1alpha1.Pull,
256+
},
257+
Status: clusterv1alpha1.ClusterStatus{
258+
Conditions: []metav1.Condition{
259+
{
260+
Type: clusterv1alpha1.ClusterConditionReady,
261+
Status: metav1.ConditionFalse,
262+
},
263+
},
264+
},
265+
},
266+
ns: &corev1.Namespace{
267+
ObjectMeta: metav1.ObjectMeta{
268+
Name: names.GenerateExecutionSpaceName("test-cluster"),
269+
},
270+
},
271+
work: &workv1alpha1.Work{
272+
ObjectMeta: metav1.ObjectMeta{
273+
Name: "test-work",
274+
Namespace: names.GenerateExecutionSpaceName("test-cluster"),
275+
Finalizers: []string{util.ExecutionControllerFinalizer},
276+
},
277+
},
278+
del: true,
279+
want: controllerruntime.Result{},
280+
wantErr: true,
281+
},
282+
}
283+
for _, tt := range tests {
284+
t.Run(tt.name, func(t *testing.T) {
285+
c := newClusterController()
286+
if tt.cluster != nil {
287+
if err := c.Create(context.Background(), tt.cluster, &client.CreateOptions{}); err != nil {
288+
t.Fatalf("faild to create cluster %v", err)
289+
}
290+
}
291+
292+
if tt.ns != nil {
293+
if err := c.Create(context.Background(), tt.ns, &client.CreateOptions{}); err != nil {
294+
t.Fatalf("faild to create ns %v", err)
295+
}
296+
}
297+
298+
if tt.work != nil {
299+
if err := c.Create(context.Background(), tt.work, &client.CreateOptions{}); err != nil {
300+
t.Fatalf("faild to create work %v", err)
301+
}
302+
}
303+
304+
if tt.del {
305+
if err := c.Delete(context.Background(), tt.cluster, &client.DeleteOptions{}); err != nil {
306+
t.Fatalf("failed to delete cluster %v", err)
307+
}
308+
}
309+
310+
got, err := c.Reconcile(context.Background(), req)
311+
if (err != nil) != tt.wantErr {
312+
t.Errorf("Controller.Reconcile() error = %v, wantErr %v", err, tt.wantErr)
313+
return
314+
}
315+
if !reflect.DeepEqual(got, tt.want) {
316+
t.Errorf("Controller.Reconcile() = %v, want %v", got, tt.want)
317+
return
318+
}
319+
320+
if tt.wCluster != nil {
321+
cluster := &clusterv1alpha1.Cluster{}
322+
if err := c.Get(context.Background(), types.NamespacedName{Name: tt.cluster.Name}, cluster, &client.GetOptions{}); err != nil {
323+
t.Errorf("failed to get cluster %v", err)
324+
return
325+
}
326+
327+
cleanUpCluster(cluster)
328+
if !reflect.DeepEqual(cluster, tt.wCluster) {
329+
t.Errorf("Cluster resource reconcile get %v, want %v", *cluster, *tt.wCluster)
330+
}
331+
}
332+
})
333+
}
334+
}
335+
336+
func TestController_monitorClusterHealth(t *testing.T) {
337+
tests := []struct {
338+
name string
339+
cluster *clusterv1alpha1.Cluster
340+
wCluster *clusterv1alpha1.Cluster
341+
wantErr bool
342+
}{
343+
{
344+
name: "cluster without status",
345+
cluster: &clusterv1alpha1.Cluster{
346+
ObjectMeta: controllerruntime.ObjectMeta{
347+
Name: "test-cluster",
348+
Finalizers: []string{util.ClusterControllerFinalizer},
349+
},
350+
Spec: clusterv1alpha1.ClusterSpec{
351+
SyncMode: clusterv1alpha1.Pull,
352+
},
353+
},
354+
wCluster: &clusterv1alpha1.Cluster{
355+
ObjectMeta: controllerruntime.ObjectMeta{
356+
Name: "test-cluster",
357+
Finalizers: []string{util.ClusterControllerFinalizer},
358+
},
359+
Spec: clusterv1alpha1.ClusterSpec{
360+
SyncMode: clusterv1alpha1.Pull,
361+
Taints: []corev1.Taint{
362+
{
363+
Key: clusterv1alpha1.TaintClusterUnreachable,
364+
Effect: corev1.TaintEffectNoExecute,
365+
},
366+
},
367+
},
368+
Status: clusterv1alpha1.ClusterStatus{
369+
Conditions: []metav1.Condition{{
370+
Type: clusterv1alpha1.ClusterConditionReady,
371+
Status: metav1.ConditionUnknown,
372+
Reason: "ClusterStatusNeverUpdated",
373+
Message: "Cluster status controller never posted cluster status.",
374+
}},
375+
},
376+
},
377+
wantErr: false,
378+
},
379+
{
380+
name: "cluster with ready condition",
381+
cluster: &clusterv1alpha1.Cluster{
382+
ObjectMeta: controllerruntime.ObjectMeta{
383+
Name: "test-cluster",
384+
Finalizers: []string{util.ClusterControllerFinalizer},
385+
},
386+
Spec: clusterv1alpha1.ClusterSpec{
387+
SyncMode: clusterv1alpha1.Pull,
388+
},
389+
Status: clusterv1alpha1.ClusterStatus{
390+
Conditions: []metav1.Condition{
391+
{
392+
Type: clusterv1alpha1.ClusterConditionReady,
393+
Status: metav1.ConditionTrue,
394+
},
395+
},
396+
},
397+
},
398+
wCluster: &clusterv1alpha1.Cluster{
399+
ObjectMeta: controllerruntime.ObjectMeta{
400+
Name: "test-cluster",
401+
Finalizers: []string{util.ClusterControllerFinalizer},
402+
},
403+
Spec: clusterv1alpha1.ClusterSpec{
404+
SyncMode: clusterv1alpha1.Pull,
405+
Taints: []corev1.Taint{},
406+
},
407+
Status: clusterv1alpha1.ClusterStatus{
408+
Conditions: []metav1.Condition{{
409+
Type: clusterv1alpha1.ClusterConditionReady,
410+
Status: metav1.ConditionUnknown,
411+
Reason: "ClusterStatusUnknown",
412+
Message: "Cluster status controller stopped posting cluster status.",
413+
}},
414+
},
415+
},
416+
wantErr: false,
417+
},
418+
}
419+
for _, tt := range tests {
420+
t.Run(tt.name, func(t *testing.T) {
421+
c := newClusterController()
422+
if tt.cluster != nil {
423+
if err := c.Create(context.Background(), tt.cluster, &client.CreateOptions{}); err != nil {
424+
t.Fatalf("failed to create cluster: %v", err)
425+
}
426+
}
427+
428+
if err := c.monitorClusterHealth(context.Background()); (err != nil) != tt.wantErr {
429+
t.Errorf("Controller.monitorClusterHealth() error = %v, wantErr %v", err, tt.wantErr)
430+
return
431+
}
432+
433+
cluster := &clusterv1alpha1.Cluster{}
434+
if err := c.Get(context.Background(), types.NamespacedName{Name: "test-cluster"}, cluster, &client.GetOptions{}); err != nil {
435+
t.Errorf("failed to get cluster: %v", err)
436+
return
437+
}
438+
439+
cleanUpCluster(cluster)
440+
if !reflect.DeepEqual(cluster, tt.wCluster) {
441+
t.Errorf("Cluster resource get %+v, want %+v", cluster, tt.wantErr)
442+
return
443+
}
444+
})
445+
}
446+
}
447+
448+
// cleanUpCluster removes unnecessary fields from Cluster resource for testing purposes.
449+
func cleanUpCluster(c *clusterv1alpha1.Cluster) {
450+
c.ObjectMeta.ResourceVersion = ""
451+
452+
taints := []corev1.Taint{}
453+
for _, taint := range c.Spec.Taints {
454+
taint.TimeAdded = nil
455+
taints = append(taints, taint)
456+
}
457+
c.Spec.Taints = taints
458+
459+
cond := []metav1.Condition{}
460+
for _, condition := range c.Status.Conditions {
461+
condition.LastTransitionTime = metav1.Time{}
462+
cond = append(cond, condition)
463+
}
464+
c.Status.Conditions = cond
465+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,428 @@
1+
/*
2+
Copyright 2024 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cluster
18+
19+
import (
20+
"context"
21+
"reflect"
22+
"testing"
23+
24+
corev1 "k8s.io/api/core/v1"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/types"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
29+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
30+
31+
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
32+
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
33+
"github.com/karmada-io/karmada/pkg/util"
34+
"github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
35+
"github.com/karmada-io/karmada/pkg/util/gclient"
36+
)
37+
38+
func newNoExecuteTaintManager() *NoExecuteTaintManager {
39+
rbIndexerFunc := func(obj client.Object) []string {
40+
rb, ok := obj.(*workv1alpha2.ResourceBinding)
41+
if !ok {
42+
return nil
43+
}
44+
return util.GetBindingClusterNames(&rb.Spec)
45+
}
46+
47+
crbIndexerFunc := func(obj client.Object) []string {
48+
crb, ok := obj.(*workv1alpha2.ClusterResourceBinding)
49+
if !ok {
50+
return nil
51+
}
52+
return util.GetBindingClusterNames(&crb.Spec)
53+
}
54+
55+
mgr := &NoExecuteTaintManager{
56+
Client: fake.NewClientBuilder().WithScheme(gclient.NewSchema()).
57+
WithIndex(&workv1alpha2.ResourceBinding{}, rbClusterKeyIndex, rbIndexerFunc).
58+
WithIndex(&workv1alpha2.ClusterResourceBinding{}, crbClusterKeyIndex, crbIndexerFunc).Build(),
59+
}
60+
bindingEvictionWorkerOptions := util.Options{
61+
Name: "binding-eviction",
62+
KeyFunc: nil,
63+
ReconcileFunc: mgr.syncBindingEviction,
64+
}
65+
mgr.bindingEvictionWorker = util.NewAsyncWorker(bindingEvictionWorkerOptions)
66+
67+
clusterBindingEvictionWorkerOptions := util.Options{
68+
Name: "cluster-binding-eviction",
69+
KeyFunc: nil,
70+
ReconcileFunc: mgr.syncClusterBindingEviction,
71+
}
72+
mgr.clusterBindingEvictionWorker = util.NewAsyncWorker(clusterBindingEvictionWorkerOptions)
73+
return mgr
74+
}
75+
76+
func TestNoExecuteTaintManager_Reconcile(t *testing.T) {
77+
tests := []struct {
78+
name string
79+
cluster *clusterv1alpha1.Cluster
80+
want reconcile.Result
81+
wantErr bool
82+
}{
83+
{
84+
name: "no taints",
85+
cluster: &clusterv1alpha1.Cluster{
86+
ObjectMeta: metav1.ObjectMeta{
87+
Name: "test-cluster",
88+
},
89+
Spec: clusterv1alpha1.ClusterSpec{},
90+
},
91+
want: reconcile.Result{},
92+
wantErr: false,
93+
},
94+
{
95+
name: "have taints",
96+
cluster: &clusterv1alpha1.Cluster{
97+
ObjectMeta: metav1.ObjectMeta{
98+
Name: "test-cluster",
99+
},
100+
Spec: clusterv1alpha1.ClusterSpec{
101+
Taints: []corev1.Taint{
102+
{
103+
Key: "test-taint",
104+
Value: "test-value",
105+
Effect: corev1.TaintEffectNoExecute,
106+
},
107+
},
108+
},
109+
},
110+
want: reconcile.Result{},
111+
wantErr: false,
112+
},
113+
{
114+
name: "cluster not found",
115+
want: reconcile.Result{},
116+
wantErr: false,
117+
},
118+
}
119+
for _, tt := range tests {
120+
t.Run(tt.name, func(t *testing.T) {
121+
tc := newNoExecuteTaintManager()
122+
if err := tc.Client.Create(context.Background(), &workv1alpha2.ResourceBinding{
123+
TypeMeta: metav1.TypeMeta{
124+
Kind: "ResourceBinding",
125+
APIVersion: "work.karmada.io/v1alpha2",
126+
},
127+
ObjectMeta: metav1.ObjectMeta{
128+
Name: "test-rb",
129+
Namespace: "default",
130+
},
131+
Spec: workv1alpha2.ResourceBindingSpec{
132+
Clusters: []workv1alpha2.TargetCluster{
133+
{
134+
Name: "test-cluster",
135+
Replicas: 1,
136+
},
137+
},
138+
},
139+
}, &client.CreateOptions{}); err != nil {
140+
t.Fatalf("failed to create rb, %v", err)
141+
}
142+
143+
if err := tc.Client.Create(context.Background(), &workv1alpha2.ClusterResourceBinding{
144+
TypeMeta: metav1.TypeMeta{
145+
Kind: "ClusterResourceBinding",
146+
APIVersion: "work.karmada.io/v1alpha2",
147+
},
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "test-crb",
150+
},
151+
Spec: workv1alpha2.ResourceBindingSpec{
152+
Clusters: []workv1alpha2.TargetCluster{
153+
{
154+
Name: "test-cluster",
155+
Replicas: 1,
156+
},
157+
},
158+
},
159+
}, &client.CreateOptions{}); err != nil {
160+
t.Fatalf("failed to create crb, %v", err)
161+
}
162+
163+
if tt.cluster != nil {
164+
if err := tc.Client.Create(context.Background(), tt.cluster, &client.CreateOptions{}); err != nil {
165+
t.Fatal(err)
166+
return
167+
}
168+
}
169+
170+
req := reconcile.Request{NamespacedName: types.NamespacedName{Name: "test-cluster"}}
171+
got, err := tc.Reconcile(context.Background(), req)
172+
if (err != nil) != tt.wantErr {
173+
t.Errorf("NoExecuteTaintManager.Reconcile() error = %v, wantErr %v", err, tt.wantErr)
174+
return
175+
}
176+
if !reflect.DeepEqual(got, tt.want) {
177+
t.Errorf("NoExecuteTaintManager.Reconcile() = %v, want %v", got, tt.want)
178+
}
179+
})
180+
}
181+
}
182+
183+
func TestNoExecuteTaintManager_syncBindingEviction(t *testing.T) {
184+
tests := []struct {
185+
name string
186+
rb *workv1alpha2.ResourceBinding
187+
cluster *clusterv1alpha1.Cluster
188+
wantErr bool
189+
}{
190+
{
191+
name: "rb without tolerations",
192+
rb: &workv1alpha2.ResourceBinding{
193+
TypeMeta: metav1.TypeMeta{
194+
Kind: "ResourceBinding",
195+
APIVersion: "work.karmada.io/v1alpha2",
196+
},
197+
ObjectMeta: metav1.ObjectMeta{
198+
Name: "test-rb",
199+
Namespace: "default",
200+
Annotations: map[string]string{"policy.karmada.io/applied-placement": `{"clusterAffinity":{"clusterNames":["member1","member2"]},"clusterTolerations":[{"key":"cluster.karmada.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":30}],"replicaScheduling":{"replicaSchedulingType":"Divided","replicaDivisionPreference":"Weighted","weightPreference":{"staticWeightList":[{"targetCluster":{"clusterNames":["member1"]},"weight":1},{"targetCluster":{"clusterNames":["member2"]},"weight":1}]}}}`},
201+
},
202+
Spec: workv1alpha2.ResourceBindingSpec{
203+
Clusters: []workv1alpha2.TargetCluster{
204+
{
205+
Name: "test-cluster",
206+
Replicas: 1,
207+
},
208+
},
209+
},
210+
},
211+
cluster: &clusterv1alpha1.Cluster{
212+
ObjectMeta: metav1.ObjectMeta{
213+
Name: "test-cluster",
214+
},
215+
Spec: clusterv1alpha1.ClusterSpec{
216+
Taints: []corev1.Taint{
217+
{
218+
Key: "cluster.karmada.io/not-ready",
219+
Effect: corev1.TaintEffectNoExecute,
220+
},
221+
},
222+
},
223+
},
224+
wantErr: false,
225+
},
226+
{
227+
name: "rb with tolerations",
228+
rb: &workv1alpha2.ResourceBinding{
229+
TypeMeta: metav1.TypeMeta{
230+
Kind: "ResourceBinding",
231+
APIVersion: "work.karmada.io/v1alpha2",
232+
},
233+
ObjectMeta: metav1.ObjectMeta{
234+
Name: "test-rb",
235+
Namespace: "default",
236+
Annotations: map[string]string{"policy.karmada.io/applied-placement": `{"clusterAffinity":{"clusterNames":["member1","member2"]},"clusterTolerations":[{"key":"cluster.karmada.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":30},{"key":"cluster.karmada.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":30}],"replicaScheduling":{"replicaSchedulingType":"Divided","replicaDivisionPreference":"Weighted","weightPreference":{"staticWeightList":[{"targetCluster":{"clusterNames":["member1"]},"weight":1},{"targetCluster":{"clusterNames":["member2"]},"weight":1}]}}}`},
237+
},
238+
Spec: workv1alpha2.ResourceBindingSpec{
239+
Clusters: []workv1alpha2.TargetCluster{
240+
{
241+
Name: "test-cluster",
242+
Replicas: 1,
243+
},
244+
},
245+
},
246+
},
247+
cluster: &clusterv1alpha1.Cluster{
248+
ObjectMeta: metav1.ObjectMeta{
249+
Name: "test-cluster",
250+
},
251+
Spec: clusterv1alpha1.ClusterSpec{
252+
Taints: []corev1.Taint{
253+
{
254+
Key: "cluster.karmada.io/not-ready",
255+
Effect: corev1.TaintEffectNoExecute,
256+
},
257+
},
258+
},
259+
},
260+
wantErr: false,
261+
},
262+
{
263+
name: "rb not exist",
264+
wantErr: false,
265+
},
266+
}
267+
for _, tt := range tests {
268+
t.Run(tt.name, func(t *testing.T) {
269+
tc := newNoExecuteTaintManager()
270+
if tt.rb != nil {
271+
if err := tc.Create(context.Background(), tt.rb, &client.CreateOptions{}); err != nil {
272+
t.Fatalf("failed to create rb: %v", err)
273+
}
274+
}
275+
276+
if tt.cluster != nil {
277+
if err := tc.Create(context.Background(), tt.cluster, &client.CreateOptions{}); err != nil {
278+
t.Fatalf("failed to create cluster: %v", err)
279+
}
280+
}
281+
282+
key := keys.FederatedKey{
283+
Cluster: "test-cluster",
284+
ClusterWideKey: keys.ClusterWideKey{
285+
Kind: "ResourceBinding",
286+
Name: "test-rb",
287+
Namespace: "default",
288+
},
289+
}
290+
if err := tc.syncBindingEviction(key); (err != nil) != tt.wantErr {
291+
t.Errorf("NoExecuteTaintManager.syncBindingEviction() error = %v, wantErr %v", err, tt.wantErr)
292+
}
293+
})
294+
}
295+
}
296+
297+
func TestNoExecuteTaintManager_syncClusterBindingEviction(t *testing.T) {
298+
tests := []struct {
299+
name string
300+
crb *workv1alpha2.ClusterResourceBinding
301+
cluster *clusterv1alpha1.Cluster
302+
wantErr bool
303+
}{
304+
{
305+
name: "crb without tolerations",
306+
crb: &workv1alpha2.ClusterResourceBinding{
307+
TypeMeta: metav1.TypeMeta{
308+
Kind: "ClusterResourceBinding",
309+
APIVersion: "work.karmada.io/v1alpha2",
310+
},
311+
ObjectMeta: metav1.ObjectMeta{
312+
Name: "test-crb",
313+
Annotations: map[string]string{"policy.karmada.io/applied-placement": `{"clusterAffinity":{"clusterNames":["member1","member2"]},"clusterTolerations":[{"key":"cluster.karmada.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":30}],"replicaScheduling":{"replicaSchedulingType":"Divided","replicaDivisionPreference":"Weighted","weightPreference":{"staticWeightList":[{"targetCluster":{"clusterNames":["member1"]},"weight":1},{"targetCluster":{"clusterNames":["member2"]},"weight":1}]}}}`},
314+
},
315+
Spec: workv1alpha2.ResourceBindingSpec{
316+
Clusters: []workv1alpha2.TargetCluster{
317+
{
318+
Name: "test-cluster",
319+
Replicas: 1,
320+
},
321+
},
322+
},
323+
},
324+
cluster: &clusterv1alpha1.Cluster{
325+
ObjectMeta: metav1.ObjectMeta{
326+
Name: "test-cluster",
327+
},
328+
Spec: clusterv1alpha1.ClusterSpec{
329+
Taints: []corev1.Taint{
330+
{
331+
Key: "cluster.karmada.io/not-ready",
332+
Effect: corev1.TaintEffectNoExecute,
333+
},
334+
},
335+
},
336+
},
337+
wantErr: false,
338+
},
339+
{
340+
name: "crb with tolerations",
341+
crb: &workv1alpha2.ClusterResourceBinding{
342+
TypeMeta: metav1.TypeMeta{
343+
Kind: "ClusterResourceBinding",
344+
APIVersion: "work.karmada.io/v1alpha2",
345+
},
346+
ObjectMeta: metav1.ObjectMeta{
347+
Name: "test-crb",
348+
Annotations: map[string]string{"policy.karmada.io/applied-placement": `{"clusterAffinity":{"clusterNames":["member1","member2"]},"clusterTolerations":[{"key":"cluster.karmada.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":30},{"key":"cluster.karmada.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":30}],"replicaScheduling":{"replicaSchedulingType":"Divided","replicaDivisionPreference":"Weighted","weightPreference":{"staticWeightList":[{"targetCluster":{"clusterNames":["member1"]},"weight":1},{"targetCluster":{"clusterNames":["member2"]},"weight":1}]}}}`},
349+
},
350+
Spec: workv1alpha2.ResourceBindingSpec{
351+
Clusters: []workv1alpha2.TargetCluster{
352+
{
353+
Name: "test-cluster",
354+
Replicas: 1,
355+
},
356+
},
357+
},
358+
},
359+
cluster: &clusterv1alpha1.Cluster{
360+
ObjectMeta: metav1.ObjectMeta{
361+
Name: "test-cluster",
362+
},
363+
Spec: clusterv1alpha1.ClusterSpec{
364+
Taints: []corev1.Taint{
365+
{
366+
Key: "cluster.karmada.io/not-ready",
367+
Effect: corev1.TaintEffectNoExecute,
368+
},
369+
},
370+
},
371+
},
372+
wantErr: false,
373+
},
374+
{
375+
name: "crb not exist",
376+
wantErr: false,
377+
},
378+
{
379+
name: "cluster not exist",
380+
crb: &workv1alpha2.ClusterResourceBinding{
381+
TypeMeta: metav1.TypeMeta{
382+
Kind: "ClusterResourceBinding",
383+
APIVersion: "work.karmada.io/v1alpha2",
384+
},
385+
ObjectMeta: metav1.ObjectMeta{
386+
Name: "test-crb",
387+
Annotations: map[string]string{"policy.karmada.io/applied-placement": `{"clusterAffinity":{"clusterNames":["member1","member2"]},"clusterTolerations":[{"key":"cluster.karmada.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":30},{"key":"cluster.karmada.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":30}],"replicaScheduling":{"replicaSchedulingType":"Divided","replicaDivisionPreference":"Weighted","weightPreference":{"staticWeightList":[{"targetCluster":{"clusterNames":["member1"]},"weight":1},{"targetCluster":{"clusterNames":["member2"]},"weight":1}]}}}`},
388+
},
389+
Spec: workv1alpha2.ResourceBindingSpec{
390+
Clusters: []workv1alpha2.TargetCluster{
391+
{
392+
Name: "test-cluster",
393+
Replicas: 1,
394+
},
395+
},
396+
},
397+
},
398+
wantErr: false,
399+
},
400+
}
401+
for _, tt := range tests {
402+
t.Run(tt.name, func(t *testing.T) {
403+
tc := newNoExecuteTaintManager()
404+
if tt.crb != nil {
405+
if err := tc.Create(context.Background(), tt.crb, &client.CreateOptions{}); err != nil {
406+
t.Fatalf("failed to create crb: %v", err)
407+
}
408+
}
409+
410+
if tt.cluster != nil {
411+
if err := tc.Create(context.Background(), tt.cluster, &client.CreateOptions{}); err != nil {
412+
t.Fatalf("failed to create cluster: %v", err)
413+
}
414+
}
415+
416+
key := keys.FederatedKey{
417+
Cluster: "test-cluster",
418+
ClusterWideKey: keys.ClusterWideKey{
419+
Kind: "ClusterResourceBinding",
420+
Name: "test-crb",
421+
},
422+
}
423+
if err := tc.syncClusterBindingEviction(key); (err != nil) != tt.wantErr {
424+
t.Errorf("NoExecuteTaintManager.syncClusterBindingEviction() error = %v, wantErr %v", err, tt.wantErr)
425+
}
426+
})
427+
}
428+
}

0 commit comments

Comments
 (0)
Please sign in to comment.