Skip to content

Commit

Permalink
feat(konnect): make KongPlugin reconciler create KongPluginBindings f…
Browse files Browse the repository at this point in the history
…or plugins attached to KongRoutes and KongServices (#644)

* feat(konnect): make KongPlugin reconciler create KongPluginBindings for plugins attached to KongRoutes and KongServices

* refactor: add annotations package with ExtractPlugins() for plugin names extraction based on konghq.com/plugins annotation

* chore: add WithOwnerReference to KongPluginBuilder

---------

Co-authored-by: Grzegorz Burzyński <czeslavo@gmail.com>
  • Loading branch information
pmalek and czeslavo authored Sep 25, 2024
1 parent 1b3c328 commit da533ac
Show file tree
Hide file tree
Showing 18 changed files with 1,351 additions and 180 deletions.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@
- Add `KongService`, `KongRoute`, `KongConsumer`, and `KongConsumerGroup` watchers
in the `KongPluginBinding` reconciler.
[#571](https://github.com/Kong/gateway-operator/pull/571)
- Annotating `KongService`s with the `konghq.com/plugins` annotation results in
the creation of a managed `KongPluginBinding` resource, which is taken by the
`KongPluginBinding` reconciler to create the corresponding plugin object in Konnect.
[#550](https://github.com/Kong/gateway-operator/pull/550)
- Annotating the following resource with the `konghq.com/plugins` annotation results in
the creation of a managed `KongPluginBinding` resource:
- `KongService` [#550](https://github.com/Kong/gateway-operator/pull/550)
- `KongRoute` [#644](https://github.com/Kong/gateway-operator/pull/644)
These `KongPluginBinding`s are taken by the `KongPluginBinding` reconciler
to create the corresponding plugin objects in Konnect.
- `KongConsumer` associated with `ConsumerGroups` is now reconciled in Konnect by removing/adding
the consumer from/to the consumer groups.
[#592](https://github.com/Kong/gateway-operator/pull/592)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
kind: KonnectAPIAuthConfiguration
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: demo-auth
namespace: default
spec:
type: token
token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
serverURL: eu.api.konghq.tech
---
kind: KonnectGatewayControlPlane
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: demo-cp
namespace: default
spec:
name: demo-cp
labels:
app: demo-cp
key1: demo-cp
konnect:
authRef:
name: demo-auth
# namespace not required if APIAuthConfiguration is in the same namespace
---
kind: KongService
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: service-1
namespace: default
spec:
name: service-1
host: example.com
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: demo-cp
---
kind: KongRoute
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: route-1
namespace: default
annotations:
konghq.com/plugins: rate-limit-5-min
spec:
name: route-1
protocols:
- http
hosts:
- example.com
serviceRef:
type: namespacedRef
namespacedRef:
name: service-1
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-5-min
config:
minute: 5
policy: local
plugin: rate-limiting
---
apiVersion: configuration.konghq.com/v1alpha1
kind: KongPluginBinding
metadata:
name: plugin-binding-kongservice-kongroute
spec:
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: demo-cp
pluginRef:
name: rate-limit-5-min
targets:
serviceRef:
name: service-1
kind: KongService
group: configuration.konghq.com
routeRef:
name: route-1
kind: KongRoute
group: configuration.konghq.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
kind: KonnectAPIAuthConfiguration
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: demo-auth
namespace: default
spec:
type: token
token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
serverURL: eu.api.konghq.tech
---
kind: KonnectGatewayControlPlane
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: demo-cp
namespace: default
spec:
name: demo-cp
labels:
app: demo-cp
key1: demo-cp
konnect:
authRef:
name: demo-auth
# namespace not required if APIAuthConfiguration is in the same namespace
---
# This KongPlugin is bound to both the KongService and KongRoute
# hence it will create 1 KongPluginBinding with both of those set as targets.
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-5-min
namespace: default
config:
minute: 5
policy: local
plugin: rate-limiting
---
kind: KongService
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: service-1
namespace: default
annotations:
konghq.com/plugins: rate-limit-5-min
spec:
name: service-1
host: example.com
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: demo-cp
---
kind: KongRoute
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: route-1
namespace: default
annotations:
konghq.com/plugins: rate-limit-5-min
spec:
name: route-1
protocols:
- http
hosts:
- example.com
serviceRef:
type: namespacedRef
namespacedRef:
name: service-1
4 changes: 4 additions & 0 deletions controller/konnect/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func ReconciliationIndexOptionsForEntity[
switch any(e).(type) {
case *configurationv1alpha1.KongPluginBinding:
return IndexOptionsForKongPluginBinding()
case *configurationv1alpha1.KongService:
return IndexOptionsForKongService()
case *configurationv1alpha1.KongRoute:
return IndexOptionsForKongRoute()
case *configurationv1alpha1.KongCredentialBasicAuth:
return IndexOptionsForCredentialsBasicAuth()
case *configurationv1.KongConsumer:
Expand Down
33 changes: 33 additions & 0 deletions controller/konnect/index_kongroute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package konnect

import (
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kong/gateway-operator/pkg/annotations"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

const (
// IndexFieldKongRouteOnReferencedPluginNames is the index field for KongRoute -> KongPlugin.
IndexFieldKongRouteOnReferencedPluginNames = "kongRouteKongPluginRef"
)

// IndexOptionsForKongRoute returns required Index options for KongRoute reconciler.
func IndexOptionsForKongRoute() []ReconciliationIndexOption {
return []ReconciliationIndexOption{
{
IndexObject: &configurationv1alpha1.KongRoute{},
IndexField: IndexFieldKongRouteOnReferencedPluginNames,
ExtractValue: kongRouteUsesPlugins,
},
}
}

func kongRouteUsesPlugins(object client.Object) []string {
route, ok := object.(*configurationv1alpha1.KongRoute)
if !ok {
return nil
}
return annotations.ExtractPlugins(route)
}
34 changes: 34 additions & 0 deletions controller/konnect/index_kongservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package konnect

import (
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kong/gateway-operator/pkg/annotations"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

const (
// IndexFieldKongServiceOnReferencedPluginNames is the index field for KongService -> KongPlugin.
IndexFieldKongServiceOnReferencedPluginNames = "kongServiceKongPluginRef"
)

// IndexOptionsForKongService returns required Index options for KongService reconciler.
func IndexOptionsForKongService() []ReconciliationIndexOption {
return []ReconciliationIndexOption{
{
IndexObject: &configurationv1alpha1.KongService{},
IndexField: IndexFieldKongServiceOnReferencedPluginNames,
ExtractValue: kongServiceUsesPlugins,
},
}
}

func kongServiceUsesPlugins(object client.Object) []string {
svc, ok := object.(*configurationv1alpha1.KongService)
if !ok {
return nil
}

return annotations.ExtractPlugins(svc)
}
92 changes: 92 additions & 0 deletions controller/konnect/kongpluginbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package konnect

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

// KongPluginBindingBuilder helps to build KongPluginBinding objects.
type KongPluginBindingBuilder struct {
binding *configurationv1alpha1.KongPluginBinding
}

// NewKongPluginBindingBuilder creates a new KongPluginBindingBuilder.
func NewKongPluginBindingBuilder() *KongPluginBindingBuilder {
return &KongPluginBindingBuilder{
binding: &configurationv1alpha1.KongPluginBinding{},
}
}

// WithName sets the name of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithName(name string) *KongPluginBindingBuilder {
b.binding.Name = name
return b
}

// WithGenerateName sets the generate name of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithGenerateName(name string) *KongPluginBindingBuilder {
b.binding.GenerateName = name
return b
}

// WithNamespace sets the namespace of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithNamespace(namespace string) *KongPluginBindingBuilder {
b.binding.Namespace = namespace
return b
}

// WithPluginRef sets the plugin reference of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithPluginRef(pluginName string) *KongPluginBindingBuilder {
b.binding.Spec.PluginReference.Name = pluginName
return b
}

// WithControlPlaneRef sets the control plane reference of the KongPluginBinding.
// NOTE: Users have to ensure that the ControlPlaneRef that's set here
// is the same across all the KongPluginBinding targets.
func (b *KongPluginBindingBuilder) WithControlPlaneRef(ref *configurationv1alpha1.ControlPlaneRef) *KongPluginBindingBuilder {
b.binding.Spec.ControlPlaneRef = ref
return b
}

// WithServiceTarget sets the service target of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithServiceTarget(serviceName string) *KongPluginBindingBuilder {
b.binding.Spec.Targets.ServiceReference = &configurationv1alpha1.TargetRefWithGroupKind{
Group: configurationv1alpha1.GroupVersion.Group,
Kind: "KongService",
Name: serviceName,
}
return b
}

// WithRouteTarget sets the route target of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithRouteTarget(routeName string) *KongPluginBindingBuilder {
b.binding.Spec.Targets.RouteReference = &configurationv1alpha1.TargetRefWithGroupKind{
Group: configurationv1alpha1.GroupVersion.Group,
Kind: "KongRoute",
Name: routeName,
}
return b
}

// WithOwnerReference sets the owner reference of the KongPluginBinding.
func (b *KongPluginBindingBuilder) WithOwnerReference(owner client.Object, scheme *runtime.Scheme) (*KongPluginBindingBuilder, error) {
opts := []controllerutil.OwnerReferenceOption{
controllerutil.WithBlockOwnerDeletion(true),
}
if err := controllerutil.SetOwnerReference(owner, b.binding, scheme, opts...); err != nil {
return nil, fmt.Errorf("failed to set owner reference: %w", err)
}

return b, nil
}

// Build returns the KongPluginBinding.
func (b *KongPluginBindingBuilder) Build() *configurationv1alpha1.KongPluginBinding {
return b.binding
}
31 changes: 26 additions & 5 deletions controller/konnect/ops/ops_kongpluginbinding.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,22 @@ func getPluginBindingTargets(
}
targetObjects = append(targetObjects, &kongService)
}
if targets.RouteReference != nil {
if targets.RouteReference.Kind != "KongRoute" {
return nil, fmt.Errorf("unsupported route target kind %q", targets.RouteReference.Kind)
}

kongRoute := configurationv1alpha1.KongRoute{}
kongRoute.SetName(targets.RouteReference.Name)
kongRoute.SetNamespace(pluginBinding.GetNamespace())
if err := cl.Get(ctx, client.ObjectKeyFromObject(&kongRoute), &kongRoute); err != nil {
return nil, err
}
targetObjects = append(targetObjects, &kongRoute)
}

// TODO(mlavacca): add support for KongRoute
// TODO(mlavacca): add support for KongConsumer
// TODO: https://github.com/Kong/gateway-operator/issues/526 add support for KongConsumer
// TODO: https://github.com/Kong/gateway-operator/issues/527 add support for KongConsumerGroup

return targetObjects, nil
}
Expand Down Expand Up @@ -239,10 +252,18 @@ func kongPluginWithTargetsToKongPluginInput(
return nil, fmt.Errorf("KongService %s is not configured in Konnect yet", client.ObjectKeyFromObject(t))
}
pluginInput.Service = &sdkkonnectcomp.PluginService{
ID: lo.ToPtr(t.GetKonnectStatus().ID),
ID: lo.ToPtr(id),
}
case *configurationv1alpha1.KongRoute:
id := t.GetKonnectID()
if id == "" {
return nil, fmt.Errorf("KongRoute %s is not configured in Konnect yet", client.ObjectKeyFromObject(t))
}
pluginInput.Route = &sdkkonnectcomp.PluginRoute{
ID: lo.ToPtr(id),
}
// TODO(mlavacca): add support for KongRoute
// TODO(mlavacca): add support for KongConsumer
// TODO: https://github.com/Kong/gateway-operator/issues/526 add support for KongConsumer
// TODO: https://github.com/Kong/gateway-operator/issues/527 add support for KongConsumerGroup
default:
return nil, fmt.Errorf("unsupported target type %T", t)
}
Expand Down
Loading

0 comments on commit da533ac

Please sign in to comment.