diff --git a/pkg/agentgateway/translator/gateway_collection.go b/pkg/agentgateway/translator/gateway_collection.go index 030c7e0b5d9..cc4f957a90e 100644 --- a/pkg/agentgateway/translator/gateway_collection.go +++ b/pkg/agentgateway/translator/gateway_collection.go @@ -194,16 +194,6 @@ func (g ParentInfo) Equals(other ParentInfo) bool { slices.Equal(g.Hostnames, other.Hostnames) } -type GatewayTransformationFunction func(GatewayCollectionConfig) func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) - -type GatewayCollectionConfigOption func(o *GatewayCollectionConfig) - -func WithGatewayTransformationFunc(f GatewayTransformationFunction) GatewayCollectionConfigOption { - return func(o *GatewayCollectionConfig) { - o.transformationFunc = f - } -} - type GatewayCollectionConfig struct { ControllerName string Gateways krt.Collection[*gwv1.Gateway] @@ -227,22 +217,12 @@ func GatewayCollection( krt.StatusCollection[*gwv1.Gateway, gwv1.GatewayStatus], krt.Collection[*GatewayListener], ) { - for _, fn := range opts { - fn(&cfg) - } - cfg.listenerIndex = krt.NewIndex(cfg.ListenerSets, "gatewayParent", func(o ListenerSet) []types.NamespacedName { - return []types.NamespacedName{o.GatewayParent} - }) - if cfg.transformationFunc == nil { - cfg.transformationFunc = GatewaysTransformationFunc - } - + processGatewayCollectionOptions(&cfg, opts...) statusCol, gw := krt.NewStatusManyCollection(cfg.Gateways, cfg.transformationFunc(cfg), cfg.KrtOpts.ToOptions("KubernetesGateway")...) - return statusCol, gw } -func GatewaysTransformationFunc(cfg GatewayCollectionConfig) func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) { +func GatewayTransformationFunc(cfg GatewayCollectionConfig) func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) { return func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) { class := krt.FetchOne(ctx, cfg.GatewayClasses, krt.FilterKey(string(obj.Spec.GatewayClassName))) if class == nil { diff --git a/pkg/agentgateway/translator/option.go b/pkg/agentgateway/translator/option.go new file mode 100644 index 00000000000..6128df8a10d --- /dev/null +++ b/pkg/agentgateway/translator/option.go @@ -0,0 +1,29 @@ +package translator + +import ( + "istio.io/istio/pkg/kube/krt" + "k8s.io/apimachinery/pkg/types" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +type GatewayTransformationFunction func(GatewayCollectionConfig) func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) + +type GatewayCollectionConfigOption func(o *GatewayCollectionConfig) + +func WithGatewayTransformationFunc(f GatewayTransformationFunction) GatewayCollectionConfigOption { + return func(o *GatewayCollectionConfig) { + if f != nil { + o.transformationFunc = f + } + } +} + +func processGatewayCollectionOptions(cfg *GatewayCollectionConfig, opts ...GatewayCollectionConfigOption) { + cfg.listenerIndex = krt.NewIndex(cfg.ListenerSets, "gatewayParent", func(o ListenerSet) []types.NamespacedName { + return []types.NamespacedName{o.GatewayParent} + }) + cfg.transformationFunc = GatewayTransformationFunc + for _, fn := range opts { + fn(cfg) + } +} diff --git a/pkg/agentgateway/translator/option_test.go b/pkg/agentgateway/translator/option_test.go new file mode 100644 index 00000000000..a073017b285 --- /dev/null +++ b/pkg/agentgateway/translator/option_test.go @@ -0,0 +1,51 @@ +package translator + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "istio.io/istio/pkg/kube/krt" + corev1 "k8s.io/api/core/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kgateway-dev/kgateway/v2/pkg/pluginsdk/krtutil" +) + +func TestDefaultGatewayCollectionOptions(t *testing.T) { + cfg := getConfig(t) + processGatewayCollectionOptions(&cfg) + + assert.NotNil(t, cfg.listenerIndex) + assert.NotNil(t, cfg.transformationFunc) +} + +func TestWithGatewayTransformationFunc(t *testing.T) { + called := false + customTransformationFunc := func(cfg GatewayCollectionConfig) func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) { + called = true + return func(ctx krt.HandlerContext, obj *gwv1.Gateway) (*gwv1.GatewayStatus, []*GatewayListener) { + return nil, nil + } + } + + GatewayCollection(getConfig(t), WithGatewayTransformationFunc(customTransformationFunc)) + assert.True(t, called) +} + +func getConfig(t *testing.T) GatewayCollectionConfig { + opts := krtutil.NewKrtOptions(t.Context().Done(), nil) + return GatewayCollectionConfig{ + ControllerName: "random-name", + Gateways: krt.NewStaticCollection[*gwv1.Gateway](nil, nil, opts.ToOptions("Gateways")...), + ListenerSets: krt.NewStaticCollection[ListenerSet](nil, nil, opts.ToOptions("ListenerSets")...), + GatewayClasses: krt.NewStaticCollection[GatewayClass](nil, nil, opts.ToOptions("GatewayClasses")...), + Namespaces: krt.NewStaticCollection[*corev1.Namespace](nil, nil, opts.ToOptions("Namespaces")...), + Grants: ReferenceGrants{ + collection: krt.NewStaticCollection[ReferenceGrant](nil, nil, opts.ToOptions("Grants")...), + index: nil, + }, + Secrets: krt.NewStaticCollection[*corev1.Secret](nil, nil, opts.ToOptions("Secrets")...), + ConfigMaps: krt.NewStaticCollection[*corev1.ConfigMap](nil, nil, opts.ToOptions("ConfigMaps")...), + KrtOpts: opts, + } +} diff --git a/pkg/kgateway/agentgatewaysyncer/option.go b/pkg/kgateway/agentgatewaysyncer/option.go index 8b1cf089915..dd30c4a9684 100644 --- a/pkg/kgateway/agentgatewaysyncer/option.go +++ b/pkg/kgateway/agentgatewaysyncer/option.go @@ -18,6 +18,8 @@ func processAgentgatewaySyncerOptions(opts ...AgentgatewaySyncerOption) *agentga func WithGatewayTransformationFunc(f translator.GatewayTransformationFunction) AgentgatewaySyncerOption { return func(o *agentgatewaySyncerConfig) { - o.GatewayTransformationFunc = f + if f != nil { + o.GatewayTransformationFunc = f + } } } diff --git a/pkg/kgateway/proxy_syncer/option.go b/pkg/kgateway/proxy_syncer/option.go index 977000d191f..f267eeb186c 100644 --- a/pkg/kgateway/proxy_syncer/option.go +++ b/pkg/kgateway/proxy_syncer/option.go @@ -22,6 +22,8 @@ func processStatusSyncerOptions(opts ...StatusSyncerOption) *statusSyncerConfig func WithCustomStatusSync(customSync func(ctx context.Context, rm reports.ReportMap)) StatusSyncerOption { return func(cfg *statusSyncerConfig) { - cfg.CustomStatusSync = customSync + if customSync != nil { + cfg.CustomStatusSync = customSync + } } } diff --git a/pkg/kgateway/proxy_syncer/option_test.go b/pkg/kgateway/proxy_syncer/option_test.go new file mode 100644 index 00000000000..92fdccd27ec --- /dev/null +++ b/pkg/kgateway/proxy_syncer/option_test.go @@ -0,0 +1,19 @@ +package proxy_syncer + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kgateway-dev/kgateway/v2/pkg/pluginsdk" + "github.com/kgateway-dev/kgateway/v2/pkg/reports" +) + +func TestWithCustomStatusSync(t *testing.T) { + customStatusSync := func(ctx context.Context, rm reports.ReportMap) {} + statusSyncer := NewStatusSyncer(nil, pluginsdk.Plugin{}, "controller-name", "agw-controller-name", nil, nil, nil, nil, nil, + WithCustomStatusSync(customStatusSync)) + + assert.NotNil(t, statusSyncer.customStatusSync) +} diff --git a/pkg/krtcollections/option.go b/pkg/krtcollections/option.go new file mode 100644 index 00000000000..8523a446ff9 --- /dev/null +++ b/pkg/krtcollections/option.go @@ -0,0 +1,59 @@ +package krtcollections + +import ( + "istio.io/istio/pkg/kube/krt" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + gwxv1a1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + + "github.com/kgateway-dev/kgateway/v2/pkg/kgateway/wellknown" + "github.com/kgateway-dev/kgateway/v2/pkg/pluginsdk/ir" + krtpkg "github.com/kgateway-dev/kgateway/v2/pkg/utils/krtutil" +) + +type ( + GatewaysForDeployerTransformationFunction func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.GatewayForDeployer + GatewaysForEnvoyTransformationFunction func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.Gateway +) + +type GatewayIndexConfigOption func(o *GatewayIndexConfig) + +func WithGatewayForDeployerTransformationFunc(f GatewaysForDeployerTransformationFunction) GatewayIndexConfigOption { + return func(o *GatewayIndexConfig) { + if f != nil { + o.gatewaysForDeployerTransformationFunc = f + } + } +} + +func WithGatewayForEnvoyTransformationFunc(f GatewaysForEnvoyTransformationFunction) GatewayIndexConfigOption { + return func(o *GatewayIndexConfig) { + if f != nil { + o.gatewaysForEnvoyTransformationFunc = f + } + } +} + +func processGatewayIndexConfig(config *GatewayIndexConfig, opts ...GatewayIndexConfigOption) { + config.byParentRefIndex = krtpkg.UnnamedIndex(config.ListenerSets, func(in *gwxv1a1.XListenerSet) []TargetRefIndexKey { + pRef := in.Spec.ParentRef + ns := strOr(pRef.Namespace, "") + if ns == "" { + ns = in.GetNamespace() + } + // lookup by the root object + return []TargetRefIndexKey{{ + Group: wellknown.GatewayGroup, + Kind: wellknown.GatewayKind, + Name: string(pRef.Name), + Namespace: ns, + // this index intentionally doesn't include sectionName + }} + }) + + config.gatewaysForDeployerTransformationFunc = GatewaysForDeployerTransformationFunc + config.gatewaysForEnvoyTransformationFunc = GatewaysForEnvoyTransformationFunc + + for _, fn := range opts { + fn(config) + } +} diff --git a/pkg/krtcollections/option_test.go b/pkg/krtcollections/option_test.go new file mode 100644 index 00000000000..3114c006c68 --- /dev/null +++ b/pkg/krtcollections/option_test.go @@ -0,0 +1,66 @@ +package krtcollections + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "istio.io/istio/pkg/kube/krt" + "istio.io/istio/pkg/util/smallset" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" + gwxv1a1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + + "github.com/kgateway-dev/kgateway/v2/api/settings" + "github.com/kgateway-dev/kgateway/v2/pkg/pluginsdk/ir" + "github.com/kgateway-dev/kgateway/v2/pkg/pluginsdk/krtutil" +) + +func TestDefaultGatewayCollectionOptions(t *testing.T) { + cfg := getConfig(t) + processGatewayIndexConfig(&cfg) + + assert.NotNil(t, cfg.byParentRefIndex) + assert.NotNil(t, cfg.gatewaysForDeployerTransformationFunc) + assert.NotNil(t, cfg.gatewaysForEnvoyTransformationFunc) +} + +func TestWithGatewayForDeployerTransformationFunc(t *testing.T) { + called := false + customTransformationFunc := func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.GatewayForDeployer { + called = true + return func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.GatewayForDeployer { + return nil + } + } + cfg := getConfig(t) + + NewGatewayIndex(cfg, WithGatewayForDeployerTransformationFunc(customTransformationFunc)) + assert.True(t, called) +} + +func TestWithGatewayForEnvoyTransformationFunc(t *testing.T) { + called := false + customTransformationFunc := func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.Gateway { + called = true + return func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.Gateway { + return nil + } + } + cfg := getConfig(t) + + NewGatewayIndex(cfg, WithGatewayForEnvoyTransformationFunc(customTransformationFunc)) + assert.True(t, called) +} + +func getConfig(t *testing.T) GatewayIndexConfig { + krtOpts := krtutil.NewKrtOptions(t.Context().Done(), nil) + return GatewayIndexConfig{ + KrtOpts: krtOpts, + ControllerNames: smallset.New("controller-name"), + EnvoyControllerName: "envoy-controller-name", + PolicyIndex: NewPolicyIndex(krtOpts, nil, settings.Settings{}), + Gateways: krt.NewStaticCollection[*gwv1.Gateway](nil, nil, krtOpts.ToOptions("Gateways")...), + ListenerSets: krt.NewStaticCollection[*gwxv1a1.XListenerSet](nil, nil, krtOpts.ToOptions("ListenerSets")...), + GatewayClasses: krt.NewStaticCollection[*gwv1.GatewayClass](nil, nil, krtOpts.ToOptions("GatewayClasses")...), + Namespaces: krt.NewStaticCollection[NamespaceMetadata](nil, nil, krtOpts.ToOptions("Namespaces")...), + } +} diff --git a/pkg/krtcollections/policy.go b/pkg/krtcollections/policy.go index edb6dcf1141..fd8e9d26606 100644 --- a/pkg/krtcollections/policy.go +++ b/pkg/krtcollections/policy.go @@ -330,51 +330,6 @@ type GatewayIndex struct { GatewaysForDeployer krt.Collection[ir.GatewayForDeployer] } -type ( - GatewaysForDeployerTransformationFunction func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.GatewayForDeployer - GatewaysForEnvoyTransformationFunction func(config *GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.Gateway -) - -type GatewayIndexConfigOption func(o *GatewayIndexConfig) - -func WithGatewayForDeployerTransformationFunc(f GatewaysForDeployerTransformationFunction) GatewayIndexConfigOption { - return func(o *GatewayIndexConfig) { - o.gatewaysForDeployerTransformationFunc = f - } -} - -func WithGatewayForEnvoyTransformationFunc(f GatewaysForEnvoyTransformationFunction) GatewayIndexConfigOption { - return func(o *GatewayIndexConfig) { - o.gatewaysForEnvoyTransformationFunc = f - } -} - -func NewGatewayIndexConfig(krtOpts krtutil.KrtOptions, - controllerNames smallset.Set[string], - envoyControllerName string, - policyIndex *PolicyIndex, - gateways krt.Collection[*gwv1.Gateway], - listenerSets krt.Collection[*gwxv1a1.XListenerSet], - gatewayClasses krt.Collection[*gwv1.GatewayClass], - namespaces krt.Collection[NamespaceMetadata], - opts ...GatewayIndexConfigOption, -) GatewayIndexConfig { - gwIC := GatewayIndexConfig{ - KrtOpts: krtOpts, - ControllerNames: controllerNames, - EnvoyControllerName: envoyControllerName, - PolicyIndex: policyIndex, - Gateways: gateways, - ListenerSets: listenerSets, - GatewayClasses: gatewayClasses, - Namespaces: namespaces, - } - for _, fn := range opts { - fn(&gwIC) - } - return gwIC -} - type GatewayIndexConfig struct { KrtOpts krtutil.KrtOptions ControllerNames smallset.Set[string] @@ -390,36 +345,10 @@ type GatewayIndexConfig struct { byParentRefIndex krt.Index[TargetRefIndexKey, *gwxv1a1.XListenerSet] } -func processGatewayIndexConfig(config *GatewayIndexConfig) { - config.byParentRefIndex = krtpkg.UnnamedIndex(config.ListenerSets, func(in *gwxv1a1.XListenerSet) []TargetRefIndexKey { - pRef := in.Spec.ParentRef - ns := strOr(pRef.Namespace, "") - if ns == "" { - ns = in.GetNamespace() - } - // lookup by the root object - return []TargetRefIndexKey{{ - Group: wellknown.GatewayGroup, - Kind: wellknown.GatewayKind, - Name: string(pRef.Name), - Namespace: ns, - // this index intentionally doesn't include sectionName - }} - }) - - if config.gatewaysForDeployerTransformationFunc == nil { - config.gatewaysForDeployerTransformationFunc = GatewaysForDeployerTransformationFunc - } - if config.gatewaysForEnvoyTransformationFunc == nil { - config.gatewaysForEnvoyTransformationFunc = GatewaysForEnvoyTransformationFunc - } -} - -func NewGatewayIndex(config GatewayIndexConfig) *GatewayIndex { - processGatewayIndexConfig(&config) +func NewGatewayIndex(config GatewayIndexConfig, opts ...GatewayIndexConfigOption) *GatewayIndex { + processGatewayIndexConfig(&config, opts...) h := &GatewayIndex{} - h.GatewaysForDeployer = krt.NewCollection(config.Gateways, config.gatewaysForDeployerTransformationFunc(&config)) if config.PolicyIndex == nil { return h diff --git a/pkg/pluginsdk/collections/options.go b/pkg/pluginsdk/collections/options.go index 35640b2e342..171ebdce1d6 100644 --- a/pkg/pluginsdk/collections/options.go +++ b/pkg/pluginsdk/collections/options.go @@ -17,12 +17,16 @@ type option struct { func WithGatewayForDeployerTransformationFunc(f func(config *krtcollections.GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.GatewayForDeployer) Option { return func(o *option) { - o.gatewayForDeployerTransformationFunc = f + if f != nil { + o.gatewayForDeployerTransformationFunc = f + } } } func WithGatewayForEnvoyTransformationFunc(f func(config *krtcollections.GatewayIndexConfig) func(kctx krt.HandlerContext, gw *gwv1.Gateway) *ir.Gateway) Option { return func(o *option) { - o.gatewayForEnvoyTransformationFunc = f + if f != nil { + o.gatewayForEnvoyTransformationFunc = f + } } } diff --git a/pkg/pluginsdk/collections/setup.go b/pkg/pluginsdk/collections/setup.go index 480c2554d7f..ad132081596 100644 --- a/pkg/pluginsdk/collections/setup.go +++ b/pkg/pluginsdk/collections/setup.go @@ -60,18 +60,19 @@ func (c *CommonCollections) InitCollections( } } - gateways := krtcollections.NewGatewayIndex(krtcollections.NewGatewayIndexConfig( - c.KrtOpts, - controllerNames, - c.ControllerName, - policies, - kubeRawGateways, - kubeRawListenerSets, - gatewayClasses, - namespaces, + gateways := krtcollections.NewGatewayIndex(krtcollections.GatewayIndexConfig{ + KrtOpts: c.KrtOpts, + ControllerNames: controllerNames, + EnvoyControllerName: c.ControllerName, + PolicyIndex: policies, + Gateways: kubeRawGateways, + ListenerSets: kubeRawListenerSets, + GatewayClasses: gatewayClasses, + Namespaces: namespaces, + }, krtcollections.WithGatewayForDeployerTransformationFunc(c.options.gatewayForDeployerTransformationFunc), krtcollections.WithGatewayForEnvoyTransformationFunc(c.options.gatewayForEnvoyTransformationFunc), - )) + ) if !globalSettings.EnableEnvoy { // For now, the gateway index is used by Agentgateway as well in the deployer