k8s.io/kubernetes@v1.29.3/pkg/kubelet/status/generate_test.go (about) 1 /* 2 Copyright 2014 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 status 18 19 import ( 20 "reflect" 21 "testing" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 v1 "k8s.io/api/core/v1" 27 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 28 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 29 "k8s.io/utils/pointer" 30 ) 31 32 var ( 33 containerRestartPolicyAlways = v1.ContainerRestartPolicyAlways 34 ) 35 36 func TestGenerateContainersReadyCondition(t *testing.T) { 37 tests := []struct { 38 spec *v1.PodSpec 39 containerStatuses []v1.ContainerStatus 40 podPhase v1.PodPhase 41 expectReady v1.PodCondition 42 }{ 43 { 44 spec: nil, 45 containerStatuses: nil, 46 podPhase: v1.PodRunning, 47 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, UnknownContainerStatuses, ""), 48 }, 49 { 50 spec: &v1.PodSpec{}, 51 containerStatuses: []v1.ContainerStatus{}, 52 podPhase: v1.PodRunning, 53 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionTrue, "", ""), 54 }, 55 { 56 spec: &v1.PodSpec{ 57 Containers: []v1.Container{ 58 {Name: "1234"}, 59 }, 60 }, 61 containerStatuses: []v1.ContainerStatus{}, 62 podPhase: v1.PodRunning, 63 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [1234]"), 64 }, 65 { 66 spec: &v1.PodSpec{ 67 Containers: []v1.Container{ 68 {Name: "1234"}, 69 {Name: "5678"}, 70 }, 71 }, 72 containerStatuses: []v1.ContainerStatus{ 73 getReadyStatus("1234"), 74 getReadyStatus("5678"), 75 }, 76 podPhase: v1.PodRunning, 77 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionTrue, "", ""), 78 }, 79 { 80 spec: &v1.PodSpec{ 81 Containers: []v1.Container{ 82 {Name: "1234"}, 83 {Name: "5678"}, 84 }, 85 }, 86 containerStatuses: []v1.ContainerStatus{ 87 getReadyStatus("1234"), 88 }, 89 podPhase: v1.PodRunning, 90 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [5678]"), 91 }, 92 { 93 spec: &v1.PodSpec{ 94 Containers: []v1.Container{ 95 {Name: "1234"}, 96 {Name: "5678"}, 97 }, 98 }, 99 containerStatuses: []v1.ContainerStatus{ 100 getReadyStatus("1234"), 101 getNotReadyStatus("5678"), 102 }, 103 podPhase: v1.PodRunning, 104 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unready status: [5678]"), 105 }, 106 { 107 spec: &v1.PodSpec{ 108 Containers: []v1.Container{ 109 {Name: "1234"}, 110 }, 111 }, 112 containerStatuses: []v1.ContainerStatus{ 113 getNotReadyStatus("1234"), 114 }, 115 podPhase: v1.PodSucceeded, 116 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, PodCompleted, ""), 117 }, 118 { 119 spec: &v1.PodSpec{ 120 InitContainers: []v1.Container{ 121 {Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways}, 122 }, 123 Containers: []v1.Container{ 124 {Name: "regular-1"}, 125 }, 126 }, 127 containerStatuses: []v1.ContainerStatus{ 128 getReadyStatus("regular-1"), 129 }, 130 podPhase: v1.PodRunning, 131 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [restartable-init-1]"), 132 }, 133 { 134 spec: &v1.PodSpec{ 135 InitContainers: []v1.Container{ 136 {Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways}, 137 {Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways}, 138 }, 139 Containers: []v1.Container{ 140 {Name: "regular-1"}, 141 }, 142 }, 143 containerStatuses: []v1.ContainerStatus{ 144 getReadyStatus("restartable-init-1"), 145 getReadyStatus("restartable-init-2"), 146 getReadyStatus("regular-1"), 147 }, 148 podPhase: v1.PodRunning, 149 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionTrue, "", ""), 150 }, 151 { 152 spec: &v1.PodSpec{ 153 InitContainers: []v1.Container{ 154 {Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways}, 155 {Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways}, 156 }, 157 Containers: []v1.Container{ 158 {Name: "regular-1"}, 159 }, 160 }, 161 containerStatuses: []v1.ContainerStatus{ 162 getReadyStatus("restartable-init-1"), 163 getReadyStatus("regular-1"), 164 }, 165 podPhase: v1.PodRunning, 166 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [restartable-init-2]"), 167 }, 168 { 169 spec: &v1.PodSpec{ 170 InitContainers: []v1.Container{ 171 {Name: "restartable-init-1", RestartPolicy: &containerRestartPolicyAlways}, 172 {Name: "restartable-init-2", RestartPolicy: &containerRestartPolicyAlways}, 173 }, 174 Containers: []v1.Container{ 175 {Name: "regular-1"}, 176 }, 177 }, 178 containerStatuses: []v1.ContainerStatus{ 179 getReadyStatus("restartable-init-1"), 180 getNotReadyStatus("restartable-init-2"), 181 getReadyStatus("regular-1"), 182 }, 183 podPhase: v1.PodRunning, 184 expectReady: getPodCondition(v1.ContainersReady, v1.ConditionFalse, ContainersNotReady, "containers with unready status: [restartable-init-2]"), 185 }, 186 } 187 188 for i, test := range tests { 189 ready := GenerateContainersReadyCondition(test.spec, test.containerStatuses, test.podPhase) 190 if !reflect.DeepEqual(ready, test.expectReady) { 191 t.Errorf("On test case %v, expectReady:\n%+v\ngot\n%+v\n", i, test.expectReady, ready) 192 } 193 } 194 } 195 196 func TestGeneratePodReadyCondition(t *testing.T) { 197 tests := []struct { 198 spec *v1.PodSpec 199 conditions []v1.PodCondition 200 containerStatuses []v1.ContainerStatus 201 podPhase v1.PodPhase 202 expectReady v1.PodCondition 203 }{ 204 { 205 spec: nil, 206 conditions: nil, 207 containerStatuses: nil, 208 podPhase: v1.PodRunning, 209 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, UnknownContainerStatuses, ""), 210 }, 211 { 212 spec: &v1.PodSpec{}, 213 conditions: nil, 214 containerStatuses: []v1.ContainerStatus{}, 215 podPhase: v1.PodRunning, 216 expectReady: getPodCondition(v1.PodReady, v1.ConditionTrue, "", ""), 217 }, 218 { 219 spec: &v1.PodSpec{ 220 Containers: []v1.Container{ 221 {Name: "1234"}, 222 }, 223 }, 224 conditions: nil, 225 containerStatuses: []v1.ContainerStatus{}, 226 podPhase: v1.PodRunning, 227 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [1234]"), 228 }, 229 { 230 spec: &v1.PodSpec{ 231 Containers: []v1.Container{ 232 {Name: "1234"}, 233 {Name: "5678"}, 234 }, 235 }, 236 conditions: nil, 237 containerStatuses: []v1.ContainerStatus{ 238 getReadyStatus("1234"), 239 getReadyStatus("5678"), 240 }, 241 podPhase: v1.PodRunning, 242 expectReady: getPodCondition(v1.PodReady, v1.ConditionTrue, "", ""), 243 }, 244 { 245 spec: &v1.PodSpec{ 246 Containers: []v1.Container{ 247 {Name: "1234"}, 248 {Name: "5678"}, 249 }, 250 }, 251 conditions: nil, 252 containerStatuses: []v1.ContainerStatus{ 253 getReadyStatus("1234"), 254 }, 255 podPhase: v1.PodRunning, 256 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ContainersNotReady, "containers with unknown status: [5678]"), 257 }, 258 { 259 spec: &v1.PodSpec{ 260 Containers: []v1.Container{ 261 {Name: "1234"}, 262 {Name: "5678"}, 263 }, 264 }, 265 conditions: nil, 266 containerStatuses: []v1.ContainerStatus{ 267 getReadyStatus("1234"), 268 getNotReadyStatus("5678"), 269 }, 270 podPhase: v1.PodRunning, 271 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ContainersNotReady, "containers with unready status: [5678]"), 272 }, 273 { 274 spec: &v1.PodSpec{ 275 Containers: []v1.Container{ 276 {Name: "1234"}, 277 }, 278 }, 279 conditions: nil, 280 containerStatuses: []v1.ContainerStatus{ 281 getNotReadyStatus("1234"), 282 }, 283 podPhase: v1.PodSucceeded, 284 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, PodCompleted, ""), 285 }, 286 { 287 spec: &v1.PodSpec{ 288 ReadinessGates: []v1.PodReadinessGate{ 289 {ConditionType: v1.PodConditionType("gate1")}, 290 }, 291 }, 292 conditions: nil, 293 containerStatuses: []v1.ContainerStatus{}, 294 podPhase: v1.PodRunning, 295 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ReadinessGatesNotReady, `corresponding condition of pod readiness gate "gate1" does not exist.`), 296 }, 297 { 298 spec: &v1.PodSpec{ 299 ReadinessGates: []v1.PodReadinessGate{ 300 {ConditionType: v1.PodConditionType("gate1")}, 301 }, 302 }, 303 conditions: []v1.PodCondition{ 304 getPodCondition("gate1", v1.ConditionFalse, "", ""), 305 }, 306 containerStatuses: []v1.ContainerStatus{}, 307 podPhase: v1.PodRunning, 308 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ReadinessGatesNotReady, `the status of pod readiness gate "gate1" is not "True", but False`), 309 }, 310 { 311 spec: &v1.PodSpec{ 312 ReadinessGates: []v1.PodReadinessGate{ 313 {ConditionType: v1.PodConditionType("gate1")}, 314 }, 315 }, 316 conditions: []v1.PodCondition{ 317 getPodCondition("gate1", v1.ConditionTrue, "", ""), 318 }, 319 containerStatuses: []v1.ContainerStatus{}, 320 podPhase: v1.PodRunning, 321 expectReady: getPodCondition(v1.PodReady, v1.ConditionTrue, "", ""), 322 }, 323 { 324 spec: &v1.PodSpec{ 325 ReadinessGates: []v1.PodReadinessGate{ 326 {ConditionType: v1.PodConditionType("gate1")}, 327 {ConditionType: v1.PodConditionType("gate2")}, 328 }, 329 }, 330 conditions: []v1.PodCondition{ 331 getPodCondition("gate1", v1.ConditionTrue, "", ""), 332 }, 333 containerStatuses: []v1.ContainerStatus{}, 334 podPhase: v1.PodRunning, 335 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ReadinessGatesNotReady, `corresponding condition of pod readiness gate "gate2" does not exist.`), 336 }, 337 { 338 spec: &v1.PodSpec{ 339 ReadinessGates: []v1.PodReadinessGate{ 340 {ConditionType: v1.PodConditionType("gate1")}, 341 {ConditionType: v1.PodConditionType("gate2")}, 342 }, 343 }, 344 conditions: []v1.PodCondition{ 345 getPodCondition("gate1", v1.ConditionTrue, "", ""), 346 getPodCondition("gate2", v1.ConditionFalse, "", ""), 347 }, 348 containerStatuses: []v1.ContainerStatus{}, 349 podPhase: v1.PodRunning, 350 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ReadinessGatesNotReady, `the status of pod readiness gate "gate2" is not "True", but False`), 351 }, 352 { 353 spec: &v1.PodSpec{ 354 ReadinessGates: []v1.PodReadinessGate{ 355 {ConditionType: v1.PodConditionType("gate1")}, 356 {ConditionType: v1.PodConditionType("gate2")}, 357 }, 358 }, 359 conditions: []v1.PodCondition{ 360 getPodCondition("gate1", v1.ConditionTrue, "", ""), 361 getPodCondition("gate2", v1.ConditionTrue, "", ""), 362 }, 363 containerStatuses: []v1.ContainerStatus{}, 364 podPhase: v1.PodRunning, 365 expectReady: getPodCondition(v1.PodReady, v1.ConditionTrue, "", ""), 366 }, 367 { 368 spec: &v1.PodSpec{ 369 Containers: []v1.Container{ 370 {Name: "1234"}, 371 }, 372 ReadinessGates: []v1.PodReadinessGate{ 373 {ConditionType: v1.PodConditionType("gate1")}, 374 }, 375 }, 376 conditions: []v1.PodCondition{ 377 getPodCondition("gate1", v1.ConditionTrue, "", ""), 378 }, 379 containerStatuses: []v1.ContainerStatus{getNotReadyStatus("1234")}, 380 podPhase: v1.PodRunning, 381 expectReady: getPodCondition(v1.PodReady, v1.ConditionFalse, ContainersNotReady, "containers with unready status: [1234]"), 382 }, 383 } 384 385 for i, test := range tests { 386 ready := GeneratePodReadyCondition(test.spec, test.conditions, test.containerStatuses, test.podPhase) 387 if !reflect.DeepEqual(ready, test.expectReady) { 388 t.Errorf("On test case %v, expectReady:\n%+v\ngot\n%+v\n", i, test.expectReady, ready) 389 } 390 } 391 } 392 393 func TestGeneratePodInitializedCondition(t *testing.T) { 394 noInitContainer := &v1.PodSpec{} 395 oneInitContainer := &v1.PodSpec{ 396 InitContainers: []v1.Container{ 397 {Name: "1234"}, 398 }, 399 Containers: []v1.Container{ 400 {Name: "regular"}, 401 }, 402 } 403 twoInitContainer := &v1.PodSpec{ 404 InitContainers: []v1.Container{ 405 {Name: "1234"}, 406 {Name: "5678"}, 407 }, 408 Containers: []v1.Container{ 409 {Name: "regular"}, 410 }, 411 } 412 oneRestartableInitContainer := &v1.PodSpec{ 413 InitContainers: []v1.Container{ 414 { 415 Name: "1234", 416 RestartPolicy: func() *v1.ContainerRestartPolicy { 417 p := v1.ContainerRestartPolicyAlways 418 return &p 419 }(), 420 }, 421 }, 422 Containers: []v1.Container{ 423 {Name: "regular"}, 424 }, 425 } 426 tests := []struct { 427 spec *v1.PodSpec 428 containerStatuses []v1.ContainerStatus 429 podPhase v1.PodPhase 430 expected v1.PodCondition 431 }{ 432 { 433 spec: twoInitContainer, 434 containerStatuses: nil, 435 podPhase: v1.PodRunning, 436 expected: v1.PodCondition{ 437 Status: v1.ConditionFalse, 438 Reason: UnknownContainerStatuses, 439 }, 440 }, 441 { 442 spec: noInitContainer, 443 containerStatuses: []v1.ContainerStatus{}, 444 podPhase: v1.PodRunning, 445 expected: v1.PodCondition{ 446 Status: v1.ConditionTrue, 447 Reason: "", 448 }, 449 }, 450 { 451 spec: oneInitContainer, 452 containerStatuses: []v1.ContainerStatus{}, 453 podPhase: v1.PodRunning, 454 expected: v1.PodCondition{ 455 Status: v1.ConditionFalse, 456 Reason: ContainersNotInitialized, 457 }, 458 }, 459 { 460 spec: twoInitContainer, 461 containerStatuses: []v1.ContainerStatus{ 462 getReadyStatus("1234"), 463 getReadyStatus("5678"), 464 }, 465 podPhase: v1.PodRunning, 466 expected: v1.PodCondition{ 467 Status: v1.ConditionTrue, 468 Reason: "", 469 }, 470 }, 471 { 472 spec: twoInitContainer, 473 containerStatuses: []v1.ContainerStatus{ 474 getReadyStatus("1234"), 475 }, 476 podPhase: v1.PodRunning, 477 expected: v1.PodCondition{ 478 Status: v1.ConditionFalse, 479 Reason: ContainersNotInitialized, 480 }, 481 }, 482 { 483 spec: twoInitContainer, 484 containerStatuses: []v1.ContainerStatus{ 485 getReadyStatus("1234"), 486 getNotReadyStatus("5678"), 487 }, 488 podPhase: v1.PodRunning, 489 expected: v1.PodCondition{ 490 Status: v1.ConditionFalse, 491 Reason: ContainersNotInitialized, 492 }, 493 }, 494 { 495 spec: oneInitContainer, 496 containerStatuses: []v1.ContainerStatus{ 497 getReadyStatus("1234"), 498 }, 499 podPhase: v1.PodSucceeded, 500 expected: v1.PodCondition{ 501 Status: v1.ConditionTrue, 502 Reason: PodCompleted, 503 }, 504 }, 505 { 506 spec: oneRestartableInitContainer, 507 containerStatuses: []v1.ContainerStatus{ 508 getNotStartedStatus("1234"), 509 }, 510 podPhase: v1.PodPending, 511 expected: v1.PodCondition{ 512 Status: v1.ConditionFalse, 513 Reason: ContainersNotInitialized, 514 }, 515 }, 516 { 517 spec: oneRestartableInitContainer, 518 containerStatuses: []v1.ContainerStatus{ 519 getStartedStatus("1234"), 520 }, 521 podPhase: v1.PodRunning, 522 expected: v1.PodCondition{ 523 Status: v1.ConditionTrue, 524 }, 525 }, 526 { 527 spec: oneRestartableInitContainer, 528 containerStatuses: []v1.ContainerStatus{ 529 getNotStartedStatus("1234"), 530 { 531 Name: "regular", 532 State: v1.ContainerState{ 533 Running: &v1.ContainerStateRunning{}, 534 }, 535 }, 536 }, 537 podPhase: v1.PodRunning, 538 expected: v1.PodCondition{ 539 Status: v1.ConditionTrue, 540 }, 541 }, 542 } 543 544 for _, test := range tests { 545 test.expected.Type = v1.PodInitialized 546 condition := GeneratePodInitializedCondition(test.spec, test.containerStatuses, test.podPhase) 547 assert.Equal(t, test.expected.Type, condition.Type) 548 assert.Equal(t, test.expected.Status, condition.Status) 549 assert.Equal(t, test.expected.Reason, condition.Reason) 550 551 } 552 } 553 554 func TestGeneratePodReadyToStartContainersCondition(t *testing.T) { 555 for desc, test := range map[string]struct { 556 pod *v1.Pod 557 status *kubecontainer.PodStatus 558 expected v1.PodCondition 559 }{ 560 "Empty pod status": { 561 pod: &v1.Pod{}, 562 status: &kubecontainer.PodStatus{}, 563 expected: v1.PodCondition{ 564 Status: v1.ConditionFalse, 565 }, 566 }, 567 "Pod sandbox status not ready": { 568 pod: &v1.Pod{}, 569 status: &kubecontainer.PodStatus{ 570 SandboxStatuses: []*runtimeapi.PodSandboxStatus{ 571 { 572 Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)}, 573 State: runtimeapi.PodSandboxState_SANDBOX_NOTREADY, 574 }, 575 }, 576 }, 577 expected: v1.PodCondition{ 578 Status: v1.ConditionFalse, 579 }, 580 }, 581 "Pod sandbox status ready but no IP configured": { 582 pod: &v1.Pod{}, 583 status: &kubecontainer.PodStatus{ 584 SandboxStatuses: []*runtimeapi.PodSandboxStatus{ 585 { 586 Network: &runtimeapi.PodSandboxNetworkStatus{ 587 Ip: "", 588 }, 589 Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)}, 590 State: runtimeapi.PodSandboxState_SANDBOX_READY, 591 }, 592 }, 593 }, 594 expected: v1.PodCondition{ 595 Status: v1.ConditionFalse, 596 }, 597 }, 598 "Pod sandbox status ready and IP configured": { 599 pod: &v1.Pod{}, 600 status: &kubecontainer.PodStatus{ 601 SandboxStatuses: []*runtimeapi.PodSandboxStatus{ 602 { 603 Network: &runtimeapi.PodSandboxNetworkStatus{ 604 Ip: "10.0.0.10", 605 }, 606 Metadata: &runtimeapi.PodSandboxMetadata{Attempt: uint32(0)}, 607 State: runtimeapi.PodSandboxState_SANDBOX_READY, 608 }, 609 }, 610 }, 611 expected: v1.PodCondition{ 612 Status: v1.ConditionTrue, 613 }, 614 }, 615 } { 616 t.Run(desc, func(t *testing.T) { 617 test.expected.Type = v1.PodReadyToStartContainers 618 condition := GeneratePodReadyToStartContainersCondition(test.pod, test.status) 619 require.Equal(t, test.expected.Type, condition.Type) 620 require.Equal(t, test.expected.Status, condition.Status) 621 }) 622 } 623 } 624 625 func getPodCondition(conditionType v1.PodConditionType, status v1.ConditionStatus, reason, message string) v1.PodCondition { 626 return v1.PodCondition{ 627 Type: conditionType, 628 Status: status, 629 Reason: reason, 630 Message: message, 631 } 632 } 633 634 func getReadyStatus(cName string) v1.ContainerStatus { 635 return v1.ContainerStatus{ 636 Name: cName, 637 Ready: true, 638 } 639 } 640 641 func getNotReadyStatus(cName string) v1.ContainerStatus { 642 return v1.ContainerStatus{ 643 Name: cName, 644 Ready: false, 645 } 646 } 647 648 func getStartedStatus(cName string) v1.ContainerStatus { 649 return v1.ContainerStatus{ 650 Name: cName, 651 Started: pointer.Bool(true), 652 } 653 } 654 655 func getNotStartedStatus(cName string) v1.ContainerStatus { 656 return v1.ContainerStatus{ 657 Name: cName, 658 Started: pointer.Bool(false), 659 } 660 }