Skip to content

Commit

Permalink
Gateway API: add support for TCPRoute
Browse files Browse the repository at this point in the history
Updates #5321.

Signed-off-by: Steve Kriss <krisss@vmware.com>
  • Loading branch information
skriss committed Jun 14, 2023
1 parent b487522 commit 2aae894
Show file tree
Hide file tree
Showing 27 changed files with 1,070 additions and 44 deletions.
38 changes: 38 additions & 0 deletions changelogs/unreleased/5471-skriss-minor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Gateway API: add TCPRoute support

Contour now supports Gateway API's [TCPRoute](https://gateway-api.sigs.k8s.io/guides/tcp/) resource.
This route type provides simple TCP forwarding for traffic received on a given Listener port.

This is a simple example of a Gateway and TCPRoute configuration:

```yaml
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: contour
namespace: projectcontour
spec:
gatewayClassName: contour
listeners:
- name: tcp-listener
protocol: TCP
port: 10000
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: echo-1
namespace: default
spec:
parentRefs:
- namespace: projectcontour
name: contour
sectionName: tcp-listener
rules:
- backendRefs:
- name: s1
port: 80
```
10 changes: 9 additions & 1 deletion cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext)
serve.Flag("debug", "Enable debug logging.").Short('d').BoolVar(&ctx.Config.Debug)
serve.Flag("debug-http-address", "Address the debug http endpoint will bind to.").PlaceHolder("<ipaddr>").StringVar(&ctx.debugAddr)
serve.Flag("debug-http-port", "Port the debug http endpoint will bind to.").PlaceHolder("<port>").IntVar(&ctx.debugPort)
serve.Flag("disable-feature", "Do not start an informer for the specified resources.").PlaceHolder("<extensionservices,tlsroutes,grpcroutes>").EnumsVar(&ctx.disabledFeatures, "extensionservices", "tlsroutes", "grpcroutes")
serve.Flag("disable-feature", "Do not start an informer for the specified resources.").PlaceHolder("<extensionservices,tlsroutes,grpcroutes,tcproutes>").EnumsVar(&ctx.disabledFeatures, "extensionservices", "tlsroutes", "grpcroutes", "tcproutes")
serve.Flag("disable-leader-election", "Disable leader election mechanism.").BoolVar(&ctx.LeaderElection.Disable)

serve.Flag("envoy-http-access-log", "Envoy HTTP access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpAccessLog)
Expand Down Expand Up @@ -983,6 +983,7 @@ func (s *Server) setupGatewayAPI(contourConfiguration contour_api_v1alpha1.Conto
features := map[string]struct{}{
"tlsroutes": {},
"grpcroutes": {},
"tcproutes": {},
}
for _, f := range s.ctx.disabledFeatures {
delete(features, f)
Expand All @@ -1007,6 +1008,13 @@ func (s *Server) setupGatewayAPI(contourConfiguration contour_api_v1alpha1.Conto
}
}

// Create and register the TCPRoute controller with the manager.
if _, enabled := features["tcproutes"]; enabled {
if err := controller.RegisterTCPRouteController(s.log.WithField("context", "tcproute-controller"), mgr, eventHandler); err != nil {
s.log.WithError(err).Fatal("failed to create tcproute-controller")
}
}

// Inform on ReferenceGrants.
if err := informOnResource(&gatewayapi_v1beta1.ReferenceGrant{}, eventHandler, mgr.GetCache()); err != nil {
s.log.WithError(err).WithField("resource", "referencegrants").Fatal("failed to create informer")
Expand Down
2 changes: 2 additions & 0 deletions examples/contour/02-role-contour.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -38,6 +39,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
2 changes: 2 additions & 0 deletions examples/gateway-provisioner/01-roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -95,6 +96,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
2 changes: 2 additions & 0 deletions examples/render/contour-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7758,6 +7758,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -7770,6 +7771,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
2 changes: 2 additions & 0 deletions examples/render/contour-gateway-provisioner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16913,6 +16913,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -16932,6 +16933,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
2 changes: 2 additions & 0 deletions examples/render/contour-gateway.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7764,6 +7764,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -7776,6 +7777,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
2 changes: 2 additions & 0 deletions examples/render/contour.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7758,6 +7758,7 @@ rules:
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
verbs:
- get
Expand All @@ -7770,6 +7771,7 @@ rules:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
verbs:
- update
Expand Down
77 changes: 77 additions & 0 deletions internal/controller/tcproute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright Project Contour Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
"context"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
gatewayapi_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

type tcpRouteReconciler struct {
client client.Client
eventHandler cache.ResourceEventHandler
logrus.FieldLogger
}

// RegisterTCPRouteController creates the tcproute controller from mgr. The controller will be pre-configured
// to watch for TCPRoute objects across all namespaces.
func RegisterTCPRouteController(log logrus.FieldLogger, mgr manager.Manager, eventHandler cache.ResourceEventHandler) error {
r := &tcpRouteReconciler{
client: mgr.GetClient(),
eventHandler: eventHandler,
FieldLogger: log,
}
c, err := controller.NewUnmanaged("tcproute-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
if err := mgr.Add(&noLeaderElectionController{c}); err != nil {
return err
}

return c.Watch(source.Kind(mgr.GetCache(), &gatewayapi_v1alpha2.TCPRoute{}), &handler.EnqueueRequestForObject{})
}

func (r *tcpRouteReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {

// Fetch the TCPRoute from the cache.
tcpRoute := &gatewayapi_v1alpha2.TCPRoute{}
err := r.client.Get(ctx, request.NamespacedName, tcpRoute)
if errors.IsNotFound(err) {
r.eventHandler.OnDelete(&gatewayapi_v1alpha2.TCPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: request.Name,
Namespace: request.Namespace,
},
})
return reconcile.Result{}, nil
}

// Pass the new changed object off to the eventHandler.
r.eventHandler.OnAdd(tcpRoute, false)

return reconcile.Result{}, nil
}
4 changes: 4 additions & 0 deletions internal/dag/accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ func (d *DAG) GetClusters() []*Cluster {
var res []*Cluster

for _, listener := range d.Listeners {
if listener.TCPProxy != nil {
res = append(res, listener.TCPProxy.Clusters...)
}

for _, vhost := range listener.VirtualHosts {
for _, route := range vhost.Routes {
res = append(res, route.Clusters...)
Expand Down
4 changes: 4 additions & 0 deletions internal/dag/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ func (b *Builder) Build() *DAG {

listeners[listener.Name] = listener
}

if listener.TCPProxy != nil {
listeners[listener.Name] = listener
}
}

dag.Listeners = listeners
Expand Down
21 changes: 21 additions & 0 deletions internal/dag/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type KubernetesCache struct {
httproutes map[types.NamespacedName]*gatewayapi_v1beta1.HTTPRoute
tlsroutes map[types.NamespacedName]*gatewayapi_v1alpha2.TLSRoute
grpcroutes map[types.NamespacedName]*gatewayapi_v1alpha2.GRPCRoute
tcproutes map[types.NamespacedName]*gatewayapi_v1alpha2.TCPRoute
referencegrants map[types.NamespacedName]*gatewayapi_v1beta1.ReferenceGrant
extensions map[types.NamespacedName]*contour_api_v1alpha1.ExtensionService

Expand All @@ -96,6 +97,7 @@ func (kc *KubernetesCache) init() {
kc.referencegrants = make(map[types.NamespacedName]*gatewayapi_v1beta1.ReferenceGrant)
kc.tlsroutes = make(map[types.NamespacedName]*gatewayapi_v1alpha2.TLSRoute)
kc.grpcroutes = make(map[types.NamespacedName]*gatewayapi_v1alpha2.GRPCRoute)
kc.tcproutes = make(map[types.NamespacedName]*gatewayapi_v1alpha2.TCPRoute)
kc.extensions = make(map[types.NamespacedName]*contour_api_v1alpha1.ExtensionService)
}

Expand Down Expand Up @@ -217,6 +219,10 @@ func (kc *KubernetesCache) Insert(obj any) bool {
kc.grpcroutes[k8s.NamespacedNameOf(obj)] = obj
return kc.routeTriggersRebuild(obj.Spec.ParentRefs), len(kc.grpcroutes)

case *gatewayapi_v1alpha2.TCPRoute:
kc.tcproutes[k8s.NamespacedNameOf(obj)] = obj
return kc.routeTriggersRebuild(obj.Spec.ParentRefs), len(kc.tcproutes)

case *gatewayapi_v1beta1.ReferenceGrant:
kc.referencegrants[k8s.NamespacedNameOf(obj)] = obj
return true, len(kc.referencegrants)
Expand Down Expand Up @@ -363,6 +369,11 @@ func (kc *KubernetesCache) remove(obj any) (bool, int) {
delete(kc.grpcroutes, m)
return kc.routeTriggersRebuild(obj.Spec.ParentRefs), len(kc.grpcroutes)

case *gatewayapi_v1alpha2.TCPRoute:
m := k8s.NamespacedNameOf(obj)
delete(kc.tcproutes, m)
return kc.routeTriggersRebuild(obj.Spec.ParentRefs), len(kc.tcproutes)

case *gatewayapi_v1beta1.ReferenceGrant:
m := k8s.NamespacedNameOf(obj)
_, ok := kc.referencegrants[m]
Expand Down Expand Up @@ -448,6 +459,16 @@ func (kc *KubernetesCache) serviceTriggersRebuild(service *v1.Service) bool {
}
}

for _, route := range kc.tcproutes {
for _, rule := range route.Spec.Rules {
for _, backend := range rule.BackendRefs {
if isRefToService(backend.BackendObjectReference, service, route.Namespace) {
return true
}
}
}
}

return false
}

Expand Down
Loading

0 comments on commit 2aae894

Please sign in to comment.