Skip to content

Commit

Permalink
bugfix: hostname config in httproute and gateway
Browse files Browse the repository at this point in the history
This fixes a bug where the hostname config isn't
respected when set on a Gateway Listener and
on an HTTPRoute's spec.

Fixes: #30685

Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

Signed-off-by: Christopher Virtucio <cjv287@gmail.com>
  • Loading branch information
cjvirtucio87 committed Feb 19, 2024
1 parent 2ac3f52 commit 96898af
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 0 deletions.
192 changes: 192 additions & 0 deletions operator/pkg/gateway-api/httproute_reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,25 @@ var (
Status: gatewayv1.GatewayStatus{},
},

// Gateway for valid HTTPRoute with hostnames
&gatewayv1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Name: "dummy-gateway-hostnames",
Namespace: "default",
},
Spec: gatewayv1.GatewaySpec{
GatewayClassName: "cilium",
Listeners: []gatewayv1.Listener{
{
Name: "http",
Port: 80,
Hostname: model.AddressOf[gatewayv1.Hostname]("bar.foo.com"),
},
},
},
Status: gatewayv1.GatewayStatus{},
},

// Gateway in another namespace
&gatewayv1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -145,6 +164,7 @@ var (
},
Status: gatewayv1.GatewayStatus{},
},

// Gateway in default namespace
&gatewayv1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -241,6 +261,85 @@ var (
},
},
},

// Valid HTTPRoute with Hostnames
&gatewayv1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-http-route-hostname-service",
Namespace: "default",
},
Spec: gatewayv1.HTTPRouteSpec{
Hostnames: []gatewayv1.Hostname{
"bar.foo.com",
},
CommonRouteSpec: gatewayv1.CommonRouteSpec{
ParentRefs: []gatewayv1.ParentReference{
{
Name: "dummy-gateway-hostnames",
},
},
},
Rules: []gatewayv1.HTTPRouteRule{
{
BackendRefs: []gatewayv1.HTTPBackendRef{
{
BackendRef: gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
Name: "dummy-backend",
Port: model.AddressOf[gatewayv1.PortNumber](8080),
},
},
},
},
},
},
},
},

// Valid HTTPRoute with Hostnames
&gatewayv1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-http-route-hostname-serviceimport",
Namespace: "default",
},
Spec: gatewayv1.HTTPRouteSpec{
Hostnames: []gatewayv1.Hostname{
"bar.foo.com",
},
CommonRouteSpec: gatewayv1.CommonRouteSpec{
ParentRefs: []gatewayv1.ParentReference{
{
Name: "dummy-gateway-hostnames",
},
},
},
Rules: []gatewayv1.HTTPRouteRule{
{
BackendRefs: []gatewayv1.HTTPBackendRef{
{
BackendRef: gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
Name: "dummy-backend",
Port: model.AddressOf[gatewayv1.PortNumber](8080),
},
},
},
{
BackendRef: gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
Group: GroupPtr(mcsapiv1alpha1.GroupName),
Kind: KindPtr("ServiceImport"),
Name: "dummy-backend",
Port: model.AddressOf[gatewayv1.PortNumber](8080),
},
},
},
},
},
},
},
},

&gatewayv1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-http-route-serviceimport",
Expand Down Expand Up @@ -499,6 +598,41 @@ var (
},
},
},
// HTTPRoute with hostnames and cross namespace listener
&gatewayv1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "http-route-with-hostnames-and-cross-namespace-listener",
Namespace: "another-namespace",
},
Spec: gatewayv1.HTTPRouteSpec{
Hostnames: []gatewayv1.Hostname{
"bar.foo.com",
},
CommonRouteSpec: gatewayv1.CommonRouteSpec{
ParentRefs: []gatewayv1.ParentReference{
{
Name: "dummy-gateway-two-listeners",
Namespace: model.AddressOf[gatewayv1.Namespace]("default"),
},
},
},
Rules: []gatewayv1.HTTPRouteRule{
{
BackendRefs: []gatewayv1.HTTPBackendRef{
{
BackendRef: gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
Name: "dummy-backend",
Namespace: model.AddressOf[gatewayv1.Namespace]("another-namespace"),
Port: model.AddressOf(gatewayv1.PortNumber(8080)),
},
},
},
},
},
},
},
},
// HTTPRoute with cross namespace backend
&gatewayv1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -845,6 +979,64 @@ func Test_httpRouteReconciler_Reconcile(t *testing.T) {
}
})

t.Run("valid http route and hostname matches", func(t *testing.T) {
for _, name := range []string{"service", "serviceimport"} {
key := types.NamespacedName{
Name: "valid-http-route-hostname-" + name,
Namespace: "default",
}
result, err := r.Reconcile(context.Background(), ctrl.Request{
NamespacedName: key,
})

require.NoError(t, err, "Error reconciling httpRoute")
require.Equal(t, ctrl.Result{}, result, "Result should be empty")

route := &gatewayv1.HTTPRoute{}
err = c.Get(context.Background(), key, route)

require.NoError(t, err)
require.Len(t, route.Status.RouteStatus.Parents, 1, "Should have 1 parent")
require.Len(t, route.Status.RouteStatus.Parents[0].Conditions, 2)

require.Equal(t, "Accepted", route.Status.RouteStatus.Parents[0].Conditions[0].Type)
require.Equal(t, metav1.ConditionStatus("True"), route.Status.RouteStatus.Parents[0].Conditions[0].Status)
require.Equal(t, "Accepted", route.Status.RouteStatus.Parents[0].Conditions[0].Reason)
require.Equal(t, "Accepted HTTPRoute", route.Status.RouteStatus.Parents[0].Conditions[0].Message)

require.Equal(t, "ResolvedRefs", route.Status.RouteStatus.Parents[0].Conditions[1].Type)
require.Equal(t, metav1.ConditionStatus("True"), route.Status.RouteStatus.Parents[0].Conditions[1].Status)
}
})

t.Run("valid http route and hostname matches cross-namespace", func(t *testing.T) {
key := types.NamespacedName{
Name: "http-route-with-hostnames-and-cross-namespace-listener",
Namespace: "another-namespace",
}
result, err := r.Reconcile(context.Background(), ctrl.Request{
NamespacedName: key,
})

require.NoError(t, err, "Error reconciling httpRoute")
require.Equal(t, ctrl.Result{}, result, "Result should be empty")

route := &gatewayv1.HTTPRoute{}
err = c.Get(context.Background(), key, route)

require.NoError(t, err)
require.Len(t, route.Status.RouteStatus.Parents, 1, "Should have 1 parent")
require.Len(t, route.Status.RouteStatus.Parents[0].Conditions, 2)

require.Equal(t, "Accepted", route.Status.RouteStatus.Parents[0].Conditions[0].Type)
require.Equal(t, metav1.ConditionStatus("True"), route.Status.RouteStatus.Parents[0].Conditions[0].Status)
require.Equal(t, "Accepted", route.Status.RouteStatus.Parents[0].Conditions[0].Reason)
require.Equal(t, "Accepted HTTPRoute", route.Status.RouteStatus.Parents[0].Conditions[0].Message)

require.Equal(t, "ResolvedRefs", route.Status.RouteStatus.Parents[0].Conditions[1].Type)
require.Equal(t, metav1.ConditionStatus("True"), route.Status.RouteStatus.Parents[0].Conditions[1].Status)
})

t.Run("http route with nonexistent backend", func(t *testing.T) {
for _, name := range []string{"svc", "svcimport", "svcimport-svc", "svcimport-svc-annotation"} {
key := types.NamespacedName{
Expand Down
20 changes: 20 additions & 0 deletions operator/pkg/gateway-api/routechecks/gateway_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ func CheckGatewayAllowedForNamespace(input Input, parentRef gatewayv1.ParentRefe

return false, nil
}

hostnames := input.GetHostnames()
allHostnames := make(map[string]bool)
for _, hostname := range hostnames {
allHostnames[string(hostname)] = true
}

hasNamespaceRestriction := false
for _, listener := range gw.Spec.Listeners {

Expand All @@ -34,9 +41,22 @@ func CheckGatewayAllowedForNamespace(input Input, parentRef gatewayv1.ParentRefe
if listener.AllowedRoutes.Namespaces == nil {
continue
}

if parentRef.SectionName != nil && listener.Name != *parentRef.SectionName {
continue
}

if len(allHostnames) > 0 {
if listener.Hostname == nil {
continue
}

listenerHostname := string(*listener.Hostname)
if _, ok := allHostnames[listenerHostname]; !ok {
continue
}
}

// if gateway allows all namespaces, we do not need to check anything here
if *listener.AllowedRoutes.Namespaces.From == gatewayv1.NamespacesFromAll {
return true, nil
Expand Down

0 comments on commit 96898af

Please sign in to comment.