github.com/verrazzano/verrazzano@v1.7.1/pkg/k8s/ready/deployment_ready_test.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 package ready 4 5 import ( 6 "testing" 7 8 "sigs.k8s.io/controller-runtime/pkg/client" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 12 appsv1 "k8s.io/api/apps/v1" 13 corev1 "k8s.io/api/core/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/types" 16 k8scheme "k8s.io/client-go/kubernetes/scheme" 17 "sigs.k8s.io/controller-runtime/pkg/client/fake" 18 ) 19 20 var testReadyDeployment = &appsv1.Deployment{ 21 ObjectMeta: metav1.ObjectMeta{ 22 Namespace: "bar", 23 Name: "foo", 24 Labels: map[string]string{"app": "foo"}, 25 }, 26 Spec: appsv1.DeploymentSpec{ 27 Selector: &metav1.LabelSelector{ 28 MatchLabels: map[string]string{"app": "foo"}, 29 }, 30 }, 31 Status: appsv1.DeploymentStatus{ 32 AvailableReplicas: 1, 33 Replicas: 1, 34 UpdatedReplicas: 1, 35 }, 36 } 37 var testReadyPod = &corev1.Pod{ 38 ObjectMeta: metav1.ObjectMeta{ 39 Namespace: "bar", 40 Name: "foo-95d8c5d96-m6mbr", 41 Labels: map[string]string{ 42 podTemplateHashLabel: "95d8c5d96", 43 "app": "foo", 44 }, 45 }, 46 Status: corev1.PodStatus{ 47 InitContainerStatuses: []corev1.ContainerStatus{ 48 { 49 Ready: true, 50 }, 51 }, 52 ContainerStatuses: []corev1.ContainerStatus{ 53 { 54 Ready: true, 55 }, 56 }, 57 }, 58 } 59 var testReadyReplicaSet = &appsv1.ReplicaSet{ 60 ObjectMeta: metav1.ObjectMeta{ 61 Namespace: "bar", 62 Name: "foo-95d8c5d96", 63 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 64 }, 65 } 66 67 func TestDeploymentsReadyBySelectors(t *testing.T) { 68 opts := []client.ListOption{ 69 client.MatchingLabels{ 70 "app": "foo", 71 }, 72 } 73 log := vzlog.DefaultLogger() 74 var tests = []struct { 75 name string 76 c client.Client 77 opts []client.ListOption 78 ready bool 79 }{ 80 { 81 "not ready when no matching deployments", 82 fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build(), 83 []client.ListOption{}, 84 false, 85 }, 86 { 87 "not ready when matched deployment is not ready", 88 fake.NewClientBuilder().WithScheme(k8scheme.Scheme). 89 WithObjects(testReadyDeployment). 90 Build(), 91 opts, 92 false, 93 }, 94 { 95 "ready when matched deployment is ready", 96 fake.NewClientBuilder().WithScheme(k8scheme.Scheme). 97 WithObjects(testReadyDeployment, testReadyPod, testReadyReplicaSet). 98 Build(), 99 opts, 100 true, 101 }, 102 } 103 104 for _, tt := range tests { 105 t.Run(tt.name, func(t *testing.T) { 106 ready := DeploymentsReadyBySelectors(log, tt.c, 1, "foo", tt.opts...) 107 assert.Equal(t, tt.ready, ready) 108 }) 109 } 110 } 111 112 // TestDeploymentsReady tests a deployment ready status check 113 // GIVEN a call validate DeploymentsReady 114 // WHEN the target Deployment object has a minimum of desired available replicas 115 // THEN true is returned 116 func TestDeploymentsReady(t *testing.T) { 117 namespacedName := []types.NamespacedName{ 118 { 119 Name: "foo", 120 Namespace: "bar", 121 }, 122 } 123 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 124 testReadyDeployment, 125 testReadyPod, 126 testReadyReplicaSet, 127 ).Build() 128 assert.True(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 129 } 130 131 // TestDeploymentsContainerNotReady tests a deployment ready status check 132 // GIVEN a call validate DeploymentsReady 133 // WHEN the target Deployment object has a minimum of number of containers ready 134 // THEN false is returned 135 func TestDeploymentsContainerNotReady(t *testing.T) { 136 selector := &metav1.LabelSelector{ 137 MatchLabels: map[string]string{ 138 "app": "foo", 139 }, 140 } 141 namespacedName := []types.NamespacedName{ 142 { 143 Name: "foo", 144 Namespace: "bar", 145 }, 146 } 147 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 148 &appsv1.Deployment{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Namespace: "bar", 151 Name: "foo", 152 }, 153 Spec: appsv1.DeploymentSpec{ 154 Selector: selector, 155 }, 156 Status: appsv1.DeploymentStatus{ 157 AvailableReplicas: 1, 158 Replicas: 1, 159 UpdatedReplicas: 1, 160 }, 161 }, 162 &corev1.Pod{ 163 ObjectMeta: metav1.ObjectMeta{ 164 Namespace: "bar", 165 Name: "foo-95d8c5d96-m6mbr", 166 Labels: map[string]string{ 167 podTemplateHashLabel: "95d8c5d96", 168 "app": "foo", 169 }, 170 }, 171 Status: corev1.PodStatus{ 172 ContainerStatuses: []corev1.ContainerStatus{ 173 { 174 Ready: false, 175 }, 176 }, 177 }, 178 }, 179 &appsv1.ReplicaSet{ 180 ObjectMeta: metav1.ObjectMeta{ 181 Namespace: "bar", 182 Name: "foo-95d8c5d96", 183 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 184 }, 185 }, 186 ).Build() 187 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 188 } 189 190 // TestDeploymentsInitContainerNotReady tests a deployment ready status check 191 // GIVEN a call validate DeploymentsReady 192 // WHEN the target Deployment object has a minimum of number of init containers ready 193 // THEN false is returned 194 func TestDeploymentsInitContainerNotReady(t *testing.T) { 195 selector := &metav1.LabelSelector{ 196 MatchLabels: map[string]string{ 197 "app": "foo", 198 }, 199 } 200 namespacedName := []types.NamespacedName{ 201 { 202 Name: "foo", 203 Namespace: "bar", 204 }, 205 } 206 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 207 &appsv1.Deployment{ 208 ObjectMeta: metav1.ObjectMeta{ 209 Namespace: "bar", 210 Name: "foo", 211 }, 212 Spec: appsv1.DeploymentSpec{ 213 Selector: selector, 214 }, 215 Status: appsv1.DeploymentStatus{ 216 AvailableReplicas: 1, 217 Replicas: 1, 218 UpdatedReplicas: 1, 219 }, 220 }, 221 &corev1.Pod{ 222 ObjectMeta: metav1.ObjectMeta{ 223 Namespace: "bar", 224 Name: "foo-95d8c5d96-m6mbr", 225 Labels: map[string]string{ 226 podTemplateHashLabel: "95d8c5d96", 227 "app": "foo", 228 }, 229 }, 230 Status: corev1.PodStatus{ 231 InitContainerStatuses: []corev1.ContainerStatus{ 232 { 233 Ready: false, 234 }, 235 }, 236 }, 237 }, 238 &appsv1.ReplicaSet{ 239 ObjectMeta: metav1.ObjectMeta{ 240 Namespace: "bar", 241 Name: "foo-95d8c5d96", 242 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 243 }, 244 }, 245 ).Build() 246 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 247 } 248 249 // TestMultipleReplicasReady tests a deployment ready status check 250 // GIVEN a call validate DeploymentsReady for more than one replica 251 // WHEN the target Deployment object has met the minimum of desired available replicas > 1 252 // THEN true is returned 253 func TestMultipleReplicasReady(t *testing.T) { 254 namespacedName := []types.NamespacedName{ 255 { 256 Name: "foo", 257 Namespace: "bar", 258 }, 259 } 260 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 261 &appsv1.Deployment{ 262 ObjectMeta: metav1.ObjectMeta{ 263 Namespace: "bar", 264 Name: "foo", 265 Labels: map[string]string{"app": "foo"}, 266 }, 267 Spec: appsv1.DeploymentSpec{ 268 Selector: &metav1.LabelSelector{ 269 MatchLabels: map[string]string{"app": "foo"}, 270 }, 271 }, 272 Status: appsv1.DeploymentStatus{ 273 AvailableReplicas: 2, 274 Replicas: 2, 275 UpdatedReplicas: 2, 276 }, 277 }, 278 &corev1.Pod{ 279 ObjectMeta: metav1.ObjectMeta{ 280 Namespace: "bar", 281 Name: "foo-95d8c5d96-m6mbr", 282 Labels: map[string]string{ 283 podTemplateHashLabel: "95d8c5d96", 284 "app": "foo", 285 }, 286 }, 287 Status: corev1.PodStatus{ 288 ContainerStatuses: []corev1.ContainerStatus{ 289 { 290 Ready: true, 291 }, 292 }, 293 }, 294 }, 295 &corev1.Pod{ 296 ObjectMeta: metav1.ObjectMeta{ 297 Namespace: "bar", 298 Name: "foo-95d8c5d96-l6r96", 299 Labels: map[string]string{ 300 podTemplateHashLabel: "95d8c5d96", 301 "app": "foo", 302 }, 303 }, 304 Status: corev1.PodStatus{ 305 ContainerStatuses: []corev1.ContainerStatus{ 306 { 307 Ready: true, 308 }, 309 }, 310 }, 311 }, 312 &appsv1.ReplicaSet{ 313 ObjectMeta: metav1.ObjectMeta{ 314 Namespace: "bar", 315 Name: "foo-95d8c5d96", 316 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 317 }, 318 }, 319 ).Build() 320 assert.True(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 2, "")) 321 } 322 323 // TestMultipleReplicasReadyAboveThreshold tests a deployment ready status check 324 // GIVEN a call validate DeploymentsReady for more than one replica 325 // WHEN the target Deployment object has more than the minimum desired replicas available 326 // THEN true is returned 327 func TestMultipleReplicasReadyAboveThreshold(t *testing.T) { 328 namespacedName := []types.NamespacedName{ 329 { 330 Name: "foo", 331 Namespace: "bar", 332 }, 333 } 334 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 335 &appsv1.Deployment{ 336 ObjectMeta: metav1.ObjectMeta{ 337 Namespace: "bar", 338 Name: "foo", 339 Labels: map[string]string{"app": "foo"}, 340 }, 341 Spec: appsv1.DeploymentSpec{ 342 Selector: &metav1.LabelSelector{ 343 MatchLabels: map[string]string{"app": "foo"}, 344 }, 345 }, 346 Status: appsv1.DeploymentStatus{ 347 AvailableReplicas: 2, 348 Replicas: 2, 349 UpdatedReplicas: 2, 350 }, 351 }, 352 &corev1.Pod{ 353 ObjectMeta: metav1.ObjectMeta{ 354 Namespace: "bar", 355 Name: "foo-95d8c5d96-m6mbr", 356 Labels: map[string]string{ 357 podTemplateHashLabel: "95d8c5d96", 358 "app": "foo", 359 }, 360 }, 361 Status: corev1.PodStatus{ 362 ContainerStatuses: []corev1.ContainerStatus{ 363 { 364 Ready: true, 365 }, 366 }, 367 }, 368 }, 369 &corev1.Pod{ 370 ObjectMeta: metav1.ObjectMeta{ 371 Namespace: "bar", 372 Name: "foo-95d8c5d96-l6r96", 373 Labels: map[string]string{ 374 podTemplateHashLabel: "95d8c5d96", 375 "app": "foo", 376 }, 377 }, 378 Status: corev1.PodStatus{ 379 ContainerStatuses: []corev1.ContainerStatus{ 380 { 381 Ready: true, 382 }, 383 }, 384 }, 385 }, 386 &appsv1.ReplicaSet{ 387 ObjectMeta: metav1.ObjectMeta{ 388 Namespace: "bar", 389 Name: "foo-95d8c5d96", 390 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 391 }, 392 }, 393 ).Build() 394 assert.True(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 395 } 396 397 // TestDeploymentsNoneAvailable tests a deployment ready status check 398 // GIVEN a call validate DeploymentsReady 399 // WHEN the target Deployment object does not have a minimum number of desired available replicas 400 // THEN false is returned 401 func TestDeploymentsNoneAvailable(t *testing.T) { 402 namespacedName := []types.NamespacedName{ 403 { 404 Name: "foo", 405 Namespace: "bar", 406 }, 407 } 408 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(&appsv1.Deployment{ 409 ObjectMeta: metav1.ObjectMeta{ 410 Namespace: "bar", 411 Name: "foo", 412 }, 413 Status: appsv1.DeploymentStatus{ 414 AvailableReplicas: 0, 415 Replicas: 1, 416 UpdatedReplicas: 1, 417 }, 418 }).Build() 419 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 420 } 421 422 // TestDeploymentsNoneUpdated tests a deployment ready status check 423 // GIVEN a call validate DeploymentsReady 424 // WHEN the target Deployment object does not have a minimum number of desired updated replicas 425 // THEN false is returned 426 func TestDeploymentsNoneUpdated(t *testing.T) { 427 namespacedName := []types.NamespacedName{ 428 { 429 Name: "foo", 430 Namespace: "bar", 431 }, 432 } 433 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(&appsv1.Deployment{ 434 ObjectMeta: metav1.ObjectMeta{ 435 Namespace: "bar", 436 Name: "foo", 437 }, 438 Status: appsv1.DeploymentStatus{ 439 AvailableReplicas: 0, 440 Replicas: 1, 441 UpdatedReplicas: 0, 442 }, 443 }).Build() 444 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 445 } 446 447 // TestMultipleReplicasReadyBelowThreshold tests a deployment ready status check 448 // GIVEN a call validate DeploymentsReady for more than one replica 449 // WHEN the target Deployment object has less than the minimum desired replicas available 450 // THEN false is returned 451 func TestMultipleReplicasReadyBelowThreshold(t *testing.T) { 452 selector := &metav1.LabelSelector{ 453 MatchLabels: map[string]string{ 454 "app": "foo", 455 }, 456 } 457 namespacedName := []types.NamespacedName{ 458 { 459 Name: "foo", 460 Namespace: "bar", 461 }, 462 } 463 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 464 &appsv1.Deployment{ 465 ObjectMeta: metav1.ObjectMeta{ 466 Namespace: "bar", 467 Name: "foo", 468 }, 469 Spec: appsv1.DeploymentSpec{ 470 Selector: selector, 471 }, 472 Status: appsv1.DeploymentStatus{ 473 AvailableReplicas: 3, 474 Replicas: 3, 475 UpdatedReplicas: 3, 476 }, 477 }, 478 &corev1.Pod{ 479 ObjectMeta: metav1.ObjectMeta{ 480 Namespace: "bar", 481 Name: "foo-95d8c5d96-m6mbr", 482 Labels: map[string]string{ 483 podTemplateHashLabel: "95d8c5d96", 484 "app": "foo", 485 }, 486 }, 487 Status: corev1.PodStatus{ 488 ContainerStatuses: []corev1.ContainerStatus{ 489 { 490 Ready: true, 491 }, 492 }, 493 }, 494 }, 495 &appsv1.ReplicaSet{ 496 ObjectMeta: metav1.ObjectMeta{ 497 Namespace: "bar", 498 Name: "foo-95d8c5d96", 499 Annotations: map[string]string{deploymentRevisionAnnotation: "1"}, 500 }, 501 }, 502 ).Build() 503 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 3, "")) 504 } 505 506 // TestDeploymentsReadyDeploymentNotFound tests a deployment ready status check 507 // GIVEN a call validate DeploymentsReady 508 // WHEN the target Deployment object is not found 509 // THEN false is returned 510 func TestDeploymentsReadyDeploymentNotFound(t *testing.T) { 511 namespacedName := []types.NamespacedName{ 512 { 513 Name: "foo", 514 Namespace: "bar", 515 }, 516 } 517 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 518 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 519 } 520 521 // TestDeploymentsReadyReplicaSetNotFound tests a deployment ready status check 522 // GIVEN a call validate DeploymentsReady 523 // WHEN the target ReplicaSet object is not found 524 // THEN false is returned 525 func TestDeploymentsReadyReplicaSetNotFound(t *testing.T) { 526 selector := &metav1.LabelSelector{ 527 MatchLabels: map[string]string{ 528 "app": "foo", 529 }, 530 } 531 namespacedName := []types.NamespacedName{ 532 { 533 Name: "foo", 534 Namespace: "bar", 535 }, 536 } 537 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 538 &appsv1.Deployment{ 539 ObjectMeta: metav1.ObjectMeta{ 540 Namespace: "bar", 541 Name: "foo", 542 }, 543 Spec: appsv1.DeploymentSpec{ 544 Selector: selector, 545 }, 546 Status: appsv1.DeploymentStatus{ 547 AvailableReplicas: 1, 548 Replicas: 1, 549 UpdatedReplicas: 1, 550 }, 551 }, 552 &corev1.Pod{ 553 ObjectMeta: metav1.ObjectMeta{ 554 Namespace: "bar", 555 Name: "foo-95d8c5d96-m6mbr", 556 Labels: map[string]string{ 557 podTemplateHashLabel: "95d8c5d96", 558 "app": "foo", 559 }, 560 }, 561 Status: corev1.PodStatus{ 562 ContainerStatuses: []corev1.ContainerStatus{ 563 { 564 Ready: true, 565 }, 566 }, 567 }, 568 }, 569 ).Build() 570 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 571 } 572 573 // TestDeploymentsReadyPodNotFound tests a deployment ready status check 574 // GIVEN a call validate DeploymentsReady 575 // WHEN the target Pod object is not found 576 // THEN false is returned 577 func TestDeploymentsReadyPodNotFound(t *testing.T) { 578 selector := &metav1.LabelSelector{ 579 MatchLabels: map[string]string{ 580 "app": "foo", 581 }, 582 } 583 namespacedName := []types.NamespacedName{ 584 { 585 Name: "foo", 586 Namespace: "bar", 587 }, 588 } 589 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 590 &appsv1.Deployment{ 591 ObjectMeta: metav1.ObjectMeta{ 592 Namespace: "bar", 593 Name: "foo", 594 }, 595 Spec: appsv1.DeploymentSpec{ 596 Selector: selector, 597 }, 598 Status: appsv1.DeploymentStatus{ 599 AvailableReplicas: 1, 600 Replicas: 1, 601 UpdatedReplicas: 1, 602 }, 603 }).Build() 604 605 assert.False(t, DeploymentsAreReady(vzlog.DefaultLogger(), fakeClient, namespacedName, 1, "")) 606 } 607 608 func TestDoDeploymentsExist(t *testing.T) { 609 tests := []struct { 610 name string 611 depNames []types.NamespacedName 612 want bool 613 }{ 614 {"no deployment names provided", []types.NamespacedName{}, true}, 615 {"deployment exists", []types.NamespacedName{{Name: "foo", Namespace: "bar"}}, true}, 616 {"all deployments exist", []types.NamespacedName{{Name: "foo", Namespace: "bar"}, {Name: "foo2", Namespace: "bar2"}}, true}, 617 {"only some deployments exist", []types.NamespacedName{{Name: "foo", Namespace: "bar"}, {Name: "nonexist", Namespace: "bar"}}, false}, 618 {"none of the deployments exist", []types.NamespacedName{{Name: "nofoo", Namespace: "nobar"}, {Name: "nofoo2", Namespace: "bar2"}}, false}, 619 {"deployment in different namespace", []types.NamespacedName{{Name: "foo", Namespace: "bar2"}}, false}, 620 } 621 for _, tt := range tests { 622 t.Run(tt.name, func(t *testing.T) { 623 log := vzlog.DefaultLogger() 624 fakeClient := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects( 625 &appsv1.Deployment{ 626 ObjectMeta: metav1.ObjectMeta{ 627 Namespace: "bar", 628 Name: "foo", 629 }, 630 Spec: appsv1.DeploymentSpec{ 631 Selector: &metav1.LabelSelector{ 632 MatchLabels: map[string]string{ 633 "app": "foo", 634 }, 635 }, 636 }, 637 }, 638 &appsv1.Deployment{ 639 ObjectMeta: metav1.ObjectMeta{ 640 Namespace: "bar2", 641 Name: "foo2", 642 }, 643 Spec: appsv1.DeploymentSpec{ 644 Selector: &metav1.LabelSelector{ 645 MatchLabels: map[string]string{ 646 "app": "foo", 647 }, 648 }, 649 }, 650 }).Build() 651 assert.Equalf(t, tt.want, DoDeploymentsExist(log, fakeClient, tt.depNames, 1, tt.name), "Expected %v for %s", tt.want, tt.name) 652 }) 653 } 654 }