Skip to content

Commit

Permalink
Makes the TCPRoute controller optional (#10305)
Browse files Browse the repository at this point in the history
Signed-off-by: Daneyon Hansen <[email protected]>
  • Loading branch information
danehans authored Nov 12, 2024
1 parent 205f4c0 commit 2731f39
Show file tree
Hide file tree
Showing 23 changed files with 1,051 additions and 104 deletions.
6 changes: 6 additions & 0 deletions changelog/v1.18.0-beta34/issue_10304.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
changelog:
- type: FIX
issueLink: https://github.com/solo-io/gloo/issues/10304
resolvesIssue: false
description: >-
Makes the Gateway API TCPRoute controller optional.
54 changes: 54 additions & 0 deletions pkg/schemes/extended_scheme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package schemes

import (
"fmt"

"github.com/solo-io/gloo/projects/gateway2/wellknown"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"

gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

// AddGatewayV1A2Scheme adds the gwv1a2 scheme to the provided scheme if the TCPRoute CRD exists.
func AddGatewayV1A2Scheme(restConfig *rest.Config, scheme *runtime.Scheme) error {
exists, err := CRDExists(restConfig, gwv1a2.GroupVersion.Group, gwv1a2.GroupVersion.Version, wellknown.TCPRouteKind)
if err != nil {
return fmt.Errorf("error checking if %s CRD exists: %w", wellknown.TCPRouteKind, err)
}

if exists {
if err := gwv1a2.Install(scheme); err != nil {
return fmt.Errorf("error adding Gateway API v1alpha2 to scheme: %w", err)
}
}

return nil
}

// Helper function to check if a CRD exists
func CRDExists(restConfig *rest.Config, group, version, kind string) (bool, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(restConfig)
if err != nil {
return false, err
}

groupVersion := fmt.Sprintf("%s/%s", group, version)
apiResourceList, err := discoveryClient.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) || meta.IsNoMatchError(err) {
return false, nil
}
return false, err
}

for _, apiResource := range apiResourceList.APIResources {
if apiResource.Kind == kind {
return true, nil
}
}

return false, nil
}
12 changes: 11 additions & 1 deletion pkg/schemes/scheme.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package schemes

import (
"fmt"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
Expand All @@ -23,7 +25,6 @@ import (
var SchemeBuilder = runtime.SchemeBuilder{
// K8s Gateway API resources
gwv1.Install,
gwv1a2.Install,
gwv1b1.Install,

// Kubernetes Core resources
Expand Down Expand Up @@ -62,3 +63,12 @@ func DefaultScheme() *runtime.Scheme {
_ = AddToScheme(s)
return s
}

// TestingScheme unconditionally includes the default scheme and the gwv1a2 scheme (which includes TCPRoute).
func TestingScheme() *runtime.Scheme {
s := DefaultScheme()
if err := gwv1a2.Install(s); err != nil {
panic(fmt.Sprintf("Failed to install gwv1a2 scheme: %v", err))
}
return s
}
38 changes: 32 additions & 6 deletions projects/gateway2/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controller

import (
"context"
"errors"
"fmt"

corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -56,6 +57,8 @@ type GatewayConfig struct {
Aws *deployer.AwsInfo

Extensions extensions.K8sGatewayExtensions
// CRDs defines the set of discovered Gateway API CRDs
CRDs sets.Set[string]
}

func NewBaseGatewayController(ctx context.Context, cfg GatewayConfig) error {
Expand Down Expand Up @@ -114,9 +117,26 @@ type controllerBuilder struct {
}

func (c *controllerBuilder) addIndexes(ctx context.Context) error {
return query.IterateIndices(func(obj client.Object, field string, indexer client.IndexerFunc) error {
return c.cfg.Mgr.GetFieldIndexer().IndexField(ctx, obj, field, indexer)
})
var errs []error

// Index for HTTPRoute
if err := c.cfg.Mgr.GetFieldIndexer().IndexField(ctx, &apiv1.HTTPRoute{}, query.HttpRouteTargetField, query.IndexerByObjType); err != nil {
errs = append(errs, err)
}

// Index for ReferenceGrant
if err := c.cfg.Mgr.GetFieldIndexer().IndexField(ctx, &apiv1beta1.ReferenceGrant{}, query.ReferenceGrantFromField, query.IndexerByObjType); err != nil {
errs = append(errs, err)
}

// Conditionally index for TCPRoute
if c.cfg.CRDs.Has(wellknown.TCPRouteCRD) {
if err := c.cfg.Mgr.GetFieldIndexer().IndexField(ctx, &apiv1a2.TCPRoute{}, query.TcpRouteTargetField, query.IndexerByObjType); err != nil {
errs = append(errs, err)
}
}

return errors.Join(errs...)
}

func (c *controllerBuilder) addGwParamsIndexes(ctx context.Context) error {
Expand Down Expand Up @@ -231,7 +251,7 @@ func (c *controllerBuilder) watchGw(ctx context.Context) error {
return fmt.Errorf("object %T is not a client.Object", obj)
}
log.Info("watching gvk as gateway child", "gvk", gvk)
// unless its a service, we don't care about the status
// unless it's a service, we don't care about the status
var opts []builder.OwnsOption
if shouldIgnoreStatusChild(gvk) {
opts = append(opts, builder.WithPredicates(predicate.GenerationChangedPredicate{}))
Expand Down Expand Up @@ -282,7 +302,7 @@ func (c *controllerBuilder) watchCustomResourceDefinitions(_ context.Context) er
return false
}
// Check if the CRD is one we care about
return wellknown.GatewayCRDs.Has(crd.Name)
return c.cfg.CRDs.Has(crd.Name)
}),
)).
For(&apiextensionsv1.CustomResourceDefinition{}).
Expand All @@ -296,7 +316,13 @@ func (c *controllerBuilder) watchHttpRoute(_ context.Context) error {
Complete(reconcile.Func(c.reconciler.ReconcileHttpRoutes))
}

func (c *controllerBuilder) watchTcpRoute(_ context.Context) error {
func (c *controllerBuilder) watchTcpRoute(ctx context.Context) error {
if !c.cfg.CRDs.Has(wellknown.TCPRouteCRD) {
log.FromContext(ctx).Info("TCPRoute type not registered in scheme; skipping TCPRoute controller setup")
return nil
}

// Proceed to set up the controller for TCPRoute
return ctrl.NewControllerManagedBy(c.cfg.Mgr).
WithEventFilter(predicate.GenerationChangedPredicate{}).
For(&apiv1a2.TCPRoute{}).
Expand Down
32 changes: 31 additions & 1 deletion projects/gateway2/controller/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

gatewaykubev1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1"
"github.com/solo-io/gloo/projects/gateway2/deployer"
Expand Down Expand Up @@ -98,9 +99,16 @@ func NewControllerBuilder(ctx context.Context, cfg StartConfig) (*ControllerBuil
}
ctrl.SetLogger(zap.New(opts...))

scheme := glooschemes.DefaultScheme()

// Extend the scheme if the TCPRoute CRD exists.
if err := glooschemes.AddGatewayV1A2Scheme(cfg.RestConfig, scheme); err != nil {
return nil, err
}

mgrOpts := ctrl.Options{
BaseContext: func() context.Context { return ctx },
Scheme: glooschemes.DefaultScheme(),
Scheme: scheme,
PprofBindAddress: "127.0.0.1:9099",
// if you change the port here, also change the port "health" in the helmchart.
HealthProbeBindAddress: ":9093",
Expand Down Expand Up @@ -227,6 +235,12 @@ func (c *ControllerBuilder) Start(ctx context.Context) error {
}
}

// Initialize the set of Gateway API CRDs we care about
crds, err := getGatewayCRDs(c.cfg.RestConfig)
if err != nil {
return err
}

gwCfg := GatewayConfig{
Mgr: c.mgr,
GWClasses: sets.New(append(c.cfg.SetupOpts.ExtraGatewayClasses, wellknown.GatewayClassName)...),
Expand All @@ -241,6 +255,7 @@ func (c *ControllerBuilder) Start(ctx context.Context) error {
Aws: awsInfo,
Kick: c.inputChannels.Kick,
Extensions: c.k8sGwExtensions,
CRDs: crds,
}
if err := NewBaseGatewayController(ctx, gwCfg); err != nil {
setupLog.Error(err, "unable to create controller")
Expand All @@ -249,3 +264,18 @@ func (c *ControllerBuilder) Start(ctx context.Context) error {

return c.mgr.Start(ctx)
}

func getGatewayCRDs(restConfig *rest.Config) (sets.Set[string], error) {
crds := wellknown.GatewayStandardCRDs

tcpRouteExists, err := glooschemes.CRDExists(restConfig, gwv1a2.GroupVersion.Group, gwv1a2.GroupVersion.Version, wellknown.TCPRouteKind)
if err != nil {
return nil, err
}

if tcpRouteExists {
crds.Insert(wellknown.TCPRouteCRD)
}

return crds, nil
}
Loading

0 comments on commit 2731f39

Please sign in to comment.