github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/status_test.go (about) 1 package k8s 2 3 import ( 4 "context" 5 "reflect" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 10 conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1" 11 fake_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/client/clientset/versioned/fake" 12 v1 "k8s.io/api/core/v1" 13 networking "k8s.io/api/networking/v1beta1" 14 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/fields" 16 "k8s.io/apimachinery/pkg/util/intstr" 17 "k8s.io/client-go/kubernetes/fake" 18 "k8s.io/client-go/tools/cache" 19 ) 20 21 func TestUpdateTransportServerStatus(t *testing.T) { 22 ts := &conf_v1alpha1.TransportServer{ 23 ObjectMeta: meta_v1.ObjectMeta{ 24 Name: "ts-1", 25 Namespace: "default", 26 }, 27 Status: conf_v1alpha1.TransportServerStatus{ 28 State: "before status", 29 Reason: "before reason", 30 Message: "before message", 31 }, 32 } 33 34 fakeClient := fake_v1alpha1.NewSimpleClientset( 35 &conf_v1alpha1.TransportServerList{ 36 Items: []conf_v1alpha1.TransportServer{ 37 *ts, 38 }}) 39 40 tsLister := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc) 41 42 err := tsLister.Add(ts) 43 if err != nil { 44 t.Errorf("Error adding TransportServer to the transportserver lister: %v", err) 45 } 46 su := statusUpdater{ 47 transportServerLister: tsLister, 48 confClient: fakeClient, 49 keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, 50 } 51 52 err = su.UpdateTransportServerStatus(ts, "after status", "after reason", "after message") 53 if err != nil { 54 t.Errorf("error updating transportserver status: %v", err) 55 } 56 updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) 57 58 expectedStatus := conf_v1alpha1.TransportServerStatus{ 59 State: "after status", 60 Reason: "after reason", 61 Message: "after message", 62 } 63 64 if diff := cmp.Diff(expectedStatus, updatedTs.Status); diff != "" { 65 t.Errorf("Unexpected status (-want +got):\n%s", diff) 66 } 67 } 68 69 func TestUpdateTransportServerStatusIgnoreNoChange(t *testing.T) { 70 ts := &conf_v1alpha1.TransportServer{ 71 ObjectMeta: meta_v1.ObjectMeta{ 72 Name: "ts-1", 73 Namespace: "default", 74 }, 75 Status: conf_v1alpha1.TransportServerStatus{ 76 State: "same status", 77 Reason: "same reason", 78 Message: "same message", 79 }, 80 } 81 82 fakeClient := fake_v1alpha1.NewSimpleClientset( 83 &conf_v1alpha1.TransportServerList{ 84 Items: []conf_v1alpha1.TransportServer{ 85 *ts, 86 }}) 87 88 tsLister, _ := cache.NewInformer( 89 cache.NewListWatchFromClient( 90 fakeClient.K8sV1alpha1().RESTClient(), 91 "transportservers", 92 "nginx-ingress", 93 fields.Everything(), 94 ), 95 &conf_v1alpha1.TransportServer{}, 96 2, 97 nil, 98 ) 99 100 err := tsLister.Add(ts) 101 if err != nil { 102 t.Errorf("Error adding TransportServer to the transportserver lister: %v", err) 103 } 104 su := statusUpdater{ 105 transportServerLister: tsLister, 106 confClient: fakeClient, 107 keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, 108 } 109 110 err = su.UpdateTransportServerStatus(ts, "same status", "same reason", "same message") 111 if err != nil { 112 t.Errorf("error updating transportserver status: %v", err) 113 } 114 updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) 115 116 if updatedTs.Status.State != "same status" { 117 t.Errorf("expected: %v actual: %v", "same status", updatedTs.Status.State) 118 } 119 if updatedTs.Status.Message != "same message" { 120 t.Errorf("expected: %v actual: %v", "same message", updatedTs.Status.Message) 121 } 122 if updatedTs.Status.Reason != "same reason" { 123 t.Errorf("expected: %v actual: %v", "same reason", updatedTs.Status.Reason) 124 } 125 } 126 127 func TestUpdateTransportServerStatusMissingTransportServer(t *testing.T) { 128 ts := &conf_v1alpha1.TransportServer{ 129 ObjectMeta: meta_v1.ObjectMeta{ 130 Name: "ts-1", 131 Namespace: "default", 132 }, 133 Status: conf_v1alpha1.TransportServerStatus{ 134 State: "before status", 135 Reason: "before reason", 136 Message: "before message", 137 }, 138 } 139 140 fakeClient := fake_v1alpha1.NewSimpleClientset( 141 &conf_v1alpha1.TransportServerList{ 142 Items: []conf_v1alpha1.TransportServer{}}) 143 144 tsLister, _ := cache.NewInformer( 145 cache.NewListWatchFromClient( 146 fakeClient.K8sV1alpha1().RESTClient(), 147 "transportservers", 148 "nginx-ingress", 149 fields.Everything(), 150 ), 151 &conf_v1alpha1.TransportServer{}, 152 2, 153 nil, 154 ) 155 156 su := statusUpdater{ 157 transportServerLister: tsLister, 158 confClient: fakeClient, 159 keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, 160 externalEndpoints: []conf_v1.ExternalEndpoint{ 161 { 162 IP: "123.123.123.123", 163 Ports: "1234", 164 }, 165 }, 166 } 167 168 err := su.UpdateTransportServerStatus(ts, "after status", "after reason", "after message") 169 if err != nil { 170 t.Errorf("unexpected error: %v, result should be empty as no matching TransportServer is present", err) 171 } 172 173 updatedTs, _ := fakeClient.K8sV1alpha1().TransportServers(ts.Namespace).Get(context.TODO(), ts.Name, meta_v1.GetOptions{}) 174 if updatedTs != nil { 175 t.Errorf("expected TransportServer Store would be empty as provided TransportServer was not found. Unexpected updated TransportServer: %v", updatedTs) 176 } 177 } 178 179 func TestStatusUpdateWithExternalStatusAndExternalService(t *testing.T) { 180 ing := networking.Ingress{ 181 ObjectMeta: meta_v1.ObjectMeta{ 182 Name: "ing-1", 183 Namespace: "namespace", 184 }, 185 Status: networking.IngressStatus{ 186 LoadBalancer: v1.LoadBalancerStatus{ 187 Ingress: []v1.LoadBalancerIngress{ 188 { 189 IP: "1.2.3.4", 190 }, 191 }, 192 }, 193 }, 194 } 195 fakeClient := fake.NewSimpleClientset( 196 &networking.IngressList{Items: []networking.Ingress{ 197 ing, 198 }}, 199 ) 200 ingLister := storeToIngressLister{} 201 ingLister.Store, _ = cache.NewInformer( 202 cache.NewListWatchFromClient(fakeClient.NetworkingV1beta1().RESTClient(), "ingresses", "nginx-ingress", fields.Everything()), 203 &networking.Ingress{}, 2, nil) 204 205 err := ingLister.Store.Add(&ing) 206 if err != nil { 207 t.Errorf("Error adding Ingress to the ingress lister: %v", err) 208 } 209 210 su := statusUpdater{ 211 client: fakeClient, 212 namespace: "namespace", 213 externalServiceName: "service-name", 214 externalStatusAddress: "123.123.123.123", 215 ingressLister: &ingLister, 216 keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, 217 } 218 err = su.ClearIngressStatus(ing) 219 if err != nil { 220 t.Errorf("error clearing ing status: %v", err) 221 } 222 ings, _ := fakeClient.NetworkingV1beta1().Ingresses("namespace").List(context.TODO(), meta_v1.ListOptions{}) 223 ingf := ings.Items[0] 224 if !checkStatus("", ingf) { 225 t.Errorf("expected: %v actual: %v", "", ingf.Status.LoadBalancer.Ingress[0]) 226 } 227 228 su.SaveStatusFromExternalStatus("1.1.1.1") 229 err = su.UpdateIngressStatus(ing) 230 if err != nil { 231 t.Errorf("error updating ing status: %v", err) 232 } 233 ring, _ := fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 234 if !checkStatus("1.1.1.1", *ring) { 235 t.Errorf("expected: %v actual: %v", "", ring.Status.LoadBalancer.Ingress) 236 } 237 238 svc := v1.Service{ 239 ObjectMeta: meta_v1.ObjectMeta{ 240 Namespace: "namespace", 241 Name: "service-name", 242 }, 243 Status: v1.ServiceStatus{ 244 LoadBalancer: v1.LoadBalancerStatus{ 245 Ingress: []v1.LoadBalancerIngress{{ 246 IP: "2.2.2.2", 247 }}, 248 }, 249 }, 250 } 251 su.SaveStatusFromExternalService(&svc) 252 err = su.UpdateIngressStatus(ing) 253 if err != nil { 254 t.Errorf("error updating ing status: %v", err) 255 } 256 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 257 if !checkStatus("1.1.1.1", *ring) { 258 t.Errorf("expected: %v actual: %v", "1.1.1.1", ring.Status.LoadBalancer.Ingress) 259 } 260 261 su.SaveStatusFromExternalStatus("") 262 err = su.UpdateIngressStatus(ing) 263 if err != nil { 264 t.Errorf("error updating ing status: %v", err) 265 } 266 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 267 if !checkStatus("2.2.2.2", *ring) { 268 t.Errorf("expected: %v actual: %v", "2.2.2.2", ring.Status.LoadBalancer.Ingress) 269 } 270 271 su.ClearStatusFromExternalService() 272 err = su.UpdateIngressStatus(ing) 273 if err != nil { 274 t.Errorf("error updating ing status: %v", err) 275 } 276 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 277 if !checkStatus("", *ring) { 278 t.Errorf("expected: %v actual: %v", "", ring.Status.LoadBalancer.Ingress) 279 } 280 } 281 282 func TestStatusUpdateWithExternalStatusAndIngressLink(t *testing.T) { 283 ing := networking.Ingress{ 284 ObjectMeta: meta_v1.ObjectMeta{ 285 Name: "ing-1", 286 Namespace: "namespace", 287 }, 288 Status: networking.IngressStatus{ 289 LoadBalancer: v1.LoadBalancerStatus{ 290 Ingress: []v1.LoadBalancerIngress{ 291 { 292 IP: "1.2.3.4", 293 }, 294 }, 295 }, 296 }, 297 } 298 fakeClient := fake.NewSimpleClientset( 299 &networking.IngressList{Items: []networking.Ingress{ 300 ing, 301 }}, 302 ) 303 ingLister := storeToIngressLister{} 304 ingLister.Store, _ = cache.NewInformer( 305 cache.NewListWatchFromClient(fakeClient.NetworkingV1beta1().RESTClient(), "ingresses", "nginx-ingress", fields.Everything()), 306 &networking.Ingress{}, 2, nil) 307 308 err := ingLister.Store.Add(&ing) 309 if err != nil { 310 t.Errorf("Error adding Ingress to the ingress lister: %v", err) 311 } 312 313 su := statusUpdater{ 314 client: fakeClient, 315 namespace: "namespace", 316 externalStatusAddress: "", 317 ingressLister: &ingLister, 318 keyFunc: cache.DeletionHandlingMetaNamespaceKeyFunc, 319 } 320 321 su.SaveStatusFromIngressLink("3.3.3.3") 322 err = su.UpdateIngressStatus(ing) 323 if err != nil { 324 t.Errorf("error updating ing status: %v", err) 325 } 326 ring, _ := fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 327 if !checkStatus("3.3.3.3", *ring) { 328 t.Errorf("expected: %v actual: %v", "3.3.3.3", ring.Status.LoadBalancer.Ingress) 329 } 330 331 su.SaveStatusFromExternalStatus("1.1.1.1") 332 err = su.UpdateIngressStatus(ing) 333 if err != nil { 334 t.Errorf("error updating ing status: %v", err) 335 } 336 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 337 if !checkStatus("1.1.1.1", *ring) { 338 t.Errorf("expected: %v actual: %v", "1.1.1.1", ring.Status.LoadBalancer.Ingress) 339 } 340 341 su.ClearStatusFromIngressLink() 342 err = su.UpdateIngressStatus(ing) 343 if err != nil { 344 t.Errorf("error updating ing status: %v", err) 345 } 346 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 347 if !checkStatus("1.1.1.1", *ring) { 348 t.Errorf("expected: %v actual: %v", "1.1.1.1", ring.Status.LoadBalancer.Ingress) 349 } 350 351 su.SaveStatusFromIngressLink("4.4.4.4") 352 err = su.UpdateIngressStatus(ing) 353 if err != nil { 354 t.Errorf("error updating ing status: %v", err) 355 } 356 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 357 if !checkStatus("1.1.1.1", *ring) { 358 t.Errorf("expected: %v actual: %v", "1.1.1.1", ring.Status.LoadBalancer.Ingress) 359 } 360 361 su.SaveStatusFromExternalStatus("") 362 err = su.UpdateIngressStatus(ing) 363 if err != nil { 364 t.Errorf("error updating ing status: %v", err) 365 } 366 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 367 if !checkStatus("4.4.4.4", *ring) { 368 t.Errorf("expected: %v actual: %v", "4.4.4.4", ring.Status.LoadBalancer.Ingress) 369 } 370 371 su.ClearStatusFromIngressLink() 372 err = su.UpdateIngressStatus(ing) 373 if err != nil { 374 t.Errorf("error updating ing status: %v", err) 375 } 376 ring, _ = fakeClient.NetworkingV1beta1().Ingresses(ing.Namespace).Get(context.TODO(), ing.Name, meta_v1.GetOptions{}) 377 if !checkStatus("", *ring) { 378 t.Errorf("expected: %v actual: %v", "", ring.Status.LoadBalancer.Ingress) 379 } 380 } 381 382 func checkStatus(expected string, actual networking.Ingress) bool { 383 if len(actual.Status.LoadBalancer.Ingress) == 0 { 384 return expected == "" 385 } 386 return expected == actual.Status.LoadBalancer.Ingress[0].IP 387 } 388 389 func TestGenerateExternalEndpointsFromStatus(t *testing.T) { 390 su := statusUpdater{ 391 status: []v1.LoadBalancerIngress{ 392 { 393 IP: "8.8.8.8", 394 }, 395 }, 396 } 397 398 expectedEndpoints := []conf_v1.ExternalEndpoint{ 399 {IP: "8.8.8.8", Ports: ""}, 400 } 401 402 endpoints := su.generateExternalEndpointsFromStatus(su.status) 403 404 if !reflect.DeepEqual(endpoints, expectedEndpoints) { 405 t.Errorf("generateExternalEndpointsFromStatus(%v) returned %v but expected %v", su.status, endpoints, expectedEndpoints) 406 } 407 } 408 409 func TestHasVsStatusChanged(t *testing.T) { 410 state := "Valid" 411 reason := "AddedOrUpdated" 412 msg := "Configuration was added or updated" 413 414 tests := []struct { 415 expected bool 416 vs conf_v1.VirtualServer 417 }{ 418 { 419 expected: false, 420 vs: conf_v1.VirtualServer{ 421 Status: conf_v1.VirtualServerStatus{ 422 State: state, 423 Reason: reason, 424 Message: msg, 425 }, 426 }, 427 }, 428 { 429 expected: true, 430 vs: conf_v1.VirtualServer{ 431 Status: conf_v1.VirtualServerStatus{ 432 State: "DifferentState", 433 Reason: reason, 434 Message: msg, 435 }, 436 }, 437 }, 438 { 439 expected: true, 440 vs: conf_v1.VirtualServer{ 441 Status: conf_v1.VirtualServerStatus{ 442 State: state, 443 Reason: "DifferentReason", 444 Message: msg, 445 }, 446 }, 447 }, 448 { 449 expected: true, 450 vs: conf_v1.VirtualServer{ 451 Status: conf_v1.VirtualServerStatus{ 452 State: state, 453 Reason: reason, 454 Message: "DifferentMessage", 455 }, 456 }, 457 }, 458 } 459 460 for _, test := range tests { 461 changed := hasVsStatusChanged(&test.vs, state, reason, msg) 462 463 if changed != test.expected { 464 t.Errorf("hasVsStatusChanged(%v, %v, %v, %v) returned %v but expected %v.", test.vs, state, reason, msg, changed, test.expected) 465 } 466 } 467 } 468 469 func TestHasVsrStatusChanged(t *testing.T) { 470 471 referencedBy := "namespace/name" 472 state := "Valid" 473 reason := "AddedOrUpdated" 474 msg := "Configuration was added or updated" 475 476 tests := []struct { 477 expected bool 478 vsr conf_v1.VirtualServerRoute 479 }{ 480 { 481 expected: false, 482 vsr: conf_v1.VirtualServerRoute{ 483 Status: conf_v1.VirtualServerRouteStatus{ 484 State: state, 485 Reason: reason, 486 Message: msg, 487 ReferencedBy: referencedBy, 488 }, 489 }, 490 }, 491 { 492 expected: true, 493 vsr: conf_v1.VirtualServerRoute{ 494 Status: conf_v1.VirtualServerRouteStatus{ 495 State: "DifferentState", 496 Reason: reason, 497 Message: msg, 498 ReferencedBy: referencedBy, 499 }, 500 }, 501 }, 502 { 503 expected: true, 504 vsr: conf_v1.VirtualServerRoute{ 505 Status: conf_v1.VirtualServerRouteStatus{ 506 State: state, 507 Reason: "DifferentReason", 508 Message: msg, 509 ReferencedBy: referencedBy, 510 }, 511 }, 512 }, 513 { 514 expected: true, 515 vsr: conf_v1.VirtualServerRoute{ 516 Status: conf_v1.VirtualServerRouteStatus{ 517 State: state, 518 Reason: reason, 519 Message: "DifferentMessage", 520 ReferencedBy: referencedBy, 521 }, 522 }, 523 }, 524 { 525 expected: true, 526 vsr: conf_v1.VirtualServerRoute{ 527 Status: conf_v1.VirtualServerRouteStatus{ 528 State: state, 529 Reason: reason, 530 Message: msg, 531 ReferencedBy: "DifferentReferencedBy", 532 }, 533 }, 534 }, 535 } 536 537 for _, test := range tests { 538 changed := hasVsrStatusChanged(&test.vsr, state, reason, msg, referencedBy) 539 540 if changed != test.expected { 541 t.Errorf("hasVsrStatusChanged(%v, %v, %v, %v) returned %v but expected %v.", test.vsr, state, reason, msg, changed, test.expected) 542 } 543 } 544 } 545 546 func TestGetExternalServicePorts(t *testing.T) { 547 svc := v1.Service{ 548 Spec: v1.ServiceSpec{ 549 Ports: []v1.ServicePort{ 550 { 551 Port: int32(80), 552 TargetPort: intstr.IntOrString{ 553 Type: intstr.Int, 554 IntVal: 80, 555 }, 556 }, 557 { 558 Port: int32(443), 559 TargetPort: intstr.IntOrString{ 560 Type: intstr.Int, 561 IntVal: 443, 562 }, 563 }, 564 }, 565 }, 566 } 567 568 expected := "[80,443]" 569 ports := getExternalServicePorts(&svc) 570 571 if ports != expected { 572 t.Errorf("getExternalServicePorts(%v) returned %v but expected %v", svc, ports, expected) 573 } 574 } 575 576 func TestIsRequiredPort(t *testing.T) { 577 tests := []struct { 578 port intstr.IntOrString 579 expected bool 580 }{ 581 { 582 port: intstr.IntOrString{ 583 Type: intstr.Int, 584 IntVal: 999, 585 }, 586 expected: false, 587 }, 588 { 589 port: intstr.IntOrString{ 590 Type: intstr.Int, 591 IntVal: 80, 592 }, 593 expected: true, 594 }, 595 { 596 port: intstr.IntOrString{ 597 Type: intstr.Int, 598 IntVal: 443, 599 }, 600 expected: true, 601 }, 602 { 603 port: intstr.IntOrString{ 604 Type: intstr.String, 605 StrVal: "name", 606 }, 607 expected: false, 608 }, 609 { 610 port: intstr.IntOrString{ 611 Type: intstr.String, 612 StrVal: "http", 613 }, 614 expected: true, 615 }, 616 { 617 port: intstr.IntOrString{ 618 Type: intstr.String, 619 StrVal: "https", 620 }, 621 expected: true, 622 }, 623 } 624 625 for _, test := range tests { 626 result := isRequiredPort(test.port) 627 628 if result != test.expected { 629 t.Errorf("isRequiredPort(%+v) returned %v but expected %v", test.port, result, test.expected) 630 } 631 } 632 } 633 634 func TestHasPolicyStatusChanged(t *testing.T) { 635 636 state := "Valid" 637 reason := "AddedOrUpdated" 638 msg := "Configuration was added or updated" 639 640 tests := []struct { 641 expected bool 642 pol conf_v1.Policy 643 }{ 644 { 645 expected: false, 646 pol: conf_v1.Policy{ 647 Status: conf_v1.PolicyStatus{ 648 State: state, 649 Reason: reason, 650 Message: msg, 651 }, 652 }, 653 }, 654 { 655 expected: true, 656 pol: conf_v1.Policy{ 657 Status: conf_v1.PolicyStatus{ 658 State: "DifferentState", 659 Reason: reason, 660 Message: msg, 661 }, 662 }, 663 }, 664 { 665 expected: true, 666 pol: conf_v1.Policy{ 667 Status: conf_v1.PolicyStatus{ 668 State: state, 669 Reason: "DifferentReason", 670 Message: msg, 671 }, 672 }, 673 }, 674 { 675 expected: true, 676 pol: conf_v1.Policy{ 677 Status: conf_v1.PolicyStatus{ 678 State: state, 679 Reason: reason, 680 Message: "DifferentMessage", 681 }, 682 }, 683 }, 684 } 685 686 for _, test := range tests { 687 changed := hasPolicyStatusChanged(&test.pol, state, reason, msg) 688 689 if changed != test.expected { 690 t.Errorf("hasPolicyStatusChanged(%v, %v, %v, %v) returned %v but expected %v.", test.pol, state, reason, msg, changed, test.expected) 691 } 692 } 693 }