Skip to content

Commit

Permalink
Hostname Matching helper created
Browse files Browse the repository at this point in the history
Signed-off-by: Mattia Lavacca <lavacca.mattia@gmail.com>
  • Loading branch information
mlavacca committed Jul 27, 2022
1 parent 2bd0472 commit d9cd14d
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 3 deletions.
22 changes: 21 additions & 1 deletion internal/controllers/gateway/route_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"

"github.com/kong/kubernetes-ingress-controller/v2/internal/util"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -93,6 +94,7 @@ func getSupportedGatewayForRoute(ctx context.Context, mgrc client.Client, obj cl
allowedNamespaces := make(map[string]interface{})
// set true if we find any AllowedRoutes. there may be none, in which case any namespace is permitted
filtered := false
matchingHostname := false
for _, listener := range gateway.Spec.Listeners {
// TODO https://github.com/Kong/kubernetes-ingress-controller/issues/2408
// This currently only performs a baseline filter to ensure that routes cannot match based on namespace
Expand All @@ -101,6 +103,8 @@ func getSupportedGatewayForRoute(ctx context.Context, mgrc client.Client, obj cl
// implementation with default allowed kinds when there's no user-specified filter.
switch obj.(type) {
case *gatewayv1alpha2.HTTPRoute:
hostnames := obj.(*gatewayv1alpha2.HTTPRoute).Spec.Hostnames
matchingHostname = matchHostname(listener, hostnames)
if !(listener.Protocol == gatewayv1alpha2.HTTPProtocolType || listener.Protocol == gatewayv1alpha2.HTTPSProtocolType) {
continue
}
Expand All @@ -113,6 +117,8 @@ func getSupportedGatewayForRoute(ctx context.Context, mgrc client.Client, obj cl
continue
}
case *gatewayv1alpha2.TLSRoute:
hostnames := obj.(*gatewayv1alpha2.TLSRoute).Spec.Hostnames
matchingHostname = matchHostname(listener, hostnames)
if listener.Protocol != gatewayv1alpha2.TLSProtocolType {
continue
}
Expand Down Expand Up @@ -147,7 +153,7 @@ func getSupportedGatewayForRoute(ctx context.Context, mgrc client.Client, obj cl
}

_, allowedNamespace := allowedNamespaces[obj.GetNamespace()]
if !filtered || allowedNamespace {
if (!filtered || allowedNamespace) && matchingHostname {
gateways = append(gateways, &gateway)
}
}
Expand All @@ -161,3 +167,17 @@ func getSupportedGatewayForRoute(ctx context.Context, mgrc client.Client, obj cl

return gateways, nil
}

func matchHostname(listener gatewayv1alpha2.Listener, hostnames []gatewayv1alpha2.Hostname) bool {
if listener.Hostname == nil || *listener.Hostname == "" || len(hostnames) == 0 {
return true
}

for _, hostname := range hostnames {
if util.Match(string(*listener.Hostname), string(hostname)) {
return true
}
}

return false
}
63 changes: 63 additions & 0 deletions internal/util/hostname.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package util

import "strings"

// TODO: comment exported function and code sections
func Match(pattern, value string) bool {
patternParts := strings.Split(pattern, ".")
valueParts := strings.Split(value, ".")

var i, j int
var wildcardj, wildcardi bool
for i, j = 0, 0; i < len(patternParts) && j < len(valueParts); i, j = i+1, j+1 {
var matchFound bool

if wildcardj {
for ; i < len(patternParts); i += 1 {
if patternParts[i] == valueParts[j] {
matchFound = true
break
}
}
if !matchFound {
return false
}
}

if wildcardi {
for ; j < len(valueParts); j += 1 {
if patternParts[i] == valueParts[j] {
matchFound = true
break
}
}
if !matchFound {
return false
}
}

if (wildcardj || wildcardi) && (len(valueParts)-j != len(patternParts)-i) {
return false
}

if patternParts[i] == "*" {
wildcardi = true
continue
}
if valueParts[j] == "*" {
wildcardj = true
continue
}

wildcardi, wildcardj = false, false

if patternParts[i] != valueParts[j] {
return false
}
}
if len(valueParts)-j != len(patternParts)-i {
return false
}

return true
}
83 changes: 83 additions & 0 deletions internal/util/hostname_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package util_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/kong/kubernetes-ingress-controller/v2/internal/util"
)

func TestMatch(t *testing.T) {
for _, tt := range []struct {
name string
pattern string
value string
expected bool
}{
{
name: "same hostname",
pattern: "test.com",
value: "test.com",
expected: true,
},
{
name: "different hostname suffix",
pattern: "test.com",
value: "test.net",
expected: false,
},
{
name: "different hostname prefix",
pattern: "foo.com",
value: "bar.com",
expected: false,
},
{
name: "different hostname lengths with only suffix matching",
pattern: "foo.test.com",
value: "test.com",
expected: false,
},
{
name: "different hostname lengths with only prefix matching",
pattern: "test.test.com",
value: "test.test",
expected: false,
},
{
name: "valid wildcard for one element",
pattern: "*.test.com",
value: "foo.test.com",
expected: true,
},
{
name: "valid wildcard for many elements",
pattern: "*.example.com",
value: "so.many.names.example.com",
expected: true,
},
{
name: "not matching wildcard",
pattern: "*.example.com",
value: "example.com",
expected: false,
},
{
name: "double matching wildcard",
pattern: "*.example.com",
value: "*.example.com",
expected: true,
},
{
name: "double not matching wildcard",
pattern: "*.example.com",
value: "*.example.net",
expected: false,
},
} {
// Test that the functions behave in the same way even swapping the parameters
assert.Equal(t, tt.expected, util.Match(tt.pattern, tt.value), tt.name)
assert.Equal(t, tt.expected, util.Match(tt.value, tt.pattern), tt.name)
}
}
4 changes: 2 additions & 2 deletions test/conformance/gateway_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var enabledGatewayConformanceTests = sets.NewString(
// "HTTPRouteDisallowedKind", //OK
// "HTTPExactPathMatching", //OK
// "HTTPRouteHeaderMatching", //OK
// "HTTPRouteHostnameIntersection", //FAIL
"HTTPRouteHostnameIntersection", //FAIL
// "HTTPRouteInvalidNonExistentBackendRef", //FAIL
// "HTTPRouteInvalidBackendRefUnknownKind", //FAIL
// "HTTPRouteInvalidCrossNamespaceBackendRef", //FAIL
Expand All @@ -90,6 +90,6 @@ var enabledGatewayConformanceTests = sets.NewString(
// "HTTPRouteMatching", //OK
// "HTTPRouteQueryParamMatching", //?
// "HTTPRouteReferenceGrant", //?
"HTTPRouteRequestHeaderModifier", //OK
// "HTTPRouteRequestHeaderModifier", //OK
// "HTTPRouteSimpleSameNamespace", //OK
)

0 comments on commit d9cd14d

Please sign in to comment.