mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-28 06:29:29 +01:00
test(kubeClientSandbox): more unit tests
This commit is contained in:
@@ -19,35 +19,34 @@ package descheduler
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policy "k8s.io/api/policy/v1"
|
policy "k8s.io/api/policy/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"sigs.k8s.io/descheduler/test"
|
"sigs.k8s.io/descheduler/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKubeClientSandboxReset(t *testing.T) {
|
// setupTestSandbox creates and initializes a kubeClientSandbox for testing.
|
||||||
ctx := context.Background()
|
// It creates a fake client, informer factory, registers pod and node informers,
|
||||||
node1 := test.BuildTestNode("n1", 2000, 3000, 10, nil)
|
// creates the sandbox, and starts both factories.
|
||||||
p1 := test.BuildTestPod("p1", 100, 0, node1.Name, test.SetRSOwnerRef)
|
func setupTestSandbox(ctx context.Context, t *testing.T, initialObjects ...runtime.Object) (*kubeClientSandbox, informers.SharedInformerFactory, clientset.Interface) {
|
||||||
p2 := test.BuildTestPod("p2", 100, 0, node1.Name, test.SetRSOwnerRef)
|
// Create a "real" fake client to act as the source of truth
|
||||||
|
realClient := fakeclientset.NewSimpleClientset(initialObjects...)
|
||||||
|
realFactory := informers.NewSharedInformerFactoryWithOptions(realClient, 0)
|
||||||
|
|
||||||
client := fakeclientset.NewSimpleClientset(node1, p1, p2)
|
// Register pods and nodes informers BEFORE creating the sandbox
|
||||||
sharedInformerFactory := informers.NewSharedInformerFactoryWithOptions(client, 0)
|
_ = realFactory.Core().V1().Pods().Informer()
|
||||||
|
_ = realFactory.Core().V1().Nodes().Informer()
|
||||||
|
|
||||||
// Explicitly get the informers to ensure they're registered
|
// Create the sandbox with only pods and nodes resources
|
||||||
_ = sharedInformerFactory.Core().V1().Pods().Informer()
|
sandbox, err := newKubeClientSandbox(realClient, realFactory,
|
||||||
_ = sharedInformerFactory.Core().V1().Nodes().Informer()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
sharedInformerFactory.Start(ctx.Done())
|
|
||||||
sharedInformerFactory.WaitForCacheSync(ctx.Done())
|
|
||||||
|
|
||||||
sandbox, err := newKubeClientSandbox(client, sharedInformerFactory,
|
|
||||||
v1.SchemeGroupVersion.WithResource("pods"),
|
v1.SchemeGroupVersion.WithResource("pods"),
|
||||||
v1.SchemeGroupVersion.WithResource("nodes"),
|
v1.SchemeGroupVersion.WithResource("nodes"),
|
||||||
)
|
)
|
||||||
@@ -55,6 +54,179 @@ func TestKubeClientSandboxReset(t *testing.T) {
|
|||||||
t.Fatalf("Failed to create kubeClientSandbox: %v", err)
|
t.Fatalf("Failed to create kubeClientSandbox: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fake factory created by newKubeClientSandbox needs to be started before
|
||||||
|
// the "real" fake client factory to have all handlers registered
|
||||||
|
// to get complete propagation
|
||||||
|
sandbox.fakeSharedInformerFactory().Start(ctx.Done())
|
||||||
|
sandbox.fakeSharedInformerFactory().WaitForCacheSync(ctx.Done())
|
||||||
|
|
||||||
|
realFactory.Start(ctx.Done())
|
||||||
|
realFactory.WaitForCacheSync(ctx.Done())
|
||||||
|
|
||||||
|
return sandbox, realFactory, realClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKubeClientSandboxEventHandlers(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
sandbox, realFactory, realClient := setupTestSandbox(ctx, t)
|
||||||
|
|
||||||
|
// Register a third resource (secrets) in the real factory AFTER sandbox creation
|
||||||
|
// This should NOT be synced to the fake client
|
||||||
|
_ = realFactory.Core().V1().Secrets().Informer()
|
||||||
|
|
||||||
|
// Create test objects
|
||||||
|
testPod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-pod",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: "pod-uid-12345",
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
NodeName: "test-node",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testNode := &v1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-node",
|
||||||
|
UID: "node-uid-67890",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testSecret := &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: "secret-uid-abcde",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"key": []byte("value"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add objects to the real client
|
||||||
|
var err error
|
||||||
|
_, err = realClient.CoreV1().Pods(testPod.Namespace).Create(ctx, testPod, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create pod in real client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = realClient.CoreV1().Nodes().Create(ctx, testNode, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create node in real client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = realClient.CoreV1().Secrets(testSecret.Namespace).Create(ctx, testSecret, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create secret in real client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to wait for a resource to appear in the sandbox's fake client indexer
|
||||||
|
waitForResourceInIndexer := func(resourceType, namespace, name, description string) error {
|
||||||
|
t.Logf("Waiting for %s to appear in fake client indexer...", description)
|
||||||
|
return wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||||
|
exists, err := sandbox.hasObjectInIndexer(
|
||||||
|
v1.SchemeGroupVersion.WithResource(resourceType),
|
||||||
|
namespace,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Error checking %s in indexer: %v", description, err)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
t.Logf("%s appeared in fake client indexer", description)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the pod to appear in the sandbox's fake client indexer
|
||||||
|
if err := waitForResourceInIndexer("pods", testPod.Namespace, testPod.Name, "pod"); err != nil {
|
||||||
|
t.Fatalf("Pod did not appear in fake client indexer within timeout: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the node to appear in the sandbox's fake client indexer
|
||||||
|
if err := waitForResourceInIndexer("nodes", "", testNode.Name, "node"); err != nil {
|
||||||
|
t.Fatalf("Node did not appear in fake client indexer within timeout: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the pod can be retrieved from the fake client
|
||||||
|
retrievedPod, err := sandbox.fakeClient().CoreV1().Pods(testPod.Namespace).Get(ctx, testPod.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to retrieve pod from fake client: %v", err)
|
||||||
|
}
|
||||||
|
if retrievedPod.Namespace != testPod.Namespace || retrievedPod.Name != testPod.Name || retrievedPod.UID != testPod.UID {
|
||||||
|
t.Errorf("Retrieved pod mismatch: got namespace=%s name=%s uid=%s, want namespace=%s name=%s uid=%s",
|
||||||
|
retrievedPod.Namespace, retrievedPod.Name, retrievedPod.UID, testPod.Namespace, testPod.Name, testPod.UID)
|
||||||
|
}
|
||||||
|
t.Logf("Successfully retrieved pod from fake client: %s/%s", retrievedPod.Namespace, retrievedPod.Name)
|
||||||
|
|
||||||
|
// Verify the node can be retrieved from the fake client
|
||||||
|
retrievedNode, err := sandbox.fakeClient().CoreV1().Nodes().Get(ctx, testNode.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to retrieve node from fake client: %v", err)
|
||||||
|
}
|
||||||
|
if retrievedNode.Name != testNode.Name || retrievedNode.UID != testNode.UID {
|
||||||
|
t.Errorf("Retrieved node mismatch: got name=%s uid=%s, want name=%s uid=%s",
|
||||||
|
retrievedNode.Name, retrievedNode.UID, testNode.Name, testNode.UID)
|
||||||
|
}
|
||||||
|
t.Logf("Successfully retrieved node from fake client: %s", retrievedNode.Name)
|
||||||
|
|
||||||
|
// Wait a bit longer and verify the secret does NOT appear in the fake client indexer
|
||||||
|
// because secrets were registered AFTER the sandbox was created
|
||||||
|
t.Log("Verifying secret does NOT appear in fake client indexer...")
|
||||||
|
time.Sleep(500 * time.Millisecond) // Give extra time to ensure it's not just a timing issue
|
||||||
|
|
||||||
|
// First, verify we can get the informer for secrets in the fake factory
|
||||||
|
secretInformer, err := sandbox.fakeSharedInformerFactory().ForResource(v1.SchemeGroupVersion.WithResource("secrets"))
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Expected: Cannot get secret informer from fake factory: %v", err)
|
||||||
|
} else {
|
||||||
|
// If we can get the informer, check if the secret exists in it
|
||||||
|
key := "default/test-secret"
|
||||||
|
_, exists, err := secretInformer.Informer().GetIndexer().GetByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Error checking secret in fake indexer: %v", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
t.Error("Secret should NOT exist in fake client indexer (it was registered after sandbox creation)")
|
||||||
|
} else {
|
||||||
|
t.Log("Correctly verified: Secret does not exist in fake client indexer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also verify that attempting to get the secret directly from fake client should fail
|
||||||
|
_, err = sandbox.fakeClient().CoreV1().Secrets(testSecret.Namespace).Get(ctx, testSecret.Name, metav1.GetOptions{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Secret should NOT be retrievable from fake client (it was not synced)")
|
||||||
|
} else {
|
||||||
|
t.Logf("Correctly verified: Secret not retrievable from fake client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the secret IS in the real client (sanity check)
|
||||||
|
_, err = realClient.CoreV1().Secrets(testSecret.Namespace).Get(ctx, testSecret.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Secret should exist in real client but got error: %v", err)
|
||||||
|
}
|
||||||
|
t.Log("Sanity check passed: Secret exists in real client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKubeClientSandboxReset(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
node1 := test.BuildTestNode("n1", 2000, 3000, 10, nil)
|
||||||
|
p1 := test.BuildTestPod("p1", 100, 0, node1.Name, test.SetRSOwnerRef)
|
||||||
|
p2 := test.BuildTestPod("p2", 100, 0, node1.Name, test.SetRSOwnerRef)
|
||||||
|
|
||||||
|
sandbox, _, _ := setupTestSandbox(ctx, t, node1, p1, p2)
|
||||||
|
|
||||||
eviction1 := &policy.Eviction{
|
eviction1 := &policy.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: p1.Name,
|
Name: p1.Name,
|
||||||
@@ -68,9 +240,6 @@ func TestKubeClientSandboxReset(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sandbox.fakeSharedInformerFactory().Start(ctx.Done())
|
|
||||||
sandbox.fakeSharedInformerFactory().WaitForCacheSync(ctx.Done())
|
|
||||||
|
|
||||||
if err := sandbox.fakeClient().CoreV1().Pods(p1.Namespace).EvictV1(context.TODO(), eviction1); err != nil {
|
if err := sandbox.fakeClient().CoreV1().Pods(p1.Namespace).EvictV1(context.TODO(), eviction1); err != nil {
|
||||||
t.Fatalf("Error evicting p1: %v", err)
|
t.Fatalf("Error evicting p1: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user