sigs.k8s.io/external-dns@v0.14.1/source/ingress_test.go (about) 1 /* 2 Copyright 2017 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 source 18 19 import ( 20 "context" 21 "testing" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "github.com/stretchr/testify/suite" 26 networkv1 "k8s.io/api/networking/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/client-go/kubernetes/fake" 30 31 "sigs.k8s.io/external-dns/endpoint" 32 ) 33 34 // Validates that ingressSource is a Source 35 var _ Source = &ingressSource{} 36 37 type IngressSuite struct { 38 suite.Suite 39 sc Source 40 fooWithTargets *networkv1.Ingress 41 } 42 43 func (suite *IngressSuite) SetupTest() { 44 fakeClient := fake.NewSimpleClientset() 45 46 suite.fooWithTargets = (fakeIngress{ 47 name: "foo-with-targets", 48 namespace: "default", 49 dnsnames: []string{"foo"}, 50 ips: []string{"8.8.8.8"}, 51 hostnames: []string{"v1"}, 52 annotations: map[string]string{ALBDualstackAnnotationKey: ALBDualstackAnnotationValue}, 53 }).Ingress() 54 _, err := fakeClient.NetworkingV1().Ingresses(suite.fooWithTargets.Namespace).Create(context.Background(), suite.fooWithTargets, metav1.CreateOptions{}) 55 suite.NoError(err, "should succeed") 56 57 suite.sc, err = NewIngressSource( 58 context.TODO(), 59 fakeClient, 60 "", 61 "", 62 "{{.Name}}", 63 false, 64 false, 65 false, 66 false, 67 labels.Everything(), 68 []string{}, 69 ) 70 suite.NoError(err, "should initialize ingress source") 71 } 72 73 func (suite *IngressSuite) TestResourceLabelIsSet() { 74 endpoints, _ := suite.sc.Endpoints(context.Background()) 75 for _, ep := range endpoints { 76 suite.Equal("ingress/default/foo-with-targets", ep.Labels[endpoint.ResourceLabelKey], "should set correct resource label") 77 } 78 } 79 80 func (suite *IngressSuite) TestDualstackLabelIsSet() { 81 endpoints, _ := suite.sc.Endpoints(context.Background()) 82 for _, ep := range endpoints { 83 suite.Equal("true", ep.Labels[endpoint.DualstackLabelKey], "should set dualstack label to true") 84 } 85 } 86 87 func TestIngress(t *testing.T) { 88 t.Parallel() 89 90 suite.Run(t, new(IngressSuite)) 91 t.Run("endpointsFromIngress", testEndpointsFromIngress) 92 t.Run("endpointsFromIngressHostnameSourceAnnotation", testEndpointsFromIngressHostnameSourceAnnotation) 93 t.Run("Endpoints", testIngressEndpoints) 94 } 95 96 func TestNewIngressSource(t *testing.T) { 97 t.Parallel() 98 99 for _, ti := range []struct { 100 title string 101 annotationFilter string 102 fqdnTemplate string 103 combineFQDNAndAnnotation bool 104 expectError bool 105 ingressClassNames []string 106 }{ 107 { 108 title: "invalid template", 109 expectError: true, 110 fqdnTemplate: "{{.Name", 111 }, 112 { 113 title: "valid empty template", 114 expectError: false, 115 }, 116 { 117 title: "valid template", 118 expectError: false, 119 fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com", 120 }, 121 { 122 title: "valid template", 123 expectError: false, 124 fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com", 125 }, 126 { 127 title: "valid template", 128 expectError: false, 129 fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com, {{.Name}}-{{.Namespace}}.ext-dna.test.com", 130 combineFQDNAndAnnotation: true, 131 }, 132 { 133 title: "non-empty annotation filter label", 134 expectError: false, 135 annotationFilter: "kubernetes.io/ingress.class=nginx", 136 }, 137 { 138 title: "non-empty ingress class name list", 139 expectError: false, 140 ingressClassNames: []string{"internal", "external"}, 141 }, 142 { 143 title: "ingress class name and annotation filter jointly specified", 144 expectError: true, 145 ingressClassNames: []string{"internal", "external"}, 146 annotationFilter: "kubernetes.io/ingress.class=nginx", 147 }, 148 } { 149 ti := ti 150 t.Run(ti.title, func(t *testing.T) { 151 t.Parallel() 152 153 _, err := NewIngressSource( 154 context.TODO(), 155 fake.NewSimpleClientset(), 156 "", 157 ti.annotationFilter, 158 ti.fqdnTemplate, 159 ti.combineFQDNAndAnnotation, 160 false, 161 false, 162 false, 163 labels.Everything(), 164 ti.ingressClassNames, 165 ) 166 if ti.expectError { 167 assert.Error(t, err) 168 } else { 169 assert.NoError(t, err) 170 } 171 }) 172 } 173 } 174 175 func testEndpointsFromIngress(t *testing.T) { 176 t.Parallel() 177 178 for _, ti := range []struct { 179 title string 180 ingress fakeIngress 181 ignoreHostnameAnnotation bool 182 ignoreIngressTLSSpec bool 183 ignoreIngressRulesSpec bool 184 expected []*endpoint.Endpoint 185 }{ 186 { 187 title: "one rule.host one lb.hostname", 188 ingress: fakeIngress{ 189 dnsnames: []string{"foo.bar"}, // Kubernetes requires removal of trailing dot 190 hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot 191 }, 192 expected: []*endpoint.Endpoint{ 193 { 194 DNSName: "foo.bar", 195 RecordType: endpoint.RecordTypeCNAME, 196 Targets: endpoint.Targets{"lb.com"}, 197 }, 198 }, 199 }, 200 { 201 title: "one rule.host one lb.IP", 202 ingress: fakeIngress{ 203 dnsnames: []string{"foo.bar"}, 204 ips: []string{"8.8.8.8"}, 205 }, 206 expected: []*endpoint.Endpoint{ 207 { 208 DNSName: "foo.bar", 209 RecordType: endpoint.RecordTypeA, 210 Targets: endpoint.Targets{"8.8.8.8"}, 211 }, 212 }, 213 }, 214 { 215 title: "one rule.host two lb.IP and two lb.Hostname", 216 ingress: fakeIngress{ 217 dnsnames: []string{"foo.bar"}, 218 ips: []string{"8.8.8.8", "127.0.0.1"}, 219 hostnames: []string{"elb.com", "alb.com"}, 220 }, 221 expected: []*endpoint.Endpoint{ 222 { 223 DNSName: "foo.bar", 224 RecordType: endpoint.RecordTypeA, 225 Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, 226 }, 227 { 228 DNSName: "foo.bar", 229 RecordType: endpoint.RecordTypeCNAME, 230 Targets: endpoint.Targets{"elb.com", "alb.com"}, 231 }, 232 }, 233 }, 234 { 235 title: "no rule.host", 236 ingress: fakeIngress{ 237 ips: []string{"8.8.8.8", "127.0.0.1"}, 238 hostnames: []string{"elb.com", "alb.com"}, 239 }, 240 expected: []*endpoint.Endpoint{}, 241 }, 242 { 243 title: "one empty rule.host", 244 ingress: fakeIngress{ 245 dnsnames: []string{""}, 246 ips: []string{"8.8.8.8", "127.0.0.1"}, 247 hostnames: []string{"elb.com", "alb.com"}, 248 }, 249 expected: []*endpoint.Endpoint{}, 250 }, 251 { 252 title: "no targets", 253 ingress: fakeIngress{ 254 dnsnames: []string{""}, 255 }, 256 expected: []*endpoint.Endpoint{}, 257 }, 258 { 259 title: "ignore rules with one rule.host one lb.hostname", 260 ingress: fakeIngress{ 261 dnsnames: []string{"test"}, // Kubernetes requires removal of trailing dot 262 hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot 263 }, 264 expected: []*endpoint.Endpoint{}, 265 ignoreIngressRulesSpec: true, 266 }, 267 { 268 title: "invalid hostname does not generate endpoints", 269 ingress: fakeIngress{ 270 dnsnames: []string{"this-is-an-exceedingly-long-label-that-external-dns-should-reject.example.org"}, 271 }, 272 expected: []*endpoint.Endpoint{}, 273 }, 274 } { 275 t.Run(ti.title, func(t *testing.T) { 276 realIngress := ti.ingress.Ingress() 277 validateEndpoints(t, endpointsFromIngress(realIngress, ti.ignoreHostnameAnnotation, ti.ignoreIngressTLSSpec, ti.ignoreIngressRulesSpec), ti.expected) 278 }) 279 } 280 } 281 282 func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { 283 // Host names and host name annotation provided, with various values of the ingress-hostname-source annotation 284 for _, ti := range []struct { 285 title string 286 ingress fakeIngress 287 expected []*endpoint.Endpoint 288 }{ 289 { 290 title: "No ingress-hostname-source annotation, one rule.host, one annotation host", 291 ingress: fakeIngress{ 292 dnsnames: []string{"foo.bar"}, 293 annotations: map[string]string{hostnameAnnotationKey: "foo.baz"}, 294 hostnames: []string{"lb.com"}, 295 }, 296 expected: []*endpoint.Endpoint{ 297 { 298 DNSName: "foo.bar", 299 RecordType: endpoint.RecordTypeCNAME, 300 Targets: endpoint.Targets{"lb.com"}, 301 }, 302 { 303 DNSName: "foo.baz", 304 RecordType: endpoint.RecordTypeCNAME, 305 Targets: endpoint.Targets{"lb.com"}, 306 }, 307 }, 308 }, 309 { 310 title: "No ingress-hostname-source annotation, one rule.host", 311 ingress: fakeIngress{ 312 dnsnames: []string{"foo.bar"}, 313 hostnames: []string{"lb.com"}, 314 }, 315 expected: []*endpoint.Endpoint{ 316 { 317 DNSName: "foo.bar", 318 RecordType: endpoint.RecordTypeCNAME, 319 Targets: endpoint.Targets{"lb.com"}, 320 }, 321 }, 322 }, 323 { 324 title: "No ingress-hostname-source annotation, one rule.host, one annotation host", 325 ingress: fakeIngress{ 326 dnsnames: []string{"foo.bar"}, 327 annotations: map[string]string{hostnameAnnotationKey: "foo.baz"}, 328 hostnames: []string{"lb.com"}, 329 }, 330 expected: []*endpoint.Endpoint{ 331 { 332 DNSName: "foo.bar", 333 RecordType: endpoint.RecordTypeCNAME, 334 Targets: endpoint.Targets{"lb.com"}, 335 }, 336 { 337 DNSName: "foo.baz", 338 RecordType: endpoint.RecordTypeCNAME, 339 Targets: endpoint.Targets{"lb.com"}, 340 }, 341 }, 342 }, 343 { 344 title: "Ingress-hostname-source=defined-hosts-only, one rule.host, one annotation host", 345 ingress: fakeIngress{ 346 dnsnames: []string{"foo.bar"}, 347 annotations: map[string]string{hostnameAnnotationKey: "foo.baz", ingressHostnameSourceKey: "defined-hosts-only"}, 348 hostnames: []string{"lb.com"}, 349 }, 350 expected: []*endpoint.Endpoint{ 351 { 352 DNSName: "foo.bar", 353 RecordType: endpoint.RecordTypeCNAME, 354 Targets: endpoint.Targets{"lb.com"}, 355 }, 356 }, 357 }, 358 { 359 title: "Ingress-hostname-source=annotation-only, one rule.host, one annotation host", 360 ingress: fakeIngress{ 361 dnsnames: []string{"foo.bar"}, 362 annotations: map[string]string{hostnameAnnotationKey: "foo.baz", ingressHostnameSourceKey: "annotation-only"}, 363 hostnames: []string{"lb.com"}, 364 }, 365 expected: []*endpoint.Endpoint{ 366 { 367 DNSName: "foo.baz", 368 RecordType: endpoint.RecordTypeCNAME, 369 Targets: endpoint.Targets{"lb.com"}, 370 }, 371 }, 372 }, 373 } { 374 t.Run(ti.title, func(t *testing.T) { 375 realIngress := ti.ingress.Ingress() 376 validateEndpoints(t, endpointsFromIngress(realIngress, false, false, false), ti.expected) 377 }) 378 } 379 } 380 381 func testIngressEndpoints(t *testing.T) { 382 t.Parallel() 383 384 namespace := "testing" 385 for _, ti := range []struct { 386 title string 387 targetNamespace string 388 annotationFilter string 389 ingressItems []fakeIngress 390 expected []*endpoint.Endpoint 391 expectError bool 392 fqdnTemplate string 393 combineFQDNAndAnnotation bool 394 ignoreHostnameAnnotation bool 395 ignoreIngressTLSSpec bool 396 ignoreIngressRulesSpec bool 397 ingressLabelSelector labels.Selector 398 ingressClassNames []string 399 }{ 400 { 401 title: "no ingress", 402 targetNamespace: "", 403 }, 404 { 405 title: "two simple ingresses", 406 targetNamespace: "", 407 ingressItems: []fakeIngress{ 408 { 409 name: "fake1", 410 namespace: namespace, 411 dnsnames: []string{"example.org"}, 412 ips: []string{"8.8.8.8"}, 413 }, 414 { 415 name: "fake2", 416 namespace: namespace, 417 dnsnames: []string{"new.org"}, 418 hostnames: []string{"lb.com"}, 419 }, 420 }, 421 expected: []*endpoint.Endpoint{ 422 { 423 DNSName: "example.org", 424 RecordType: endpoint.RecordTypeA, 425 Targets: endpoint.Targets{"8.8.8.8"}, 426 }, 427 { 428 DNSName: "new.org", 429 RecordType: endpoint.RecordTypeCNAME, 430 Targets: endpoint.Targets{"lb.com"}, 431 }, 432 }, 433 }, 434 { 435 title: "ipv6 ingress", 436 targetNamespace: "", 437 ingressItems: []fakeIngress{ 438 { 439 name: "fake1", 440 namespace: namespace, 441 dnsnames: []string{"example.org"}, 442 ips: []string{"2001:DB8::1"}, 443 }, 444 }, 445 expected: []*endpoint.Endpoint{ 446 { 447 DNSName: "example.org", 448 RecordType: endpoint.RecordTypeAAAA, 449 Targets: endpoint.Targets{"2001:DB8::1"}, 450 }, 451 }, 452 }, 453 { 454 title: "ignore rules", 455 targetNamespace: "", 456 ignoreIngressRulesSpec: true, 457 ingressItems: []fakeIngress{ 458 { 459 name: "fake1", 460 namespace: namespace, 461 dnsnames: []string{"example.org"}, 462 ips: []string{"8.8.8.8"}, 463 }, 464 { 465 name: "fake2", 466 namespace: namespace, 467 dnsnames: []string{"new.org"}, 468 hostnames: []string{"lb.com"}, 469 }, 470 }, 471 expected: []*endpoint.Endpoint{}, 472 }, 473 { 474 title: "two simple ingresses on different namespaces", 475 targetNamespace: "", 476 ingressItems: []fakeIngress{ 477 { 478 name: "fake1", 479 namespace: "testing1", 480 dnsnames: []string{"example.org"}, 481 ips: []string{"8.8.8.8"}, 482 }, 483 { 484 name: "fake2", 485 namespace: "testing2", 486 dnsnames: []string{"new.org"}, 487 hostnames: []string{"lb.com"}, 488 }, 489 }, 490 expected: []*endpoint.Endpoint{ 491 { 492 DNSName: "example.org", 493 RecordType: endpoint.RecordTypeA, 494 Targets: endpoint.Targets{"8.8.8.8"}, 495 }, 496 { 497 DNSName: "new.org", 498 RecordType: endpoint.RecordTypeCNAME, 499 Targets: endpoint.Targets{"lb.com"}, 500 }, 501 }, 502 }, 503 { 504 title: "two simple ingresses on different namespaces with target namespace", 505 targetNamespace: "testing1", 506 ingressItems: []fakeIngress{ 507 { 508 name: "fake1", 509 namespace: "testing1", 510 dnsnames: []string{"example.org"}, 511 ips: []string{"8.8.8.8"}, 512 }, 513 { 514 name: "fake2", 515 namespace: "testing2", 516 dnsnames: []string{"new.org"}, 517 hostnames: []string{"lb.com"}, 518 }, 519 }, 520 expected: []*endpoint.Endpoint{ 521 { 522 DNSName: "example.org", 523 RecordType: endpoint.RecordTypeA, 524 Targets: endpoint.Targets{"8.8.8.8"}, 525 }, 526 }, 527 }, 528 { 529 title: "valid matching annotation filter expression", 530 targetNamespace: "", 531 annotationFilter: "kubernetes.io/ingress.class in (alb, nginx)", 532 ingressItems: []fakeIngress{ 533 { 534 name: "fake1", 535 namespace: namespace, 536 annotations: map[string]string{ 537 "kubernetes.io/ingress.class": "nginx", 538 }, 539 dnsnames: []string{"example.org"}, 540 ips: []string{"8.8.8.8"}, 541 }, 542 }, 543 expected: []*endpoint.Endpoint{ 544 { 545 DNSName: "example.org", 546 RecordType: endpoint.RecordTypeA, 547 Targets: endpoint.Targets{"8.8.8.8"}, 548 }, 549 }, 550 }, 551 { 552 title: "valid non-matching annotation filter expression", 553 targetNamespace: "", 554 annotationFilter: "kubernetes.io/ingress.class in (alb, nginx)", 555 ingressItems: []fakeIngress{ 556 { 557 name: "fake1", 558 namespace: namespace, 559 annotations: map[string]string{ 560 "kubernetes.io/ingress.class": "tectonic", 561 }, 562 dnsnames: []string{"example.org"}, 563 ips: []string{"8.8.8.8"}, 564 }, 565 }, 566 expected: []*endpoint.Endpoint{}, 567 }, 568 { 569 title: "invalid annotation filter expression", 570 targetNamespace: "", 571 annotationFilter: "kubernetes.io/ingress.name in (a b)", 572 ingressItems: []fakeIngress{ 573 { 574 name: "fake1", 575 namespace: namespace, 576 annotations: map[string]string{ 577 "kubernetes.io/ingress.class": "alb", 578 }, 579 dnsnames: []string{"example.org"}, 580 ips: []string{"8.8.8.8"}, 581 }, 582 }, 583 expected: []*endpoint.Endpoint{}, 584 expectError: true, 585 }, 586 { 587 title: "valid matching annotation filter label", 588 targetNamespace: "", 589 annotationFilter: "kubernetes.io/ingress.class=nginx", 590 ingressItems: []fakeIngress{ 591 { 592 name: "fake1", 593 namespace: namespace, 594 annotations: map[string]string{ 595 "kubernetes.io/ingress.class": "nginx", 596 }, 597 dnsnames: []string{"example.org"}, 598 ips: []string{"8.8.8.8"}, 599 }, 600 }, 601 expected: []*endpoint.Endpoint{ 602 { 603 DNSName: "example.org", 604 RecordType: endpoint.RecordTypeA, 605 Targets: endpoint.Targets{"8.8.8.8"}, 606 }, 607 }, 608 }, 609 { 610 title: "valid non-matching annotation filter label", 611 targetNamespace: "", 612 annotationFilter: "kubernetes.io/ingress.class=nginx", 613 ingressItems: []fakeIngress{ 614 { 615 name: "fake1", 616 namespace: namespace, 617 annotations: map[string]string{ 618 "kubernetes.io/ingress.class": "alb", 619 }, 620 dnsnames: []string{"example.org"}, 621 ips: []string{"8.8.8.8"}, 622 }, 623 }, 624 expected: []*endpoint.Endpoint{}, 625 }, 626 { 627 title: "our controller type is dns-controller", 628 targetNamespace: "", 629 ingressItems: []fakeIngress{ 630 { 631 name: "fake1", 632 namespace: namespace, 633 annotations: map[string]string{ 634 controllerAnnotationKey: controllerAnnotationValue, 635 }, 636 dnsnames: []string{"example.org"}, 637 ips: []string{"8.8.8.8"}, 638 }, 639 }, 640 expected: []*endpoint.Endpoint{ 641 { 642 DNSName: "example.org", 643 RecordType: endpoint.RecordTypeA, 644 Targets: endpoint.Targets{"8.8.8.8"}, 645 }, 646 }, 647 }, 648 { 649 title: "different controller types are ignored", 650 targetNamespace: "", 651 ingressItems: []fakeIngress{ 652 { 653 name: "fake1", 654 namespace: namespace, 655 annotations: map[string]string{ 656 controllerAnnotationKey: "some-other-tool", 657 }, 658 dnsnames: []string{"example.org"}, 659 ips: []string{"8.8.8.8"}, 660 }, 661 }, 662 expected: []*endpoint.Endpoint{}, 663 }, 664 { 665 title: "template for ingress if host is missing", 666 targetNamespace: "", 667 ingressItems: []fakeIngress{ 668 { 669 name: "fake1", 670 namespace: namespace, 671 annotations: map[string]string{ 672 controllerAnnotationKey: controllerAnnotationValue, 673 }, 674 dnsnames: []string{}, 675 ips: []string{"8.8.8.8"}, 676 hostnames: []string{"elb.com"}, 677 }, 678 }, 679 expected: []*endpoint.Endpoint{ 680 { 681 DNSName: "fake1.ext-dns.test.com", 682 RecordType: endpoint.RecordTypeA, 683 Targets: endpoint.Targets{"8.8.8.8"}, 684 }, 685 { 686 DNSName: "fake1.ext-dns.test.com", 687 RecordType: endpoint.RecordTypeCNAME, 688 Targets: endpoint.Targets{"elb.com"}, 689 }, 690 }, 691 fqdnTemplate: "{{.Name}}.ext-dns.test.com", 692 }, 693 { 694 title: "another controller annotation skipped even with template", 695 targetNamespace: "", 696 ingressItems: []fakeIngress{ 697 { 698 name: "fake1", 699 namespace: namespace, 700 annotations: map[string]string{ 701 controllerAnnotationKey: "other-controller", 702 }, 703 dnsnames: []string{}, 704 ips: []string{"8.8.8.8"}, 705 }, 706 }, 707 expected: []*endpoint.Endpoint{}, 708 fqdnTemplate: "{{.Name}}.ext-dns.test.com", 709 }, 710 { 711 title: "multiple FQDN template hostnames", 712 targetNamespace: "", 713 ingressItems: []fakeIngress{ 714 { 715 name: "fake1", 716 namespace: namespace, 717 annotations: map[string]string{}, 718 dnsnames: []string{}, 719 ips: []string{"8.8.8.8"}, 720 }, 721 }, 722 expected: []*endpoint.Endpoint{ 723 { 724 DNSName: "fake1.ext-dns.test.com", 725 Targets: endpoint.Targets{"8.8.8.8"}, 726 RecordType: endpoint.RecordTypeA, 727 }, 728 { 729 DNSName: "fake1.ext-dna.test.com", 730 Targets: endpoint.Targets{"8.8.8.8"}, 731 RecordType: endpoint.RecordTypeA, 732 }, 733 }, 734 fqdnTemplate: "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com", 735 }, 736 { 737 title: "multiple FQDN template hostnames", 738 targetNamespace: "", 739 ingressItems: []fakeIngress{ 740 { 741 name: "fake1", 742 namespace: namespace, 743 annotations: map[string]string{}, 744 dnsnames: []string{}, 745 ips: []string{"8.8.8.8"}, 746 }, 747 { 748 name: "fake2", 749 namespace: namespace, 750 annotations: map[string]string{ 751 targetAnnotationKey: "ingress-target.com", 752 }, 753 dnsnames: []string{"example.org"}, 754 ips: []string{}, 755 }, 756 }, 757 expected: []*endpoint.Endpoint{ 758 { 759 DNSName: "fake1.ext-dns.test.com", 760 Targets: endpoint.Targets{"8.8.8.8"}, 761 RecordType: endpoint.RecordTypeA, 762 }, 763 { 764 DNSName: "fake1.ext-dna.test.com", 765 Targets: endpoint.Targets{"8.8.8.8"}, 766 RecordType: endpoint.RecordTypeA, 767 }, 768 { 769 DNSName: "example.org", 770 Targets: endpoint.Targets{"ingress-target.com"}, 771 RecordType: endpoint.RecordTypeCNAME, 772 }, 773 { 774 DNSName: "fake2.ext-dns.test.com", 775 Targets: endpoint.Targets{"ingress-target.com"}, 776 RecordType: endpoint.RecordTypeCNAME, 777 }, 778 { 779 DNSName: "fake2.ext-dna.test.com", 780 Targets: endpoint.Targets{"ingress-target.com"}, 781 RecordType: endpoint.RecordTypeCNAME, 782 }, 783 }, 784 fqdnTemplate: "{{.Name}}.ext-dns.test.com, {{.Name}}.ext-dna.test.com", 785 combineFQDNAndAnnotation: true, 786 }, 787 { 788 title: "ingress rules with annotation", 789 targetNamespace: "", 790 ingressItems: []fakeIngress{ 791 { 792 name: "fake1", 793 namespace: namespace, 794 annotations: map[string]string{ 795 targetAnnotationKey: "ingress-target.com", 796 }, 797 dnsnames: []string{"example.org"}, 798 ips: []string{}, 799 }, 800 { 801 name: "fake2", 802 namespace: namespace, 803 annotations: map[string]string{ 804 targetAnnotationKey: "ingress-target.com", 805 }, 806 dnsnames: []string{"example2.org"}, 807 ips: []string{"8.8.8.8"}, 808 }, 809 { 810 name: "fake3", 811 namespace: namespace, 812 annotations: map[string]string{ 813 targetAnnotationKey: "1.2.3.4", 814 }, 815 dnsnames: []string{"example3.org"}, 816 ips: []string{}, 817 }, 818 }, 819 expected: []*endpoint.Endpoint{ 820 { 821 DNSName: "example.org", 822 Targets: endpoint.Targets{"ingress-target.com"}, 823 RecordType: endpoint.RecordTypeCNAME, 824 }, 825 { 826 DNSName: "example2.org", 827 Targets: endpoint.Targets{"ingress-target.com"}, 828 RecordType: endpoint.RecordTypeCNAME, 829 }, 830 { 831 DNSName: "example3.org", 832 Targets: endpoint.Targets{"1.2.3.4"}, 833 RecordType: endpoint.RecordTypeA, 834 }, 835 }, 836 }, 837 { 838 title: "ingress rules with single tls having single hostname", 839 targetNamespace: "", 840 ingressItems: []fakeIngress{ 841 { 842 name: "fake1", 843 namespace: namespace, 844 tlsdnsnames: [][]string{{"example.org"}}, 845 ips: []string{"1.2.3.4"}, 846 }, 847 }, 848 expected: []*endpoint.Endpoint{ 849 { 850 DNSName: "example.org", 851 Targets: endpoint.Targets{"1.2.3.4"}, 852 RecordType: endpoint.RecordTypeA, 853 }, 854 }, 855 }, 856 { 857 title: "ingress rules with single tls having multiple hostnames", 858 targetNamespace: "", 859 ingressItems: []fakeIngress{ 860 { 861 name: "fake1", 862 namespace: namespace, 863 tlsdnsnames: [][]string{{"example.org", "example2.org"}}, 864 ips: []string{"1.2.3.4"}, 865 }, 866 }, 867 expected: []*endpoint.Endpoint{ 868 { 869 DNSName: "example.org", 870 Targets: endpoint.Targets{"1.2.3.4"}, 871 RecordType: endpoint.RecordTypeA, 872 }, 873 { 874 DNSName: "example2.org", 875 Targets: endpoint.Targets{"1.2.3.4"}, 876 RecordType: endpoint.RecordTypeA, 877 }, 878 }, 879 }, 880 { 881 title: "ingress rules with multiple tls having multiple hostnames", 882 targetNamespace: "", 883 ingressItems: []fakeIngress{ 884 { 885 name: "fake1", 886 namespace: namespace, 887 tlsdnsnames: [][]string{{"example.org", "example2.org"}, {"example3.org", "example4.org"}}, 888 ips: []string{"1.2.3.4"}, 889 }, 890 }, 891 expected: []*endpoint.Endpoint{ 892 { 893 DNSName: "example.org", 894 Targets: endpoint.Targets{"1.2.3.4"}, 895 RecordType: endpoint.RecordTypeA, 896 }, 897 { 898 DNSName: "example2.org", 899 Targets: endpoint.Targets{"1.2.3.4"}, 900 RecordType: endpoint.RecordTypeA, 901 }, 902 { 903 DNSName: "example3.org", 904 Targets: endpoint.Targets{"1.2.3.4"}, 905 RecordType: endpoint.RecordTypeA, 906 }, 907 { 908 DNSName: "example4.org", 909 Targets: endpoint.Targets{"1.2.3.4"}, 910 RecordType: endpoint.RecordTypeA, 911 }, 912 }, 913 }, 914 { 915 title: "ingress rules with hostname annotation", 916 targetNamespace: "", 917 ingressItems: []fakeIngress{ 918 { 919 name: "fake1", 920 namespace: namespace, 921 annotations: map[string]string{ 922 hostnameAnnotationKey: "dns-through-hostname.com", 923 }, 924 dnsnames: []string{"example.org"}, 925 ips: []string{"1.2.3.4"}, 926 }, 927 }, 928 expected: []*endpoint.Endpoint{ 929 { 930 DNSName: "example.org", 931 Targets: endpoint.Targets{"1.2.3.4"}, 932 RecordType: endpoint.RecordTypeA, 933 }, 934 { 935 DNSName: "dns-through-hostname.com", 936 Targets: endpoint.Targets{"1.2.3.4"}, 937 RecordType: endpoint.RecordTypeA, 938 }, 939 }, 940 }, 941 { 942 title: "ingress rules with hostname annotation having multiple hostnames", 943 targetNamespace: "", 944 ingressItems: []fakeIngress{ 945 { 946 name: "fake1", 947 namespace: namespace, 948 annotations: map[string]string{ 949 hostnameAnnotationKey: "dns-through-hostname.com, another-dns-through-hostname.com", 950 }, 951 dnsnames: []string{"example.org"}, 952 ips: []string{"1.2.3.4"}, 953 }, 954 }, 955 expected: []*endpoint.Endpoint{ 956 { 957 DNSName: "example.org", 958 Targets: endpoint.Targets{"1.2.3.4"}, 959 RecordType: endpoint.RecordTypeA, 960 }, 961 { 962 DNSName: "dns-through-hostname.com", 963 Targets: endpoint.Targets{"1.2.3.4"}, 964 RecordType: endpoint.RecordTypeA, 965 }, 966 { 967 DNSName: "another-dns-through-hostname.com", 968 Targets: endpoint.Targets{"1.2.3.4"}, 969 RecordType: endpoint.RecordTypeA, 970 }, 971 }, 972 }, 973 { 974 title: "ingress rules with hostname and target annotation", 975 targetNamespace: "", 976 ingressItems: []fakeIngress{ 977 { 978 name: "fake1", 979 namespace: namespace, 980 annotations: map[string]string{ 981 hostnameAnnotationKey: "dns-through-hostname.com", 982 targetAnnotationKey: "ingress-target.com", 983 }, 984 dnsnames: []string{"example.org"}, 985 ips: []string{}, 986 }, 987 }, 988 expected: []*endpoint.Endpoint{ 989 { 990 DNSName: "example.org", 991 Targets: endpoint.Targets{"ingress-target.com"}, 992 RecordType: endpoint.RecordTypeCNAME, 993 }, 994 { 995 DNSName: "dns-through-hostname.com", 996 Targets: endpoint.Targets{"ingress-target.com"}, 997 RecordType: endpoint.RecordTypeCNAME, 998 }, 999 }, 1000 }, 1001 { 1002 title: "ingress rules with annotation and custom TTL", 1003 targetNamespace: "", 1004 ingressItems: []fakeIngress{ 1005 { 1006 name: "fake1", 1007 namespace: namespace, 1008 annotations: map[string]string{ 1009 targetAnnotationKey: "ingress-target.com", 1010 ttlAnnotationKey: "6", 1011 }, 1012 dnsnames: []string{"example.org"}, 1013 ips: []string{}, 1014 }, 1015 { 1016 name: "fake2", 1017 namespace: namespace, 1018 annotations: map[string]string{ 1019 targetAnnotationKey: "ingress-target.com", 1020 ttlAnnotationKey: "1", 1021 }, 1022 dnsnames: []string{"example2.org"}, 1023 ips: []string{"8.8.8.8"}, 1024 }, 1025 { 1026 name: "fake3", 1027 namespace: namespace, 1028 annotations: map[string]string{ 1029 targetAnnotationKey: "ingress-target.com", 1030 ttlAnnotationKey: "10s", 1031 }, 1032 dnsnames: []string{"example3.org"}, 1033 ips: []string{"8.8.4.4"}, 1034 }, 1035 }, 1036 expected: []*endpoint.Endpoint{ 1037 { 1038 DNSName: "example.org", 1039 RecordType: endpoint.RecordTypeCNAME, 1040 Targets: endpoint.Targets{"ingress-target.com"}, 1041 RecordTTL: endpoint.TTL(6), 1042 }, 1043 { 1044 DNSName: "example2.org", 1045 RecordType: endpoint.RecordTypeCNAME, 1046 Targets: endpoint.Targets{"ingress-target.com"}, 1047 RecordTTL: endpoint.TTL(1), 1048 }, 1049 { 1050 DNSName: "example3.org", 1051 RecordType: endpoint.RecordTypeCNAME, 1052 Targets: endpoint.Targets{"ingress-target.com"}, 1053 RecordTTL: endpoint.TTL(10), 1054 }, 1055 }, 1056 }, 1057 { 1058 title: "ingress rules with alias and target annotation", 1059 targetNamespace: "", 1060 ingressItems: []fakeIngress{ 1061 { 1062 name: "fake1", 1063 namespace: namespace, 1064 annotations: map[string]string{ 1065 targetAnnotationKey: "ingress-target.com", 1066 aliasAnnotationKey: "true", 1067 }, 1068 dnsnames: []string{"example.org"}, 1069 ips: []string{}, 1070 }, 1071 }, 1072 expected: []*endpoint.Endpoint{ 1073 { 1074 DNSName: "example.org", 1075 Targets: endpoint.Targets{"ingress-target.com"}, 1076 RecordType: endpoint.RecordTypeCNAME, 1077 ProviderSpecific: endpoint.ProviderSpecific{{ 1078 Name: "alias", Value: "true", 1079 }}, 1080 }, 1081 }, 1082 }, 1083 { 1084 title: "ingress rules with alias set false and target annotation", 1085 targetNamespace: "", 1086 ingressItems: []fakeIngress{ 1087 { 1088 name: "fake1", 1089 namespace: namespace, 1090 annotations: map[string]string{ 1091 targetAnnotationKey: "ingress-target.com", 1092 aliasAnnotationKey: "false", 1093 }, 1094 dnsnames: []string{"example.org"}, 1095 ips: []string{}, 1096 }, 1097 }, 1098 expected: []*endpoint.Endpoint{ 1099 { 1100 DNSName: "example.org", 1101 Targets: endpoint.Targets{"ingress-target.com"}, 1102 RecordType: endpoint.RecordTypeCNAME, 1103 }, 1104 }, 1105 }, 1106 { 1107 title: "template for ingress with annotation", 1108 targetNamespace: "", 1109 ingressItems: []fakeIngress{ 1110 { 1111 name: "fake1", 1112 namespace: namespace, 1113 annotations: map[string]string{ 1114 targetAnnotationKey: "ingress-target.com", 1115 }, 1116 dnsnames: []string{}, 1117 ips: []string{}, 1118 hostnames: []string{}, 1119 }, 1120 { 1121 name: "fake2", 1122 namespace: namespace, 1123 annotations: map[string]string{ 1124 targetAnnotationKey: "ingress-target.com", 1125 }, 1126 dnsnames: []string{}, 1127 ips: []string{"8.8.8.8"}, 1128 }, 1129 { 1130 name: "fake3", 1131 namespace: namespace, 1132 annotations: map[string]string{ 1133 targetAnnotationKey: "1.2.3.4", 1134 }, 1135 dnsnames: []string{}, 1136 ips: []string{}, 1137 hostnames: []string{}, 1138 }, 1139 }, 1140 expected: []*endpoint.Endpoint{ 1141 { 1142 DNSName: "fake1.ext-dns.test.com", 1143 Targets: endpoint.Targets{"ingress-target.com"}, 1144 RecordType: endpoint.RecordTypeCNAME, 1145 }, 1146 { 1147 DNSName: "fake2.ext-dns.test.com", 1148 Targets: endpoint.Targets{"ingress-target.com"}, 1149 RecordType: endpoint.RecordTypeCNAME, 1150 }, 1151 { 1152 DNSName: "fake3.ext-dns.test.com", 1153 Targets: endpoint.Targets{"1.2.3.4"}, 1154 RecordType: endpoint.RecordTypeA, 1155 }, 1156 }, 1157 fqdnTemplate: "{{.Name}}.ext-dns.test.com", 1158 }, 1159 { 1160 title: "Ingress with empty annotation", 1161 targetNamespace: "", 1162 ingressItems: []fakeIngress{ 1163 { 1164 name: "fake1", 1165 namespace: namespace, 1166 annotations: map[string]string{ 1167 targetAnnotationKey: "", 1168 }, 1169 dnsnames: []string{}, 1170 ips: []string{}, 1171 hostnames: []string{}, 1172 }, 1173 }, 1174 expected: []*endpoint.Endpoint{}, 1175 fqdnTemplate: "{{.Name}}.ext-dns.test.com", 1176 }, 1177 { 1178 title: "ignore hostname annotation", 1179 targetNamespace: "", 1180 ignoreHostnameAnnotation: true, 1181 ingressItems: []fakeIngress{ 1182 { 1183 name: "fake1", 1184 namespace: namespace, 1185 dnsnames: []string{"example.org"}, 1186 ips: []string{"8.8.8.8"}, 1187 }, 1188 { 1189 name: "fake2", 1190 namespace: namespace, 1191 annotations: map[string]string{ 1192 hostnameAnnotationKey: "dns-through-hostname.com", 1193 }, 1194 dnsnames: []string{"new.org"}, 1195 hostnames: []string{"lb.com"}, 1196 }, 1197 }, 1198 expected: []*endpoint.Endpoint{ 1199 { 1200 DNSName: "example.org", 1201 RecordType: endpoint.RecordTypeA, 1202 Targets: endpoint.Targets{"8.8.8.8"}, 1203 }, 1204 { 1205 DNSName: "new.org", 1206 RecordType: endpoint.RecordTypeCNAME, 1207 Targets: endpoint.Targets{"lb.com"}, 1208 }, 1209 }, 1210 }, 1211 { 1212 title: "ignore tls section", 1213 targetNamespace: "", 1214 ignoreIngressTLSSpec: true, 1215 ingressItems: []fakeIngress{ 1216 { 1217 name: "fake1", 1218 namespace: namespace, 1219 tlsdnsnames: [][]string{{"example.org"}}, 1220 ips: []string{"1.2.3.4"}, 1221 }, 1222 }, 1223 expected: []*endpoint.Endpoint{}, 1224 }, 1225 { 1226 title: "reading tls section", 1227 targetNamespace: "", 1228 ignoreIngressTLSSpec: false, 1229 ingressItems: []fakeIngress{ 1230 { 1231 name: "fake1", 1232 namespace: namespace, 1233 tlsdnsnames: [][]string{{"example.org"}}, 1234 ips: []string{"1.2.3.4"}, 1235 }, 1236 }, 1237 expected: []*endpoint.Endpoint{ 1238 { 1239 DNSName: "example.org", 1240 RecordType: endpoint.RecordTypeA, 1241 Targets: endpoint.Targets{"1.2.3.4"}, 1242 }, 1243 }, 1244 }, 1245 { 1246 title: "ingressClassName filtering", 1247 targetNamespace: "", 1248 ingressClassNames: []string{"public", "dmz"}, 1249 ingressItems: []fakeIngress{ 1250 { 1251 name: "none", 1252 namespace: namespace, 1253 tlsdnsnames: [][]string{{"none.example.org"}}, 1254 ips: []string{"1.0.0.0"}, 1255 }, 1256 { 1257 name: "fake-public", 1258 namespace: namespace, 1259 tlsdnsnames: [][]string{{"example.org"}}, 1260 ips: []string{"1.2.3.4"}, 1261 ingressClassName: "public", // match 1262 }, 1263 { 1264 name: "fake-internal", 1265 namespace: namespace, 1266 tlsdnsnames: [][]string{{"int.example.org"}}, 1267 ips: []string{"2.3.4.5"}, 1268 ingressClassName: "internal", 1269 }, 1270 { 1271 name: "fake-dmz", 1272 namespace: namespace, 1273 tlsdnsnames: [][]string{{"dmz.example.org"}}, 1274 ips: []string{"3.4.5.6"}, 1275 ingressClassName: "dmz", // match 1276 }, 1277 { 1278 name: "annotated-dmz", 1279 namespace: namespace, 1280 tlsdnsnames: [][]string{{"annodmz.example.org"}}, 1281 ips: []string{"4.5.6.7"}, 1282 annotations: map[string]string{ 1283 "kubernetes.io/ingress.class": "dmz", // match 1284 }, 1285 }, 1286 { 1287 name: "fake-internal-annotated-dmz", 1288 namespace: namespace, 1289 tlsdnsnames: [][]string{{"int-annodmz.example.org"}}, 1290 ips: []string{"5.6.7.8"}, 1291 annotations: map[string]string{ 1292 "kubernetes.io/ingress.class": "dmz", // match but ignored (non-empty ingressClassName) 1293 }, 1294 ingressClassName: "internal", 1295 }, 1296 { 1297 name: "fake-dmz-annotated-internal", 1298 namespace: namespace, 1299 tlsdnsnames: [][]string{{"dmz-annoint.example.org"}}, 1300 ips: []string{"6.7.8.9"}, 1301 annotations: map[string]string{ 1302 "kubernetes.io/ingress.class": "internal", 1303 }, 1304 ingressClassName: "dmz", // match 1305 }, 1306 { 1307 name: "empty-annotated-dmz", 1308 namespace: namespace, 1309 tlsdnsnames: [][]string{{"empty-annotdmz.example.org"}}, 1310 ips: []string{"7.8.9.0"}, 1311 annotations: map[string]string{ 1312 "kubernetes.io/ingress.class": "dmz", // match (empty ingressClassName) 1313 }, 1314 ingressClassName: "", 1315 }, 1316 { 1317 name: "empty-annotated-internal", 1318 namespace: namespace, 1319 tlsdnsnames: [][]string{{"empty-annotint.example.org"}}, 1320 ips: []string{"8.9.0.1"}, 1321 annotations: map[string]string{ 1322 "kubernetes.io/ingress.class": "internal", 1323 }, 1324 ingressClassName: "", 1325 }, 1326 }, 1327 expected: []*endpoint.Endpoint{ 1328 { 1329 DNSName: "example.org", 1330 RecordType: endpoint.RecordTypeA, 1331 Targets: endpoint.Targets{"1.2.3.4"}, 1332 }, 1333 { 1334 DNSName: "dmz.example.org", 1335 RecordType: endpoint.RecordTypeA, 1336 Targets: endpoint.Targets{"3.4.5.6"}, 1337 }, 1338 { 1339 DNSName: "annodmz.example.org", 1340 RecordType: endpoint.RecordTypeA, 1341 Targets: endpoint.Targets{"4.5.6.7"}, 1342 }, 1343 { 1344 DNSName: "dmz-annoint.example.org", 1345 RecordType: endpoint.RecordTypeA, 1346 Targets: endpoint.Targets{"6.7.8.9"}, 1347 }, 1348 { 1349 DNSName: "empty-annotdmz.example.org", 1350 RecordType: endpoint.RecordTypeA, 1351 Targets: endpoint.Targets{"7.8.9.0"}, 1352 }, 1353 }, 1354 }, 1355 { 1356 ingressLabelSelector: labels.SelectorFromSet(labels.Set{"app": "web-external"}), 1357 title: "ingress with matching labels", 1358 targetNamespace: "", 1359 ingressItems: []fakeIngress{ 1360 { 1361 name: "fake1", 1362 namespace: namespace, 1363 dnsnames: []string{"example.org"}, 1364 ips: []string{"8.8.8.8"}, 1365 labels: map[string]string{"app": "web-external", "name": "reverse-proxy"}, 1366 }, 1367 }, 1368 expected: []*endpoint.Endpoint{ 1369 { 1370 DNSName: "example.org", 1371 RecordType: endpoint.RecordTypeA, 1372 Targets: endpoint.Targets{"8.8.8.8"}, 1373 }, 1374 }, 1375 }, 1376 { 1377 ingressLabelSelector: labels.SelectorFromSet(labels.Set{"app": "web-external"}), 1378 title: "ingress without matching labels", 1379 targetNamespace: "", 1380 ingressItems: []fakeIngress{ 1381 { 1382 name: "fake1", 1383 namespace: namespace, 1384 dnsnames: []string{"example.org"}, 1385 ips: []string{"8.8.8.8"}, 1386 labels: map[string]string{"app": "web-internal", "name": "reverse-proxy"}, 1387 }, 1388 }, 1389 expected: []*endpoint.Endpoint{}, 1390 }, 1391 } { 1392 ti := ti 1393 t.Run(ti.title, func(t *testing.T) { 1394 t.Parallel() 1395 1396 fakeClient := fake.NewSimpleClientset() 1397 for _, item := range ti.ingressItems { 1398 ingress := item.Ingress() 1399 _, err := fakeClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{}) 1400 require.NoError(t, err) 1401 } 1402 1403 if ti.ingressLabelSelector == nil { 1404 ti.ingressLabelSelector = labels.Everything() 1405 } 1406 1407 source, _ := NewIngressSource( 1408 context.TODO(), 1409 fakeClient, 1410 ti.targetNamespace, 1411 ti.annotationFilter, 1412 ti.fqdnTemplate, 1413 ti.combineFQDNAndAnnotation, 1414 ti.ignoreHostnameAnnotation, 1415 ti.ignoreIngressTLSSpec, 1416 ti.ignoreIngressRulesSpec, 1417 ti.ingressLabelSelector, 1418 ti.ingressClassNames, 1419 ) 1420 // Informer cache has all of the ingresses. Retrieve and validate their endpoints. 1421 res, err := source.Endpoints(context.Background()) 1422 if ti.expectError { 1423 require.Error(t, err) 1424 } else { 1425 require.NoError(t, err) 1426 } 1427 validateEndpoints(t, res, ti.expected) 1428 }) 1429 } 1430 } 1431 1432 // ingress specific helper functions 1433 type fakeIngress struct { 1434 dnsnames []string 1435 tlsdnsnames [][]string 1436 ips []string 1437 hostnames []string 1438 namespace string 1439 name string 1440 annotations map[string]string 1441 labels map[string]string 1442 ingressClassName string 1443 } 1444 1445 func (ing fakeIngress) Ingress() *networkv1.Ingress { 1446 ingress := &networkv1.Ingress{ 1447 ObjectMeta: metav1.ObjectMeta{ 1448 Namespace: ing.namespace, 1449 Name: ing.name, 1450 Annotations: ing.annotations, 1451 Labels: ing.labels, 1452 }, 1453 Spec: networkv1.IngressSpec{ 1454 Rules: []networkv1.IngressRule{}, 1455 IngressClassName: &ing.ingressClassName, 1456 }, 1457 Status: networkv1.IngressStatus{ 1458 LoadBalancer: networkv1.IngressLoadBalancerStatus{ 1459 Ingress: []networkv1.IngressLoadBalancerIngress{}, 1460 }, 1461 }, 1462 } 1463 for _, dnsname := range ing.dnsnames { 1464 ingress.Spec.Rules = append(ingress.Spec.Rules, networkv1.IngressRule{ 1465 Host: dnsname, 1466 }) 1467 } 1468 for _, hosts := range ing.tlsdnsnames { 1469 ingress.Spec.TLS = append(ingress.Spec.TLS, networkv1.IngressTLS{ 1470 Hosts: hosts, 1471 }) 1472 } 1473 for _, ip := range ing.ips { 1474 ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, networkv1.IngressLoadBalancerIngress{ 1475 IP: ip, 1476 }) 1477 } 1478 for _, hostname := range ing.hostnames { 1479 ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, networkv1.IngressLoadBalancerIngress{ 1480 Hostname: hostname, 1481 }) 1482 } 1483 return ingress 1484 }