Skip to content

Commit 104ee25

Browse files
committed
chore: support hostname in aigatewayroute
1 parent 6f0c41b commit 104ee25

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

internal/controller/ai_gateway_route.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const (
4141
httpRouteAnnotationForAIGatewayGeneratedIndication = egAnnotationPrefix + internalapi.AIGatewayGeneratedHTTPRouteAnnotation
4242
egOwningGatewayNameLabel = egAnnotationPrefix + "owning-gateway-name"
4343
egOwningGatewayNamespaceLabel = egAnnotationPrefix + "owning-gateway-namespace"
44+
// Annotation on AIGatewayRoute to set generated HTTPRoute.Spec.Hostnames (comma-separated list).
45+
aigatewayHTTPRouteHostnamesAnnotation = "aigateway.envoyproxy.io/http-route-hostnames"
4446
// apiKeyInSecret is the key to store OpenAI API key.
4547
apiKeyInSecret = "apiKey"
4648
)
@@ -352,6 +354,25 @@ func (c *AIGatewayRouteController) newHTTPRoute(ctx context.Context, dst *gwapiv
352354
dst.Annotations[httpRouteAnnotationForAIGatewayGeneratedIndication] = "true"
353355

354356
dst.Spec.ParentRefs = aiGatewayRoute.Spec.ParentRefs
357+
358+
// Apply hostnames from annotation if provided.
359+
if aiGatewayRoute.Annotations != nil {
360+
if v, ok := aiGatewayRoute.Annotations[aigatewayHTTPRouteHostnamesAnnotation]; ok {
361+
parts := strings.Split(v, ",")
362+
hostnames := make([]gwapiv1.Hostname, 0, len(parts))
363+
for _, p := range parts {
364+
h := strings.TrimSpace(p)
365+
if h == "" {
366+
continue
367+
}
368+
hostnames = append(hostnames, gwapiv1.Hostname(h))
369+
}
370+
if len(hostnames) > 0 {
371+
c.logger.Info("Applying hostnames from annotation to HTTPRoute", "hostnames", hostnames)
372+
dst.Spec.Hostnames = hostnames
373+
}
374+
}
375+
}
355376
return nil
356377
}
357378

internal/controller/ai_gateway_route_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,95 @@ func Test_newHTTPRoute_LabelAndAnnotationPropagation(t *testing.T) {
555555
require.Equal(t, "ann-value-2", httpRoute.Annotations["custom-annotation-2"])
556556
}
557557

558+
func Test_newHTTPRoute_HostnamesAnnotation_NotSet(t *testing.T) {
559+
c := requireNewFakeClientWithIndexes(t)
560+
561+
// Minimal backend to satisfy newHTTPRoute validation.
562+
backend := &aigv1a1.AIServiceBackend{
563+
ObjectMeta: metav1.ObjectMeta{Name: "test-backend", Namespace: "test-ns"},
564+
Spec: aigv1a1.AIServiceBackendSpec{
565+
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend", Namespace: ptr.To(gwapiv1.Namespace("test-ns"))},
566+
},
567+
}
568+
require.NoError(t, c.Create(context.Background(), backend))
569+
570+
aiGatewayRoute := &aigv1a1.AIGatewayRoute{
571+
ObjectMeta: metav1.ObjectMeta{
572+
Name: "test-route",
573+
Namespace: "test-ns",
574+
},
575+
Spec: aigv1a1.AIGatewayRouteSpec{
576+
Rules: []aigv1a1.AIGatewayRouteRule{
577+
{
578+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
579+
{Name: "test-backend", Weight: ptr.To[int32](100)},
580+
},
581+
},
582+
},
583+
},
584+
}
585+
586+
controller := &AIGatewayRouteController{client: c}
587+
httpRoute := &gwapiv1.HTTPRoute{
588+
ObjectMeta: metav1.ObjectMeta{
589+
Name: "test-route",
590+
Namespace: "test-ns",
591+
},
592+
}
593+
594+
err := controller.newHTTPRoute(context.Background(), httpRoute, aiGatewayRoute)
595+
require.NoError(t, err)
596+
597+
// Without annotation, Hostnames should remain empty.
598+
require.Empty(t, httpRoute.Spec.Hostnames)
599+
}
600+
601+
func Test_newHTTPRoute_HostnamesAnnotation_Set(t *testing.T) {
602+
c := requireNewFakeClientWithIndexes(t)
603+
604+
// Minimal backend to satisfy newHTTPRoute validation.
605+
backend := &aigv1a1.AIServiceBackend{
606+
ObjectMeta: metav1.ObjectMeta{Name: "test-backend", Namespace: "test-ns"},
607+
Spec: aigv1a1.AIServiceBackendSpec{
608+
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend", Namespace: ptr.To(gwapiv1.Namespace("test-ns"))},
609+
},
610+
}
611+
require.NoError(t, c.Create(context.Background(), backend))
612+
613+
aiGatewayRoute := &aigv1a1.AIGatewayRoute{
614+
ObjectMeta: metav1.ObjectMeta{
615+
Name: "test-route",
616+
Namespace: "test-ns",
617+
Annotations: map[string]string{
618+
aigatewayHTTPRouteHostnamesAnnotation: "api.example.com, *.example.net , sub.example.com ,",
619+
},
620+
},
621+
Spec: aigv1a1.AIGatewayRouteSpec{
622+
Rules: []aigv1a1.AIGatewayRouteRule{
623+
{
624+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
625+
{Name: "test-backend", Weight: ptr.To[int32](100)},
626+
},
627+
},
628+
},
629+
},
630+
}
631+
632+
controller := &AIGatewayRouteController{client: c, logger: logr.Discard()}
633+
httpRoute := &gwapiv1.HTTPRoute{
634+
ObjectMeta: metav1.ObjectMeta{
635+
Name: "test-route",
636+
Namespace: "test-ns",
637+
},
638+
}
639+
640+
err := controller.newHTTPRoute(context.Background(), httpRoute, aiGatewayRoute)
641+
require.NoError(t, err)
642+
643+
expected := []gwapiv1.Hostname{"api.example.com", "*.example.net", "sub.example.com"}
644+
require.Equal(t, expected, httpRoute.Spec.Hostnames)
645+
}
646+
558647
func TestAIGatewayRouteController_syncGateways_NamespaceDetermination(t *testing.T) {
559648
fakeClient := requireNewFakeClientWithIndexes(t)
560649
eventCh := internaltesting.NewControllerEventChan[*gwapiv1.Gateway]()

0 commit comments

Comments
 (0)