k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/controlled_pods_indexer_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes 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 util
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	appsv1 "k8s.io/api/apps/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/client-go/informers"
    32  	"k8s.io/client-go/kubernetes/fake"
    33  )
    34  
    35  const (
    36  	ns1 = "namespace-1"
    37  )
    38  
    39  var (
    40  	daemonsetKind  = appsv1.SchemeGroupVersion.WithKind("DaemonSet")
    41  	replicaSetKind = appsv1.SchemeGroupVersion.WithKind("ReplicaSet")
    42  	deploymentKind = appsv1.SchemeGroupVersion.WithKind("Deployment")
    43  	podKind        = corev1.SchemeGroupVersion.WithKind("Pod")
    44  
    45  	daemonset = &appsv1.DaemonSet{
    46  		TypeMeta: metav1.TypeMeta{
    47  			APIVersion: daemonsetKind.GroupVersion().String(),
    48  			Kind:       daemonsetKind.Kind,
    49  		},
    50  		ObjectMeta: metav1.ObjectMeta{
    51  			Name:      "daemonset-1",
    52  			Namespace: ns1,
    53  			UID:       types.UID("uid-1"),
    54  		},
    55  	}
    56  	deployment = &appsv1.Deployment{
    57  		TypeMeta: metav1.TypeMeta{
    58  			APIVersion: deploymentKind.GroupVersion().String(),
    59  			Kind:       deploymentKind.Kind,
    60  		},
    61  		ObjectMeta: metav1.ObjectMeta{
    62  			Name:      "deployment-1",
    63  			Namespace: ns1,
    64  			UID:       types.UID("uid-2"),
    65  		},
    66  	}
    67  
    68  	replicaSet1 = &appsv1.ReplicaSet{
    69  		TypeMeta: metav1.TypeMeta{
    70  			APIVersion: replicaSetKind.GroupVersion().String(),
    71  			Kind:       replicaSetKind.Kind,
    72  		},
    73  		ObjectMeta: metav1.ObjectMeta{
    74  			Name:      "rs-1",
    75  			Namespace: ns1,
    76  			UID:       types.UID("uid-3"),
    77  			OwnerReferences: []metav1.OwnerReference{
    78  				*metav1.NewControllerRef(deployment, deploymentKind),
    79  			},
    80  		},
    81  	}
    82  	replicaSet2 = &appsv1.ReplicaSet{
    83  		TypeMeta: metav1.TypeMeta{
    84  			APIVersion: replicaSetKind.GroupVersion().String(),
    85  			Kind:       replicaSetKind.Kind,
    86  		},
    87  		ObjectMeta: metav1.ObjectMeta{
    88  			Name:      "rs-2",
    89  			Namespace: ns1,
    90  			UID:       types.UID("uid-7"),
    91  			OwnerReferences: []metav1.OwnerReference{
    92  				*metav1.NewControllerRef(deployment, deploymentKind),
    93  			},
    94  		},
    95  	}
    96  
    97  	daemonsetPod = &corev1.Pod{
    98  		TypeMeta: metav1.TypeMeta{
    99  			APIVersion: podKind.GroupVersion().String(),
   100  			Kind:       podKind.Kind,
   101  		},
   102  		ObjectMeta: metav1.ObjectMeta{
   103  			Name:      "pod-1",
   104  			Namespace: ns1,
   105  			UID:       types.UID("uid-4"),
   106  			OwnerReferences: []metav1.OwnerReference{
   107  				*metav1.NewControllerRef(daemonset, daemonsetKind),
   108  			},
   109  		},
   110  	}
   111  
   112  	replicaSetPod1 = &corev1.Pod{
   113  		ObjectMeta: metav1.ObjectMeta{
   114  			Name:      "rs-pod-1",
   115  			Namespace: ns1,
   116  			UID:       types.UID("uid-5"),
   117  			OwnerReferences: []metav1.OwnerReference{
   118  				*metav1.NewControllerRef(replicaSet1, replicaSetKind),
   119  			},
   120  		},
   121  	}
   122  
   123  	replicaSetPod2 = &corev1.Pod{
   124  		ObjectMeta: metav1.ObjectMeta{
   125  			Name:      "rs-pod-2",
   126  			Namespace: ns1,
   127  			UID:       types.UID("uid-6"),
   128  			OwnerReferences: []metav1.OwnerReference{
   129  				*metav1.NewControllerRef(replicaSet2, replicaSetKind),
   130  			},
   131  		},
   132  	}
   133  )
   134  
   135  func toUnstructured(t *testing.T, obj interface{}) *unstructured.Unstructured {
   136  	content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   137  	if err != nil {
   138  		t.Fatalf("failed to convert %T to unstructured: %v", obj, err)
   139  	}
   140  	return &unstructured.Unstructured{Object: content}
   141  }
   142  
   143  func newMockedControlledPodsIndexer(ctx context.Context, t *testing.T, client *fake.Clientset) (*ControlledPodsIndexer, error) {
   144  	informerFactory := informers.NewSharedInformerFactory(client, 0 /* resyncPeriod */)
   145  	podsInformer := informerFactory.Core().V1().Pods()
   146  	rsInformer := informerFactory.Apps().V1().ReplicaSets()
   147  	p, err := NewControlledPodsIndexer(podsInformer, rsInformer)
   148  	if err != nil {
   149  		t.Fatalf("failed to create ControlledPodsIndexer instance: %v", err)
   150  	}
   151  	informerFactory.Start(ctx.Done())
   152  	if !p.WaitForCacheSync(ctx) {
   153  		t.Fatalf("failed to sync informer")
   154  	}
   155  
   156  	return p, nil
   157  }
   158  
   159  func TestControlledPodsIndexer_PodsControlledBy(t *testing.T) {
   160  	var allObjects = []runtime.Object{daemonset, deployment, replicaSet1, replicaSet2, daemonsetPod, replicaSetPod1, replicaSetPod2}
   161  	tests := []struct {
   162  		name    string
   163  		obj     interface{}
   164  		want    []*corev1.Pod
   165  		wantErr bool
   166  	}{
   167  		{
   168  			name: "daemonset",
   169  			obj:  daemonset,
   170  			want: []*corev1.Pod{daemonsetPod},
   171  		},
   172  		{
   173  			name: "replicaset",
   174  			obj:  replicaSet1,
   175  			want: []*corev1.Pod{replicaSetPod1},
   176  		},
   177  		{
   178  			name: "deployment",
   179  			obj:  deployment,
   180  			want: []*corev1.Pod{replicaSetPod1, replicaSetPod2},
   181  		},
   182  		{
   183  			// When we fetch objects using dynamic informer, objects are returend as unstructured.
   184  			// We expect result to be the same as for static-typed deployment.
   185  			name: "deployment as unstructured",
   186  			obj:  toUnstructured(t, deployment),
   187  			want: []*corev1.Pod{replicaSetPod1, replicaSetPod2},
   188  		},
   189  		{
   190  			name: "daemonset as unstructured",
   191  			obj:  toUnstructured(t, daemonset),
   192  			want: []*corev1.Pod{daemonsetPod},
   193  		},
   194  		{
   195  			name:    "unknown type",
   196  			obj:     "string doesn't implement metav1.Object",
   197  			wantErr: true,
   198  		},
   199  	}
   200  	for _, tt := range tests {
   201  		t.Run(tt.name, func(t *testing.T) {
   202  			ctx, cancel := context.WithCancel(context.Background())
   203  			defer cancel()
   204  
   205  			fakeClient := fake.NewSimpleClientset(allObjects...)
   206  			p, err := newMockedControlledPodsIndexer(ctx, t, fakeClient)
   207  			if err != nil {
   208  				t.Fatalf("failed to create ControlledPodsIndexer instance: %v", err)
   209  			}
   210  
   211  			got, err := p.PodsControlledBy(tt.obj)
   212  			if (err != nil) != tt.wantErr {
   213  				t.Errorf("PodsIndexer.PodsControlledBy() error = %v, wantErr %v", err, tt.wantErr)
   214  				return
   215  			}
   216  			assert.ElementsMatch(t, got, tt.want)
   217  		})
   218  	}
   219  }
   220  
   221  func TestControlledPodsIndexer_PodsControlledBy_ReplicasetDeleted(t *testing.T) {
   222  	ctx, cancel := context.WithCancel(context.Background())
   223  	defer cancel()
   224  
   225  	fakeClient := fake.NewSimpleClientset(deployment, replicaSet1, replicaSetPod1)
   226  	p, err := newMockedControlledPodsIndexer(ctx, t, fakeClient)
   227  	if err != nil {
   228  		t.Fatalf("failed to create ControlledPodsIndexer instance: %v", err)
   229  	}
   230  
   231  	if err := fakeClient.AppsV1().Deployments(ns1).Delete(ctx, deployment.Name, metav1.DeleteOptions{}); err != nil {
   232  		t.Fatalf("unexpected error during deployment deletion: %v", err)
   233  	}
   234  	if err := fakeClient.AppsV1().ReplicaSets(ns1).Delete(ctx, replicaSet1.Name, metav1.DeleteOptions{}); err != nil {
   235  		t.Fatalf("unexpected error during replicaset deletion: %v", err)
   236  	}
   237  
   238  	// Sleeping in order for the replicaset informer to catch up with the changes.
   239  	time.Sleep(1 * time.Second)
   240  
   241  	_, exists, err := p.rsIndexer.GetByKey(string(replicaSet1.UID))
   242  	if err != nil {
   243  		t.Fatalf("unexpected error while getting replicaset: %v", err)
   244  	}
   245  
   246  	if !exists {
   247  		t.Errorf("expected replicaSet to exists in store at this point, but exists=%v", exists)
   248  	}
   249  
   250  	want := []*corev1.Pod{replicaSetPod1}
   251  	got, err := p.PodsControlledBy(deployment)
   252  	if err != nil {
   253  		t.Errorf("PodsIndexer.PodsControlledBy() error = %v, wantErr %v", err, nil)
   254  		return
   255  	}
   256  
   257  	assert.ElementsMatch(t, got, want)
   258  
   259  	if err := fakeClient.CoreV1().Pods(ns1).Delete(ctx, replicaSetPod1.Name, metav1.DeleteOptions{}); err != nil {
   260  		t.Fatalf("unexpected error during pod deletion: %v", err)
   261  	}
   262  
   263  	// Sleeping in order for the pod informer to catch up with the changes.
   264  	time.Sleep(1 * time.Second)
   265  
   266  	_, exists, err = p.rsIndexer.GetByKey(string(replicaSet1.UID))
   267  	if err != nil {
   268  		t.Fatalf("unexpected error while getting replicaset: %v", err)
   269  	}
   270  
   271  	if exists {
   272  		t.Errorf("expected replicaSet to be deleted in store at this point, but exists=%v", exists)
   273  	}
   274  }
   275  
   276  func TestControlledPodsIndexer_PodsControlledBy_PodUpdate(t *testing.T) {
   277  	ctx, cancel := context.WithCancel(context.Background())
   278  	defer cancel()
   279  
   280  	fakeClient := fake.NewSimpleClientset(deployment, replicaSet1, replicaSetPod1)
   281  	p, err := newMockedControlledPodsIndexer(ctx, t, fakeClient)
   282  	if err != nil {
   283  		t.Fatalf("failed to create ControlledPodsIndexer instance: %v", err)
   284  	}
   285  
   286  	if err := fakeClient.AppsV1().Deployments(ns1).Delete(ctx, deployment.Name, metav1.DeleteOptions{}); err != nil {
   287  		t.Fatalf("unexpected error during deployment deletion: %v", err)
   288  	}
   289  	if err := fakeClient.AppsV1().ReplicaSets(ns1).Delete(ctx, replicaSet1.Name, metav1.DeleteOptions{}); err != nil {
   290  		t.Fatalf("unexpected error during replicaset deletion: %v", err)
   291  	}
   292  
   293  	// Sleeping in order for the replicaset informer to catch up with the changes.
   294  	time.Sleep(1 * time.Second)
   295  
   296  	changedReplicaSetPod := replicaSetPod1.DeepCopy()
   297  	changedReplicaSetPod.Status.Phase = "Running"
   298  	if _, err := fakeClient.CoreV1().Pods(ns1).Update(ctx, changedReplicaSetPod, metav1.UpdateOptions{}); err != nil {
   299  		t.Fatalf("unexpected error during pod update: %v", err)
   300  	}
   301  
   302  	// Sleeping in order for the pod informer to catch up with the changes.
   303  	time.Sleep(1 * time.Second)
   304  
   305  	want := []*corev1.Pod{changedReplicaSetPod.DeepCopy()}
   306  	got, err := p.PodsControlledBy(deployment)
   307  	if err != nil {
   308  		t.Errorf("PodsIndexer.PodsControlledBy() error = %v, wantErr %v", err, nil)
   309  		return
   310  	}
   311  
   312  	assert.ElementsMatch(t, got, want)
   313  }
   314  
   315  func TestControlledPodsIndexer_PodsControlledBy_PodUpdateUID(t *testing.T) {
   316  	ctx, cancel := context.WithCancel(context.Background())
   317  	defer cancel()
   318  
   319  	fakeClient := fake.NewSimpleClientset(deployment, replicaSet1, replicaSetPod1)
   320  	p, err := newMockedControlledPodsIndexer(ctx, t, fakeClient)
   321  	if err != nil {
   322  		t.Fatalf("failed to create ControlledPodsIndexer instance: %v", err)
   323  	}
   324  
   325  	if err := fakeClient.AppsV1().Deployments(ns1).Delete(ctx, deployment.Name, metav1.DeleteOptions{}); err != nil {
   326  		t.Fatalf("unexpected error during deployment deletion: %v", err)
   327  	}
   328  	if err := fakeClient.AppsV1().ReplicaSets(ns1).Delete(ctx, replicaSet1.Name, metav1.DeleteOptions{}); err != nil {
   329  		t.Fatalf("unexpected error during replicaset deletion: %v", err)
   330  	}
   331  
   332  	// Sleeping in order for the replicaset informer to catch up with the changes.
   333  	time.Sleep(1 * time.Second)
   334  
   335  	changedReplicaSetPod := replicaSetPod1.DeepCopy()
   336  	changedReplicaSetPod.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
   337  		{
   338  			UID: "some-other-uid",
   339  		},
   340  	}
   341  	if _, err := fakeClient.CoreV1().Pods(ns1).Update(ctx, changedReplicaSetPod, metav1.UpdateOptions{}); err != nil {
   342  		t.Fatalf("unexpected error during pod update: %v", err)
   343  	}
   344  
   345  	// Sleeping in order for the pod informer to catch up with the changes.
   346  	time.Sleep(1 * time.Second)
   347  
   348  	want := []*corev1.Pod{}
   349  	got, err := p.PodsControlledBy(deployment)
   350  	if err != nil {
   351  		t.Errorf("PodsIndexer.PodsControlledBy() error = %v, wantErr %v", err, nil)
   352  		return
   353  	}
   354  
   355  	assert.ElementsMatch(t, got, want)
   356  
   357  	_, exists, err := p.rsIndexer.GetByKey(string(replicaSet1.UID))
   358  	if err != nil {
   359  		t.Fatalf("unexpected error while getting replicaset: %v", err)
   360  	}
   361  
   362  	if exists {
   363  		t.Errorf("expected replicaSet to be deleted in store at this point, but exists=%v", exists)
   364  	}
   365  }