Skip to content

Commit 04bd19b

Browse files
Merge pull request #1213 from grzpiotrowski/NE-1957-gatewayapi-dns-e2e
NE-1957: Add Gateway API DNS Feature e2e tests
2 parents 6897ce1 + d2b8c89 commit 04bd19b

File tree

2 files changed

+464
-0
lines changed

2 files changed

+464
-0
lines changed

test/e2e/gateway_api_test.go

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/openshift/api/features"
14+
iov1 "github.com/openshift/api/operatoringress/v1"
1415
operatorclient "github.com/openshift/cluster-ingress-operator/pkg/operator/client"
1516
operatorcontroller "github.com/openshift/cluster-ingress-operator/pkg/operator/controller"
1617

@@ -101,6 +102,10 @@ func TestGatewayAPI(t *testing.T) {
101102
t.Run("testGatewayAPIObjects", testGatewayAPIObjects)
102103
t.Run("testGatewayAPIManualDeployment", testGatewayAPIManualDeployment)
103104
t.Run("testGatewayAPIIstioInstallation", testGatewayAPIIstioInstallation)
105+
t.Run("testGatewayAPIDNS", testGatewayAPIDNS)
106+
t.Run("testGatewayAPIDNSListenerUpdate", testGatewayAPIDNSListenerUpdate)
107+
t.Run("testGatewayAPIDNSListenerWithNoHostname", testGatewayAPIDNSListenerWithNoHostname)
108+
104109
} else {
105110
t.Log("Gateway API Controller not enabled, skipping controller tests")
106111
}
@@ -423,6 +428,279 @@ func testGatewayAPIRBAC(t *testing.T) {
423428
}
424429
}
425430

431+
func testGatewayAPIDNS(t *testing.T) {
432+
domain := "gws." + dnsConfig.Spec.BaseDomain
433+
434+
gatewayClass, err := createGatewayClass(operatorcontroller.OpenShiftDefaultGatewayClassName, operatorcontroller.OpenShiftGatewayClassControllerName)
435+
if err != nil {
436+
t.Fatalf("failed to create gatewayclass: %v", err)
437+
}
438+
439+
testCases := []struct {
440+
name string
441+
createGateways []testGateway
442+
expectedListenerConditions []metav1.Condition
443+
expectedDNSRecords map[expectedDnsRecord]bool
444+
}{
445+
// TODO: In this case Gateway Listeners should be reported as conflicted. To be fixed in the future release.
446+
{
447+
name: "multipleGatewaysSameListenerHostname",
448+
createGateways: []testGateway{
449+
{gatewayName: "gw1",
450+
namespace: operatorcontroller.DefaultOperandNamespace,
451+
listeners: []testListener{
452+
{
453+
name: "http",
454+
hostname: ptr.To("abc." + domain),
455+
},
456+
},
457+
},
458+
{gatewayName: "gw2",
459+
namespace: operatorcontroller.DefaultOperandNamespace,
460+
listeners: []testListener{
461+
{
462+
name: "http",
463+
hostname: ptr.To("abc." + domain)},
464+
},
465+
},
466+
},
467+
expectedListenerConditions: []metav1.Condition{
468+
{Type: "Accepted", Status: metav1.ConditionTrue},
469+
{Type: "Conflicted", Status: metav1.ConditionFalse},
470+
{Type: "Programmed", Status: metav1.ConditionTrue},
471+
{Type: "ResolvedRefs", Status: metav1.ConditionTrue},
472+
},
473+
expectedDNSRecords: map[expectedDnsRecord]bool{
474+
{dnsName: "abc." + domain + ".", gatewayName: "gw1"}: true,
475+
{dnsName: "abc." + domain + ".", gatewayName: "gw2"}: true,
476+
},
477+
},
478+
{
479+
name: "gatewayListenersWithOverlappingHostname",
480+
createGateways: []testGateway{
481+
{gatewayName: "gw3",
482+
namespace: operatorcontroller.DefaultOperandNamespace,
483+
listeners: []testListener{
484+
{
485+
name: "http",
486+
hostname: ptr.To("qwe." + domain)},
487+
},
488+
},
489+
{gatewayName: "gw4",
490+
namespace: operatorcontroller.DefaultOperandNamespace,
491+
listeners: []testListener{
492+
{
493+
name: "http",
494+
hostname: ptr.To("*." + domain),
495+
},
496+
},
497+
},
498+
},
499+
expectedListenerConditions: []metav1.Condition{
500+
{Type: "Accepted", Status: metav1.ConditionTrue},
501+
{Type: "Conflicted", Status: metav1.ConditionFalse},
502+
{Type: "Programmed", Status: metav1.ConditionTrue},
503+
{Type: "ResolvedRefs", Status: metav1.ConditionTrue},
504+
},
505+
expectedDNSRecords: map[expectedDnsRecord]bool{
506+
{dnsName: "qwe." + domain + ".", gatewayName: "gw3"}: true,
507+
{dnsName: "*." + domain + ".", gatewayName: "gw4"}: true,
508+
},
509+
},
510+
}
511+
512+
for _, tc := range testCases {
513+
t.Run(tc.name, func(t *testing.T) {
514+
var gateways []*gatewayapiv1.Gateway
515+
516+
// Create gateways
517+
for _, gateway := range tc.createGateways {
518+
createdGateway, err := createGatewayWithListeners(gatewayClass, gateway.gatewayName, gateway.namespace, gateway.listeners)
519+
gateways = append(gateways, createdGateway)
520+
if err != nil {
521+
t.Fatalf("failed to create gateway %s: %v", gateway.gatewayName, err)
522+
}
523+
}
524+
525+
t.Cleanup(func() {
526+
for _, gateway := range gateways {
527+
if err := kclient.Delete(context.TODO(), gateway); err != nil {
528+
if errors.IsNotFound(err) {
529+
continue
530+
}
531+
t.Errorf("Failed to delete gateway %q: %v", gateway.Name, err)
532+
}
533+
}
534+
})
535+
536+
for _, gateway := range gateways {
537+
gateway, err := assertGatewaySuccessful(t, operatorcontroller.DefaultOperandNamespace, gateway.Name)
538+
if err != nil {
539+
t.Errorf("Failed to create %s gateway: %v", gateway.Name, err)
540+
}
541+
}
542+
543+
if err := assertExpectedDNSRecords(t, tc.expectedDNSRecords); err != nil {
544+
t.Fatalf("dnsRecord expectations not met: %v", err)
545+
}
546+
547+
t.Logf("Check if gateways listener conditions match expected state.")
548+
for _, gateway := range gateways {
549+
for _, listener := range gateway.Spec.Listeners {
550+
if err := waitForGatewayListenerCondition(t, types.NamespacedName{Namespace: gateway.Namespace, Name: gateway.Name}, string(listener.Name), tc.expectedListenerConditions...); err != nil {
551+
t.Fatalf("did not get expected listener %s condition: %v", listener.Name, err)
552+
} else {
553+
t.Logf("gateway: %s listener %s conditions match expected state.", gateway.Name, listener.Name)
554+
}
555+
}
556+
}
557+
})
558+
}
559+
560+
}
561+
562+
func testGatewayAPIDNSListenerUpdate(t *testing.T) {
563+
gatewayClass, err := createGatewayClass(operatorcontroller.OpenShiftDefaultGatewayClassName, operatorcontroller.OpenShiftGatewayClassControllerName)
564+
if err != nil {
565+
t.Fatalf("failed to create gatewayclass: %v", err)
566+
}
567+
568+
domain := "gws." + dnsConfig.Spec.BaseDomain
569+
570+
gateway, err := createGatewayWithListeners(gatewayClass, "test-gateway-update", operatorcontroller.DefaultOperandNamespace, []testListener{
571+
{name: "http-listener1", hostname: ptr.To("foo." + domain)},
572+
{name: "http-listener2", hostname: ptr.To("bar." + domain)},
573+
})
574+
575+
t.Cleanup(func() {
576+
if err := kclient.Delete(context.TODO(), gateway); err != nil {
577+
if errors.IsNotFound(err) {
578+
return
579+
}
580+
t.Errorf("failed to delete gateway %q: %v", gateway.Name, err)
581+
}
582+
})
583+
584+
if err != nil {
585+
t.Fatalf("failed to create gateway with multiple listeners: %v", err)
586+
}
587+
t.Logf("Created gateway %s with multiple hostnames", gateway.Name)
588+
589+
gateway, err = assertGatewaySuccessful(t, operatorcontroller.DefaultOperandNamespace, "test-gateway-update")
590+
if err != nil {
591+
t.Fatalf("failed to accept/program gateway test-gateway-update: %v", err)
592+
}
593+
594+
if err := assertExpectedDNSRecords(t, map[expectedDnsRecord]bool{
595+
{dnsName: "foo." + domain + ".", gatewayName: "test-gateway-update"}: true,
596+
}); err != nil {
597+
t.Fatalf("DNSRecord %s expectations not met: %v", "foo."+domain+".", err)
598+
}
599+
600+
// Fetch the latest version of the Gateway resource.
601+
if err := kclient.Get(context.TODO(), types.NamespacedName{Namespace: operatorcontroller.DefaultOperandNamespace, Name: gateway.Name}, gateway); err != nil {
602+
t.Fatalf("failed to get gateway resource: %v", err)
603+
}
604+
605+
// Modify one gateway listener hostname
606+
newHostname := gatewayapiv1.Hostname("baz." + domain)
607+
gateway.Spec.Listeners[0].Hostname = &newHostname
608+
609+
if err := kclient.Update(context.TODO(), gateway); err != nil {
610+
t.Fatalf("failed to update gateway %v: %v", gateway.Name, err)
611+
}
612+
t.Logf("Modified gateway %s's listener hostname from %s to: %s", gateway.Name, "foo."+domain+".", "baz."+domain+".")
613+
614+
if err := assertExpectedDNSRecords(t, map[expectedDnsRecord]bool{
615+
{dnsName: "foo." + domain + ".", gatewayName: "test-gateway-update"}: false,
616+
{dnsName: "bar." + domain + ".", gatewayName: "test-gateway-update"}: true,
617+
{dnsName: "baz." + domain + ".", gatewayName: "test-gateway-update"}: true,
618+
}); err != nil {
619+
t.Fatalf("DNSRecord %s expectations not met: %v", "baz."+domain+".", err)
620+
}
621+
622+
// Fetch the latest version of the Gateway resource.
623+
if err := kclient.Get(context.TODO(), types.NamespacedName{Namespace: operatorcontroller.DefaultOperandNamespace, Name: gateway.Name}, gateway); err != nil {
624+
t.Fatalf("failed to get gateway resource: %v", err)
625+
}
626+
627+
// Delete one of the listeners
628+
gateway.Spec.Listeners = gateway.Spec.Listeners[0 : len(gateway.Spec.Listeners)-1]
629+
630+
if err := kclient.Update(context.TODO(), gateway); err != nil {
631+
t.Fatalf("failed to update gateway with removed listener: %v", err)
632+
}
633+
t.Logf("Deleted listener %s from gateway %s.", "http-listener2", gateway.Name)
634+
635+
t.Logf("Checking that the dnsRecord for %s gets removed after removing the listener.", "bar."+domain+".")
636+
637+
if err := assertExpectedDNSRecords(t, map[expectedDnsRecord]bool{
638+
{dnsName: "baz." + domain + ".", gatewayName: "test-gateway-update"}: true,
639+
{dnsName: "bar." + domain + ".", gatewayName: "test-gateway-update"}: false,
640+
}); err != nil {
641+
t.Fatalf("expected bar.%s. to be deleted, but it was not", domain)
642+
}
643+
644+
if err := kclient.Delete(context.TODO(), gateway); err != nil {
645+
t.Errorf("failed to delete gateway %q: %v", gateway.Name, err)
646+
}
647+
648+
t.Logf("Checking the remaining DNSRecord %s gets deleted after gateway deletion.", "baz."+domain+".")
649+
if err := assertExpectedDNSRecords(t, map[expectedDnsRecord]bool{
650+
{dnsName: "baz." + domain + ".", gatewayName: "test-gateway-update"}: false,
651+
}); err != nil {
652+
t.Fatalf("expected baz.%s. to be deleted, but it was not", domain)
653+
}
654+
t.Logf("Confirmed DNSRecord removed after gateway deletion.")
655+
}
656+
657+
func testGatewayAPIDNSListenerWithNoHostname(t *testing.T) {
658+
gatewayClass, err := createGatewayClass(operatorcontroller.OpenShiftDefaultGatewayClassName, operatorcontroller.OpenShiftGatewayClassControllerName)
659+
if err != nil {
660+
t.Fatalf("failed to create gatewayclass: %v", err)
661+
}
662+
663+
gateway, err := createGatewayWithListeners(gatewayClass, "test-nohost-gateway", operatorcontroller.DefaultOperandNamespace, []testListener{
664+
{name: "http-listener-no-host", hostname: nil},
665+
})
666+
667+
t.Cleanup(func() {
668+
if err := kclient.Delete(context.TODO(), gateway); err != nil {
669+
t.Errorf("failed to delete gateway %q: %v", gateway.Name, err)
670+
}
671+
})
672+
673+
if err != nil {
674+
t.Fatalf("failed to create gateway with a listener with no hostname: %v", err)
675+
}
676+
677+
gateway, err = assertGatewaySuccessful(t, operatorcontroller.DefaultOperandNamespace, "test-nohost-gateway")
678+
if err != nil {
679+
t.Fatalf("failed to accept/program gateway test-nohost-gateway: %v", err)
680+
}
681+
682+
t.Logf("Created gateway %s with a listener with no hostname", gateway.Name)
683+
684+
err = wait.PollUntilContextTimeout(context.Background(), 1*time.Second, 1*time.Minute, false, func(context context.Context) (bool, error) {
685+
dnsRecords := &iov1.DNSRecordList{}
686+
// List all DNSRecords from the default operand namespace.
687+
if err := kclient.List(context, dnsRecords, client.InNamespace(operatorcontroller.DefaultOperandNamespace)); err != nil {
688+
t.Logf("failed to list DNSRecords: %v; retrying...", err)
689+
return false, nil
690+
}
691+
692+
for _, record := range dnsRecords.Items {
693+
if record.Labels["gateway.networking.k8s.io/gateway-name"] == gateway.Name {
694+
t.Fatalf("dnsrecord found while not expected: %v", err)
695+
}
696+
}
697+
t.Logf("No DNSRecord for gateway listener with no hostname, continuing polling...")
698+
return false, nil
699+
})
700+
t.Logf("Confirmed no DNSRecord created for gateway listener with no hostname.")
701+
702+
}
703+
426704
// ensureCRDs tests that the Gateway API custom resource definitions exist.
427705
func ensureCRDs(t *testing.T) {
428706
t.Helper()

0 commit comments

Comments
 (0)