github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/overcommit/node/matcher/matcher_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 matcher 18 19 import ( 20 "fmt" 21 "sort" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/assert" 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 v12 "k8s.io/client-go/listers/core/v1" 29 "k8s.io/client-go/tools/cache" 30 31 "github.com/kubewharf/katalyst-api/pkg/apis/overcommit/v1alpha1" 32 v1alpha12 "github.com/kubewharf/katalyst-api/pkg/client/listers/overcommit/v1alpha1" 33 "github.com/kubewharf/katalyst-api/pkg/consts" 34 ) 35 36 func fakeIndexer() cache.Indexer { 37 return cache.NewIndexer(func(obj interface{}) (string, error) { 38 noc, ok := obj.(*v1alpha1.NodeOvercommitConfig) 39 if !ok { 40 return "", nil 41 } 42 return noc.Name, nil 43 }, cache.Indexers{ 44 LabelSelectorValIndex: func(obj interface{}) ([]string, error) { 45 noc, ok := obj.(*v1alpha1.NodeOvercommitConfig) 46 if !ok { 47 return []string{}, nil 48 } 49 return []string{noc.Spec.NodeOvercommitSelectorVal}, nil 50 }, 51 }) 52 } 53 54 func fakeNodeIndexer() cache.Indexer { 55 return cache.NewIndexer(func(obj interface{}) (string, error) { 56 node, ok := obj.(*v1.Node) 57 if !ok { 58 return "", nil 59 } 60 return node.Name, nil 61 }, cache.Indexers{ 62 "test": func(obj interface{}) ([]string, error) { 63 return []string{}, nil 64 }, 65 }) 66 } 67 68 func makeTestMatcher() *MatcherImpl { 69 nodeIndexer := testNodeIndexer() 70 indexer := testNocIndexer() 71 nocLister := v1alpha12.NewNodeOvercommitConfigLister(indexer) 72 nodeLister := v12.NewNodeLister(nodeIndexer) 73 74 return NewMatcher(nodeLister, nocLister, indexer) 75 } 76 77 func makeInitedMatcher() (*MatcherImpl, error) { 78 m := makeTestMatcher() 79 err := m.Reconcile() 80 return m, err 81 } 82 83 func testNodeIndexer() cache.Indexer { 84 indexer := fakeNodeIndexer() 85 86 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1", UID: "01", Labels: map[string]string{consts.NodeOvercommitSelectorKey: "pool1"}}}) 87 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node2", UID: "02", Labels: map[string]string{consts.NodeOvercommitSelectorKey: "pool2"}}}) 88 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node3", UID: "03", Labels: map[string]string{consts.NodeOvercommitSelectorKey: "pool1"}}}) 89 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node4", UID: "04", Labels: map[string]string{consts.NodeOvercommitSelectorKey: "pool3"}}}) 90 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node5", UID: "05"}}) 91 indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node6", UID: "06", Labels: map[string]string{"pool": "pool1"}}}) 92 return indexer 93 } 94 95 func testNocIndexer() cache.Indexer { 96 indexer := fakeIndexer() 97 98 indexer.Add(&v1alpha1.NodeOvercommitConfig{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "config1", 101 }, 102 Spec: v1alpha1.NodeOvercommitConfigSpec{ 103 NodeOvercommitSelectorVal: "pool1", 104 ResourceOvercommitRatio: map[v1.ResourceName]string{ 105 v1.ResourceCPU: "1.5", 106 v1.ResourceMemory: "1", 107 }, 108 }, 109 }) 110 111 indexer.Add(&v1alpha1.NodeOvercommitConfig{ 112 ObjectMeta: metav1.ObjectMeta{ 113 Name: "config2", 114 }, 115 Spec: v1alpha1.NodeOvercommitConfigSpec{ 116 NodeOvercommitSelectorVal: "pool2", 117 ResourceOvercommitRatio: map[v1.ResourceName]string{ 118 v1.ResourceCPU: "2", 119 v1.ResourceMemory: "1", 120 }, 121 }, 122 }) 123 124 return indexer 125 } 126 127 func makeMatcherByIndexer(nodeIndexer, nocIndexer cache.Indexer) *MatcherImpl { 128 nodeLister := v12.NewNodeLister(nodeIndexer) 129 nocLister := v1alpha12.NewNodeOvercommitConfigLister(nocIndexer) 130 131 matcher := NewMatcher(nodeLister, nocLister, nocIndexer) 132 _ = matcher.Reconcile() 133 return matcher 134 } 135 136 func TestMatchConfigNameToNodes(t *testing.T) { 137 t.Parallel() 138 139 testCases := []struct { 140 name string 141 configName string 142 result []string 143 }{ 144 { 145 name: "nodeList", 146 configName: "config1", 147 result: []string{"node1", "node3"}, 148 }, 149 { 150 name: "default matches all nodes", 151 configName: "config2", 152 result: []string{"node2"}, 153 }, 154 } 155 156 matcher := makeTestMatcher() 157 158 for _, tc := range testCases { 159 tc := tc 160 t.Run(tc.name, func(t *testing.T) { 161 t.Parallel() 162 163 out, err := matcher.matchConfigNameToNodes(tc.configName) 164 assert.Nil(t, err) 165 assert.Equal(t, len(tc.result), len(out)) 166 }) 167 } 168 } 169 170 func TestMatchConfig(t *testing.T) { 171 t.Parallel() 172 173 nodeIndexer1 := testNodeIndexer() 174 nocIndexer1 := testNocIndexer() 175 matcher1 := makeMatcherByIndexer(nodeIndexer1, nocIndexer1) 176 nocIndexer1.Update( 177 &v1alpha1.NodeOvercommitConfig{ 178 ObjectMeta: metav1.ObjectMeta{ 179 Name: "config2", 180 }, 181 Spec: v1alpha1.NodeOvercommitConfigSpec{ 182 NodeOvercommitSelectorVal: "pool3", 183 ResourceOvercommitRatio: map[v1.ResourceName]string{ 184 v1.ResourceCPU: "2", 185 v1.ResourceMemory: "1", 186 }, 187 }, 188 }) 189 190 nodeIndexer2 := testNodeIndexer() 191 nocIndexer2 := testNocIndexer() 192 matcher2 := makeMatcherByIndexer(nodeIndexer2, nocIndexer2) 193 nocIndexer2.Add(&v1alpha1.NodeOvercommitConfig{ 194 ObjectMeta: metav1.ObjectMeta{ 195 Name: "config3", 196 }, 197 Spec: v1alpha1.NodeOvercommitConfigSpec{ 198 NodeOvercommitSelectorVal: "pool3", 199 ResourceOvercommitRatio: map[v1.ResourceName]string{ 200 v1.ResourceCPU: "3", 201 }, 202 }, 203 }) 204 205 nodeIndexer3 := testNodeIndexer() 206 nocIndexer3 := testNocIndexer() 207 matcher3 := makeMatcherByIndexer(nodeIndexer3, nocIndexer3) 208 nocIndexer3.Delete(&v1alpha1.NodeOvercommitConfig{ 209 ObjectMeta: metav1.ObjectMeta{ 210 Name: "config1", 211 }, 212 Spec: v1alpha1.NodeOvercommitConfigSpec{ 213 NodeOvercommitSelectorVal: "pool1", 214 ResourceOvercommitRatio: map[v1.ResourceName]string{ 215 v1.ResourceCPU: "1.5", 216 v1.ResourceMemory: "1", 217 }, 218 }, 219 }) 220 221 testCases := []struct { 222 name string 223 matcher Matcher 224 configName string 225 result []string 226 }{ 227 { 228 name: "update selector", 229 matcher: matcher1, 230 configName: "config2", 231 result: []string{"node2", "node4"}, 232 }, 233 { 234 name: "add config", 235 matcher: matcher2, 236 configName: "config3", 237 result: []string{"node4"}, 238 }, 239 { 240 name: "delete config", 241 matcher: matcher3, 242 configName: "config1", 243 result: []string{"node1", "node3"}, 244 }, 245 } 246 247 for _, tc := range testCases { 248 tc := tc 249 t.Run(tc.name, func(t *testing.T) { 250 t.Parallel() 251 252 out, err := tc.matcher.MatchConfig(tc.configName) 253 assert.Nil(t, err) 254 sort.Strings(out) 255 assert.Equal(t, tc.result, out) 256 }) 257 } 258 } 259 260 func TestMatchNode(t *testing.T) { 261 t.Parallel() 262 263 nodeIndexer1 := testNodeIndexer() 264 nocIndexer1 := testNocIndexer() 265 matcher1 := makeMatcherByIndexer(nodeIndexer1, nocIndexer1) 266 nocIndexer1.Update(&v1alpha1.NodeOvercommitConfig{ 267 ObjectMeta: metav1.ObjectMeta{ 268 Name: "config2", 269 }, 270 Spec: v1alpha1.NodeOvercommitConfigSpec{ 271 NodeOvercommitSelectorVal: "pool3", 272 ResourceOvercommitRatio: map[v1.ResourceName]string{ 273 v1.ResourceCPU: "2", 274 v1.ResourceMemory: "1", 275 }, 276 }, 277 }) 278 279 nodeIndexer2 := testNodeIndexer() 280 nocIndexer2 := testNocIndexer() 281 matcher2 := makeMatcherByIndexer(nodeIndexer2, nocIndexer2) 282 nocIndexer2.Add(&v1alpha1.NodeOvercommitConfig{ 283 ObjectMeta: metav1.ObjectMeta{ 284 Name: "config3", 285 }, 286 Spec: v1alpha1.NodeOvercommitConfigSpec{ 287 NodeOvercommitSelectorVal: "pool3", 288 ResourceOvercommitRatio: map[v1.ResourceName]string{ 289 v1.ResourceCPU: "3", 290 }, 291 }, 292 }) 293 294 nodeIndexer3 := testNodeIndexer() 295 nocIndexer3 := testNocIndexer() 296 matcher3 := makeMatcherByIndexer(nodeIndexer3, nocIndexer3) 297 nocIndexer3.Delete( 298 &v1alpha1.NodeOvercommitConfig{ 299 ObjectMeta: metav1.ObjectMeta{ 300 Name: "config1", 301 }, 302 }, 303 ) 304 305 nodeIndexer5 := testNodeIndexer() 306 nocIndexer5 := testNocIndexer() 307 matcher5 := makeMatcherByIndexer(nodeIndexer5, nocIndexer5) 308 nodeIndexer5.Update( 309 &v1.Node{ 310 ObjectMeta: metav1.ObjectMeta{ 311 Name: "node4", 312 Labels: map[string]string{ 313 consts.NodeOvercommitSelectorKey: "pool1", 314 }, 315 }, 316 }) 317 318 nodeIndexer6 := testNodeIndexer() 319 nocIndexer6 := testNocIndexer() 320 matcher6 := makeMatcherByIndexer(nodeIndexer6, nocIndexer6) 321 nodeIndexer5.Update( 322 &v1.Node{ 323 ObjectMeta: metav1.ObjectMeta{ 324 Name: "node1", 325 Labels: nil, 326 }, 327 }) 328 329 nodeIndexer7 := testNodeIndexer() 330 nocIndexer7 := testNocIndexer() 331 matcher7 := makeMatcherByIndexer(nodeIndexer7, nocIndexer7) 332 nodeIndexer7.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node7", UID: "07", Labels: map[string]string{consts.NodeOvercommitSelectorKey: "pool1"}}}) 333 334 oldNodeMap := map[string]string{ 335 "node1": "config1", 336 "node2": "config2", 337 "node3": "config1", 338 } 339 340 testCases := []struct { 341 name string 342 matcher Matcher 343 configName string 344 nodeName string 345 oldNodeMap map[string]string 346 newNodeMap map[string]string 347 }{ 348 { 349 name: "update selector", 350 matcher: matcher1, 351 configName: "config2", 352 nodeName: "", 353 oldNodeMap: oldNodeMap, 354 newNodeMap: map[string]string{ 355 "node1": "config1", 356 "node3": "config1", 357 "node4": "config2", 358 }, 359 }, 360 { 361 name: "add config", 362 matcher: matcher2, 363 configName: "config3", 364 nodeName: "", 365 oldNodeMap: oldNodeMap, 366 newNodeMap: map[string]string{ 367 "node1": "config1", 368 "node2": "config2", 369 "node3": "config1", 370 "node4": "config3", 371 }, 372 }, 373 { 374 name: "delete config", 375 matcher: matcher3, 376 configName: "config1", 377 nodeName: "", 378 oldNodeMap: oldNodeMap, 379 newNodeMap: map[string]string{ 380 "node2": "config2", 381 }, 382 }, 383 { 384 name: "update node label", 385 matcher: matcher5, 386 configName: "", 387 nodeName: "node4", 388 oldNodeMap: oldNodeMap, 389 newNodeMap: map[string]string{ 390 "node1": "config1", 391 "node2": "config2", 392 "node3": "config1", 393 "node4": "config1", 394 }, 395 }, 396 { 397 name: "update node label2", 398 matcher: matcher6, 399 configName: "", 400 nodeName: "node1", 401 oldNodeMap: oldNodeMap, 402 newNodeMap: map[string]string{ 403 "node2": "config2", 404 "node3": "config1", 405 }, 406 }, 407 { 408 name: "add node", 409 matcher: matcher7, 410 configName: "", 411 nodeName: "node7", 412 oldNodeMap: oldNodeMap, 413 newNodeMap: map[string]string{ 414 "node1": "config1", 415 "node2": "config2", 416 "node3": "config1", 417 "node7": "config1", 418 }, 419 }, 420 } 421 422 for _, tc := range testCases { 423 tc := tc 424 t.Run(tc.name, func(t *testing.T) { 425 t.Parallel() 426 427 if tc.configName == "" { 428 for _, c := range []string{"config1", "config2"} { 429 _, _ = tc.matcher.MatchConfig(c) 430 } 431 tc.matcher.MatchNode(tc.nodeName) 432 } else { 433 nodes, _ := tc.matcher.MatchConfig(tc.configName) 434 for _, node := range nodes { 435 tc.matcher.MatchNode(node) 436 } 437 } 438 439 for k, v := range tc.newNodeMap { 440 config := tc.matcher.GetConfig(k) 441 assert.Equal(t, config.Name, v) 442 } 443 }) 444 } 445 } 446 447 func TestDelNode(t *testing.T) { 448 t.Parallel() 449 450 matcher1, _ := makeInitedMatcher() 451 matcher2, _ := makeInitedMatcher() 452 453 testCases := []struct { 454 name string 455 nodeName string 456 matcher *MatcherImpl 457 result int 458 }{ 459 { 460 name: "exist node", 461 nodeName: "node1", 462 matcher: matcher1, 463 result: 2, 464 }, 465 { 466 name: "non-existing node", 467 nodeName: "node99", 468 matcher: matcher2, 469 result: 3, 470 }, 471 } 472 473 for _, tc := range testCases { 474 tc := tc 475 t.Run(tc.name, func(t *testing.T) { 476 t.Parallel() 477 478 tc.matcher.DelNode(tc.nodeName) 479 fmt.Printf("testCase: %v", tc) 480 assert.Equal(t, tc.result, len(tc.matcher.nodeToConfig)) 481 }) 482 } 483 } 484 485 func TestSort(t *testing.T) { 486 t.Parallel() 487 488 testCases := []struct { 489 name string 490 nocList NocList 491 result string 492 }{ 493 { 494 name: "creationTimestamp", 495 nocList: NocList{ 496 &v1alpha1.NodeOvercommitConfig{ 497 ObjectMeta: metav1.ObjectMeta{ 498 Name: "noclist1", 499 CreationTimestamp: metav1.NewTime(time.Now().Add(-10 * time.Minute)), 500 }, 501 }, 502 &v1alpha1.NodeOvercommitConfig{ 503 ObjectMeta: metav1.ObjectMeta{ 504 Name: "noclist2", 505 CreationTimestamp: metav1.NewTime(time.Now()), 506 }, 507 }, 508 }, 509 result: "noclist2", 510 }, 511 } 512 513 for _, tc := range testCases { 514 tc := tc 515 t.Run(tc.name, func(t *testing.T) { 516 t.Parallel() 517 sort.Sort(tc.nocList) 518 assert.Equal(t, tc.result, tc.nocList[0].Name) 519 }) 520 } 521 }