Skip to content

Commit

Permalink
example: kuadrant custom controller with support to multiple gateway …
Browse files Browse the repository at this point in the history
…api providers
  • Loading branch information
guicassolato committed Jul 19, 2024
1 parent c60d200 commit ef27561
Show file tree
Hide file tree
Showing 9 changed files with 815 additions and 199 deletions.
12 changes: 10 additions & 2 deletions examples/kuadrant/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ install: manifests ## Install CRDs into a cluster.

.PHONY: run
run: generate ## Run the controller.
ifneq ($(PROVIDER),)
go run *.go --gateway-provider $(PROVIDER)
ifneq ($(PROVIDERS),)
go run *.go --gateway-providers $(PROVIDERS)
else
go run *.go
endif
Expand All @@ -96,6 +96,14 @@ install-envoy-gateway: helm ## Install Envoy Gateway.
$(HELM) install eg oci://docker.io/envoyproxy/gateway-helm --version v1.0.2 -n envoy-gateway-system --create-namespace
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available

.PHONY: install-istio
install-istio: helm ## Install Istio.
$(HELM) repo add istio https://istio-release.storage.googleapis.com/charts
$(HELM) repo update
kubectl create namespace istio-system
$(HELM) install istio-base istio/base -n istio-system --set defaultRevision=default
$(HELM) install istiod istio/istiod -n istio-system --wait

.PHONY: install-kuadrant
install-kuadrant: ## Install Kuadrant CRDs.
kubectl apply -f config/crds
2 changes: 1 addition & 1 deletion examples/kuadrant/envoy-gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ make install-kuadrant
Run the controller (holds the shell):

```sh
make run PROVIDER=envoygateway
make run PROVIDERS=envoygateway
```

### Create the resources
Expand Down
176 changes: 176 additions & 0 deletions examples/kuadrant/envoy_gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package main

import (
"context"
"log"

egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/utils/ptr"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
gwapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

"github.com/kuadrant/policy-machinery/controller"
"github.com/kuadrant/policy-machinery/machinery"
)

const envoyGatewayProvider = "envoygateway"

var (
_ GatewayProvider = &EnvoyGatewayProvider{}

envoyGatewaySecurityPolicyKind = schema.GroupKind{Group: egv1alpha1.GroupName, Kind: "SecurityPolicy"}
envoyGatewaySecurityPoliciesResource = egv1alpha1.SchemeBuilder.GroupVersion.WithResource("securitypolicies")
)

type EnvoyGatewayProvider struct {
*dynamic.DynamicClient
}

func (p *EnvoyGatewayProvider) ReconcileGateway(topology *machinery.Topology, gateway machinery.Targetable, capabilities map[string][][]machinery.Targetable) {
// check if the gateway is managed by the envoy gateway controller
if !lo.ContainsBy(topology.Targetables().Parents(gateway), func(p machinery.Targetable) bool {
gc, ok := p.(*machinery.GatewayClass)
return ok && gc.Spec.ControllerName == "gateway.envoyproxy.io/gatewayclass-controller"
}) {
return
}

// reconcile envoy gateway securitypolicy resources
if lo.ContainsBy(capabilities["auth"], func(path []machinery.Targetable) bool {
return lo.Contains(lo.Map(path, machinery.MapTargetableToURLFunc), gateway.GetURL())
}) {
p.createSecurityPolicy(topology, gateway)
return
}
p.deleteSecurityPolicy(topology, gateway)
}

func (p *EnvoyGatewayProvider) createSecurityPolicy(topology *machinery.Topology, gateway machinery.Targetable) {
desiredSecurityPolicy := &egv1alpha1.SecurityPolicy{
TypeMeta: metav1.TypeMeta{
APIVersion: egv1alpha1.GroupVersion.String(),
Kind: envoyGatewaySecurityPolicyKind.Kind,
},
ObjectMeta: metav1.ObjectMeta{
Name: gateway.GetName(),
Namespace: gateway.GetNamespace(),
},
Spec: egv1alpha1.SecurityPolicySpec{
PolicyTargetReferences: egv1alpha1.PolicyTargetReferences{
TargetRef: &gwapiv1alpha2.LocalPolicyTargetReferenceWithSectionName{
LocalPolicyTargetReference: gwapiv1alpha2.LocalPolicyTargetReference{
Group: gwapiv1alpha2.GroupName,
Kind: gwapiv1alpha2.Kind("Gateway"),
Name: gwapiv1.ObjectName(gateway.GetName()),
},
},
},
ExtAuth: &egv1alpha1.ExtAuth{
GRPC: &egv1alpha1.GRPCExtAuthService{
BackendRef: &gwapiv1.BackendObjectReference{
Name: gwapiv1.ObjectName("authorino-authorino-authorization"),
Namespace: ptr.To(gwapiv1.Namespace("kuadrant-system")),
Port: ptr.To(gwapiv1.PortNumber(50051)),
},
},
},
},
}

resource := p.Resource(envoyGatewaySecurityPoliciesResource).Namespace(gateway.GetNamespace())

obj, found := lo.Find(topology.Objects().Children(gateway), func(o machinery.Object) bool {
return o.GroupVersionKind().GroupKind() == envoyGatewaySecurityPolicyKind && o.GetNamespace() == gateway.GetNamespace() && o.GetName() == gateway.GetName()
})

if !found {
o, _ := controller.Destruct(desiredSecurityPolicy)
_, err := resource.Create(context.TODO(), o, metav1.CreateOptions{})
if err != nil {
log.Println("failed to create SecurityPolicy", err)
}
return
}

securityPolicy := obj.(*controller.Object).RuntimeObject.(*egv1alpha1.SecurityPolicy)

if securityPolicy.Spec.ExtAuth != nil &&
securityPolicy.Spec.ExtAuth.GRPC != nil &&
securityPolicy.Spec.ExtAuth.GRPC.BackendRef != nil &&
securityPolicy.Spec.ExtAuth.GRPC.BackendRef.Namespace != nil &&
*securityPolicy.Spec.ExtAuth.GRPC.BackendRef.Namespace == *desiredSecurityPolicy.Spec.ExtAuth.GRPC.BackendRef.Namespace &&
securityPolicy.Spec.ExtAuth.GRPC.BackendRef.Name == desiredSecurityPolicy.Spec.ExtAuth.GRPC.BackendRef.Name &&
securityPolicy.Spec.ExtAuth.GRPC.BackendRef.Port != nil &&
*securityPolicy.Spec.ExtAuth.GRPC.BackendRef.Port == *desiredSecurityPolicy.Spec.ExtAuth.GRPC.BackendRef.Port {
return
}

securityPolicy.Spec = desiredSecurityPolicy.Spec
o, _ := controller.Destruct(securityPolicy)
_, err := resource.Update(context.TODO(), o, metav1.UpdateOptions{})
if err != nil {
log.Println("failed to update SecurityPolicy", err)
}
}

func (p *EnvoyGatewayProvider) deleteSecurityPolicy(topology *machinery.Topology, gateway machinery.Targetable) {
_, found := lo.Find(topology.Objects().Children(gateway), func(o machinery.Object) bool {
return o.GroupVersionKind().GroupKind() == envoyGatewaySecurityPolicyKind && o.GetNamespace() == gateway.GetNamespace() && o.GetName() == gateway.GetName()
})

if !found {
return
}

resource := p.Resource(envoyGatewaySecurityPoliciesResource).Namespace(gateway.GetNamespace())
err := resource.Delete(context.TODO(), gateway.GetName(), metav1.DeleteOptions{})
if err != nil {
log.Println("failed to delete SecurityPolicy", err)
}
}

func linkGatewayToEnvoyGatewaySecurityPolicyFunc(objs controller.Store) machinery.LinkFunc {
gatewayKind := schema.GroupKind{Group: gwapiv1.GroupName, Kind: "Gateway"}
gateways := lo.FilterMap(lo.Values(objs[gatewayKind]), func(obj controller.RuntimeObject, _ int) (*gwapiv1.Gateway, bool) {
g, ok := obj.(*gwapiv1.Gateway)
if !ok {
return nil, false
}
return g, true
})

return machinery.LinkFunc{
From: gatewayKind,
To: envoyGatewaySecurityPolicyKind,
Func: func(child machinery.Object) []machinery.Object {
o := child.(*controller.Object)
sp := o.RuntimeObject.(*egv1alpha1.SecurityPolicy)
refs := sp.Spec.PolicyTargetReferences.TargetRefs
if ref := sp.Spec.PolicyTargetReferences.TargetRef; ref != nil {
refs = append(refs, *ref)
}
refs = lo.Filter(refs, func(ref gwapiv1alpha2.LocalPolicyTargetReferenceWithSectionName, _ int) bool {
return ref.Group == gwapiv1.GroupName && ref.Kind == gwapiv1.Kind(gatewayKind.Kind)
})
if len(refs) == 0 {
return nil
}
gateway, ok := lo.Find(gateways, func(g *gwapiv1.Gateway) bool {
if g.GetNamespace() != sp.GetNamespace() {
return false
}
return lo.ContainsBy(refs, func(ref gwapiv1alpha2.LocalPolicyTargetReferenceWithSectionName) bool {
return ref.Name == gwapiv1.ObjectName(g.GetName())
})
})
if ok {
return []machinery.Object{&machinery.Gateway{Gateway: gateway}}
}
return nil
},
}
}
Loading

0 comments on commit ef27561

Please sign in to comment.