...

Source file src/k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/topology_manager_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/cm/topologymanager

     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 topologymanager
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"k8s.io/api/core/v1"
    25  
    26  	cadvisorapi "github.com/google/cadvisor/info/v1"
    27  
    28  	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
    29  	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
    30  )
    31  
    32  func NewTestBitMask(sockets ...int) bitmask.BitMask {
    33  	s, _ := bitmask.NewBitMask(sockets...)
    34  	return s
    35  }
    36  
    37  func TestNewManager(t *testing.T) {
    38  	tcases := []struct {
    39  		description    string
    40  		policyName     string
    41  		expectedPolicy string
    42  		expectedError  error
    43  		topologyError  error
    44  		policyOptions  map[string]string
    45  		topology       []cadvisorapi.Node
    46  	}{
    47  		{
    48  			description:    "Policy is set to none",
    49  			policyName:     "none",
    50  			expectedPolicy: "none",
    51  		},
    52  		{
    53  			description:    "Policy is set to best-effort",
    54  			policyName:     "best-effort",
    55  			expectedPolicy: "best-effort",
    56  		},
    57  		{
    58  			description:    "Policy is set to restricted",
    59  			policyName:     "restricted",
    60  			expectedPolicy: "restricted",
    61  		},
    62  		{
    63  			description:    "Policy is set to single-numa-node",
    64  			policyName:     "single-numa-node",
    65  			expectedPolicy: "single-numa-node",
    66  		},
    67  		{
    68  			description:   "Policy is set to unknown",
    69  			policyName:    "unknown",
    70  			expectedError: fmt.Errorf("unknown policy: \"unknown\""),
    71  		},
    72  		{
    73  			description:    "Unknown policy name best-effort policy",
    74  			policyName:     "best-effort",
    75  			expectedPolicy: "best-effort",
    76  			expectedError:  fmt.Errorf("unknown Topology Manager Policy option:"),
    77  			policyOptions: map[string]string{
    78  				"unknown-option": "true",
    79  			},
    80  		},
    81  		{
    82  			description:    "Unknown policy name restricted policy",
    83  			policyName:     "restricted",
    84  			expectedPolicy: "restricted",
    85  			expectedError:  fmt.Errorf("unknown Topology Manager Policy option:"),
    86  			policyOptions: map[string]string{
    87  				"unknown-option": "true",
    88  			},
    89  		},
    90  		{
    91  			description:    "can't get NUMA distances",
    92  			policyName:     "best-effort",
    93  			expectedPolicy: "best-effort",
    94  			policyOptions: map[string]string{
    95  				PreferClosestNUMANodes: "true",
    96  			},
    97  			expectedError: fmt.Errorf("error getting NUMA distances from cadvisor"),
    98  			topology: []cadvisorapi.Node{
    99  				{
   100  					Id: 0,
   101  				},
   102  			},
   103  		},
   104  		{
   105  			description:    "more than 8 NUMA nodes",
   106  			policyName:     "best-effort",
   107  			expectedPolicy: "best-effort",
   108  			expectedError:  fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", maxAllowableNUMANodes),
   109  			topology: []cadvisorapi.Node{
   110  				{
   111  					Id: 0,
   112  				},
   113  				{
   114  					Id: 1,
   115  				},
   116  				{
   117  					Id: 2,
   118  				},
   119  				{
   120  					Id: 3,
   121  				},
   122  				{
   123  					Id: 4,
   124  				},
   125  				{
   126  					Id: 5,
   127  				},
   128  				{
   129  					Id: 6,
   130  				},
   131  				{
   132  					Id: 7,
   133  				},
   134  				{
   135  					Id: 8,
   136  				},
   137  			},
   138  		},
   139  	}
   140  
   141  	for _, tc := range tcases {
   142  		topology := tc.topology
   143  
   144  		mngr, err := NewManager(topology, tc.policyName, "container", tc.policyOptions)
   145  		if tc.expectedError != nil {
   146  			if !strings.Contains(err.Error(), tc.expectedError.Error()) {
   147  				t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tc.expectedError.Error())
   148  			}
   149  		} else {
   150  			rawMgr := mngr.(*manager)
   151  			var policyName string
   152  			if rawScope, ok := rawMgr.scope.(*containerScope); ok {
   153  				policyName = rawScope.policy.Name()
   154  			} else if rawScope, ok := rawMgr.scope.(*noneScope); ok {
   155  				policyName = rawScope.policy.Name()
   156  			}
   157  			if policyName != tc.expectedPolicy {
   158  				t.Errorf("Unexpected policy name. Have: %q wants %q", policyName, tc.expectedPolicy)
   159  			}
   160  		}
   161  	}
   162  }
   163  
   164  func TestManagerScope(t *testing.T) {
   165  	tcases := []struct {
   166  		description   string
   167  		scopeName     string
   168  		expectedScope string
   169  		expectedError error
   170  	}{
   171  		{
   172  			description:   "Topology Manager Scope is set to container",
   173  			scopeName:     "container",
   174  			expectedScope: "container",
   175  		},
   176  		{
   177  			description:   "Topology Manager Scope is set to pod",
   178  			scopeName:     "pod",
   179  			expectedScope: "pod",
   180  		},
   181  		{
   182  			description:   "Topology Manager Scope is set to unknown",
   183  			scopeName:     "unknown",
   184  			expectedError: fmt.Errorf("unknown scope: \"unknown\""),
   185  		},
   186  	}
   187  
   188  	for _, tc := range tcases {
   189  		mngr, err := NewManager(nil, "best-effort", tc.scopeName, nil)
   190  
   191  		if tc.expectedError != nil {
   192  			if !strings.Contains(err.Error(), tc.expectedError.Error()) {
   193  				t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tc.expectedError.Error())
   194  			}
   195  		} else {
   196  			rawMgr := mngr.(*manager)
   197  			if rawMgr.scope.Name() != tc.expectedScope {
   198  				t.Errorf("Unexpected scope name. Have: %q wants %q", rawMgr.scope, tc.expectedScope)
   199  			}
   200  		}
   201  	}
   202  }
   203  
   204  type mockHintProvider struct {
   205  	th map[string][]TopologyHint
   206  	//TODO: Add this field and add some tests to make sure things error out
   207  	//appropriately on allocation errors.
   208  	//allocateError error
   209  }
   210  
   211  func (m *mockHintProvider) GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint {
   212  	return m.th
   213  }
   214  
   215  func (m *mockHintProvider) GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint {
   216  	return m.th
   217  }
   218  
   219  func (m *mockHintProvider) Allocate(pod *v1.Pod, container *v1.Container) error {
   220  	//return allocateError
   221  	return nil
   222  }
   223  
   224  type mockPolicy struct {
   225  	nonePolicy
   226  	ph []map[string][]TopologyHint
   227  }
   228  
   229  func (p *mockPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
   230  	p.ph = providersHints
   231  	return TopologyHint{}, true
   232  }
   233  
   234  func TestAddHintProvider(t *testing.T) {
   235  	tcases := []struct {
   236  		name string
   237  		hp   []HintProvider
   238  	}{
   239  		{
   240  			name: "Add HintProvider",
   241  			hp: []HintProvider{
   242  				&mockHintProvider{},
   243  				&mockHintProvider{},
   244  				&mockHintProvider{},
   245  			},
   246  		},
   247  	}
   248  	mngr := manager{}
   249  	mngr.scope = NewContainerScope(NewNonePolicy())
   250  	for _, tc := range tcases {
   251  		for _, hp := range tc.hp {
   252  			mngr.AddHintProvider(hp)
   253  		}
   254  		if len(tc.hp) != len(mngr.scope.(*containerScope).hintProviders) {
   255  			t.Errorf("error")
   256  		}
   257  	}
   258  }
   259  
   260  func TestAdmit(t *testing.T) {
   261  	numaInfo := &NUMAInfo{
   262  		Nodes: []int{0, 1},
   263  		NUMADistances: NUMADistances{
   264  			0: {10, 11},
   265  			1: {11, 10},
   266  		},
   267  	}
   268  
   269  	opts := PolicyOptions{}
   270  	bePolicy := NewBestEffortPolicy(numaInfo, opts)
   271  	restrictedPolicy := NewRestrictedPolicy(numaInfo, opts)
   272  	singleNumaPolicy := NewSingleNumaNodePolicy(numaInfo, opts)
   273  
   274  	tcases := []struct {
   275  		name     string
   276  		result   lifecycle.PodAdmitResult
   277  		qosClass v1.PodQOSClass
   278  		policy   Policy
   279  		hp       []HintProvider
   280  		expected bool
   281  	}{
   282  		{
   283  			name:     "QOSClass set as BestEffort. None Policy. No Hints.",
   284  			qosClass: v1.PodQOSBestEffort,
   285  			policy:   NewNonePolicy(),
   286  			hp:       []HintProvider{},
   287  			expected: true,
   288  		},
   289  		{
   290  			name:     "QOSClass set as Guaranteed. None Policy. No Hints.",
   291  			qosClass: v1.PodQOSGuaranteed,
   292  			policy:   NewNonePolicy(),
   293  			hp:       []HintProvider{},
   294  			expected: true,
   295  		},
   296  		{
   297  			name:     "QOSClass set as BestEffort. single-numa-node Policy. No Hints.",
   298  			qosClass: v1.PodQOSBestEffort,
   299  			policy:   singleNumaPolicy,
   300  			hp: []HintProvider{
   301  				&mockHintProvider{},
   302  			},
   303  			expected: true,
   304  		},
   305  		{
   306  			name:     "QOSClass set as BestEffort. Restricted Policy. No Hints.",
   307  			qosClass: v1.PodQOSBestEffort,
   308  			policy:   restrictedPolicy,
   309  			hp: []HintProvider{
   310  				&mockHintProvider{},
   311  			},
   312  			expected: true,
   313  		},
   314  		{
   315  			name:     "QOSClass set as Guaranteed. BestEffort Policy. Preferred Affinity.",
   316  			qosClass: v1.PodQOSGuaranteed,
   317  			policy:   bePolicy,
   318  			hp: []HintProvider{
   319  				&mockHintProvider{
   320  					map[string][]TopologyHint{
   321  						"resource": {
   322  							{
   323  								NUMANodeAffinity: NewTestBitMask(0),
   324  								Preferred:        true,
   325  							},
   326  							{
   327  								NUMANodeAffinity: NewTestBitMask(0, 1),
   328  								Preferred:        false,
   329  							},
   330  						},
   331  					},
   332  				},
   333  			},
   334  			expected: true,
   335  		},
   336  		{
   337  			name:     "QOSClass set as Guaranteed. BestEffort Policy. More than one Preferred Affinity.",
   338  			qosClass: v1.PodQOSGuaranteed,
   339  			policy:   bePolicy,
   340  			hp: []HintProvider{
   341  				&mockHintProvider{
   342  					map[string][]TopologyHint{
   343  						"resource": {
   344  							{
   345  								NUMANodeAffinity: NewTestBitMask(0),
   346  								Preferred:        true,
   347  							},
   348  							{
   349  								NUMANodeAffinity: NewTestBitMask(1),
   350  								Preferred:        true,
   351  							},
   352  							{
   353  								NUMANodeAffinity: NewTestBitMask(0, 1),
   354  								Preferred:        false,
   355  							},
   356  						},
   357  					},
   358  				},
   359  			},
   360  			expected: true,
   361  		},
   362  		{
   363  			name:     "QOSClass set as Burstable. BestEffort Policy. More than one Preferred Affinity.",
   364  			qosClass: v1.PodQOSBurstable,
   365  			policy:   bePolicy,
   366  			hp: []HintProvider{
   367  				&mockHintProvider{
   368  					map[string][]TopologyHint{
   369  						"resource": {
   370  							{
   371  								NUMANodeAffinity: NewTestBitMask(0),
   372  								Preferred:        true,
   373  							},
   374  							{
   375  								NUMANodeAffinity: NewTestBitMask(1),
   376  								Preferred:        true,
   377  							},
   378  							{
   379  								NUMANodeAffinity: NewTestBitMask(0, 1),
   380  								Preferred:        false,
   381  							},
   382  						},
   383  					},
   384  				},
   385  			},
   386  			expected: true,
   387  		},
   388  		{
   389  			name:     "QOSClass set as Guaranteed. BestEffort Policy. No Preferred Affinity.",
   390  			qosClass: v1.PodQOSGuaranteed,
   391  			policy:   bePolicy,
   392  			hp: []HintProvider{
   393  				&mockHintProvider{
   394  					map[string][]TopologyHint{
   395  						"resource": {
   396  							{
   397  								NUMANodeAffinity: NewTestBitMask(0, 1),
   398  								Preferred:        false,
   399  							},
   400  						},
   401  					},
   402  				},
   403  			},
   404  			expected: true,
   405  		},
   406  		{
   407  			name:     "QOSClass set as Guaranteed. Restricted Policy. Preferred Affinity.",
   408  			qosClass: v1.PodQOSGuaranteed,
   409  			policy:   restrictedPolicy,
   410  			hp: []HintProvider{
   411  				&mockHintProvider{
   412  					map[string][]TopologyHint{
   413  						"resource": {
   414  							{
   415  								NUMANodeAffinity: NewTestBitMask(0),
   416  								Preferred:        true,
   417  							},
   418  							{
   419  								NUMANodeAffinity: NewTestBitMask(0, 1),
   420  								Preferred:        false,
   421  							},
   422  						},
   423  					},
   424  				},
   425  			},
   426  			expected: true,
   427  		},
   428  		{
   429  			name:     "QOSClass set as Burstable. Restricted Policy. Preferred Affinity.",
   430  			qosClass: v1.PodQOSBurstable,
   431  			policy:   restrictedPolicy,
   432  			hp: []HintProvider{
   433  				&mockHintProvider{
   434  					map[string][]TopologyHint{
   435  						"resource": {
   436  							{
   437  								NUMANodeAffinity: NewTestBitMask(0),
   438  								Preferred:        true,
   439  							},
   440  							{
   441  								NUMANodeAffinity: NewTestBitMask(0, 1),
   442  								Preferred:        false,
   443  							},
   444  						},
   445  					},
   446  				},
   447  			},
   448  			expected: true,
   449  		},
   450  		{
   451  			name:     "QOSClass set as Guaranteed. Restricted Policy. More than one Preferred affinity.",
   452  			qosClass: v1.PodQOSGuaranteed,
   453  			policy:   restrictedPolicy,
   454  			hp: []HintProvider{
   455  				&mockHintProvider{
   456  					map[string][]TopologyHint{
   457  						"resource": {
   458  							{
   459  								NUMANodeAffinity: NewTestBitMask(0),
   460  								Preferred:        true,
   461  							},
   462  							{
   463  								NUMANodeAffinity: NewTestBitMask(1),
   464  								Preferred:        true,
   465  							},
   466  							{
   467  								NUMANodeAffinity: NewTestBitMask(0, 1),
   468  								Preferred:        false,
   469  							},
   470  						},
   471  					},
   472  				},
   473  			},
   474  			expected: true,
   475  		},
   476  		{
   477  			name:     "QOSClass set as Burstable. Restricted Policy. More than one Preferred affinity.",
   478  			qosClass: v1.PodQOSBurstable,
   479  			policy:   restrictedPolicy,
   480  			hp: []HintProvider{
   481  				&mockHintProvider{
   482  					map[string][]TopologyHint{
   483  						"resource": {
   484  							{
   485  								NUMANodeAffinity: NewTestBitMask(0),
   486  								Preferred:        true,
   487  							},
   488  							{
   489  								NUMANodeAffinity: NewTestBitMask(1),
   490  								Preferred:        true,
   491  							},
   492  							{
   493  								NUMANodeAffinity: NewTestBitMask(0, 1),
   494  								Preferred:        false,
   495  							},
   496  						},
   497  					},
   498  				},
   499  			},
   500  			expected: true,
   501  		},
   502  		{
   503  			name:     "QOSClass set as Guaranteed. Restricted Policy. No Preferred affinity.",
   504  			qosClass: v1.PodQOSGuaranteed,
   505  			policy:   restrictedPolicy,
   506  			hp: []HintProvider{
   507  				&mockHintProvider{
   508  					map[string][]TopologyHint{
   509  						"resource": {
   510  							{
   511  								NUMANodeAffinity: NewTestBitMask(0, 1),
   512  								Preferred:        false,
   513  							},
   514  						},
   515  					},
   516  				},
   517  			},
   518  			expected: false,
   519  		},
   520  		{
   521  			name:     "QOSClass set as Burstable. Restricted Policy. No Preferred affinity.",
   522  			qosClass: v1.PodQOSBurstable,
   523  			policy:   restrictedPolicy,
   524  			hp: []HintProvider{
   525  				&mockHintProvider{
   526  					map[string][]TopologyHint{
   527  						"resource": {
   528  							{
   529  								NUMANodeAffinity: NewTestBitMask(0, 1),
   530  								Preferred:        false,
   531  							},
   532  						},
   533  					},
   534  				},
   535  			},
   536  			expected: false,
   537  		},
   538  	}
   539  	for _, tc := range tcases {
   540  		ctnScopeManager := manager{}
   541  		ctnScopeManager.scope = NewContainerScope(tc.policy)
   542  		ctnScopeManager.scope.(*containerScope).hintProviders = tc.hp
   543  
   544  		podScopeManager := manager{}
   545  		podScopeManager.scope = NewPodScope(tc.policy)
   546  		podScopeManager.scope.(*podScope).hintProviders = tc.hp
   547  
   548  		pod := &v1.Pod{
   549  			Spec: v1.PodSpec{
   550  				Containers: []v1.Container{
   551  					{
   552  						Resources: v1.ResourceRequirements{},
   553  					},
   554  				},
   555  			},
   556  			Status: v1.PodStatus{
   557  				QOSClass: tc.qosClass,
   558  			},
   559  		}
   560  
   561  		podAttr := lifecycle.PodAdmitAttributes{
   562  			Pod: pod,
   563  		}
   564  
   565  		// Container scope Admit
   566  		ctnActual := ctnScopeManager.Admit(&podAttr)
   567  		if ctnActual.Admit != tc.expected {
   568  			t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, ctnActual.Admit)
   569  		}
   570  		if !ctnActual.Admit && ctnActual.Reason != ErrorTopologyAffinity {
   571  			t.Errorf("Error occurred, expected Reason in result to be %v got %v", ErrorTopologyAffinity, ctnActual.Reason)
   572  		}
   573  
   574  		// Pod scope Admit
   575  		podActual := podScopeManager.Admit(&podAttr)
   576  		if podActual.Admit != tc.expected {
   577  			t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, podActual.Admit)
   578  		}
   579  		if !ctnActual.Admit && ctnActual.Reason != ErrorTopologyAffinity {
   580  			t.Errorf("Error occurred, expected Reason in result to be %v got %v", ErrorTopologyAffinity, ctnActual.Reason)
   581  		}
   582  	}
   583  }
   584  

View as plain text