...

Source file src/k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumezone/volume_zone_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumezone

     1  /*
     2  Copyright 2019 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 volumezone
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	v1 "k8s.io/api/core/v1"
    26  	storagev1 "k8s.io/api/storage/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/klog/v2/ktesting"
    30  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    31  	"k8s.io/kubernetes/pkg/scheduler/framework"
    32  	plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing"
    33  	"k8s.io/kubernetes/pkg/scheduler/internal/cache"
    34  	st "k8s.io/kubernetes/pkg/scheduler/testing"
    35  	tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
    36  )
    37  
    38  func createPodWithVolume(pod, pvc string) *v1.Pod {
    39  	return st.MakePod().Name(pod).Namespace(metav1.NamespaceDefault).PVC(pvc).Obj()
    40  }
    41  
    42  func TestSingleZone(t *testing.T) {
    43  	pvLister := tf.PersistentVolumeLister{
    44  		{
    45  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_1", Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a"}},
    46  		},
    47  		{
    48  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_2", Labels: map[string]string{v1.LabelFailureDomainBetaRegion: "us-west1", "uselessLabel": "none"}},
    49  		},
    50  		{
    51  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_3", Labels: map[string]string{v1.LabelFailureDomainBetaRegion: "us-west1"}},
    52  		},
    53  		{
    54  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_Stable_1", Labels: map[string]string{v1.LabelTopologyZone: "us-west1-a"}},
    55  		},
    56  		{
    57  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_Stable_2", Labels: map[string]string{v1.LabelTopologyRegion: "us-west1", "uselessLabel": "none"}},
    58  		},
    59  		{
    60  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_Stable_3", Labels: map[string]string{v1.LabelTopologyZone: "us-west1-a", v1.LabelTopologyRegion: "us-west1-a"}},
    61  		},
    62  	}
    63  
    64  	pvcLister := tf.PersistentVolumeClaimLister{
    65  		{
    66  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_1", Namespace: "default"},
    67  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_1"},
    68  		},
    69  		{
    70  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_2", Namespace: "default"},
    71  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_2"},
    72  		},
    73  		{
    74  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_3", Namespace: "default"},
    75  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_3"},
    76  		},
    77  		{
    78  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_4", Namespace: "default"},
    79  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_not_exist"},
    80  		},
    81  		{
    82  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_Stable_1", Namespace: "default"},
    83  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_Stable_1"},
    84  		},
    85  		{
    86  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_Stable_2", Namespace: "default"},
    87  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_Stable_2"},
    88  		},
    89  		{
    90  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_Stable_3", Namespace: "default"},
    91  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_Stable_3"},
    92  		},
    93  	}
    94  
    95  	tests := []struct {
    96  		name                string
    97  		Pod                 *v1.Pod
    98  		Node                *v1.Node
    99  		wantPreFilterStatus *framework.Status
   100  		wantFilterStatus    *framework.Status
   101  	}{
   102  		{
   103  			name: "pod without volume",
   104  			Pod:  st.MakePod().Name("pod_1").Namespace(metav1.NamespaceDefault).Obj(),
   105  			Node: &v1.Node{
   106  				ObjectMeta: metav1.ObjectMeta{
   107  					Name:   "host1",
   108  					Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a"},
   109  				},
   110  			},
   111  			wantPreFilterStatus: framework.NewStatus(framework.Skip),
   112  		},
   113  		{
   114  			name: "node without labels",
   115  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   116  			Node: &v1.Node{
   117  				ObjectMeta: metav1.ObjectMeta{
   118  					Name: "host1",
   119  				},
   120  			},
   121  		},
   122  		{
   123  			name: "beta zone label matched",
   124  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   125  			Node: &v1.Node{
   126  				ObjectMeta: metav1.ObjectMeta{
   127  					Name:   "host1",
   128  					Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a", "uselessLabel": "none"},
   129  				},
   130  			},
   131  		},
   132  		{
   133  			name: "beta region label matched",
   134  			Pod:  createPodWithVolume("pod_1", "PVC_2"),
   135  			Node: &v1.Node{
   136  				ObjectMeta: metav1.ObjectMeta{
   137  					Name:   "host1",
   138  					Labels: map[string]string{v1.LabelFailureDomainBetaRegion: "us-west1", "uselessLabel": "none"},
   139  				},
   140  			},
   141  		},
   142  		{
   143  			name: "beta region label doesn't match",
   144  			Pod:  createPodWithVolume("pod_1", "PVC_2"),
   145  			Node: &v1.Node{
   146  				ObjectMeta: metav1.ObjectMeta{
   147  					Name:   "host1",
   148  					Labels: map[string]string{v1.LabelFailureDomainBetaRegion: "no_us-west1", "uselessLabel": "none"},
   149  				},
   150  			},
   151  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   152  		},
   153  		{
   154  			name: "beta zone label doesn't match",
   155  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   156  			Node: &v1.Node{
   157  				ObjectMeta: metav1.ObjectMeta{
   158  					Name:   "host1",
   159  					Labels: map[string]string{v1.LabelFailureDomainBetaZone: "no_us-west1-a", "uselessLabel": "none"},
   160  				},
   161  			},
   162  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   163  		},
   164  		{
   165  			name: "zone label matched",
   166  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_1"),
   167  			Node: &v1.Node{
   168  				ObjectMeta: metav1.ObjectMeta{
   169  					Name:   "host1",
   170  					Labels: map[string]string{v1.LabelTopologyZone: "us-west1-a", "uselessLabel": "none"},
   171  				},
   172  			},
   173  		},
   174  		{
   175  			name: "region label matched",
   176  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_2"),
   177  			Node: &v1.Node{
   178  				ObjectMeta: metav1.ObjectMeta{
   179  					Name:   "host1",
   180  					Labels: map[string]string{v1.LabelTopologyRegion: "us-west1", "uselessLabel": "none"},
   181  				},
   182  			},
   183  		},
   184  		{
   185  			name: "region label doesn't match",
   186  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_2"),
   187  			Node: &v1.Node{
   188  				ObjectMeta: metav1.ObjectMeta{
   189  					Name:   "host1",
   190  					Labels: map[string]string{v1.LabelTopologyRegion: "no_us-west1", "uselessLabel": "none"},
   191  				},
   192  			},
   193  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   194  		},
   195  		{
   196  			name: "zone label doesn't match",
   197  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_1"),
   198  			Node: &v1.Node{
   199  				ObjectMeta: metav1.ObjectMeta{
   200  					Name:   "host1",
   201  					Labels: map[string]string{v1.LabelTopologyZone: "no_us-west1-a", "uselessLabel": "none"},
   202  				},
   203  			},
   204  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   205  		},
   206  		{
   207  			name: "pv with zone and region, node with only zone",
   208  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_3"),
   209  			Node: &v1.Node{
   210  				ObjectMeta: metav1.ObjectMeta{
   211  					Name: "host1",
   212  					Labels: map[string]string{
   213  						v1.LabelTopologyZone: "us-west1-a",
   214  					},
   215  				},
   216  			},
   217  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   218  		},
   219  		{
   220  			name: "pv with zone,node with beta zone",
   221  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_1"),
   222  			Node: &v1.Node{
   223  				ObjectMeta: metav1.ObjectMeta{
   224  					Name: "host1",
   225  					Labels: map[string]string{
   226  						v1.LabelFailureDomainBetaZone: "us-west1-a",
   227  					},
   228  				},
   229  			},
   230  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   231  		},
   232  		{
   233  			name: "pv with beta label,node with ga label, matched",
   234  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   235  			Node: &v1.Node{
   236  				ObjectMeta: metav1.ObjectMeta{
   237  					Name: "host1",
   238  					Labels: map[string]string{
   239  						v1.LabelTopologyZone: "us-west1-a",
   240  					},
   241  				},
   242  			},
   243  		},
   244  		{
   245  			name: "pv with beta label,node with ga label, don't match",
   246  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   247  			Node: &v1.Node{
   248  				ObjectMeta: metav1.ObjectMeta{
   249  					Name: "host1",
   250  					Labels: map[string]string{
   251  						v1.LabelTopologyZone: "us-west1-b",
   252  					},
   253  				},
   254  			},
   255  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   256  		},
   257  	}
   258  
   259  	for _, test := range tests {
   260  		t.Run(test.name, func(t *testing.T) {
   261  			_, ctx := ktesting.NewTestContext(t)
   262  			ctx, cancel := context.WithCancel(ctx)
   263  			defer cancel()
   264  
   265  			state := framework.NewCycleState()
   266  			node := &framework.NodeInfo{}
   267  			node.SetNode(test.Node)
   268  			p := &VolumeZone{
   269  				pvLister,
   270  				pvcLister,
   271  				nil,
   272  			}
   273  			_, preFilterStatus := p.PreFilter(ctx, state, test.Pod)
   274  			if diff := cmp.Diff(preFilterStatus, test.wantPreFilterStatus); diff != "" {
   275  				t.Errorf("PreFilter: status does not match (-want,+got):\n%s", diff)
   276  			}
   277  			filterStatus := p.Filter(ctx, state, test.Pod, node)
   278  			if diff := cmp.Diff(filterStatus, test.wantFilterStatus); diff != "" {
   279  				t.Errorf("Filter: status does not match (-want,+got):\n%s", diff)
   280  			}
   281  		})
   282  	}
   283  }
   284  
   285  func TestMultiZone(t *testing.T) {
   286  	pvLister := tf.PersistentVolumeLister{
   287  		{
   288  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_1", Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a"}},
   289  		},
   290  		{
   291  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_2", Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-b", "uselessLabel": "none"}},
   292  		},
   293  		{
   294  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_3", Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-c__us-west1-a"}},
   295  		},
   296  		{
   297  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_Stable_1", Labels: map[string]string{v1.LabelTopologyZone: "us-west1-a"}},
   298  		},
   299  		{
   300  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_Stable_2", Labels: map[string]string{v1.LabelTopologyZone: "us-west1-c__us-west1-a"}},
   301  		},
   302  	}
   303  
   304  	pvcLister := tf.PersistentVolumeClaimLister{
   305  		{
   306  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_1", Namespace: "default"},
   307  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_1"},
   308  		},
   309  		{
   310  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_2", Namespace: "default"},
   311  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_2"},
   312  		},
   313  		{
   314  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_3", Namespace: "default"},
   315  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_3"},
   316  		},
   317  		{
   318  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_4", Namespace: "default"},
   319  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_not_exist"},
   320  		},
   321  		{
   322  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_Stable_1", Namespace: "default"},
   323  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_Stable_1"},
   324  		},
   325  		{
   326  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_Stable_2", Namespace: "default"},
   327  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_Stable_2"},
   328  		},
   329  	}
   330  
   331  	tests := []struct {
   332  		name                string
   333  		Pod                 *v1.Pod
   334  		Node                *v1.Node
   335  		wantPreFilterStatus *framework.Status
   336  		wantFilterStatus    *framework.Status
   337  	}{
   338  		{
   339  			name: "node without labels",
   340  			Pod:  createPodWithVolume("pod_1", "PVC_3"),
   341  			Node: &v1.Node{
   342  				ObjectMeta: metav1.ObjectMeta{
   343  					Name: "host1",
   344  				},
   345  			},
   346  		},
   347  		{
   348  			name: "beta zone label matched",
   349  			Pod:  createPodWithVolume("pod_1", "PVC_3"),
   350  			Node: &v1.Node{
   351  				ObjectMeta: metav1.ObjectMeta{
   352  					Name:   "host1",
   353  					Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a", "uselessLabel": "none"},
   354  				},
   355  			},
   356  		},
   357  		{
   358  			name: "beta zone label doesn't match",
   359  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   360  			Node: &v1.Node{
   361  				ObjectMeta: metav1.ObjectMeta{
   362  					Name:   "host1",
   363  					Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-b", "uselessLabel": "none"},
   364  				},
   365  			},
   366  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   367  		},
   368  		{
   369  			name: "zone label matched",
   370  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_2"),
   371  			Node: &v1.Node{
   372  				ObjectMeta: metav1.ObjectMeta{
   373  					Name:   "host1",
   374  					Labels: map[string]string{v1.LabelTopologyZone: "us-west1-a", "uselessLabel": "none"},
   375  				},
   376  			},
   377  		},
   378  		{
   379  			name: "zone label doesn't match",
   380  			Pod:  createPodWithVolume("pod_1", "PVC_Stable_1"),
   381  			Node: &v1.Node{
   382  				ObjectMeta: metav1.ObjectMeta{
   383  					Name:   "host1",
   384  					Labels: map[string]string{v1.LabelTopologyZone: "us-west1-b", "uselessLabel": "none"},
   385  				},
   386  			},
   387  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonConflict),
   388  		},
   389  	}
   390  
   391  	for _, test := range tests {
   392  		t.Run(test.name, func(t *testing.T) {
   393  			_, ctx := ktesting.NewTestContext(t)
   394  			ctx, cancel := context.WithCancel(ctx)
   395  			defer cancel()
   396  
   397  			state := framework.NewCycleState()
   398  			node := &framework.NodeInfo{}
   399  			node.SetNode(test.Node)
   400  			p := &VolumeZone{
   401  				pvLister,
   402  				pvcLister,
   403  				nil,
   404  			}
   405  			_, preFilterStatus := p.PreFilter(ctx, state, test.Pod)
   406  			if diff := cmp.Diff(preFilterStatus, test.wantPreFilterStatus); diff != "" {
   407  				t.Errorf("PreFilter: status does not match (-want,+got):\n%s", diff)
   408  			}
   409  			filterStatus := p.Filter(ctx, state, test.Pod, node)
   410  			if diff := cmp.Diff(filterStatus, test.wantFilterStatus); diff != "" {
   411  				t.Errorf("Filter: status does not match (-want,+got):\n%s", diff)
   412  			}
   413  		})
   414  	}
   415  }
   416  
   417  func TestWithBinding(t *testing.T) {
   418  	var (
   419  		modeWait = storagev1.VolumeBindingWaitForFirstConsumer
   420  
   421  		class0         = "Class_0"
   422  		classWait      = "Class_Wait"
   423  		classImmediate = "Class_Immediate"
   424  	)
   425  
   426  	scLister := tf.StorageClassLister{
   427  		{
   428  			ObjectMeta: metav1.ObjectMeta{Name: classImmediate},
   429  		},
   430  		{
   431  			ObjectMeta:        metav1.ObjectMeta{Name: classWait},
   432  			VolumeBindingMode: &modeWait,
   433  		},
   434  	}
   435  
   436  	pvLister := tf.PersistentVolumeLister{
   437  		{
   438  			ObjectMeta: metav1.ObjectMeta{Name: "Vol_1", Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a"}},
   439  		},
   440  	}
   441  
   442  	pvcLister := tf.PersistentVolumeClaimLister{
   443  		{
   444  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_1", Namespace: "default"},
   445  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: "Vol_1"},
   446  		},
   447  		{
   448  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_NoSC", Namespace: "default"},
   449  			Spec:       v1.PersistentVolumeClaimSpec{StorageClassName: &class0},
   450  		},
   451  		{
   452  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_EmptySC", Namespace: "default"},
   453  		},
   454  		{
   455  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_WaitSC", Namespace: "default"},
   456  			Spec:       v1.PersistentVolumeClaimSpec{StorageClassName: &classWait},
   457  		},
   458  		{
   459  			ObjectMeta: metav1.ObjectMeta{Name: "PVC_ImmediateSC", Namespace: "default"},
   460  			Spec:       v1.PersistentVolumeClaimSpec{StorageClassName: &classImmediate},
   461  		},
   462  	}
   463  
   464  	testNode := &v1.Node{
   465  		ObjectMeta: metav1.ObjectMeta{
   466  			Name:   "host1",
   467  			Labels: map[string]string{v1.LabelFailureDomainBetaZone: "us-west1-a", "uselessLabel": "none"},
   468  		},
   469  	}
   470  
   471  	tests := []struct {
   472  		name                string
   473  		Pod                 *v1.Pod
   474  		Node                *v1.Node
   475  		wantPreFilterStatus *framework.Status
   476  		wantFilterStatus    *framework.Status
   477  	}{
   478  		{
   479  			name: "label zone failure domain matched",
   480  			Pod:  createPodWithVolume("pod_1", "PVC_1"),
   481  			Node: testNode,
   482  		},
   483  		{
   484  			name: "unbound volume empty storage class",
   485  			Pod:  createPodWithVolume("pod_1", "PVC_EmptySC"),
   486  			Node: testNode,
   487  			wantPreFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable,
   488  				"PersistentVolumeClaim had no pv name and storageClass name"),
   489  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable,
   490  				"PersistentVolumeClaim had no pv name and storageClass name"),
   491  		},
   492  		{
   493  			name: "unbound volume no storage class",
   494  			Pod:  createPodWithVolume("pod_1", "PVC_NoSC"),
   495  			Node: testNode,
   496  			wantPreFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable,
   497  				`storageclasses.storage.k8s.io "Class_0" not found`),
   498  			wantFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable,
   499  				`storageclasses.storage.k8s.io "Class_0" not found`),
   500  		},
   501  		{
   502  			name:                "unbound volume immediate binding mode",
   503  			Pod:                 createPodWithVolume("pod_1", "PVC_ImmediateSC"),
   504  			Node:                testNode,
   505  			wantPreFilterStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, "VolumeBindingMode not set for StorageClass \"Class_Immediate\""),
   506  			wantFilterStatus:    framework.NewStatus(framework.UnschedulableAndUnresolvable, "VolumeBindingMode not set for StorageClass \"Class_Immediate\""),
   507  		},
   508  		{
   509  			name:                "unbound volume wait binding mode",
   510  			Pod:                 createPodWithVolume("pod_1", "PVC_WaitSC"),
   511  			Node:                testNode,
   512  			wantPreFilterStatus: framework.NewStatus(framework.Skip),
   513  		},
   514  	}
   515  
   516  	for _, test := range tests {
   517  		t.Run(test.name, func(t *testing.T) {
   518  			_, ctx := ktesting.NewTestContext(t)
   519  			ctx, cancel := context.WithCancel(ctx)
   520  			defer cancel()
   521  
   522  			state := framework.NewCycleState()
   523  			node := &framework.NodeInfo{}
   524  			node.SetNode(test.Node)
   525  			p := &VolumeZone{
   526  				pvLister,
   527  				pvcLister,
   528  				scLister,
   529  			}
   530  			_, preFilterStatus := p.PreFilter(ctx, state, test.Pod)
   531  			if diff := cmp.Diff(preFilterStatus, test.wantPreFilterStatus); diff != "" {
   532  				t.Errorf("PreFilter: status does not match (-want,+got):\n%s", diff)
   533  			}
   534  			filterStatus := p.Filter(ctx, state, test.Pod, node)
   535  			if diff := cmp.Diff(filterStatus, test.wantFilterStatus); diff != "" {
   536  				t.Errorf("Filter: status does not match (-want,+got):\n%s", diff)
   537  			}
   538  		})
   539  	}
   540  }
   541  
   542  func BenchmarkVolumeZone(b *testing.B) {
   543  	tests := []struct {
   544  		Name      string
   545  		Pod       *v1.Pod
   546  		NumPV     int
   547  		NumPVC    int
   548  		NumNodes  int
   549  		PreFilter bool
   550  	}{
   551  		{
   552  			Name:      "with prefilter",
   553  			Pod:       createPodWithVolume("pod_0", "PVC_Stable_0"),
   554  			NumPV:     1000,
   555  			NumPVC:    1000,
   556  			NumNodes:  1000,
   557  			PreFilter: true,
   558  		},
   559  		{
   560  			Name:      "without prefilter",
   561  			Pod:       createPodWithVolume("pod_0", "PVC_Stable_0"),
   562  			NumPV:     1000,
   563  			NumPVC:    1000,
   564  			NumNodes:  1000,
   565  			PreFilter: false,
   566  		},
   567  	}
   568  
   569  	for _, tt := range tests {
   570  		b.Run(tt.Name, func(b *testing.B) {
   571  			ctx, cancel := context.WithCancel(context.Background())
   572  			defer cancel()
   573  			nodes := makeNodesWithTopologyZone(tt.NumNodes)
   574  			pl := newPluginWithListers(ctx, b, []*v1.Pod{tt.Pod}, nodes, makePVCsWithPV(tt.NumPVC), makePVsWithZoneLabel(tt.NumPV))
   575  			nodeInfos := make([]*framework.NodeInfo, len(nodes), len(nodes))
   576  			for i := 0; i < len(nodes); i++ {
   577  				nodeInfo := &framework.NodeInfo{}
   578  				nodeInfo.SetNode(nodes[i])
   579  				nodeInfos[i] = nodeInfo
   580  			}
   581  			p := pl.(*VolumeZone)
   582  			state := framework.NewCycleState()
   583  
   584  			b.ResetTimer()
   585  
   586  			for i := 0; i < b.N; i++ {
   587  				if tt.PreFilter {
   588  					_, _ = p.PreFilter(ctx, state, tt.Pod)
   589  				}
   590  				for _, node := range nodeInfos {
   591  					_ = p.Filter(ctx, state, tt.Pod, node)
   592  				}
   593  			}
   594  		})
   595  	}
   596  }
   597  
   598  func newPluginWithListers(ctx context.Context, tb testing.TB, pods []*v1.Pod, nodes []*v1.Node, pvcs []*v1.PersistentVolumeClaim, pvs []*v1.PersistentVolume) framework.Plugin {
   599  	snapshot := cache.NewSnapshot(pods, nodes)
   600  
   601  	objects := make([]runtime.Object, 0, len(pvcs))
   602  	for _, pvc := range pvcs {
   603  		objects = append(objects, pvc)
   604  	}
   605  	for _, pv := range pvs {
   606  		objects = append(objects, pv)
   607  	}
   608  	return plugintesting.SetupPluginWithInformers(ctx, tb, New, &config.InterPodAffinityArgs{}, snapshot, objects)
   609  }
   610  
   611  func makePVsWithZoneLabel(num int) []*v1.PersistentVolume {
   612  	pvList := make([]*v1.PersistentVolume, num, num)
   613  	for i := 0; i < len(pvList); i++ {
   614  		pvName := fmt.Sprintf("Vol_Stable_%d", i)
   615  		zone := fmt.Sprintf("us-west-%d", i)
   616  		pvList[i] = &v1.PersistentVolume{
   617  			ObjectMeta: metav1.ObjectMeta{Name: pvName, Labels: map[string]string{v1.LabelTopologyZone: zone}},
   618  		}
   619  	}
   620  	return pvList
   621  }
   622  
   623  func makePVCsWithPV(num int) []*v1.PersistentVolumeClaim {
   624  	pvcList := make([]*v1.PersistentVolumeClaim, num, num)
   625  	for i := 0; i < len(pvcList); i++ {
   626  		pvcName := fmt.Sprintf("PVC_Stable_%d", i)
   627  		pvName := fmt.Sprintf("Vol_Stable_%d", i)
   628  		pvcList[i] = &v1.PersistentVolumeClaim{
   629  			ObjectMeta: metav1.ObjectMeta{Name: pvcName, Namespace: "default"},
   630  			Spec:       v1.PersistentVolumeClaimSpec{VolumeName: pvName},
   631  		}
   632  	}
   633  	return pvcList
   634  }
   635  
   636  func makeNodesWithTopologyZone(num int) []*v1.Node {
   637  	nodeList := make([]*v1.Node, num, num)
   638  	for i := 0; i < len(nodeList); i++ {
   639  		nodeName := fmt.Sprintf("host_%d", i)
   640  		zone := fmt.Sprintf("us-west-0")
   641  		nodeList[i] = &v1.Node{
   642  			ObjectMeta: metav1.ObjectMeta{
   643  				Name:   nodeName,
   644  				Labels: map[string]string{v1.LabelTopologyZone: zone, "uselessLabel": "none"},
   645  			},
   646  		}
   647  	}
   648  	return nodeList
   649  }
   650  

View as plain text