github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/orm/topology/manager_test.go (about)

     1  /*
     2  Copyright 2022 The Katalyst 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 topology
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	v1 "k8s.io/api/core/v1"
    27  	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
    29  
    30  	"github.com/kubewharf/katalyst-core/pkg/util/bitmask"
    31  )
    32  
    33  func NewTestBitMask(sockets ...int) bitmask.BitMask {
    34  	s, _ := bitmask.NewBitMask(sockets...)
    35  	return s
    36  }
    37  
    38  type mockHintProvider struct {
    39  	th map[string][]TopologyHint
    40  	// TODO: Add this field and add some tests to make sure things error out
    41  	// appropriately on allocation errors.
    42  	// allocateError error
    43  }
    44  
    45  func (m *mockHintProvider) GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint {
    46  	return m.th
    47  }
    48  
    49  func (m *mockHintProvider) GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint {
    50  	return m.th
    51  }
    52  
    53  func (m *mockHintProvider) Allocate(pod *v1.Pod, container *v1.Container) error {
    54  	// return allocateError
    55  	return nil
    56  }
    57  
    58  func TestNewManager(t *testing.T) {
    59  	t.Parallel()
    60  	tcases := []struct {
    61  		description    string
    62  		policyName     string
    63  		expectedPolicy string
    64  		expectedError  error
    65  	}{
    66  		{
    67  			description:    "Policy is set to none",
    68  			policyName:     "none",
    69  			expectedPolicy: "none",
    70  		},
    71  		{
    72  			description:    "Policy is set to best-effort",
    73  			policyName:     "best-effort",
    74  			expectedPolicy: "best-effort",
    75  		},
    76  		{
    77  			description:    "Policy is set to restricted",
    78  			policyName:     "restricted",
    79  			expectedPolicy: "restricted",
    80  		},
    81  		{
    82  			description:    "Policy is set to single-numa-node",
    83  			policyName:     "single-numa-node",
    84  			expectedPolicy: "single-numa-node",
    85  		},
    86  		{
    87  			description:   "Policy is set to unknown",
    88  			policyName:    "unknown",
    89  			expectedError: fmt.Errorf("unknown policy: \"unknown\""),
    90  		},
    91  	}
    92  
    93  	for _, tc := range tcases {
    94  		mngr, err := NewManager(nil, tc.policyName, nil)
    95  
    96  		if tc.expectedError != nil {
    97  			if !strings.Contains(err.Error(), tc.expectedError.Error()) {
    98  				t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tc.expectedError.Error())
    99  			}
   100  		} else {
   101  			rawMgr := mngr.(*manager)
   102  			if rawMgr.policy.Name() != tc.expectedPolicy {
   103  				t.Errorf("Unexpected policy name. Have: %q wants %q", rawMgr.policy.Name(), tc.expectedPolicy)
   104  			}
   105  		}
   106  	}
   107  }
   108  
   109  func TestAddHintProvider(t *testing.T) {
   110  	t.Parallel()
   111  	tcases := []struct {
   112  		name string
   113  		hp   []HintProvider
   114  	}{
   115  		{
   116  			name: "Add HintProvider",
   117  			hp: []HintProvider{
   118  				&mockHintProvider{},
   119  				&mockHintProvider{},
   120  				&mockHintProvider{},
   121  			},
   122  		},
   123  	}
   124  	mngr := manager{
   125  		hintProviders: make([]HintProvider, 0),
   126  	}
   127  	for _, tc := range tcases {
   128  		for _, hp := range tc.hp {
   129  			mngr.AddHintProvider(hp)
   130  		}
   131  		if len(tc.hp) != len(mngr.hintProviders) {
   132  			t.Errorf("error")
   133  		}
   134  	}
   135  }
   136  
   137  func TestGetAffinity(t *testing.T) {
   138  	t.Parallel()
   139  	tcases := []struct {
   140  		name          string
   141  		resourceName  string
   142  		containerName string
   143  		podUID        string
   144  		expected      TopologyHint
   145  	}{
   146  		{
   147  			name:          "case1",
   148  			resourceName:  "*",
   149  			containerName: "nginx",
   150  			podUID:        "0aafa4c4-38e8-11e9-bcb1-a4bf01040474",
   151  			expected:      TopologyHint{},
   152  		},
   153  		{
   154  			name:          "case2",
   155  			containerName: "preferredContainer",
   156  			resourceName:  "cpu",
   157  			podUID:        "testpoduid",
   158  			expected: TopologyHint{
   159  				Preferred:        true,
   160  				NUMANodeAffinity: NewTestBitMask(0),
   161  			},
   162  		},
   163  		{
   164  			name:          "case3",
   165  			resourceName:  "cpu",
   166  			containerName: "notpreferedContainer",
   167  			podUID:        "testpoduid",
   168  			expected: TopologyHint{
   169  				Preferred:        false,
   170  				NUMANodeAffinity: NewTestBitMask(0, 1),
   171  			},
   172  		},
   173  	}
   174  
   175  	mngr := manager{
   176  		podTopologyHints: map[string]podTopologyHints{},
   177  	}
   178  	mngr.setTopologyHints("testpoduid", "preferredContainer", map[string]TopologyHint{
   179  		"cpu": {
   180  			Preferred:        true,
   181  			NUMANodeAffinity: NewTestBitMask(0),
   182  		},
   183  	})
   184  	mngr.setTopologyHints("testpoduid", "notpreferedContainer", map[string]TopologyHint{
   185  		"cpu": {
   186  			Preferred:        false,
   187  			NUMANodeAffinity: NewTestBitMask(0, 1),
   188  		},
   189  	})
   190  
   191  	for _, tc := range tcases {
   192  		actual := mngr.GetAffinity(tc.podUID, tc.containerName, tc.resourceName)
   193  		if !reflect.DeepEqual(actual, tc.expected) {
   194  			t.Errorf("Expected Affinity in result to be %v, got %v", tc.expected, actual)
   195  		}
   196  	}
   197  }
   198  
   199  func TestRemovePod(t *testing.T) {
   200  	t.Parallel()
   201  	mngr := manager{
   202  		podTopologyHints: map[string]podTopologyHints{},
   203  	}
   204  	mngr.setTopologyHints("testpoduid", "testContainer", map[string]TopologyHint{
   205  		"cpu": {
   206  			Preferred:        true,
   207  			NUMANodeAffinity: NewTestBitMask(0),
   208  		},
   209  	})
   210  
   211  	mngr.RemovePod("none")
   212  	assert.Equal(t, 1, len(mngr.podTopologyHints))
   213  	mngr.RemovePod("testpoduid")
   214  	assert.Equal(t, 0, len(mngr.podTopologyHints))
   215  }
   216  
   217  func TestAdmit(t *testing.T) {
   218  	t.Parallel()
   219  	numaNodes := []int{0, 1}
   220  
   221  	tcases := []struct {
   222  		name        string
   223  		result      lifecycle.PodAdmitResult
   224  		policy      Policy
   225  		hp          []HintProvider
   226  		expectedErr error
   227  		expected    TopologyHint
   228  	}{
   229  		{
   230  			name:        "None Policy. No Hints.",
   231  			policy:      NewNonePolicy(),
   232  			hp:          []HintProvider{},
   233  			expectedErr: nil,
   234  			expected:    TopologyHint{},
   235  		},
   236  		{
   237  			name:        "None Policy. No Hints.",
   238  			policy:      NewNonePolicy(),
   239  			hp:          []HintProvider{},
   240  			expectedErr: nil,
   241  			expected:    TopologyHint{},
   242  		},
   243  		{
   244  			name:   "single-numa-node Policy. No Hints.",
   245  			policy: NewSingleNumaNodePolicy(numaNodes),
   246  			hp: []HintProvider{
   247  				&mockHintProvider{},
   248  			},
   249  			expectedErr: nil,
   250  			expected:    TopologyHint{},
   251  		},
   252  		{
   253  			name:   "Restricted Policy. No Hints.",
   254  			policy: NewRestrictedPolicy(numaNodes),
   255  			hp: []HintProvider{
   256  				&mockHintProvider{},
   257  			},
   258  			expectedErr: nil,
   259  			expected:    TopologyHint{},
   260  		},
   261  		{
   262  			name:   "BestEffort Policy. Preferred Affinity.",
   263  			policy: NewBestEffortPolicy(numaNodes),
   264  			hp: []HintProvider{
   265  				&mockHintProvider{
   266  					map[string][]TopologyHint{
   267  						"resource": {
   268  							{
   269  								NUMANodeAffinity: NewTestBitMask(0),
   270  								Preferred:        true,
   271  							},
   272  							{
   273  								NUMANodeAffinity: NewTestBitMask(0, 1),
   274  								Preferred:        false,
   275  							},
   276  						},
   277  					},
   278  				},
   279  			},
   280  			expectedErr: nil,
   281  			expected: TopologyHint{
   282  				NUMANodeAffinity: NewTestBitMask(0),
   283  				Preferred:        true,
   284  			},
   285  		},
   286  		{
   287  			name:   "BestEffort Policy. More than one Preferred Affinity.",
   288  			policy: NewBestEffortPolicy(numaNodes),
   289  			hp: []HintProvider{
   290  				&mockHintProvider{
   291  					map[string][]TopologyHint{
   292  						"resource": {
   293  							{
   294  								NUMANodeAffinity: NewTestBitMask(0),
   295  								Preferred:        true,
   296  							},
   297  							{
   298  								NUMANodeAffinity: NewTestBitMask(1),
   299  								Preferred:        true,
   300  							},
   301  							{
   302  								NUMANodeAffinity: NewTestBitMask(0, 1),
   303  								Preferred:        false,
   304  							},
   305  						},
   306  					},
   307  				},
   308  			},
   309  			expectedErr: nil,
   310  			expected: TopologyHint{
   311  				NUMANodeAffinity: NewTestBitMask(0),
   312  				Preferred:        true,
   313  			},
   314  		},
   315  		{
   316  			name:   "BestEffort Policy. More than one Preferred Affinity.",
   317  			policy: NewBestEffortPolicy(numaNodes),
   318  			hp: []HintProvider{
   319  				&mockHintProvider{
   320  					map[string][]TopologyHint{
   321  						"resource": {
   322  							{
   323  								NUMANodeAffinity: NewTestBitMask(0),
   324  								Preferred:        true,
   325  							},
   326  							{
   327  								NUMANodeAffinity: NewTestBitMask(1),
   328  								Preferred:        true,
   329  							},
   330  							{
   331  								NUMANodeAffinity: NewTestBitMask(0, 1),
   332  								Preferred:        false,
   333  							},
   334  						},
   335  					},
   336  				},
   337  			},
   338  			expectedErr: nil,
   339  			expected: TopologyHint{
   340  				NUMANodeAffinity: NewTestBitMask(0),
   341  				Preferred:        true,
   342  			},
   343  		},
   344  		{
   345  			name:   "BestEffort Policy. No Preferred Affinity.",
   346  			policy: NewBestEffortPolicy(numaNodes),
   347  			hp: []HintProvider{
   348  				&mockHintProvider{
   349  					map[string][]TopologyHint{
   350  						"resource": {
   351  							{
   352  								NUMANodeAffinity: NewTestBitMask(0, 1),
   353  								Preferred:        false,
   354  							},
   355  						},
   356  					},
   357  				},
   358  			},
   359  			expectedErr: nil,
   360  			expected: TopologyHint{
   361  				NUMANodeAffinity: NewTestBitMask(0, 1),
   362  				Preferred:        false,
   363  			},
   364  		},
   365  		{
   366  			name:   "Restricted Policy. Preferred Affinity.",
   367  			policy: NewRestrictedPolicy(numaNodes),
   368  			hp: []HintProvider{
   369  				&mockHintProvider{
   370  					map[string][]TopologyHint{
   371  						"resource": {
   372  							{
   373  								NUMANodeAffinity: NewTestBitMask(0),
   374  								Preferred:        true,
   375  							},
   376  							{
   377  								NUMANodeAffinity: NewTestBitMask(0, 1),
   378  								Preferred:        false,
   379  							},
   380  						},
   381  					},
   382  				},
   383  			},
   384  			expectedErr: nil,
   385  			expected: TopologyHint{
   386  				NUMANodeAffinity: NewTestBitMask(0),
   387  				Preferred:        true,
   388  			},
   389  		},
   390  		{
   391  			name:   "Restricted Policy. Preferred Affinity.",
   392  			policy: NewRestrictedPolicy(numaNodes),
   393  			hp: []HintProvider{
   394  				&mockHintProvider{
   395  					map[string][]TopologyHint{
   396  						"resource": {
   397  							{
   398  								NUMANodeAffinity: NewTestBitMask(0),
   399  								Preferred:        true,
   400  							},
   401  							{
   402  								NUMANodeAffinity: NewTestBitMask(0, 1),
   403  								Preferred:        false,
   404  							},
   405  						},
   406  					},
   407  				},
   408  			},
   409  			expectedErr: nil,
   410  			expected: TopologyHint{
   411  				NUMANodeAffinity: NewTestBitMask(0),
   412  				Preferred:        true,
   413  			},
   414  		},
   415  		{
   416  			name:   "Restricted Policy. More than one Preferred affinity.",
   417  			policy: NewRestrictedPolicy(numaNodes),
   418  			hp: []HintProvider{
   419  				&mockHintProvider{
   420  					map[string][]TopologyHint{
   421  						"resource": {
   422  							{
   423  								NUMANodeAffinity: NewTestBitMask(0),
   424  								Preferred:        true,
   425  							},
   426  							{
   427  								NUMANodeAffinity: NewTestBitMask(1),
   428  								Preferred:        true,
   429  							},
   430  							{
   431  								NUMANodeAffinity: NewTestBitMask(0, 1),
   432  								Preferred:        false,
   433  							},
   434  						},
   435  					},
   436  				},
   437  			},
   438  			expectedErr: nil,
   439  			expected: TopologyHint{
   440  				NUMANodeAffinity: NewTestBitMask(0),
   441  				Preferred:        true,
   442  			},
   443  		},
   444  		{
   445  			name:   "Restricted Policy. More than one Preferred affinity.",
   446  			policy: NewRestrictedPolicy(numaNodes),
   447  			hp: []HintProvider{
   448  				&mockHintProvider{
   449  					map[string][]TopologyHint{
   450  						"resource": {
   451  							{
   452  								NUMANodeAffinity: NewTestBitMask(0),
   453  								Preferred:        true,
   454  							},
   455  							{
   456  								NUMANodeAffinity: NewTestBitMask(1),
   457  								Preferred:        true,
   458  							},
   459  							{
   460  								NUMANodeAffinity: NewTestBitMask(0, 1),
   461  								Preferred:        false,
   462  							},
   463  						},
   464  					},
   465  				},
   466  			},
   467  			expectedErr: nil,
   468  			expected: TopologyHint{
   469  				NUMANodeAffinity: NewTestBitMask(0),
   470  				Preferred:        true,
   471  			},
   472  		},
   473  		{
   474  			name:   "Restricted Policy. No Preferred affinity.",
   475  			policy: NewRestrictedPolicy(numaNodes),
   476  			hp: []HintProvider{
   477  				&mockHintProvider{
   478  					map[string][]TopologyHint{
   479  						"resource": {
   480  							{
   481  								NUMANodeAffinity: NewTestBitMask(0, 1),
   482  								Preferred:        false,
   483  							},
   484  						},
   485  					},
   486  				},
   487  			},
   488  			expectedErr: fmt.Errorf("pod: %v, containerName: %v not admit", "testPod", "testContainer"),
   489  			expected:    TopologyHint{},
   490  		},
   491  		{
   492  			name:   "Restricted Policy. No Preferred affinity.",
   493  			policy: NewRestrictedPolicy(numaNodes),
   494  			hp: []HintProvider{
   495  				&mockHintProvider{
   496  					map[string][]TopologyHint{
   497  						"resource": {
   498  							{
   499  								NUMANodeAffinity: NewTestBitMask(0, 1),
   500  								Preferred:        false,
   501  							},
   502  						},
   503  					},
   504  				},
   505  			},
   506  			expectedErr: fmt.Errorf("pod: %v, containerName: %v not admit", "testPod", "testContainer"),
   507  			expected:    TopologyHint{},
   508  		},
   509  	}
   510  	for _, tc := range tcases {
   511  		topologyManager := manager{
   512  			policy:           tc.policy,
   513  			hintProviders:    tc.hp,
   514  			podTopologyHints: map[string]podTopologyHints{},
   515  		}
   516  
   517  		pod := &v1.Pod{
   518  			ObjectMeta: v12.ObjectMeta{
   519  				Name: "testPod",
   520  				UID:  "testUID",
   521  			},
   522  			Spec: v1.PodSpec{
   523  				Containers: []v1.Container{
   524  					{
   525  						Name:      "testContainer",
   526  						Resources: v1.ResourceRequirements{},
   527  					},
   528  				},
   529  			},
   530  			Status: v1.PodStatus{},
   531  		}
   532  
   533  		err := topologyManager.Admit(pod)
   534  		assert.Equal(t, tc.expectedErr, err)
   535  		if err != nil {
   536  			assert.Equal(t, tc.expected, topologyManager.GetAffinity("testUID", "testContainer", "resource"))
   537  		}
   538  	}
   539  }