Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions test/e2e/gateway_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ func TestGatewayAPI(t *testing.T) {
t.Run("testGatewayAPIDNS", testGatewayAPIDNS)
t.Run("testGatewayAPIDNSListenerUpdate", testGatewayAPIDNSListenerUpdate)
t.Run("testGatewayAPIDNSListenerWithNoHostname", testGatewayAPIDNSListenerWithNoHostname)
t.Run("testGatewayAPIInfrastructureAnnotations", testGatewayAPIInfrastructureAnnotations)

} else {
t.Log("Gateway API Controller not enabled, skipping controller tests")
}
Expand Down Expand Up @@ -827,6 +829,117 @@ func testGatewayAPIDNSListenerWithNoHostname(t *testing.T) {
t.Logf("Confirmed no DNSRecord created for gateway listener with no hostname.")
}

func testGatewayAPIInfrastructureAnnotations(t *testing.T) {
// This test uses AWS-specific annotations, so skip on other platforms
if infraConfig.Status.PlatformStatus == nil {
t.Skip("Skipping test: platform status is nil")
}
if infraConfig.Status.PlatformStatus.Type != configv1.AWSPlatformType {
t.Skipf("Skipping test on platform %q: test is specific to AWS", infraConfig.Status.PlatformStatus.Type)
}

gatewayClass, err := createGatewayClass(t, operatorcontroller.OpenShiftDefaultGatewayClassName, operatorcontroller.OpenShiftGatewayClassControllerName)
if err != nil {
t.Fatalf("Failed to create gatewayclass: %v", err)
}

gatewayName := "test-gateway-infra-annotations"

// Create a gateway with infrastructure annotations
// Use a unique wildcard hostname to avoid conflicts with other gateways
gateway := &gatewayapiv1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Name: gatewayName,
Namespace: operatorcontroller.DefaultOperandNamespace,
},
Spec: gatewayapiv1.GatewaySpec{
GatewayClassName: gatewayapiv1.ObjectName(gatewayClass.Name),
Infrastructure: &gatewayapiv1.GatewayInfrastructure{
Annotations: map[gatewayapiv1.AnnotationKey]gatewayapiv1.AnnotationValue{
"service.beta.kubernetes.io/aws-load-balancer-internal": "true",
},
},
Listeners: []gatewayapiv1.Listener{{
Name: "http",
Hostname: ptr.To(gatewayapiv1.Hostname(fmt.Sprintf("*.infra-annotations.%s", dnsConfig.Spec.BaseDomain))),
Port: 80,
Protocol: "HTTP",
}},
},
}

t.Logf("Creating gateway %s with infrastructure annotations...", gatewayName)
if err := createWithRetryOnError(t, context.Background(), gateway, 2*time.Minute); err != nil {
t.Fatalf("Failed to create gateway %s: %v", gatewayName, err)
}

t.Cleanup(func() {
if err := kclient.Delete(context.TODO(), gateway); err != nil {
if errors.IsNotFound(err) {
return
}
t.Errorf("Failed to delete gateway %q: %v", gateway.Name, err)
}
})

// Wait for the gateway to be accepted and programmed
if _, err = assertGatewaySuccessful(t, operatorcontroller.DefaultOperandNamespace, gatewayName); err != nil {
t.Fatalf("Failed to accept/program gateway %s: %v", gatewayName, err)
}

// Wait for the service to be created and verify it has the expected annotation
// Use label selectors to find the service (same approach as the gateway-service-dns controller)
// Istio converts the controller name "openshift.io/gateway-controller/v1" to the label value
// "openshift.io-gateway-controller-v1" by replacing slashes with dashes
managedLabelValue := strings.ReplaceAll(operatorcontroller.OpenShiftGatewayClassControllerName, "/", "-")
interval, timeout := 5*time.Second, 3*time.Minute
t.Logf("Polling for up to %v to verify that service for gateway %q has the expected annotation...", timeout, gatewayName)
if err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, false, func(context context.Context) (bool, error) {
// List services with the gateway labels
var services corev1.ServiceList
listOpts := []client.ListOption{
client.MatchingLabels{
"gateway.istio.io/managed": managedLabelValue,
"gateway.networking.k8s.io/gateway-name": gatewayName,
},
client.InNamespace(gateway.Namespace),
}
if err := kclient.List(context, &services, listOpts...); err != nil {
t.Logf("Failed to list services for gateway %s: %v; retrying...", gatewayName, err)
return false, nil
}

if len(services.Items) == 0 {
t.Logf("No services found for gateway %s yet; retrying...", gatewayName)
return false, nil
}

if len(services.Items) > 1 {
t.Fatalf("Expected 1 service for gateway %s, found %d", gatewayName, len(services.Items))
}

service := services.Items[0]

// Check if the annotation is present on the service
annotationKey := "service.beta.kubernetes.io/aws-load-balancer-internal"
if value, ok := service.Annotations[annotationKey]; ok {
if value == "true" {
t.Logf("Found expected annotation %s=%s on service %s", annotationKey, value, service.Name)
return true, nil
}
t.Logf("Service %s has annotation %s but value is %q, expected %q; retrying...", service.Name, annotationKey, value, "true")
return false, nil
}

t.Logf("Service %s does not have annotation %s yet; retrying...", service.Name, annotationKey)
return false, nil
}); err != nil {
t.Fatalf("Failed to observe the expected annotation on service for gateway %s: %v", gatewayName, err)
}

t.Logf("Successfully verified that infrastructure annotation was propagated to service for gateway %s", gatewayName)
}

// testOperatorDegradedCondition verifies that unmanaged Gateway API CRDs affect
// the ingress cluster operator's Degraded status.
func testOperatorDegradedCondition(t *testing.T) {
Expand Down