Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ Adding a new version? You'll need three changes:
- CRDs' validations improvements: `UDPIngressRule.Port`, `IngressRule.Port` and `IngressBackend.ServiceName`
instead of being validated in the Parser, are validated by the Kubernetes API now.
[#3136](https://github.com/Kong/kubernetes-ingress-controller/pull/3136)
- Gateway API: Implement port matching for routes as defined in
[GEP-957](https://gateway-api.sigs.k8s.io/geps/gep-957/)
[#3129](https://github.com/Kong/kubernetes-ingress-controller/pull/3129)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday v1.6.0 // indirect
github.com/samber/lo v1.33.0
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shirou/gopsutil/v3 v3.22.8 // indirect
github.com/ssgelm/cookiejarparser v1.0.1 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,8 @@ github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNl
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samber/lo v1.33.0 h1:2aKucr+rQV6gHpY3bpeZu69uYoQOzVhGT3J22Op6Cjk=
github.com/samber/lo v1.33.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
Expand Down Expand Up @@ -729,6 +731,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
Expand Down
2 changes: 1 addition & 1 deletion internal/controllers/gateway/gateway_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ func (r *GatewayReconciler) reconcileUnmanagedGateway(ctx context.Context, log l
// 0.0.0.0 as the address for its listeners internally). In order to get addresses we have to derive them
// from the Kubernetes Service which will also give us all the L4 information about the proxy. From there
// we can use that L4 information to derive the higher level TLS and HTTP,GRPC, e.t.c. information from
// the data-plane's // metadata.
// the data-plane's metadata.
debug(log, gateway, "determining listener configurations from publish service")
kongAddresses, kongListeners, err := r.determineL4ListenersFromService(log, svc)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions internal/controllers/gateway/httproute_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{Requeue: true}, nil
}
}
} else {
// route is not accepted, remove it from kong store
if err := r.DataplaneClient.DeleteObject(httproute); err != nil {
debug(log, httproute, "failed to delete object in data-plane, requeueing")
return ctrl.Result{}, err
}
}

// now that the object has been successfully configured for in the dataplane
Expand Down
80 changes: 68 additions & 12 deletions 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/samber/lo"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -69,6 +70,13 @@ func parentRefsForRoute[T types.RouteT](route T) ([]ParentReference, error) {
}
}

const (
// This reason is used with the "Accepted" condition when the Gateway has no
// compatible Listeners whose Port matches the route
// NOTE: This should probably be proposed upstream.
RouteReasonNoMatchingListenerPort gatewayv1beta1.RouteConditionReason = "NoMatchingListenerPort"
)

// getSupportedGatewayForRoute will retrieve the Gateway and GatewayClass object for any
// Gateway APIs route object (e.g. HTTPRoute, TCPRoute, e.t.c.) from the provided cached
// client if they match this controller. If there are no gateways present for this route
Expand Down Expand Up @@ -125,10 +133,29 @@ func getSupportedGatewayForRoute[T types.RouteT](ctx context.Context, mgrc clien
// should reconcile this object.
if gatewayClass.Spec.ControllerName == ControllerName {
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 := metav1.ConditionFalse
var (
// set true if we find any AllowedRoutes. there may be none, in which case any namespace is permitted
filtered = false
matchingHostname = metav1.ConditionFalse
// set to true if ParentRef specifies a Port and a listener matches that Port.
portMatched = false
)
for _, listener := range gateway.Spec.Listeners {
// TODO check listenerStatus.SupportedKinds

// Check if we already have a matching listener in status.
if !existsMatchingReadyListenerInStatus(listener, gateway.Status.Listeners) {
continue
}

// Perform the port matching as described in GEP-957.
if parentRef.Port != nil && *parentRef.Port != listener.Port {
// This ParentRef has a port specified and it's different than current listener's port.
continue
} else if parentRef.Port != nil && *parentRef.Port == listener.Port {
portMatched = true
}

// 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
// criteria on a listener that cannot possibly handle them (e.g. an HTTPRoute should not be included
Expand All @@ -142,6 +169,7 @@ func getSupportedGatewayForRoute[T types.RouteT](ctx context.Context, mgrc clien
if !(listener.Protocol == HTTPProtocolType || listener.Protocol == HTTPSProtocolType) {
continue
}

case *gatewayv1alpha2.TCPRoute:
if listener.Protocol != (TCPProtocolType) {
continue
Expand Down Expand Up @@ -176,8 +204,7 @@ func getSupportedGatewayForRoute[T types.RouteT](ctx context.Context, mgrc clien
return nil, fmt.Errorf("failed to convert LabelSelector to Selector for gateway %s",
gateway.ObjectMeta.Name)
}
err = mgrc.List(ctx, namespaces,
&client.ListOptions{LabelSelector: selector})
err = mgrc.List(ctx, namespaces, &client.ListOptions{LabelSelector: selector})
if err != nil {
return nil, fmt.Errorf("could not fetch allowed namespaces for gateway %s",
gateway.ObjectMeta.Name)
Expand All @@ -190,22 +217,30 @@ func getSupportedGatewayForRoute[T types.RouteT](ctx context.Context, mgrc clien
}

_, allowedNamespace := allowedNamespaces[route.GetNamespace()]
if !filtered || allowedNamespace {
// if there is no matchingHostname, the gateway Status Condition Accepted must be set to False
// with reason NoMatchingListenerHostname
reason := gatewayv1alpha2.RouteReasonAccepted
if matchingHostname == metav1.ConditionFalse {
reason = gatewayv1alpha2.RouteReasonNoMatchingListenerHostname
if ((parentRef.Port != nil) && !portMatched) ||
(!filtered || allowedNamespace) {

reason := gatewayv1beta1.RouteReasonAccepted
if (parentRef.Port != nil) && !portMatched {
// If ParentRef specified a Port but none of the listeners matched, the gateway Status
// Condition Accepted must be set to False with reason NoMatchingListenerPort
reason = RouteReasonNoMatchingListenerPort
} else if matchingHostname == metav1.ConditionFalse {
// If there is no matchingHostname, the gateway Status Condition Accepted must be set to False
// with reason NoMatchingListenerHostname
reason = gatewayv1beta1.RouteReasonNoMatchingListenerHostname
}

var listenerName string
if parentRef.SectionName != nil && *parentRef.SectionName != "" {
listenerName = string(*parentRef.SectionName)
}

gateways = append(gateways, supportedGatewayWithCondition{
gateway: &gateway,
listenerName: listenerName,
condition: metav1.Condition{
Type: string(gatewayv1alpha2.RouteConditionAccepted),
Type: string(gatewayv1beta1.RouteConditionAccepted),
Status: matchingHostname,
Reason: string(reason),
},
Expand All @@ -223,6 +258,27 @@ func getSupportedGatewayForRoute[T types.RouteT](ctx context.Context, mgrc clien
return gateways, nil
}

func existsMatchingReadyListenerInStatus(listener Listener, lss []ListenerStatus) bool {
// Find listener's status...
listenerStatus, ok := lo.Find(lss, func(ls gatewayv1beta1.ListenerStatus) bool {
return ls.Name == listener.Name
})
if !ok {
return false // Listener's status not found
}
// ... and verify if it's ready.
lReadyCond, ok := lo.Find(listenerStatus.Conditions, func(c metav1.Condition) bool {
return c.Type == string(gatewayv1beta1.ListenerConditionReady)
})
if !ok {
return false
}
if lReadyCond.Status != "True" {
return false // Listener is not ready yet.
}
return true
}

func listenerHostnameIntersectWithRouteHostnames[H types.HostnameT, L types.ListenerT](listener L, hostnames []H) bool {
if len(hostnames) == 0 {
return true
Expand Down
Loading