k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/node_container_manager_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2017 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package cm 21 22 import ( 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/api/resource" 28 evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api" 29 ) 30 31 func TestNodeAllocatableReservationForScheduling(t *testing.T) { 32 memoryEvictionThreshold := resource.MustParse("100Mi") 33 cpuMemCases := []struct { 34 kubeReserved v1.ResourceList 35 systemReserved v1.ResourceList 36 expected v1.ResourceList 37 capacity v1.ResourceList 38 hardThreshold evictionapi.ThresholdValue 39 }{ 40 { 41 kubeReserved: getResourceList("100m", "100Mi"), 42 systemReserved: getResourceList("50m", "50Mi"), 43 capacity: getResourceList("10", "10Gi"), 44 expected: getResourceList("150m", "150Mi"), 45 }, 46 { 47 kubeReserved: getResourceList("100m", "100Mi"), 48 systemReserved: getResourceList("50m", "50Mi"), 49 hardThreshold: evictionapi.ThresholdValue{ 50 Quantity: &memoryEvictionThreshold, 51 }, 52 capacity: getResourceList("10", "10Gi"), 53 expected: getResourceList("150m", "250Mi"), 54 }, 55 { 56 kubeReserved: getResourceList("100m", "100Mi"), 57 systemReserved: getResourceList("50m", "50Mi"), 58 capacity: getResourceList("10", "10Gi"), 59 hardThreshold: evictionapi.ThresholdValue{ 60 Percentage: 0.05, 61 }, 62 expected: getResourceList("150m", "694157320"), 63 }, 64 65 { 66 kubeReserved: v1.ResourceList{}, 67 systemReserved: v1.ResourceList{}, 68 capacity: getResourceList("10", "10Gi"), 69 expected: getResourceList("", ""), 70 }, 71 { 72 kubeReserved: getResourceList("", "100Mi"), 73 systemReserved: getResourceList("50m", "50Mi"), 74 capacity: getResourceList("10", "10Gi"), 75 expected: getResourceList("50m", "150Mi"), 76 }, 77 78 { 79 kubeReserved: getResourceList("50m", "100Mi"), 80 systemReserved: getResourceList("", "50Mi"), 81 capacity: getResourceList("10", "10Gi"), 82 expected: getResourceList("50m", "150Mi"), 83 }, 84 { 85 kubeReserved: getResourceList("", "100Mi"), 86 systemReserved: getResourceList("", "50Mi"), 87 capacity: getResourceList("10", ""), 88 expected: getResourceList("", "150Mi"), 89 }, 90 } 91 for idx, tc := range cpuMemCases { 92 nc := NodeConfig{ 93 NodeAllocatableConfig: NodeAllocatableConfig{ 94 KubeReserved: tc.kubeReserved, 95 SystemReserved: tc.systemReserved, 96 HardEvictionThresholds: []evictionapi.Threshold{ 97 { 98 Signal: evictionapi.SignalMemoryAvailable, 99 Operator: evictionapi.OpLessThan, 100 Value: tc.hardThreshold, 101 }, 102 }, 103 }, 104 } 105 cm := &containerManagerImpl{ 106 NodeConfig: nc, 107 capacity: tc.capacity, 108 } 109 for k, v := range cm.GetNodeAllocatableReservation() { 110 expected, exists := tc.expected[k] 111 assert.True(t, exists, "test case %d expected resource %q", idx+1, k) 112 assert.Equal(t, expected.MilliValue(), v.MilliValue(), "test case %d failed for resource %q", idx+1, k) 113 } 114 } 115 116 ephemeralStorageEvictionThreshold := resource.MustParse("100Mi") 117 ephemeralStorageTestCases := []struct { 118 kubeReserved v1.ResourceList 119 expected v1.ResourceList 120 capacity v1.ResourceList 121 hardThreshold evictionapi.ThresholdValue 122 }{ 123 { 124 kubeReserved: getEphemeralStorageResourceList("100Mi"), 125 capacity: getEphemeralStorageResourceList("10Gi"), 126 expected: getEphemeralStorageResourceList("100Mi"), 127 }, 128 { 129 kubeReserved: getEphemeralStorageResourceList("100Mi"), 130 hardThreshold: evictionapi.ThresholdValue{ 131 Quantity: &ephemeralStorageEvictionThreshold, 132 }, 133 capacity: getEphemeralStorageResourceList("10Gi"), 134 expected: getEphemeralStorageResourceList("200Mi"), 135 }, 136 { 137 kubeReserved: getEphemeralStorageResourceList("150Mi"), 138 capacity: getEphemeralStorageResourceList("10Gi"), 139 hardThreshold: evictionapi.ThresholdValue{ 140 Percentage: 0.05, 141 }, 142 expected: getEphemeralStorageResourceList("694157320"), 143 }, 144 145 { 146 kubeReserved: v1.ResourceList{}, 147 capacity: getEphemeralStorageResourceList("10Gi"), 148 expected: getEphemeralStorageResourceList(""), 149 }, 150 } 151 for idx, tc := range ephemeralStorageTestCases { 152 nc := NodeConfig{ 153 NodeAllocatableConfig: NodeAllocatableConfig{ 154 KubeReserved: tc.kubeReserved, 155 HardEvictionThresholds: []evictionapi.Threshold{ 156 { 157 Signal: evictionapi.SignalNodeFsAvailable, 158 Operator: evictionapi.OpLessThan, 159 Value: tc.hardThreshold, 160 }, 161 }, 162 }, 163 } 164 cm := &containerManagerImpl{ 165 NodeConfig: nc, 166 capacity: tc.capacity, 167 } 168 for k, v := range cm.GetNodeAllocatableReservation() { 169 expected, exists := tc.expected[k] 170 assert.True(t, exists, "test case %d expected resource %q", idx+1, k) 171 assert.Equal(t, expected.MilliValue(), v.MilliValue(), "test case %d failed for resource %q", idx+1, k) 172 } 173 } 174 } 175 176 func TestNodeAllocatableForEnforcement(t *testing.T) { 177 memoryEvictionThreshold := resource.MustParse("100Mi") 178 testCases := []struct { 179 kubeReserved v1.ResourceList 180 systemReserved v1.ResourceList 181 capacity v1.ResourceList 182 expected v1.ResourceList 183 hardThreshold evictionapi.ThresholdValue 184 }{ 185 { 186 kubeReserved: getResourceList("100m", "100Mi"), 187 systemReserved: getResourceList("50m", "50Mi"), 188 capacity: getResourceList("10", "10Gi"), 189 expected: getResourceList("9850m", "10090Mi"), 190 }, 191 { 192 kubeReserved: getResourceList("100m", "100Mi"), 193 systemReserved: getResourceList("50m", "50Mi"), 194 hardThreshold: evictionapi.ThresholdValue{ 195 Quantity: &memoryEvictionThreshold, 196 }, 197 capacity: getResourceList("10", "10Gi"), 198 expected: getResourceList("9850m", "10090Mi"), 199 }, 200 { 201 kubeReserved: getResourceList("100m", "100Mi"), 202 systemReserved: getResourceList("50m", "50Mi"), 203 hardThreshold: evictionapi.ThresholdValue{ 204 Percentage: 0.05, 205 }, 206 capacity: getResourceList("10", "10Gi"), 207 expected: getResourceList("9850m", "10090Mi"), 208 }, 209 210 { 211 kubeReserved: v1.ResourceList{}, 212 systemReserved: v1.ResourceList{}, 213 capacity: getResourceList("10", "10Gi"), 214 expected: getResourceList("10", "10Gi"), 215 }, 216 { 217 kubeReserved: getResourceList("", "100Mi"), 218 systemReserved: getResourceList("50m", "50Mi"), 219 capacity: getResourceList("10", "10Gi"), 220 expected: getResourceList("9950m", "10090Mi"), 221 }, 222 223 { 224 kubeReserved: getResourceList("50m", "100Mi"), 225 systemReserved: getResourceList("", "50Mi"), 226 capacity: getResourceList("10", "10Gi"), 227 expected: getResourceList("9950m", "10090Mi"), 228 }, 229 { 230 kubeReserved: getResourceList("", "100Mi"), 231 systemReserved: getResourceList("", "50Mi"), 232 capacity: getResourceList("10", ""), 233 expected: getResourceList("10", ""), 234 }, 235 } 236 for idx, tc := range testCases { 237 nc := NodeConfig{ 238 NodeAllocatableConfig: NodeAllocatableConfig{ 239 KubeReserved: tc.kubeReserved, 240 SystemReserved: tc.systemReserved, 241 HardEvictionThresholds: []evictionapi.Threshold{ 242 { 243 Signal: evictionapi.SignalMemoryAvailable, 244 Operator: evictionapi.OpLessThan, 245 Value: tc.hardThreshold, 246 }, 247 }, 248 }, 249 } 250 cm := &containerManagerImpl{ 251 NodeConfig: nc, 252 capacity: tc.capacity, 253 } 254 for k, v := range cm.GetNodeAllocatableAbsolute() { 255 expected, exists := tc.expected[k] 256 assert.True(t, exists) 257 assert.Equal(t, expected.MilliValue(), v.MilliValue(), "test case %d failed for resource %q", idx+1, k) 258 } 259 } 260 } 261 262 func TestNodeAllocatableInputValidation(t *testing.T) { 263 memoryEvictionThreshold := resource.MustParse("100Mi") 264 highMemoryEvictionThreshold := resource.MustParse("2Gi") 265 cpuMemTestCases := []struct { 266 kubeReserved v1.ResourceList 267 systemReserved v1.ResourceList 268 capacity v1.ResourceList 269 hardThreshold evictionapi.ThresholdValue 270 invalidConfiguration bool 271 }{ 272 { 273 kubeReserved: getResourceList("100m", "100Mi"), 274 systemReserved: getResourceList("50m", "50Mi"), 275 capacity: getResourceList("10", "10Gi"), 276 }, 277 { 278 kubeReserved: getResourceList("100m", "100Mi"), 279 systemReserved: getResourceList("50m", "50Mi"), 280 hardThreshold: evictionapi.ThresholdValue{ 281 Quantity: &memoryEvictionThreshold, 282 }, 283 capacity: getResourceList("10", "10Gi"), 284 }, 285 { 286 kubeReserved: getResourceList("100m", "100Mi"), 287 systemReserved: getResourceList("50m", "50Mi"), 288 hardThreshold: evictionapi.ThresholdValue{ 289 Percentage: 0.05, 290 }, 291 capacity: getResourceList("10", "10Gi"), 292 }, 293 { 294 kubeReserved: v1.ResourceList{}, 295 systemReserved: v1.ResourceList{}, 296 capacity: getResourceList("10", "10Gi"), 297 }, 298 { 299 kubeReserved: getResourceList("", "100Mi"), 300 systemReserved: getResourceList("50m", "50Mi"), 301 capacity: getResourceList("10", "10Gi"), 302 }, 303 { 304 kubeReserved: getResourceList("50m", "100Mi"), 305 systemReserved: getResourceList("", "50Mi"), 306 capacity: getResourceList("10", "10Gi"), 307 }, 308 { 309 kubeReserved: getResourceList("", "100Mi"), 310 systemReserved: getResourceList("", "50Mi"), 311 capacity: getResourceList("10", ""), 312 }, 313 { 314 kubeReserved: getResourceList("5", "10Gi"), 315 systemReserved: getResourceList("5", "10Gi"), 316 hardThreshold: evictionapi.ThresholdValue{ 317 Quantity: &highMemoryEvictionThreshold, 318 }, 319 capacity: getResourceList("10", "11Gi"), 320 invalidConfiguration: true, 321 }, 322 } 323 for _, tc := range cpuMemTestCases { 324 nc := NodeConfig{ 325 NodeAllocatableConfig: NodeAllocatableConfig{ 326 KubeReserved: tc.kubeReserved, 327 SystemReserved: tc.systemReserved, 328 HardEvictionThresholds: []evictionapi.Threshold{ 329 { 330 Signal: evictionapi.SignalMemoryAvailable, 331 Operator: evictionapi.OpLessThan, 332 Value: tc.hardThreshold, 333 }, 334 }, 335 }, 336 } 337 cm := &containerManagerImpl{ 338 NodeConfig: nc, 339 capacity: tc.capacity, 340 } 341 err := cm.validateNodeAllocatable() 342 if err == nil && tc.invalidConfiguration { 343 t.Fatalf("Expected invalid node allocatable configuration") 344 } else if err != nil && !tc.invalidConfiguration { 345 t.Fatalf("Expected valid node allocatable configuration: %v", err) 346 } 347 } 348 349 ephemeralStorageEvictionThreshold := resource.MustParse("100Mi") 350 ephemeralStorageTestCases := []struct { 351 kubeReserved v1.ResourceList 352 capacity v1.ResourceList 353 hardThreshold evictionapi.ThresholdValue 354 invalidConfiguration bool 355 }{ 356 { 357 kubeReserved: getEphemeralStorageResourceList("100Mi"), 358 capacity: getEphemeralStorageResourceList("500Mi"), 359 }, 360 { 361 kubeReserved: getEphemeralStorageResourceList("20Gi"), 362 hardThreshold: evictionapi.ThresholdValue{ 363 Quantity: &ephemeralStorageEvictionThreshold, 364 }, 365 capacity: getEphemeralStorageResourceList("20Gi"), 366 invalidConfiguration: true, 367 }, 368 } 369 for _, tc := range ephemeralStorageTestCases { 370 nc := NodeConfig{ 371 NodeAllocatableConfig: NodeAllocatableConfig{ 372 KubeReserved: tc.kubeReserved, 373 HardEvictionThresholds: []evictionapi.Threshold{ 374 { 375 Signal: evictionapi.SignalNodeFsAvailable, 376 Operator: evictionapi.OpLessThan, 377 Value: tc.hardThreshold, 378 }, 379 }, 380 }, 381 } 382 cm := &containerManagerImpl{ 383 NodeConfig: nc, 384 capacity: tc.capacity, 385 } 386 err := cm.validateNodeAllocatable() 387 if err == nil && tc.invalidConfiguration { 388 t.Fatalf("Expected invalid node allocatable configuration") 389 } else if err != nil && !tc.invalidConfiguration { 390 t.Fatalf("Expected valid node allocatable configuration: %v", err) 391 } 392 } 393 } 394 395 // getEphemeralStorageResourceList returns a ResourceList with the 396 // specified ephemeral storage resource values 397 func getEphemeralStorageResourceList(storage string) v1.ResourceList { 398 res := v1.ResourceList{} 399 if storage != "" { 400 res[v1.ResourceEphemeralStorage] = resource.MustParse(storage) 401 } 402 return res 403 }