/* Copyright 2022 The Karmada Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package helper import ( "reflect" "testing" "time" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" ) var ( unreachableTaintTemplate = &corev1.Taint{ Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, } notReadyTaintTemplate = &corev1.Taint{ Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, } ) func TestSetCurrentClusterTaints(t *testing.T) { type args struct { taints []corev1.Taint taintsToAdd []*corev1.Taint taintsToRemove []*corev1.Taint } tests := []struct { name string args args wantTaints []corev1.Taint }{ { name: "ready condition from true to false", args: args{ taints: nil, taintsToAdd: []*corev1.Taint{notReadyTaintTemplate.DeepCopy()}, taintsToRemove: []*corev1.Taint{unreachableTaintTemplate.DeepCopy()}, }, wantTaints: []corev1.Taint{*notReadyTaintTemplate}, }, { name: "ready condition from true to unknown", args: args{ taints: nil, taintsToAdd: []*corev1.Taint{unreachableTaintTemplate.DeepCopy()}, taintsToRemove: []*corev1.Taint{notReadyTaintTemplate.DeepCopy()}, }, wantTaints: []corev1.Taint{*unreachableTaintTemplate}, }, { name: "ready condition from false to unknown", args: args{ taints: []corev1.Taint{*notReadyTaintTemplate}, taintsToAdd: []*corev1.Taint{unreachableTaintTemplate.DeepCopy()}, taintsToRemove: []*corev1.Taint{notReadyTaintTemplate.DeepCopy()}, }, wantTaints: []corev1.Taint{*unreachableTaintTemplate}, }, { name: "ready condition from false to true", args: args{ taints: []corev1.Taint{*notReadyTaintTemplate}, taintsToAdd: []*corev1.Taint{}, taintsToRemove: []*corev1.Taint{notReadyTaintTemplate.DeepCopy(), unreachableTaintTemplate.DeepCopy()}, }, wantTaints: nil, }, { name: "ready condition from unknown to true", args: args{ taints: []corev1.Taint{*unreachableTaintTemplate}, taintsToAdd: []*corev1.Taint{}, taintsToRemove: []*corev1.Taint{notReadyTaintTemplate.DeepCopy(), unreachableTaintTemplate.DeepCopy()}, }, wantTaints: nil, }, { name: "ready condition from unknown to false", args: args{ taints: []corev1.Taint{*unreachableTaintTemplate}, taintsToAdd: []*corev1.Taint{notReadyTaintTemplate.DeepCopy()}, taintsToRemove: []*corev1.Taint{unreachableTaintTemplate.DeepCopy()}, }, wantTaints: []corev1.Taint{*notReadyTaintTemplate}, }, { name: "clusterTaintsToAdd is nil and clusterTaintsToRemove is nil", args: args{ taints: []corev1.Taint{*unreachableTaintTemplate}, taintsToAdd: []*corev1.Taint{unreachableTaintTemplate.DeepCopy()}, taintsToRemove: []*corev1.Taint{notReadyTaintTemplate.DeepCopy()}, }, wantTaints: []corev1.Taint{*unreachableTaintTemplate}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cluster := &clusterv1alpha1.Cluster{ ObjectMeta: metav1.ObjectMeta{Name: "member"}, Spec: clusterv1alpha1.ClusterSpec{ Taints: tt.args.taints, }, } taints := SetCurrentClusterTaints(tt.args.taintsToAdd, tt.args.taintsToRemove, cluster) if len(taints) != len(tt.wantTaints) { t.Errorf("Cluster gotTaints = %v, want %v", taints, tt.wantTaints) } for i := range taints { if taints[i].Key != tt.wantTaints[i].Key || taints[i].Value != tt.wantTaints[i].Value || taints[i].Effect != tt.wantTaints[i].Effect { t.Errorf("Cluster gotTaints = %v, want %v", taints, tt.wantTaints) } } }) } } func TestTaintExists(t *testing.T) { type args struct { taints []corev1.Taint taintToFind *corev1.Taint } tests := []struct { name string args args want bool }{ { name: "exist", args: args{ taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, }, }, taintToFind: &corev1.Taint{ Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, }, want: true, }, { name: "not exist", args: args{ taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, }, }, taintToFind: &corev1.Taint{ Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := TaintExists(tt.args.taints, tt.args.taintToFind); got != tt.want { t.Errorf("TaintExists() = %v, want %v", got, tt.want) } }) } } func TestTolerationExists(t *testing.T) { type args struct { tolerations []corev1.Toleration tolerationToFind *corev1.Toleration } tests := []struct { name string args args want bool }{ { name: "not exist", args: args{ tolerations: []corev1.Toleration{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpEqual, Value: "foo", }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpEqual, Value: "foo", }, }, tolerationToFind: &corev1.Toleration{ Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, Operator: corev1.TolerationOpEqual, Value: "foo", }, }, want: false, }, { name: "exist", args: args{ tolerations: []corev1.Toleration{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpEqual, Value: "foo", }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpEqual, Value: "foo", }, }, tolerationToFind: &corev1.Toleration{ Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpEqual, Value: "foo", }, }, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := TolerationExists(tt.args.tolerations, tt.args.tolerationToFind); got != tt.want { t.Errorf("TolerationExists() = %v, want %v", got, tt.want) } }) } } func TestAddTolerations(t *testing.T) { placement := &policyv1alpha1.Placement{ ClusterTolerations: []corev1.Toleration{}, } toleration1 := &corev1.Toleration{ Key: "key1", Operator: corev1.TolerationOpEqual, Value: "value1", Effect: corev1.TaintEffectNoSchedule, } toleration2 := &corev1.Toleration{ Key: "key2", Operator: corev1.TolerationOpEqual, Value: "value2", Effect: corev1.TaintEffectNoSchedule, } AddTolerations(placement, toleration1, toleration2) assert.Equal(t, 2, len(placement.ClusterTolerations)) assert.Equal(t, *toleration1, placement.ClusterTolerations[0]) assert.Equal(t, *toleration2, placement.ClusterTolerations[1]) } func TestHasNoExecuteTaints(t *testing.T) { tests := []struct { name string taints []corev1.Taint want bool }{ { name: "has NoExecute taints", taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, }, }, want: true, }, { name: "no NoExecute taints", taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectPreferNoSchedule, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := HasNoExecuteTaints(tt.taints); got != tt.want { t.Errorf("HasNoExecuteTaints() = %v, want %v", got, tt.want) } }) } } func TestGetNoExecuteTaints(t *testing.T) { tests := []struct { name string taints []corev1.Taint want []corev1.Taint }{ { name: "has NoExecute taints", taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, }, }, want: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectNoExecute, }, }, }, { name: "no NoExecute taints", taints: []corev1.Taint{ { Key: clusterv1alpha1.TaintClusterUnreachable, Effect: corev1.TaintEffectPreferNoSchedule, }, { Key: clusterv1alpha1.TaintClusterNotReady, Effect: corev1.TaintEffectNoSchedule, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := GetNoExecuteTaints(tt.taints); !reflect.DeepEqual(got, tt.want) { t.Errorf("GetNoExecuteTaints() = %v, want %v", got, tt.want) } }) } } func TestGetMinTolerationTime(t *testing.T) { tests := []struct { name string noExecuteTaints []corev1.Taint usedTolerantion []corev1.Toleration wantResult time.Duration }{ { name: "no noExecuteTaints", noExecuteTaints: []corev1.Taint{}, usedTolerantion: []corev1.Toleration{ { Key: "key", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0], }, }, wantResult: -1, }, { name: "no usedTolerations", noExecuteTaints: []corev1.Taint{ { Key: "key", Value: "value", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{Time: time.Now()}, }, }, usedTolerantion: []corev1.Toleration{}, wantResult: 0, }, { name: "with noExecuteTaints and usedTolerations", noExecuteTaints: []corev1.Taint{ { Key: "key", Value: "value", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{Time: time.Now()}, }, }, usedTolerantion: []corev1.Toleration{ { Key: "key", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0], }, }, wantResult: 60, }, { name: "usedTolerantion.TolerationSeconds is nil", noExecuteTaints: []corev1.Taint{ { Key: "key", Value: "value", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{Time: time.Now()}, }, }, usedTolerantion: []corev1.Toleration{ { Key: "key", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: nil, }, }, wantResult: -1, }, { name: "noExecuteTaints.TimeAdded is nil", noExecuteTaints: []corev1.Taint{ { Key: "key", Value: "value", Effect: corev1.TaintEffectNoExecute, TimeAdded: nil, }, }, usedTolerantion: []corev1.Toleration{ { Key: "key", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0], }, }, wantResult: -1, }, { name: "find the latest trigger time", noExecuteTaints: []corev1.Taint{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{Time: time.Now()}, }, { Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{Time: time.Now()}, }, }, usedTolerantion: []corev1.Toleration{ { Key: "key1", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{120}[0], }, { Key: "key2", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0], }, }, wantResult: 60, }, { name: "trigger time is up", noExecuteTaints: []corev1.Taint{ { Key: "key", Value: "value", Effect: corev1.TaintEffectNoExecute, TimeAdded: &metav1.Time{ Time: time.Now().Add(-time.Hour), }, }, }, usedTolerantion: []corev1.Toleration{ { Key: "key", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &[]int64{60}[0], }, }, wantResult: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := GetMinTolerationTime(tt.noExecuteTaints, tt.usedTolerantion) if result > 0 { if result > (tt.wantResult+1)*time.Second || result < (tt.wantResult-1)*time.Second { t.Errorf("GetMinTolerationTime() = %v, want %v", result, tt.wantResult) } } else if result != tt.wantResult { t.Errorf("GetMinTolerationTime() = %v, want %v", result, tt.wantResult) } }) } } func TestGetMatchingTolerations(t *testing.T) { tests := []struct { name string taints []corev1.Taint tolerations []corev1.Toleration wantActual bool wantActualTolerations []corev1.Toleration }{ { name: "taints is nil", taints: []corev1.Taint{}, tolerations: []corev1.Toleration{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, }, wantActual: true, wantActualTolerations: []corev1.Toleration{}, }, { name: "tolerations is nil", taints: []corev1.Taint{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, }, tolerations: []corev1.Toleration{}, wantActual: false, wantActualTolerations: []corev1.Toleration{}, }, { name: "tolerated is true", taints: []corev1.Taint{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, { Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoSchedule, }, }, tolerations: []corev1.Toleration{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, { Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoSchedule, }, }, wantActual: true, wantActualTolerations: []corev1.Toleration{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, { Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoSchedule, }, }, }, { name: "tolerated is false", taints: []corev1.Taint{ { Key: "key1", Value: "value1", Effect: corev1.TaintEffectNoSchedule, }, { Key: "key2", Value: "value2", Effect: corev1.TaintEffectNoSchedule, }, }, tolerations: []corev1.Toleration{ { Key: "key1", Value: "value_1", Effect: corev1.TaintEffectNoSchedule, }, { Key: "key2", Value: "value_2", Effect: corev1.TaintEffectNoSchedule, }, }, wantActual: false, wantActualTolerations: []corev1.Toleration{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { actual, actualTolerations := GetMatchingTolerations(tt.taints, tt.tolerations) if actual != tt.wantActual || !reflect.DeepEqual(actualTolerations, tt.wantActualTolerations) { t.Errorf("GetMatchingTolerations(%v, %v) = (%v, %v), expected (%v, %v)", tt.taints, tt.tolerations, actual, actualTolerations, tt.wantActual, tt.wantActualTolerations) } }) } } func TestNewNotReadyToleration(t *testing.T) { expectedKey := clusterv1alpha1.TaintClusterNotReady expectedOperator := corev1.TolerationOpExists expectedEffect := corev1.TaintEffectNoExecute expectedSeconds := int64(123) toleration := NewNotReadyToleration(expectedSeconds) if toleration.Key != expectedKey { t.Errorf("Expected key %q but got %q", expectedKey, toleration.Key) } if toleration.Operator != expectedOperator { t.Errorf("Expected operator %q but got %q", expectedOperator, toleration.Operator) } if toleration.Effect != expectedEffect { t.Errorf("Expected effect %q but got %q", expectedEffect, toleration.Effect) } if *toleration.TolerationSeconds != expectedSeconds { t.Errorf("Expected seconds %d but got %d", expectedSeconds, *toleration.TolerationSeconds) } } func TestNewUnreachableToleration(t *testing.T) { tolerationSeconds := int64(300) expectedToleration := &corev1.Toleration{ Key: clusterv1alpha1.TaintClusterUnreachable, Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &tolerationSeconds, } actualToleration := NewUnreachableToleration(tolerationSeconds) if !reflect.DeepEqual(actualToleration, expectedToleration) { t.Errorf("NewUnreachableToleration() = %v, want %v", actualToleration, expectedToleration) } }