Improved test coverage for preemption in pkg/detector

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
This commit is contained in:
Nishant Bansal 2024-08-23 00:50:49 +05:30
parent d4bfbb5a70
commit 134b29cd60
1 changed files with 651 additions and 0 deletions

View File

@ -0,0 +1,651 @@
/*
Copyright 2024 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 detector
import (
"fmt"
"testing"
"time"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
dynamicfake "k8s.io/client-go/dynamic/fake"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
)
type MockAsyncWorker struct {
queue []interface{}
}
// Note: This is a dummy implementation of Add for testing purposes.
func (m *MockAsyncWorker) Add(item interface{}) {
// No actual work is done in the mock; we just simulate running
m.queue = append(m.queue, item)
}
// Note: This is a dummy implementation of AddAfter for testing purposes.
func (m *MockAsyncWorker) AddAfter(item interface{}, duration time.Duration) {
// No actual work is done in the mock; we just simulate running
fmt.Printf("%v", duration)
m.queue = append(m.queue, item)
}
// Note: This is a dummy implementation of Enqueue for testing purposes.
func (m *MockAsyncWorker) Enqueue(obj interface{}) {
// Assuming KeyFunc is used to generate a key; for simplicity, we use obj directly
m.queue = append(m.queue, obj)
}
// Note: This is a dummy implementation of Run for testing purposes.
func (m *MockAsyncWorker) Run(workerNumber int, stopChan <-chan struct{}) {
// No actual work is done in the mock; we just simulate running
fmt.Printf("%v", workerNumber)
fmt.Printf("%v", <-stopChan)
}
// GetQueue returns the current state of the queue
func (m *MockAsyncWorker) GetQueue() []interface{} {
return m.queue
}
func TestHandleDeprioritizedPropagationPolicy(t *testing.T) {
scheme := runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(appsv1.AddToScheme(scheme))
utilruntime.Must(v1alpha2.Install(scheme))
utilruntime.Must(policyv1alpha1.Install(scheme))
propagationPolicyGVR := schema.GroupVersionResource{
Group: policyv1alpha1.GroupVersion.Group,
Version: policyv1alpha1.GroupVersion.Version,
Resource: policyv1alpha1.ResourcePluralPropagationPolicy,
}
tests := []struct {
name string
newPolicy *policyv1alpha1.PropagationPolicy
oldPolicy *policyv1alpha1.PropagationPolicy
objects []runtime.Object
setupClient func() *fake.ClientBuilder
wantQueueSize int
}{
{
name: "preempt deprioritized propagation policy of len 1",
newPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: []runtime.Object{
&policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
},
setupClient: func() *fake.ClientBuilder {
obj := &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(obj)
},
wantQueueSize: 1,
},
{
name: "preempt deprioritized propagation policy of len 2",
newPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](5),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: []runtime.Object{
&policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
&policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-2",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-2",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test-2",
Name: "default-2",
},
},
},
},
},
setupClient: func() *fake.ClientBuilder {
obj := []client.Object{
&policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
&policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-2",
Namespace: "test",
Labels: map[string]string{
policyv1alpha1.PropagationPolicyPermanentIDLabel: "policy-2",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test-2",
Name: "default-2",
},
},
},
},
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(obj...)
},
wantQueueSize: 2,
},
{
name: "no policy to preempt",
newPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: nil,
setupClient: func() *fake.ClientBuilder {
return fake.NewClientBuilder().WithScheme(scheme)
},
wantQueueSize: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient := tt.setupClient().Build()
stopCh := make(chan struct{})
defer close(stopCh)
fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(scheme, tt.objects...)
genMgr := genericmanager.NewSingleClusterInformerManager(fakeDynamicClient, 0, stopCh)
resourceDetector := &ResourceDetector{
Client: fakeClient,
DynamicClient: fakeDynamicClient,
InformerManager: genMgr,
propagationPolicyLister: genMgr.Lister(propagationPolicyGVR),
}
mockWorker := &MockAsyncWorker{}
resourceDetector.policyReconcileWorker = mockWorker
resourceDetector.InformerManager.Start()
resourceDetector.InformerManager.WaitForCacheSync()
resourceDetector.HandleDeprioritizedPropagationPolicy(*tt.oldPolicy, *tt.newPolicy)
gotQueueSize := len(mockWorker.GetQueue())
if gotQueueSize != tt.wantQueueSize {
t.Errorf("HandleDeprioritizedPropagationPolicy() want queue size %v, got %v", tt.wantQueueSize, gotQueueSize)
}
})
}
}
func TestHandleDeprioritizedClusterPropagationPolicy(t *testing.T) {
scheme := runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(appsv1.AddToScheme(scheme))
utilruntime.Must(v1alpha2.Install(scheme))
utilruntime.Must(policyv1alpha1.Install(scheme))
clusterPropagationPolicyGVR := schema.GroupVersionResource{
Group: policyv1alpha1.GroupVersion.Group,
Version: policyv1alpha1.GroupVersion.Version,
Resource: policyv1alpha1.ResourcePluralClusterPropagationPolicy,
}
tests := []struct {
name string
newPolicy *policyv1alpha1.ClusterPropagationPolicy
oldPolicy *policyv1alpha1.ClusterPropagationPolicy
objects []runtime.Object
setupClient func() *fake.ClientBuilder
wantQueueSize int
}{
{
name: "preempt deprioritized cluster propagation policy of len 1",
newPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: []runtime.Object{
&policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
},
setupClient: func() *fake.ClientBuilder {
obj := &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(obj)
},
wantQueueSize: 1,
},
{
name: "preempt deprioritized cluster propagation policy of len 2",
newPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](5),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: []runtime.Object{
&policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
&policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-2",
Namespace: "bar-2",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-2",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test-2",
Name: "default-2",
},
},
},
},
},
setupClient: func() *fake.ClientBuilder {
obj := []client.Object{
&policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-1",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](3),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
Name: "default",
},
},
},
},
&policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-2",
Namespace: "bar-2",
Labels: map[string]string{
policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: "policy-2",
},
},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
Preemption: policyv1alpha1.PreemptAlways,
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test-2",
Name: "default-2",
},
},
},
},
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(obj...)
},
wantQueueSize: 2,
},
{
name: "no cluster policy to preempt",
newPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](2),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
oldPolicy: &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "test"},
Spec: policyv1alpha1.PropagationSpec{
Priority: ptr.To[int32](4),
ResourceSelectors: []policyv1alpha1.ResourceSelector{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "test",
LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
},
},
},
},
objects: nil,
setupClient: func() *fake.ClientBuilder {
return fake.NewClientBuilder().WithScheme(scheme)
},
wantQueueSize: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient := tt.setupClient().Build()
stopCh := make(chan struct{})
defer close(stopCh)
fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(scheme, tt.objects...)
genMgr := genericmanager.NewSingleClusterInformerManager(fakeDynamicClient, 0, stopCh)
resourceDetector := &ResourceDetector{
Client: fakeClient,
DynamicClient: fakeDynamicClient,
InformerManager: genMgr,
clusterPropagationPolicyLister: genMgr.Lister(clusterPropagationPolicyGVR),
}
mockWorker := &MockAsyncWorker{}
resourceDetector.clusterPolicyReconcileWorker = mockWorker
resourceDetector.InformerManager.Start()
resourceDetector.InformerManager.WaitForCacheSync()
resourceDetector.HandleDeprioritizedClusterPropagationPolicy(*tt.oldPolicy, *tt.newPolicy)
gotQueueSize := len(mockWorker.GetQueue())
if gotQueueSize != tt.wantQueueSize {
t.Errorf("HandleDeprioritizedClusterPropagationPolicy() want queue size %v, got %v", tt.wantQueueSize, gotQueueSize)
}
})
}
}