Skip to content

Commit

Permalink
Merge 46151c9 into 4ea6983
Browse files Browse the repository at this point in the history
  • Loading branch information
rainest committed Apr 18, 2022
2 parents 4ea6983 + 46151c9 commit 9c08059
Show file tree
Hide file tree
Showing 7 changed files with 692 additions and 73 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
annotation is set). They also now support weights to enable more
fine-tuning of the load-balancing between those backend services.
[#2166](https://github.com/Kong/kubernetes-ingress-controller/issues/2166)
- `Gateway` resources now honor [`listener.allowedRoutes.namespaces`
filters](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces).
Note that the unmanaged Kong Gateway implementation populates listeners
automatically based on the Kong Service and Deployment, and user-provided
`allowedRoutes` filters are merged into generated listeners with the same
protocol.
[#2389](https://github.com/Kong/kubernetes-ingress-controller/issues/2389)

## [2.3.1]

Expand Down
115 changes: 78 additions & 37 deletions internal/controllers/gateway/gateway_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
// ManagedGatewaysUnsupported is an error used whenever a failure occurs
// due to a Gateway that is not properly configured for unmanaged mode.
ManagedGatewaysUnsupported = fmt.Errorf("invalid gateway spec: managed gateways are not currently supported") //nolint:revive
gatewayV1alpha2Group = gatewayv1alpha2.Group(gatewayv1alpha2.GroupName)
)

// -----------------------------------------------------------------------------
Expand All @@ -50,7 +51,6 @@ type GatewayReconciler struct { //nolint:revive
PublishService string
WatchNamespaces []string

allowedRoutes gatewayv1alpha2.AllowedRoutes
publishServiceRef types.NamespacedName
}

Expand All @@ -63,38 +63,6 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error {
return err
}

// determine the AllowedRoutes for Gateways
group := gatewayv1alpha2.Group(gatewayv1alpha2.GroupName)
r.allowedRoutes.Kinds = []gatewayv1alpha2.RouteGroupKind{
{
Group: &group,
Kind: gatewayv1alpha2.Kind("HTTPRoute"),
},
{
Group: &group,
Kind: gatewayv1alpha2.Kind("TCPRoute"),
},
{
Group: &group,
Kind: gatewayv1alpha2.Kind("UDPRoute"),
},
}

// determine whether AllowedRoutes will come from ALL namespaces, or specific ones
namespaces := gatewayv1alpha2.RouteNamespaces{}
if len(r.WatchNamespaces) == 0 {
fromAll := gatewayv1alpha2.NamespacesFromAll
namespaces.From = &fromAll
} else {
fromSelector := gatewayv1alpha2.NamespacesFromSelector
namespaces.From = &fromSelector
// TODO: this currently doesn't handle namespace restrictions and instead all
// namespaces are currently allowed. This will be implemented in an
// upcoming iteration.
// See: https://github.com/Kong/kubernetes-ingress-controller/issues/2080
}
r.allowedRoutes.Namespaces = &namespaces

// generate the controller object and attach it to the manager and link the reconciler object
c, err := controller.New("gateway-controller", mgr, controller.Options{
Reconciler: r,
Expand Down Expand Up @@ -322,6 +290,10 @@ func (r *GatewayReconciler) reconcileUnmanagedGateway(ctx context.Context, log l
return ctrl.Result{}, r.Status().Update(ctx, pruneGatewayStatusConds(gateway))
}

if !areAllowedRoutesConsistentByProtocol(gateway.Spec.Listeners) {
return ctrl.Result{}, fmt.Errorf("all listeners for a protocol must use the same AllowedRoutes")
}

// When deployed on Kubernetes Kong can not be relied on for the address data needed for Gateway because
// it's commonly deployed in a way agnostic to the container network (e.g. it's simply configured with
// 0.0.0.0 as the address for its listeners internally). In order to get addresses we have to derive them
Expand All @@ -339,6 +311,8 @@ func (r *GatewayReconciler) reconcileUnmanagedGateway(ctx context.Context, log l
return ctrl.Result{}, err
}

gatewayListeners = mergeAllowedRoutes(gateway.Spec.Listeners, gatewayListeners)

debug(log, gateway, "updating the gateway if any changes to listeners occurred")
isChanged, err := r.updateAddressesAndListenersSpec(ctx, gateway, gatewayAddresses, gatewayListeners)
if err != nil {
Expand Down Expand Up @@ -436,6 +410,10 @@ func (r *GatewayReconciler) determineL4ListenersFromService(
// for all service types we're going to capture the ClusterIP
addresses := make([]gatewayv1alpha2.GatewayAddress, 0, len(svc.Spec.ClusterIPs))
listeners := make([]gatewayv1alpha2.Listener, 0, len(svc.Spec.Ports))
protocolToRouteGroupKind := map[corev1.Protocol]gatewayv1alpha2.RouteGroupKind{
corev1.ProtocolTCP: {Group: &gatewayV1alpha2Group, Kind: gatewayv1alpha2.Kind("TCPRoute")},
corev1.ProtocolUDP: {Group: &gatewayV1alpha2Group, Kind: gatewayv1alpha2.Kind("UDPRoute")},
}
for _, clusterIP := range svc.Spec.ClusterIPs {
addresses = append(addresses, gatewayv1alpha2.GatewayAddress{
Type: &gatewayIPAddrType,
Expand All @@ -444,10 +422,14 @@ func (r *GatewayReconciler) determineL4ListenersFromService(

for _, port := range svc.Spec.Ports {
listeners = append(listeners, gatewayv1alpha2.Listener{
Name: gatewayv1alpha2.SectionName(port.Name),
Protocol: gatewayv1alpha2.ProtocolType(port.Protocol),
Port: gatewayv1alpha2.PortNumber(port.Port),
AllowedRoutes: &r.allowedRoutes,
Name: gatewayv1alpha2.SectionName(port.Name),
Protocol: gatewayv1alpha2.ProtocolType(port.Protocol),
Port: gatewayv1alpha2.PortNumber(port.Port),
AllowedRoutes: &gatewayv1alpha2.AllowedRoutes{
Kinds: []gatewayv1alpha2.RouteGroupKind{
protocolToRouteGroupKind[port.Protocol],
},
},
})
}
}
Expand Down Expand Up @@ -517,13 +499,28 @@ func (r *GatewayReconciler) determineListenersFromDataPlane(ctx context.Context,
if streamListener, ok := streamListenersMap[portMapper[int(listener.Port)]]; ok {
if streamListener.SSL {
listener.Protocol = gatewayv1alpha2.TLSProtocolType
listener.AllowedRoutes = &gatewayv1alpha2.AllowedRoutes{
Kinds: []gatewayv1alpha2.RouteGroupKind{
{Group: &gatewayV1alpha2Group, Kind: gatewayv1alpha2.Kind("TLSRoute")},
},
}
}
}
if proxyListener, ok := proxyListenersMap[portMapper[int(listener.Port)]]; ok {
if proxyListener.SSL {
listener.Protocol = gatewayv1alpha2.HTTPSProtocolType
listener.AllowedRoutes = &gatewayv1alpha2.AllowedRoutes{
Kinds: []gatewayv1alpha2.RouteGroupKind{
{Group: &gatewayV1alpha2Group, Kind: gatewayv1alpha2.Kind("HTTPRoute")},
},
}
} else {
listener.Protocol = gatewayv1alpha2.HTTPProtocolType
listener.AllowedRoutes = &gatewayv1alpha2.AllowedRoutes{
Kinds: []gatewayv1alpha2.RouteGroupKind{
{Group: &gatewayV1alpha2Group, Kind: gatewayv1alpha2.Kind("HTTPRoute")},
},
}
}
}
upgradedListeners = append(upgradedListeners, listener)
Expand Down Expand Up @@ -573,3 +570,47 @@ func (r *GatewayReconciler) updateAddressesAndListenersStatus(
}
return false, nil
}

// mergeAllowedRoutes takes two sets of listeners, scans the source for AllowedRoutes filters, and copies those into
// listeners in the target set that share the same protocol as the source listener. As implemented, it makes no attempt
// to account for same-protocol listeners in the source set having different AllowedRoutes: it assumes
// areAllowedRoutesConsistentByProtocol has been run first because Kong would not support multiple listeners with
// different protocols anyway
func mergeAllowedRoutes(source, target []gatewayv1alpha2.Listener) (merged []gatewayv1alpha2.Listener) {
mappings := make(map[gatewayv1alpha2.ProtocolType]gatewayv1alpha2.RouteNamespaces, len(target))
for _, listener := range source {
mappings[listener.Protocol] = *listener.AllowedRoutes.Namespaces
}
for _, listener := range target {
if namespaces, exists := mappings[listener.Protocol]; exists {
if listener.AllowedRoutes == nil {
listener.AllowedRoutes = &gatewayv1alpha2.AllowedRoutes{}
}
listener.AllowedRoutes.Namespaces = &namespaces
}
merged = append(merged, listener)
}
return merged
}

// areAllowedRoutesConsistentByProtocol returns an error if a set of listeners includes multiple listeners for the same
// protocol that do not use the same AllowedRoutes filters. Kong does not support limiting routes to a specific listen:
// all routes are always served on all listens compatible with their protocol. As such, while we can filter the routes
// we ingest, if we ingest routes from two incompatible filters, we will combine them into a single proxy configuration
// It may be possible to write a new Kong plugin that checks the inbound port/address to de facto apply listen-based
// filters in the future.
func areAllowedRoutesConsistentByProtocol(listeners []gatewayv1alpha2.Listener) bool {
allowedByProtocol := make(map[gatewayv1alpha2.ProtocolType]gatewayv1alpha2.AllowedRoutes)
for _, listener := range listeners {
var allowed gatewayv1alpha2.AllowedRoutes
var exists bool
if allowed, exists = allowedByProtocol[listener.Protocol]; !exists {
allowedByProtocol[listener.Protocol] = *listener.AllowedRoutes
} else {
if !reflect.DeepEqual(allowed, *listener.AllowedRoutes) {
return false
}
}
}
return true
}
Loading

0 comments on commit 9c08059

Please sign in to comment.